rlang 0.4.1 → 0.6.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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -3
  3. data/Gemfile.lock +4 -6
  4. data/README +11 -0
  5. data/README.md +18 -10
  6. data/bin/rlang +17 -5
  7. data/docs/RlangCompiler.md +5 -1
  8. data/docs/RlangManual.md +98 -20
  9. data/examples/fib/fib.rb +5 -1
  10. data/lib/builder/rlang/compiler.rb +2 -21
  11. data/lib/rlang/lib/array/array32.rb +59 -0
  12. data/lib/rlang/lib/array/array64.rb +56 -0
  13. data/lib/rlang/lib/array.rb +6 -0
  14. data/lib/rlang/lib/base64.rb +223 -0
  15. data/lib/rlang/lib/io.rb +75 -0
  16. data/lib/rlang/lib/kernel.rb +23 -0
  17. data/lib/rlang/lib/malloc.rb +12 -8
  18. data/lib/rlang/lib/memory.rb +102 -2
  19. data/lib/rlang/lib/object.rb +40 -4
  20. data/lib/rlang/lib/rlang.rb +29 -0
  21. data/lib/rlang/lib/rlang_core.rb +15 -0
  22. data/lib/rlang/lib/string.rb +106 -8
  23. data/lib/rlang/lib/type/i32.rb +43 -0
  24. data/lib/rlang/lib/type/i64.rb +2 -0
  25. data/lib/rlang/lib/unistd.rb +1 -2
  26. data/lib/rlang/lib/wasi.rb +184 -0
  27. data/lib/rlang/parser/attr.rb +9 -13
  28. data/lib/rlang/parser/const.rb +105 -1
  29. data/lib/rlang/parser/cvar.rb +11 -7
  30. data/lib/rlang/parser/data.rb +17 -6
  31. data/lib/rlang/parser/export.rb +4 -3
  32. data/lib/rlang/parser/ext/integer.rb +3 -1
  33. data/lib/rlang/parser/ext/type.rb +30 -1
  34. data/lib/rlang/parser/global.rb +10 -2
  35. data/lib/rlang/parser/ivar.rb +3 -7
  36. data/lib/rlang/parser/klass.rb +8 -35
  37. data/lib/rlang/parser/method.rb +36 -12
  38. data/lib/rlang/parser/module.rb +143 -0
  39. data/lib/rlang/parser/wgenerator.rb +462 -168
  40. data/lib/rlang/parser/wnode.rb +387 -142
  41. data/lib/rlang/parser/wtype.rb +30 -7
  42. data/lib/rlang/parser.rb +506 -231
  43. data/lib/rlang/version.rb +1 -1
  44. data/lib/rlang.rb +3 -0
  45. data/lib/ruby/mirror/rstring.rb +16 -0
  46. data/lib/utils/exceptions.rb +12 -0
  47. data/rlang.gemspec +4 -4
  48. metadata +25 -13
  49. data/lib/rlang/lib.rb +0 -11
@@ -1,4 +1,5 @@
1
1
  require_relative './malloc'
2
+ require_relative './memory'
2
3
 
3
4
  class String
4
5
  attr_reader :length, :ptr
@@ -10,21 +11,118 @@ class String
10
11
  # ptr is a simple memory address of type
11
12
  # :I32 (see it as the equivalent of a
12
13
  # char * in C)
14
+ #
15
+ # There are 3 ways to initialize a new String object
16
+ # * with a string literal (e.g. mystring = "Hello World!")
17
+ # * by pointing at an existing memory location (e.g. String.new(ptr, length))
18
+ # * by asking Rlang to allocate the String space when ptr is NULL (e.g. String.new(0, length))
19
+ # No memory is allocated for the string bytes if the string is empty
13
20
  def initialize(ptr, length)
14
- @ptr = ptr
21
+ result :none
22
+ if ptr == 0
23
+ if length == 0
24
+ @ptr = 0
25
+ else
26
+ @ptr = Malloc.malloc(length)
27
+ end
28
+ else
29
+ @ptr = ptr
30
+ end
15
31
  @length = length
16
32
  end
17
33
 
