rlang 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rake_tasks~ +0 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE +373 -0
- data/README.md +61 -0
- data/Rakefile +10 -0
- data/bin/rlang +164 -0
- data/docs/RlangCompiler.md +37 -0
- data/docs/RlangManual.md +391 -0
- data/lib/builder/ext/tempfile.rb +7 -0
- data/lib/builder/ext.rb +5 -0
- data/lib/builder/rlang/builder.rb +31 -0
- data/lib/builder/rlang.rb +2 -0
- data/lib/builder/wat/builder.rb +52 -0
- data/lib/builder/wat/renderer.rb +28 -0
- data/lib/builder/wat.rb +3 -0
- data/lib/builder.rb +7 -0
- data/lib/rlang/lib/malloc.c +97 -0
- data/lib/rlang/lib/malloc.rb +169 -0
- data/lib/rlang/lib/memory.rb +11 -0
- data/lib/rlang/lib/type/i32.rb +7 -0
- data/lib/rlang/lib/type/i64.rb +7 -0
- data/lib/rlang/lib/type.rb +6 -0
- data/lib/rlang/lib/unistd.rb +47 -0
- data/lib/rlang/lib.rb +10 -0
- data/lib/rlang/parser/const.rb +15 -0
- data/lib/rlang/parser/cvar.rb +44 -0
- data/lib/rlang/parser/data.rb +105 -0
- data/lib/rlang/parser/export.rb +22 -0
- data/lib/rlang/parser/ext/integer.rb +5 -0
- data/lib/rlang/parser/ext/string.rb +5 -0
- data/lib/rlang/parser/ext/type.rb +64 -0
- data/lib/rlang/parser/global.rb +65 -0
- data/lib/rlang/parser/lvar.rb +29 -0
- data/lib/rlang/parser/marg.rb +30 -0
- data/lib/rlang/parser/method.rb +76 -0
- data/lib/rlang/parser/wattr.rb +65 -0
- data/lib/rlang/parser/wgenerator.rb +509 -0
- data/lib/rlang/parser/winstruction.rb +148 -0
- data/lib/rlang/parser/wnode.rb +455 -0
- data/lib/rlang/parser/wtree.rb +19 -0
- data/lib/rlang/parser/wtype.rb +116 -0
- data/lib/rlang/parser.rb +1842 -0
- data/lib/rlang/version.rb +3 -0
- data/lib/rlang.rb +4 -0
- data/lib/simul/classes/data.rb +80 -0
- data/lib/simul/classes/global.rb +38 -0
- data/lib/simul/classes/memory.rb +131 -0
- data/lib/utils/log.rb +32 -0
- data/rlang.gemspec +38 -0
- metadata +158 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
|
5
|
+
require_relative '../ext/tempfile'
|
6
|
+
|
7
|
+
module Builder::Wat
|
8
|
+
class Builder
|
9
|
+
|
10
|
+
@@wat_compiler = 'wat2wasm'
|
11
|
+
|
12
|
+
attr_reader :target, :source
|
13
|
+
|
14
|
+
def initialize(source, target, include_paths = nil)
|
15
|
+
check_compiler
|
16
|
+
@source = source
|
17
|
+
@target = target
|
18
|
+
@include_paths = include_paths || ['.', File.expand_path('../../machine', source)]
|
19
|
+
if File.extname(source) == '.erb'
|
20
|
+
@wat_path = self.assemble
|
21
|
+
else
|
22
|
+
@wat_path = source
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def check_compiler
|
27
|
+
raise "wat2wasm compiler not found. Make sure it is in your PATH" \
|
28
|
+
unless system("#{@@wat_compiler} --help >/dev/null")
|
29
|
+
end
|
30
|
+
|
31
|
+
def compile
|
32
|
+
@target ||= @wat_path.gsub(/\.wat$/,'.wasm')
|
33
|
+
%x{ #{@@wat_compiler} #{@wat_path} -o #{@target} }
|
34
|
+
@target
|
35
|
+
end
|
36
|
+
|
37
|
+
def cleanup
|
38
|
+
File.unlink(@wat_path) unless @wat_path == @source
|
39
|
+
end
|
40
|
+
|
41
|
+
# Create a tempfile with .wat extension from
|
42
|
+
# an erb template
|
43
|
+
def assemble
|
44
|
+
renderer = Renderer.new(@include_paths)
|
45
|
+
tf = Tempfile.new([File.basename(@source), '.wat'])
|
46
|
+
tf.persist! # do not delete tempfile if inspection needed
|
47
|
+
tf.write(renderer.render(@source))
|
48
|
+
tf.close
|
49
|
+
tf.path
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
|
5
|
+
require 'pathname'
|
6
|
+
require 'erb'
|
7
|
+
|
8
|
+
module Builder
|
9
|
+
module Wat
|
10
|
+
class Renderer
|
11
|
+
attr_reader :template_path, :binding_klass
|
12
|
+
|
13
|
+
def initialize(template_paths)
|
14
|
+
# an array of load path to consider for erb files
|
15
|
+
@template_paths = template_paths
|
16
|
+
end
|
17
|
+
|
18
|
+
def render(erb_file)
|
19
|
+
path = ''
|
20
|
+
unless Pathname.new(erb_file).absolute?
|
21
|
+
path = @template_paths.find { |tp| File.exist?(File.join(tp, erb_file)) } || ''
|
22
|
+
end
|
23
|
+
#puts "Rendering #{File.join(path, erb_file)}"
|
24
|
+
ERB.new(File.read(File.join(path, erb_file))).result(binding)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/builder/wat.rb
ADDED
data/lib/builder.rb
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
/* Rubinius WebAssembly VM
|
2
|
+
Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
All rights reserved.
|
4
|
+
|
5
|
+
This file is provided for reference and as a point of
|
6
|
+
comparison with the Rlang implementation in malloc.rb
|
7
|
+
*/
|
8
|
+
|
9
|
+
#define NULL 0
|
10
|
+
typedef long Align; /* for alignment to long boundary */
|
11
|
+
union header { /* block header */
|
12
|
+
struct
|
13
|
+
{
|
14
|
+
union header *ptr; /* next block if on free list */
|
15
|
+
unsigned size; /* size of this block */
|
16
|
+
} s;
|
17
|
+
Align x; /* force alignment of blocks */
|
18
|
+
};
|
19
|
+
typedef union header Header;
|
20
|
+
|
21
|
+
static Header base; /* empty list to get started */
|
22
|
+
static Header *freep = NULL; /* start of free list */
|
23
|
+
|
24
|
+
/* malloc: general-purpose storage allocator */
|
25
|
+
void *malloc(unsigned nbytes)
|
26
|
+
{
|
27
|
+
Header *p, *prevp;
|
28
|
+
Header *morecore(unsigned);
|
29
|
+
unsigned nunits;
|
30
|
+
nunits = (nbytes + sizeof(Header) - 1) / sizeof(Header) + 1;
|
31
|
+
if ((prevp = freep) == NULL)
|
32
|
+
{ /* no free list yet */
|
33
|
+
base.s.ptr = freep = prevp = &base;
|
34
|
+
base.s.size = 0;
|
35
|
+
}
|
36
|
+
for (p = prevp->s.ptr;; prevp = p, p = p->s.ptr)
|
37
|
+
{
|
38
|
+
if (p->s.size >= nunits)
|
39
|
+
{ /* big enough */
|
40
|
+
if (p->s.size == nunits) /* exactly */
|
41
|
+
prevp->s.ptr = p->s.ptr;
|
42
|
+
else
|
43
|
+
{ /* allocate tail end */
|
44
|
+
p->s.size -= nunits;
|
45
|
+
p += p->s.size;
|
46
|
+
p->s.size = nunits;
|
47
|
+
}
|
48
|
+
freep = prevp;
|
49
|
+
return (void *)(p + 1);
|
50
|
+
}
|
51
|
+
if (p == freep) /* wrapped around free list */
|
52
|
+
if ((p = morecore(nunits)) == NULL)
|
53
|
+
return NULL; /* none left */
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
#define NALLOC 1024 /* minimum #units to request */
|
58
|
+
/* morecore: ask system for more memory */
|
59
|
+
static Header *morecore(unsigned nu)
|
60
|
+
{
|
61
|
+
char *cp, *sbrk(int);
|
62
|
+
Header *up;
|
63
|
+
if (nu < NALLOC)
|
64
|
+
nu = NALLOC;
|
65
|
+
cp = sbrk(nu * sizeof(Header));
|
66
|
+
if (cp == (char *)-1) /* no space at all */
|
67
|
+
return NULL;
|
68
|
+
up = (Header *)cp;
|
69
|
+
up->s.size = nu;
|
70
|
+
free((void *)(up + 1));
|
71
|
+
return freep;
|
72
|
+
}
|
73
|
+
|
74
|
+
/* free: put block ap in free list */
|
75
|
+
void free(void *ap)
|
76
|
+
{
|
77
|
+
Header *bp, *p;
|
78
|
+
bp = (Header *)ap - 1; /* point to block header */
|
79
|
+
for (p = freep; !(bp > p && bp < p->s.ptr); p = p->s.ptr)
|
80
|
+
if (p >= p->s.ptr && (bp > p || bp < p->s.ptr))
|
81
|
+
break; /* freed block at start or end of arena */
|
82
|
+
if (bp + bp->s.size == p->s.ptr)
|
83
|
+
{ /* join to upper nbr */
|
84
|
+
bp->s.size += p->s.ptr->s.size;
|
85
|
+
bp->s.ptr = p->s.ptr->s.ptr;
|
86
|
+
}
|
87
|
+
else
|
88
|
+
bp->s.ptr = p->s.ptr;
|
89
|
+
if (p + p->s.size == bp)
|
90
|
+
{ /* join to lower nbr */
|
91
|
+
p->s.size += bp->s.size;
|
92
|
+
p->s.ptr = bp->s.ptr;
|
93
|
+
}
|
94
|
+
else
|
95
|
+
p->s.ptr = bp;
|
96
|
+
freep = p;
|
97
|
+
}
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# This is a simple memory allocator derived
|
6
|
+
# from K&R C Language 2nd Edition p 185-189
|
7
|
+
#
|
8
|
+
# For a detailed explanation of the allocator code see
|
9
|
+
# https://gnuchops.wordpress.com/2013/02/26/memory-allocator-for-embedded-system-k-r-ritchie-book/
|
10
|
+
|
11
|
+
require 'rlang/lib/memory'
|
12
|
+
require 'rlang/lib/unistd'
|
13
|
+
|
14
|
+
# These 3 global variables below are used by the
|
15
|
+
# dynamic memory allocator. They must however be
|
16
|
+
# defined in the end applications.
|
17
|
+
|
18
|
+
# Heap base address (make sure it's aligned on
|
19
|
+
# an address compatible with the most restrictive data type
|
20
|
+
# used in WASM (I64). So make this address a multiple of 8
|
21
|
+
# $HEAP = 10024
|
22
|
+
|
23
|
+
# Maximum amount of memory the heap can grow
|
24
|
+
# NOTE: must be less than the upper WASM memory limit (4GB)
|
25
|
+
# $HEAP_MAX_SIZE = 1073741824 # 1GB
|
26
|
+
|
27
|
+
# Current heap size (starts at 0, the first malloc will
|
28
|
+
# set it up)
|
29
|
+
# $HEAP_SIZE = 0
|
30
|
+
|
31
|
+
# minimum number of units to request
|
32
|
+
$NALLOC = 1024
|
33
|
+
|
34
|
+
# Header structure at beginning of each allocated
|
35
|
+
# memory block
|
36
|
+
# (this structure is also called a unit and the memory
|
37
|
+
# allocation will always happen on a boundary aligned
|
38
|
+
# with the most restrictive type of WASM that is to say i64)
|
39
|
+
# As the struct below is 8 byte long and i64 is too our
|
40
|
+
# unit memory allocation size will be 8 bytes here.
|
41
|
+
#
|
42
|
+
# struct {
|
43
|
+
# header *ptr; /* next block if on free list */ => 4 bytes in WASM
|
44
|
+
# unsigned size /* size of this block */ => 4 bytes in WASM
|
45
|
+
# } header;
|
46
|
+
#
|
47
|
+
|
48
|
+
# Allocate some unused memory space to make
|
49
|
+
# sure so that freep doesn't point to memory
|
50
|
+
# address 0 because it has a sepcial meaning
|
51
|
+
# Allocate 20 bytes (5 x I32 integers)
|
52
|
+
DAta[:dummy_malloc_data] = [0, 0, 0, 0, 0]
|
53
|
+
|
54
|
+
class Header
|
55
|
+
wattr :ptr, :size
|
56
|
+
wattr_type ptr: :Header, size: :I32
|
57
|
+
end
|
58
|
+
|
59
|
+
class Malloc
|
60
|
+
|
61
|
+
@@base = Header.new # empty list to get started
|
62
|
+
@@freep = 0.cast_to(:Header) # start of free list
|
63
|
+
|
64
|
+
# declare ahead of time because is used in
|
65
|
+
# the code before it is actually defined
|
66
|
+
result :Malloc, :free, :nil
|
67
|
+
|
68
|
+
# -------- Dynamic Memory Allocator Functions -----------
|
69
|
+
|
70
|
+
# malloc: allocate n bytes of memory and return pointer
|
71
|
+
# to data block
|
72
|
+
def self.malloc(nbytes)
|
73
|
+
local p: :Header, prevp: :Header
|
74
|
+
|
75
|
+
# allocate memory by chunk of units (the unit is
|
76
|
+
# the size of a Header object here)
|
77
|
+
# units = (nbytes+sizeof(Header)-1)/sizeof(Header) + 1;
|
78
|
+
nunits = (nbytes + Header.size - 1) / Header.size + 1
|
79
|
+
|
80
|
+
# No free list yet. Initialize it.
|
81
|
+
if (prevp = @@freep) == 0
|
82
|
+
@@base.ptr = @@freep = prevp = @@base
|
83
|
+
@@base.size = 0
|
84
|
+
end
|
85
|
+
|
86
|
+
# scan the free list for a big enough free block
|
87
|
+
# (first in list is taken)
|
88
|
+
p = prevp.ptr
|
89
|
+
while true
|
90
|
+
if (p.size >= nunits) # big enough
|
91
|
+
if (p.size == nunits)
|
92
|
+
# exactly the requested size
|
93
|
+
prevp.ptr = p.ptr
|
94
|
+
else
|
95
|
+
# bigger free block found
|
96
|
+
# Allocate tail end
|
97
|
+
p.size -= nunits
|
98
|
+
p += p.size
|
99
|
+
p.size = nunits
|
100
|
+
end
|
101
|
+
@@freep = prevp
|
102
|
+
# TODO: we should actually cast to default WASM type
|
103
|
+
return (p + 1).cast_to(:I32)
|
104
|
+
end
|
105
|
+
|
106
|
+
# wrapped around free list
|
107
|
+
if p == @@freep
|
108
|
+
if (p = self.morecore(nunits)) == 0
|
109
|
+
return 0
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
prevp = p; p = p.ptr
|
114
|
+
end
|
115
|
+
|
116
|
+
# Rlang specific: remember that while construct
|
117
|
+
# doesn't evaluate to a value so we must add an
|
118
|
+
# explicit return here.
|
119
|
+
# However we should **never** get there so return NULL
|
120
|
+
return 0
|
121
|
+
end
|
122
|
+
|
123
|
+
# morecore: ask system for more memory
|
124
|
+
def self.morecore(nu)
|
125
|
+
result :Header
|
126
|
+
local up: :Header
|
127
|
+
|
128
|
+
nu = $NALLOC if nu < $NALLOC
|
129
|
+
cp = Unistd::sbrk(nu * Header.size)
|
130
|
+
return 0 if cp == -1 # no space at all
|
131
|
+
|
132
|
+
up = cp.cast_to(:Header)
|
133
|
+
up.size = nu
|
134
|
+
self.free((up + 1).to_I32)
|
135
|
+
return @@freep
|
136
|
+
end
|
137
|
+
|
138
|
+
# Free memory block
|
139
|
+
def self.free(ap)
|
140
|
+
result :none
|
141
|
+
local bp: :Header, p: :Header
|
142
|
+
|
143
|
+
bp = ap.cast_to(:Header) - 1 # point to block header
|
144
|
+
p = @@freep
|
145
|
+
while !(bp > p && bp < p.ptr)
|
146
|
+
# freed block at start or end of arena
|
147
|
+
break if (p >= p.ptr && (bp > p || bp < p.ptr))
|
148
|
+
p = p.ptr
|
149
|
+
end
|
150
|
+
|
151
|
+
if (bp + bp.size == p.ptr)
|
152
|
+
# join to upper nbr
|
153
|
+
bp.size += p.ptr.size
|
154
|
+
bp.ptr = p.ptr.ptr
|
155
|
+
else
|
156
|
+
bp.ptr = p.ptr
|
157
|
+
end
|
158
|
+
|
159
|
+
if (p + p.size == bp)
|
160
|
+
# join to lower nbr
|
161
|
+
p.size += bp.size
|
162
|
+
p.ptr = bp.ptr
|
163
|
+
else
|
164
|
+
p.ptr = bp
|
165
|
+
end
|
166
|
+
@@freep = p
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
|
2
|
+
# Rubinius WebAssembly VM
|
3
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# mimic the sbrk Unix function, requesting n more bytes
|
7
|
+
# sbrk(n) returns the address of the allocated block or -1 if it failed
|
8
|
+
# srbk(0) returns the current value of the break
|
9
|
+
# Note: in WASM we can only grow linear memory by pages (64 KB block)
|
10
|
+
class Unistd
|
11
|
+
def self.sbrk(n)
|
12
|
+
# local variables used. All default type
|
13
|
+
# wasm_mem_size: current wasm memory size (in bytes)
|
14
|
+
# heap_break: current heap break
|
15
|
+
#
|
16
|
+
# new_heap_size: new heap size (HEAP_SIZE + n)
|
17
|
+
# new_heap_break: new heap break (HEAP + HEAP_SIZE + n)
|
18
|
+
# more_pages: how many more WASM pages are needed
|
19
|
+
|
20
|
+
# return current break if n is 0
|
21
|
+
heap_break = $HEAP + $HEAP_SIZE
|
22
|
+
return heap_break if n == 0
|
23
|
+
|
24
|
+
# check if new heap size is beyond authorized limit
|
25
|
+
new_heap_size = $HEAP_SIZE + n
|
26
|
+
return -1 if new_heap_size >= $HEAP_MAX_SIZE
|
27
|
+
|
28
|
+
# We are good, so now check if we need more WASM
|
29
|
+
# memory pages (1 page = 64 KB)
|
30
|
+
wasm_mem_size = Memory.size * 65536
|
31
|
+
new_heap_break = heap_break + n
|
32
|
+
|
33
|
+
# need to grow memory? if so by how many WASM pages ?
|
34
|
+
if new_heap_break >= wasm_mem_size
|
35
|
+
more_pages = (new_heap_break - wasm_mem_size) / 65536 + 1
|
36
|
+
end
|
37
|
+
|
38
|
+
# go grow WASM memory by so many pages. Return -1 if it fails
|
39
|
+
return -1 if Memory.grow(more_pages) == -1
|
40
|
+
|
41
|
+
# set new heap size
|
42
|
+
$HEAP_SIZE = new_heap_size
|
43
|
+
|
44
|
+
# heap_break is the address where the new free space starts
|
45
|
+
return heap_break
|
46
|
+
end
|
47
|
+
end
|
data/lib/rlang/lib.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019-2020, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
|
5
|
+
# Rlang standard library
|
6
|
+
|
7
|
+
require_relative './lib/type'
|
8
|
+
require_relative './lib/memory'
|
9
|
+
require_relative './lib/unistd'
|
10
|
+
require_relative './lib/malloc'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
|
5
|
+
# Constant variables
|
6
|
+
|
7
|
+
require_relative './ext/type'
|
8
|
+
require_relative './cvar'
|
9
|
+
|
10
|
+
# Constants and Class variables are managed
|
11
|
+
# in exactly the same way
|
12
|
+
module Rlang::Parser
|
13
|
+
class Const < CVar
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Rubinius WebAssembly VM
|
2
|
+
# Copyright (c) 2019, Laurent Julliard and contributors
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Class variables
|
6
|
+
# Note: Const class inherits from this class
|
7
|
+
|
8
|
+
require_relative '../../utils/log'
|
9
|
+
require_relative './wtype'
|
10
|
+
require_relative './data'
|
11
|
+
|
12
|
+
module Rlang::Parser
|
13
|
+
class CVar
|
14
|
+
include Log
|
15
|
+
attr_reader :name, :class_name
|
16
|
+
attr_accessor :wtype
|
17
|
+
|
18
|
+
def initialize(class_name, name, value=0, wtype=WType::DEFAULT)
|
19
|
+
@name = name
|
20
|
+
@class_name = class_name
|
21
|
+
@wtype = wtype
|
22
|
+
# Allocate and initialize the new cvar
|
23
|
+
raise "Error: Class variable #{self.wasm_name} already created!" if DAta.exist? self.wasm_name.to_sym
|
24
|
+
@data = DAta.new(self.wasm_name.to_sym, value, wtype)
|
25
|
+
logger.debug "creating #{self.class} #{class_name}::#{name} @ #{@address} with value #{value} / wtype #{wtype}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def address
|
29
|
+
@data.address
|
30
|
+
end
|
31
|
+
|
32
|
+
def value
|
33
|
+
@data.value
|
34
|
+
end
|
35
|
+
|
36
|
+
def wasm_name
|
37
|
+
"$#{@class_name}::#{@name}"
|
38
|
+
end
|
39
|
+
|
40
|
+
def wasm_type
|
41
|
+
@wtype.wasm_type
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require_relative '../../utils/log'
|
2
|
+
require_relative './ext/string'
|
3
|
+
require_relative './wtype'
|
4
|
+
require_relative './ext/integer'
|
5
|
+
|
6
|
+
|
7
|
+
# Don't use class name Data because it's a deprecated
|
8
|
+
# Ruby class and it generates warning at runtime
|
9
|
+
module Rlang::Parser
|
10
|
+
class DAta
|
11
|
+
include Log
|
12
|
+
|
13
|
+
TMPL = '(data 0 (i32.const %{addr}) "%{value}") ;; %{comment}'
|
14
|
+
|
15
|
+
@@label_table = {}
|
16
|
+
@@current_address = 0
|
17
|
+
|
18
|
+
attr_reader :label, :wtype, :address, :value
|
19
|
+
|
20
|
+
def initialize(label, value, wtype=WType::DEFAULT)
|
21
|
+
raise "Data label '#{label}' already initialized" \
|
22
|
+
if self.class.exist? label
|
23
|
+
@label = label
|
24
|
+
@wtype = wtype
|
25
|
+
@address = @@current_address
|
26
|
+
@@label_table[@label] = self
|
27
|
+
@value = []
|
28
|
+
self.append_value(value, wtype)
|
29
|
+
logger.debug "New Data[#{@label}] initialized with #{@value} at address #{@address}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def append_value(value, wtype)
|
33
|
+
@value << value
|
34
|
+
if value.is_a?(String)
|
35
|
+
@@current_address += value.length
|
36
|
+
else
|
37
|
+
logger.warn "Data type #{@wtype} misaligned!!! (Data[:#{@label}] value #{value} at address #{@address}" \
|
38
|
+
unless self.aligned?
|
39
|
+
@@current_address += @wtype.size
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def aligned?
|
44
|
+
(@@current_address % @wtype.size) == 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.exist?(label)
|
48
|
+
@@label_table.has_key? label
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.[](label)
|
52
|
+
raise "Unknown data label '#{label}'" unless self.exist? label
|
53
|
+
@@label_table[label].address
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.append(label, value, wtype=WType::DEFAULT)
|
57
|
+
logger.debug "appending #{value} to DAta[#{label}]"
|
58
|
+
if self.exist? label
|
59
|
+
@@label_table[label].append_value(value, wtype)
|
60
|
+
else
|
61
|
+
self.new(label, value, wtype)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.current_address=(address)
|
66
|
+
@@current_address = address
|
67
|
+
end
|
68
|
+
|
69
|
+
# Align current address to closest multiple of
|
70
|
+
# n by higher value
|
71
|
+
def self.align(n)
|
72
|
+
if (m = @@current_address % n) != 0
|
73
|
+
@@current_address = (@@current_address - m) + n
|
74
|
+
end
|
75
|
+
logger.debug "Aligning current address to #{@@current_address}"
|
76
|
+
@@current_address
|
77
|
+
end
|
78
|
+
|
79
|
+
# Transpile data to WAT code
|
80
|
+
# in order of increasing address
|
81
|
+
def self.transpile
|
82
|
+
output = []
|
83
|
+
@@label_table.sort_by {|s,d| d.address}.each do |s,d|
|
84
|
+
logger.debug "Generating data #{d.inspect}"
|
85
|
+
address = d.address
|
86
|
+
d.value.each do |elt|
|
87
|
+
if elt.is_a? String
|
88
|
+
output << TMPL % {addr: address, value: elt.to_wasm, comment: s}
|
89
|
+
address += d.value.size
|
90
|
+
elsif elt.is_a? Integer
|
91
|
+
output << TMPL % {addr: address, value: elt.to_little_endian(d.wtype.size), comment: "(#{elt} #{s})"}
|
92
|
+
address += d.wtype.size
|
93
|
+
elsif elt.is_a? DAta
|
94
|
+
output << TMPL % {addr: address, value: elt.address.to_little_endian(d.wtype.size), comment: "(#{elt} #{s})"}
|
95
|
+
address += d.wtype.size
|
96
|
+
else
|
97
|
+
raise "Unknown Data type: #{value.class}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
output.join("\n")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative './ext/type'
|
2
|
+
|
3
|
+
module Rlang::Parser
|
4
|
+
class Export
|
5
|
+
@@exports = []
|
6
|
+
attr_reader :object
|
7
|
+
|
8
|
+
# Object can be either a method object or a global object
|
9
|
+
def initialize(object)
|
10
|
+
@object = object
|
11
|
+
@@exports << self
|
12
|
+
end
|
13
|
+
|
14
|
+
# Export Rlang funcs, etc... grouping them
|
15
|
+
# by object type for Wasm code readability
|
16
|
+
def self.transpile
|
17
|
+
@@exports.sort_by {|e| e.object.class.to_s}.collect do |export|
|
18
|
+
export.object.export_wasm_code
|
19
|
+
end.join("\n")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|