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.
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