34
+ def size; @length; end
35
+ def to_s; self; end
36
+
37
+ def empty?
38
+ @length == 0
39
+ end
40
+
41
+ def ord
42
+ result :I32
43
+ Memory.load32_8(@ptr)
44
+ end
45
+
18
46
  def +(stg)
19
47
  arg stg: :String
20
48
  result :String
21
- new_length = self.length + stg.length
22
- # allocate space for concatenated string
23
- new_ptr = Malloc.malloc(new_length)
24
- Memory.copy(self.ptr, new_ptr, self.length)
25
- Memory.copy(stg.ptr, new_ptr + self.length, stg.length)
26
- # Create new object string
27
- String.new(new_ptr, new_length)
49
+
50
+ # Create new object string with proper size
51
+ s = String.new(0, @length + stg.length)
52
+ # Copy both strings in the new one
53
+ Memory.copy(@ptr, s.ptr, @length)
54
+ Memory.copy(stg.ptr, s.ptr + @length, stg.length)
55
+ s
56
+ end
57
+
58
+ def [](idx)
59
+ result :String
60
+ # The condition below should actually return nil
61
+ # to be compliant with the Ruby library but we don't
62
+ # have nil in Rlang so return an empty string
63
+ return "" if (idx >= @length) || (idx < -@length)
64
+ idx = (@length + idx) if idx < 0
65
+ stg = String.new(0,1)
66
+ Memory.copy(@ptr+idx, stg.ptr, 1)
67
+ stg
68
+ end
69
+
70
+ def []=(idx, stg)
71
+ arg stg: :String
72
+ result :String
73
+ if (idx >= @length) || (idx < -@length)
74
+ raise "IndexError: index out bound"
75
+ end
76
+ idx = (@length + idx) if idx < 0
77
+ i=0
78
+ tgt_ptr = @ptr+idx
79
+ while i < stg.length && (idx + i) < @length
80
+ Memory.copy(stg.ptr+i, tgt_ptr+i, 1)
81
+ i += 1
82
+ end
83
+ stg
84
+ end
85
+
86
+ def *(times)
87
+ result :String
88
+ full_length = @length * times
89
+ stg = String.new(0, full_length)
90
+ idx=0
91
+ while idx < full_length
92
+ stg[idx] = self
93
+ idx += @length
94
+ end
95
+ stg
96
+ end
97
+
98
+ def reverse!
99
+ result :String
100
+ half_size = @length/2
101
+ i=0
102
+ while i < half_size
103
+ swap = Memory.load32_8(@ptr+i)
104
+ Memory.store32_8(@ptr+i, Memory.load32_8(@ptr+@length-1-i))
105
+ Memory.store32_8(@ptr+@length-1-i, swap)
106
+ i += 1
107
+ end
108
+ self
109
+ end
110
+
111
+ def ==(stg)
112
+ arg stg: :String
113
+ return false if stg.length != @length
114
+ i = 0
115
+ stg_ptr = stg.ptr
116
+ while i < @length
117
+ return false if Memory.load32_8(@ptr+i) != Memory.load32_8(stg_ptr+i)
118
+ i += 1
119
+ end
120
+ true
121
+ end
122
+
123
+ def !=(stg)
124
+ arg stg: :String
125
+ !(self == stg)
28
126
  end
29
127
 
30
128
  end
@@ -2,6 +2,49 @@
2
2
  # Copyright (c) 2019, Laurent Julliard and contributors
3
3
  # All rights reserved.
4
4
  #
5
+ # Integer 32 methods
6
+
7
+ require_relative '../string'
8
+
5
9
  class I32
10
+
11
+ DIGITS = "0123456789ABCDEF"
12
+
6
13
  def self.size; 4; end
