rlang 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rake_tasks~ +0 -0
  4. data/.travis.yml +7 -0
  5. data/CODE_OF_CONDUCT.md +74 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE +373 -0
  8. data/README.md +61 -0
  9. data/Rakefile +10 -0
  10. data/bin/rlang +164 -0
  11. data/docs/RlangCompiler.md +37 -0
  12. data/docs/RlangManual.md +391 -0
  13. data/lib/builder/ext/tempfile.rb +7 -0
  14. data/lib/builder/ext.rb +5 -0
  15. data/lib/builder/rlang/builder.rb +31 -0
  16. data/lib/builder/rlang.rb +2 -0
  17. data/lib/builder/wat/builder.rb +52 -0
  18. data/lib/builder/wat/renderer.rb +28 -0
  19. data/lib/builder/wat.rb +3 -0
  20. data/lib/builder.rb +7 -0
  21. data/lib/rlang/lib/malloc.c +97 -0
  22. data/lib/rlang/lib/malloc.rb +169 -0
  23. data/lib/rlang/lib/memory.rb +11 -0
  24. data/lib/rlang/lib/type/i32.rb +7 -0
  25. data/lib/rlang/lib/type/i64.rb +7 -0
  26. data/lib/rlang/lib/type.rb +6 -0
  27. data/lib/rlang/lib/unistd.rb +47 -0
  28. data/lib/rlang/lib.rb +10 -0
  29. data/lib/rlang/parser/const.rb +15 -0
  30. data/lib/rlang/parser/cvar.rb +44 -0
  31. data/lib/rlang/parser/data.rb +105 -0
  32. data/lib/rlang/parser/export.rb +22 -0
  33. data/lib/rlang/parser/ext/integer.rb +5 -0
  34. data/lib/rlang/parser/ext/string.rb +5 -0
  35. data/lib/rlang/parser/ext/type.rb +64 -0
  36. data/lib/rlang/parser/global.rb +65 -0
  37. data/lib/rlang/parser/lvar.rb +29 -0
  38. data/lib/rlang/parser/marg.rb +30 -0
  39. data/lib/rlang/parser/method.rb +76 -0
  40. data/lib/rlang/parser/wattr.rb +65 -0
  41. data/lib/rlang/parser/wgenerator.rb +509 -0
  42. data/lib/rlang/parser/winstruction.rb +148 -0
  43. data/lib/rlang/parser/wnode.rb +455 -0
  44. data/lib/rlang/parser/wtree.rb +19 -0
  45. data/lib/rlang/parser/wtype.rb +116 -0
  46. data/lib/rlang/parser.rb +1842 -0
  47. data/lib/rlang/version.rb +3 -0
  48. data/lib/rlang.rb +4 -0
  49. data/lib/simul/classes/data.rb +80 -0
  50. data/lib/simul/classes/global.rb +38 -0
  51. data/lib/simul/classes/memory.rb +131 -0
  52. data/lib/utils/log.rb +32 -0
  53. data/rlang.gemspec +38 -0
  54. 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
@@ -0,0 +1,3 @@
1
+
2
+ require_relative './wat/renderer'
3
+ require_relative './wat/builder'
data/lib/builder.rb ADDED
@@ -0,0 +1,7 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+
5
+ require_relative './builder/ext'
6
+ require_relative './builder/wat'
7
+ require_relative './builder/rlang'
@@ -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,11 @@
1
+ class Memory
2
+
3
+ def self.size
4
+ inline wat: '(memory.size)'
5
+ end
6
+
7
+ def self.grow(delta)
8
+ inline wat: '(memory.grow (local.get $delta))'
9
+ end
10
+
11
+ end
@@ -0,0 +1,7 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+ #
5
+ class I32
6
+ def self.size; 4; end
7
+ end
@@ -0,0 +1,7 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+ #
5
+ class I64
6
+ def self.size; 8; end
7
+ end
@@ -0,0 +1,6 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+ #
5
+ require_relative './type/i32'
6
+ require_relative './type/i64'
@@ -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
@@ -0,0 +1,5 @@
1
+ class Integer
2
+ def to_little_endian(byte_count)
3
+ ("%0#{byte_count*2}X" % self).scan(/../).reverse.map {|byte| "\\#{byte}"}.join('')
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class String
2
+ def to_wasm
3
+ self.split('').map {|c| (32..126).include?(c.ord) ? c : "\\#{'%02X' % c.ord}"}.join('')
4
+ end
5
+ end