rlang 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|