14
+
15
+ # convert an integer to its string representation in a given base
16
+ def self.itoa(x,base)
17
+ result :String
18
+
19
+ raise "itoa base out of range" if base < 2 || base > DIGITS.length
20
+ if x <= 0
21
+ if x == 0
22
+ return DIGITS[0]
23
+ else
24
+ return "-" + self.itoa(0-x, base)
25
+ end
26
+ end
27
+
28
+ result = ""
29
+ while x > 0
30
+ remainder = x % base
31
+ x /= base
32
+ result += DIGITS[remainder]
33
+ end
34
+ result.reverse!
35
+ end
36
+
37
+ def to_s
38
+ result :String
39
+ I32.itoa(self, 10)
40
+ end
41
+
42
+ def chr
43
+ result :String
44
+ raise "out of char range" if self > 255 || self < 0
45
+ stg = String.new(0,1)
46
+ Memory.store32_8(stg.ptr, self)
47
+ stg
48
+ end
49
+
7
50
  end
@@ -2,6 +2,8 @@
2
2
  # Copyright (c) 2019, Laurent Julliard and contributors
3
3
  # All rights reserved.
4
4
  #
5
+ # Integer 64 methods
6
+
5
7
  class I64
6
8
  def self.size; 8; end
7
9
  end
@@ -8,6 +8,7 @@
8
8
  # srbk(0) returns the current value of the break
9
9
  # Note: in WASM we can only grow linear memory by pages (64 KB block)
10
10
 
11
+ require_relative './memory'
11
12
 
12
13
  # These 3 global variables below are used by the
13
14
  # dynamic memory allocator. You can redefine them
@@ -27,8 +28,6 @@ $HEAP_MAX_SIZE = 1073741824 # 1GB
27
28
  $HEAP_SIZE = 0
28
29
 
29
30
  class Unistd
30
- result :Memory, :grow, :I32
31
- result :Memory, :size, :I32
32
31
 
33
32
  def self.sbrk(n)
34
33
  # local variables used. All default type
@@ -0,0 +1,184 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019-2020, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+ #
5
+ # WASI Interface to WASM runtime
6
+
7
+ require_relative './array'
8
+ require_relative './string'
9
+
10
+ ARGC = 0
11
+ ARGV = 0.cast_to(:Array32)
12
+ ENV = 0.cast_to(:Array32)
13
+
14
+ class WASI
15
+ STDIN_FD = 0
16
+ STDOUT_FD = 1
17
+ STDERR_FD = 2
18
+ @@argv_buf_size = 0
19
+ @@environc = 0
20
+ @@environ_buf_size = 0
21
+
22
+ class CIOVec
23
+ attr_reader :ciovs
24
+ attr_type ciovs: :Array32
25
+
26
+ def initialize(n)
27
+ result :none
28
+ # a ciov is list of n (address to buffer, length of buffer)
29
+ @n = n
30
+ @index = 0
31
+ @max_index = 2 * @n
32
+ @ciovs = Array32.new(2*n)
33
+ end
34
+
35
+ def << (string)
36
+ arg string: :String
37
+ result :CIOVec
38
+ raise "CIOVec full !" if @index >= @max_index
39
+ @ciovs[@index] = string.ptr
40
+ @ciovs[@index+1] = string.length
41
+ @index += 2
42
+ self
43
+ end
44
+
45
+ def free
46
+ result :none
47
+ @ciovs.free
48
+ Object.free(self)
49
+ end
50
+ end
51
+
52
+ # An IOVec is an array of (address to buffer, length of buffer)
53
+ # where WASM runtime can read data coming from the WASM module
54
+ class IOVec
55
+ attr_reader :iovs
56
+ attr_type iovs: :Array32
57
+
58
+ IOV_SIZE = 1024
59
+
60
+ def initialize(n)
61
+ result :none
62
+ @iovs = Array32.new(2*n)
63
+ @n = n
64
+ i = 0
65
+ while i < n;
66
+ @iovs[i] = Malloc.malloc(IOV_SIZE)
67
+ @iovs[i+1] = IOV_SIZE
68
+ i += 2
69
+ end
70
+ end
71
+
72
+ def free
73
+ result :none
74
+ i = 0
75
+ while i < @n
76
+ Malloc.free(@iovs[i])
77
+ i += 2
78
+ end
79
+ @iovs.free
80
+ Object.free(self)
81
+ end
82
+ end
83
+
84
+ # Import WASI functions
85
+ import :wasi_unstable, :args_sizes_get
86
+ def self.args_sizes_get(argc, args_size); end
87
+
88
+ import :wasi_unstable, :args_get
89
+ def self.args_get(argv, argv_buf); end
90
+
91
+ import :wasi_unstable, :environ_get
92
+ def self.environ_get(environ, environ_buf); end
93
+
94
+ import :wasi_unstable, :environ_sizes_get
95
+ def self.environ_sizes_get(environc, environ_buf_size); end
96
+
97
+ import :wasi_unstable, :fd_write
98
+ def self.fd_write(fd, iovs, iovs_count, nwritten_ptr); end
99
+
100
+ import :wasi_unstable, :fd_read
101
+ def self.fd_read(fd, iovs, iovs_count, nread_ptr); end
102
+
103
+ import :wasi_unstable, :proc_exit
104
+ def self.proc_exit(exitcode); result :none; end
105
+
106
+ def self.argv_init
107
+ local argv: :Array32, environ: :Array32
108
+
109
+ # Get number of arguments and their total size
110
+ errno = WASI.args_sizes_get(ARGC.addr, @@argv_buf_size.addr)
111
+ raise "Errno args_sizes_get" if errno != 0
112
+
113
+ # Allocate memory areas to receive the argument pointers
114
+ # (argv) and the argument strings (argv_buf)
115
+ #
116
+ # Setup an extra slot in argv array to simplify the
117
+ # loop below
118
+ argv = Array32.new(ARGC+1) # Assuming I32 for pointers
119
+ argv_buf = Malloc.malloc(@@argv_buf_size)
120
+ errno = WASI.args_get(argv.ptr, argv_buf)
121
+
122
+ raise "Errno args_get" if errno != 0
123
+ argv[ARGC] = argv[0] + @@argv_buf_size
124
+
125
+ # Workaround to avoid dynamic constant assignment error
126
+ Memory.store32(ARGV.addr, Array32.new(ARGC))
127
+
128
+ # Now scan through arguments and turn them into a Rlang
129
+ # Array of Strings (like ARGV in Ruby)
130
+ i = 0
131
+ while i < ARGC
132
+ length = argv[i+1] - argv[i] - 1 # -1 because of null terminated
133
+ ARGV[i] = String.new(argv[i], length)
134
+ # Nullify argv[i] so that String is not freed
135
+ argv[i] = 0
136
+ i += 1
137
+ end
138
+ argv.free
139
+ return errno
140
+ end
141
+
142
+ # Initialize WASI environment and related Rlang
143
+ # objects (ARGC, ARGV,...)
144
+ def self.environ_init
145
+ local environ: :Array32
146
+
147
+ # Get environ variable count and buf size
148
+ errno = WASI.environ_sizes_get(@@environc.addr, @@environ_buf_size.addr)
149
+ raise "Errno environ_sizes_get" if errno != 0
150
+
151
+ # Allocate memory areas to receive the env var pointers
152
+ # (env) and the env var strings (env_buf)
153
+ environ = Array32.new(@@environc+1) # Assuming I32 for pointers
154
+ environ_buf = Malloc.malloc(@@environ_buf_size)
155
+ errno = WASI.environ_get(environ.ptr, environ_buf)
156
+
157
+ raise "Errno environ_get" if errno != 0
158
+ environ[@@environc] = environ[0] + @@environ_buf_size
159
+
160
+ # Workaround to avoid dynamic constant assignment error
161
+ Memory.store32(ENV.addr, Array32.new(@@environc))
162
+
163
+ # Now scan through arguments and turn them into a Rlang
164
+ # Array of Strings (like ARGV in Ruby)
165
+ i = 0
166
+ while i < @@environc
167
+ length = environ[i+1] - environ[i] - 1 # -1 because of null terminated
168
+ ENV[i] = String.new(environ[i], length)
169
+ # Nullify environ[i] so that String is not freed
170
+ #environ[i] = 0
171
+ i += 1
172
+ end
173
+ #environ.free
174
+
175
+ return errno
176
+ end
177
+
178
+ def self.init
179
+ self.argv_init
180
+ self.environ_init
181
+ end
182
+
183
+ end
184
+
@@ -11,14 +11,14 @@ require_relative './wtype'
11
11
  module Rlang::Parser
12
12
  class Attr
13
13
  include Log
14
- attr_reader :name, :getter, :setter, :ivar
14
+ attr_reader :name, :getter, :setter, :ivar, :klass
15
15
 
16
16
  # The name argument can either be the attribute name
17
17
  # (e.g. :size) or an ivar name (e.g. :@size)
18
- def initialize(class_wnode, name, wtype=WType::DEFAULT)
19
- @class_wnode = class_wnode
18
+ def initialize(klass, name, wtype=WType::DEFAULT)
19
+ @klass = klass
20
20
  @name = name
21
- @ivar = class_wnode.create_ivar(:"@#{name}", wtype)
21
+ @ivar = klass.wnode.create_ivar(:"@#{name}", wtype)
22
22
  @getter = nil
23
23
  @setter = nil
24
24
  @export = false
@@ -26,16 +26,16 @@ module Rlang::Parser
26
26
  end
27
27
 
28
28
  def attr_reader
29
- @getter = @class_wnode.find_or_create_method(self.getter_name, nil, wtype, :instance)
29
+ @getter = @klass.wnode.find_or_create_method(nil, self.getter_name, :instance, wtype)
30
30
  @getter.export! if @export
31
- logger.debug "Getter created: #{@getter.inspect}"
31
+ logger.debug "Getter created: #{@getter}"
32
32
  @getter
33
33
  end
34
34
 
35
35
  def attr_writer
36
- @setter = @class_wnode.find_or_create_method(self.setter_name, nil, wtype, :instance)
36
+ @setter = @klass.wnode.find_or_create_method(nil, self.setter_name, :instance, wtype)
37
37
  @setter.export! if @export
38
- logger.debug "Setter created: #{@setter.inspect}"
38
+ logger.debug "Setter created: #{@setter}"
39
39
  @setter
40
40
  end
41
41
 
@@ -47,10 +47,6 @@ module Rlang::Parser
47
47
  @export = true
48
48
  end
49
49
 
50
- def class_name
51
- @class_wnode.class_name
52
- end
53
-
54
50
  def wtype
55
51
  @ivar.wtype
56
52
  end
@@ -64,7 +60,7 @@ module Rlang::Parser
64
60
  @getter.wtype = wtype if @getter
65
61
  @setter.wtype = wtype if @setter
66
62
  @ivar.wtype = wtype
67
- logger.debug "Attr/Getter/Setter/ivar wtype updated : #{@getter.inspect}"
63
+ logger.debug "Attr/Getter/Setter/ivar wtype updated : #{@getter}"
68
64
  end
69
65
 
70
66
  def getter_name
@@ -10,6 +10,110 @@ require_relative './cvar'
10
10
  # Constants and Class variables are managed
11
11
  # in exactly the same way
12
12
  module Rlang::Parser
13
- class Const < CVar
13
+ # Rubinius WebAssembly VM
14
+ # Copyright (c) 2019, Laurent Julliard and contributors
15
+ # All rights reserved.
16
+ #
17
+ # Class variables
18
+ # Note: Const class inherits from this class
19
+
20
+ require_relative '../../utils/log'
21
+ require_relative './wtype'
22
+ require_relative './data'
23
+
24
+ module Rlang::Parser
25
+ class Const
26
+ include Log
27
+ attr_reader :scope_class, :name, :value
28
+ attr_accessor :wtype
29
+
30
+ def initialize(name, value, wtype=WType::DEFAULT)
31
+ @scope_class = nil
32
+ @name = name
33
+ @wtype = wtype
34
+ @data = nil
35
+ self.value = value if value
36
+ end
37
+
38
+ # class or module in which constant is defined
39
+ # not to be confused with wtype that hold the Rlang
40
+ # class of the constant
41
+ def scope_class=(scope_class_or_mod)
42
+ logger.debug "Placing Constant #{name} in scope class #{scope_class_or_mod&.name}"
43
+ raise "Error: constant scope class already initialized with #{scope_class.name} / #{@scope_class}. Got #{scope_class_or_mod.name} / #{scope_class_or_mod}." \
44
+ if @scope_class
45
+ @scope_class = scope_class_or_mod
46
+ @scope_class.consts << self if @scope_class
47
+ end
48
+
49
+ # The value attribute can either be set at
50
+ # initialize time or later (e.g. for classes and modules)
51
+ def value=(value)
52
+ # TODO: as opposed to Ruby we don't handle constant
53
+ # reassignment for now.
54
+ raise "Constant #{self.name} already initialized with value #{@value}. Now getting #{value}" \
55
+ if @value
56
+ return nil unless value
57
+ logger.debug "Initializing constant #{@name} @ #{@address} with value #{@value} / wtype #{@wtype}"
58
+ if value.kind_of? Module
59
+ # for now just store 0 in constants
60
+ # pointing to class or module
61
+ # TODO: probably point to a minimal data
62
+ # with the class path string for instance (= class name)
63
+ self.data = 0
64
+ else
65
+ self.data = value
66
+ end
67
+ @value = value
68
+ end
69
+
70
+ def data=(value)
71
+ @data = DAta.new(self.path_name.to_sym, value, @wtype)
72
+ end
73
+
74
+ # the symbol form of this constant path
75
+ # e.g. a constant A::B will return :"A::B"
76
+ def path_name
77
+ #@scope_class ? "#{@scope_class.path_name}::#{@name}".to_sym : @name
78
+ self.path.map(&:name).join('::').to_sym
79
+ end
80
+
81
+ # Returns an array of successive Const objects that
82
+ # altogether makes the full path of this Const
83
+ # e.g. a constant A::B will return [Const(:A), Const(:B)]
84
+ def path
85
+ sk = nil; c = self; p = [c]
86
+ while (sk = c.scope_class) && sk.const != c
87
+ logger.debug "c: #{c.name}/#{c}, sk: #{sk.name}/#{sk}"
88
+ c = sk.const
89
+ p.unshift(c)
90
+ end
91
+ # Do not keep Object as the first element of the
92
+ # const path unless it is the only element
93
+ p.shift if p.first.name == :Object && p.size > 1
94
+ logger.debug "Const#path : #{p.map(&:name)}"
95
+ p
96
+ end
97
+
98
+ def class?
99
+ @wtype.name == :Class
100
+ end
101
+
102
+ def module?
103
+ @wtype.name == :Module
104
+ end
105
+
106
+ def address
107
+ @data.address
108
+ end
109
+
110
+ def wasm_name
111
+ "$#{self.path_name}"
112
+ end
113
+
114
+ def wasm_type
115
+ @wtype.wasm_type
116
+ end
14
117
  end
15
118
  end
119
+ end
@@ -12,17 +12,21 @@ require_relative './data'
12
12
  module Rlang::Parser
13
13
  class CVar
14
14
  include Log
15
- attr_reader :name, :class_name
15
+ attr_reader :name, :klass
16
16
  attr_accessor :wtype
17
17
 
18
- def initialize(class_name, name, value=0, wtype=WType::DEFAULT)
18
+ def initialize(klass, name, value=0, wtype=WType::DEFAULT)
19
+ @klass = klass
19
20
  @name = name
20
- @class_name = class_name
21
21
  @wtype = wtype
22
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}"
23
+ raise "Error: #{self.class} #{self.wasm_name} already created!" if DAta.exist? self.wasm_name.to_sym
24
+ @data = DAta.new(self.wasm_name.to_sym, value, wtype) unless wtype.name == :Class
25
+ logger.debug "creating #{self.class} #{self.class_name}::#{name} @ #{@address} with value #{value} / wtype #{wtype}"
26
+ end
27
+
28
+ def class_name
29
+ @klass.path_name
26
30
  end
27
31
 
28
32
  def address
@@ -34,7 +38,7 @@ module Rlang::Parser
34
38
  end
35
39
 
36
40
  def wasm_name
37
- "$#{@class_name}::#{@name}"
41
+ "$#{self.class_name}::#{@name}"
38
42
  end
39
43
 
40
44
  def wasm_type
@@ -23,13 +23,14 @@ module Rlang::Parser
23
23
  def initialize(label, value, wtype=WType::DEFAULT)
24
24
  raise "Data label '#{label}' already initialized" \
25
25
  if self.class.exist? label
26
+ logger.debug "@@current_address: #{@@current_address}"
26
27
  @label = label
27
28
  @wtype = wtype
28
29
  @address = @@current_address
29
30
  @@label_table[@label] = self
30
31
  @value = []
31
32
  self.append_value(value, wtype)
32
- logger.debug "New Data[#{@label}] initialized with #{@value} at address #{@address}"
33
+ logger.debug "New Data[#{@label}] initialized with #{@value} at address #{@address} / new current address: #{@@current_address}"
33
34
  end
34
35
 
35
36
  def self.reset!
@@ -37,15 +38,20 @@ module Rlang::Parser
37
38
  @@current_address = 0
38
39
  end
39
40
 
41
+ # Append a value to the DAta object
42
+ # and return this DAta object
40
43
  def append_value(value, wtype)
44
+ logger.warn "Data type #{@wtype} misaligned!!! (Data[:#{@label}] value #{value} at address #{@address}" \
45
+ unless self.aligned?
41
46
  @value << value
42
- if value.is_a?(String)
47
+ if value.is_a? String
43
48
  @@current_address += value.length
44
49
  else
45
- logger.warn "Data type #{@wtype} misaligned!!! (Data[:#{@label}] value #{value} at address #{@address}" \
46
- unless self.aligned?
47
50
  @@current_address += @wtype.size
48
51
  end
52
+ # Always make sure current address is aligned
53
+ self.class.align(WType::DEFAULT.size)
54
+ self
49
55
  end
50
56
 
51
57
  def aligned?
@@ -61,6 +67,8 @@ module Rlang::Parser
61
67
  @@label_table[label].address
62
68
  end
63
69
 
70
+ # Append a value to the DAta object identified by the
71
+ # label and return this DAta object object
64
72
  def self.append(label, value, wtype=WType::DEFAULT)
65
73
  logger.debug "appending #{value} to DAta[#{label}]"
66
74
  if self.exist? label
@@ -71,6 +79,8 @@ module Rlang::Parser
71
79
  end
72
80
 
73
81
  def self.address=(address)
82
+ logger.fatal "ERROR!! Cannot decrease current DAta address (was #{@@current_address}, got #{address}) " \
83
+ if address < @@current_address
74
84
  @@current_address = address
75
85
  end
76
86
 
@@ -86,7 +96,8 @@ module Rlang::Parser
86
96
 
87
97
  # Transpile data to WAT code
88
98
  # in order of increasing address
89
- def self.transpile
99
+ def self.transpile(depth)
100
+ indent = ' ' * depth * 2
90
101
  output = []
91
102
  @@label_table.sort_by {|s,d| d.address}.each do |s,d|
92
103
  logger.debug "Generating data #{d.inspect}"
@@ -106,7 +117,7 @@ module Rlang::Parser
106
117
  end
107
118
  end
108
119
  end
109
- output.join("\n")
120
+ indent + output.join("\n #{indent}")
110
121
  end
111
122
  end
112
123
 
@@ -17,10 +17,11 @@ module Rlang::Parser
17
17
 
18
18
  # Export Rlang funcs, etc... grouping them
19
19
  # by object type for Wasm code readability
20
- def self.transpile
21
- @@exports.sort_by {|e| e.object.class.to_s}.collect do |export|
20
+ def self.transpile(depth)
21
+ indent = ' ' * depth * 2
22
+ indent + @@exports.sort_by {|e| e.object.class.to_s}.collect do |export|
22
23
  export.object.export_wasm_code
23
- end.join("\n")
24
+ end.join("\n#{indent}")
24
25
  end
25
26
  end
26
27
  end
@@ -1,5 +1,7 @@
1
1
  class Integer
2
+ # Works for both signed and unsignd 32 or 64 bits integers
2
3
  def to_little_endian(byte_count)
3
- ("%0#{byte_count*2}X" % self).scan(/../).reverse.map {|byte| "\\#{byte}"}.join('')
4
+ i = (self < 0) ? 2**(byte_count*8) + self : self
5
+ ("%0#{byte_count*2}X" % i).scan(/../).reverse.map {|byte| "\\#{byte}"}.join('')
4
6
  end
5
7
  end