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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -3
- data/Gemfile.lock +4 -6
- data/README +11 -0
- data/README.md +18 -10
- data/bin/rlang +17 -5
- data/docs/RlangCompiler.md +5 -1
- data/docs/RlangManual.md +98 -20
- data/examples/fib/fib.rb +5 -1
- data/lib/builder/rlang/compiler.rb +2 -21
- data/lib/rlang/lib/array/array32.rb +59 -0
- data/lib/rlang/lib/array/array64.rb +56 -0
- data/lib/rlang/lib/array.rb +6 -0
- data/lib/rlang/lib/base64.rb +223 -0
- data/lib/rlang/lib/io.rb +75 -0
- data/lib/rlang/lib/kernel.rb +23 -0
- data/lib/rlang/lib/malloc.rb +12 -8
- data/lib/rlang/lib/memory.rb +102 -2
- data/lib/rlang/lib/object.rb +40 -4
- data/lib/rlang/lib/rlang.rb +29 -0
- data/lib/rlang/lib/rlang_core.rb +15 -0
- data/lib/rlang/lib/string.rb +106 -8
- data/lib/rlang/lib/type/i32.rb +43 -0
- data/lib/rlang/lib/type/i64.rb +2 -0
- data/lib/rlang/lib/unistd.rb +1 -2
- data/lib/rlang/lib/wasi.rb +184 -0
- data/lib/rlang/parser/attr.rb +9 -13
- data/lib/rlang/parser/const.rb +105 -1
- data/lib/rlang/parser/cvar.rb +11 -7
- data/lib/rlang/parser/data.rb +17 -6
- data/lib/rlang/parser/export.rb +4 -3
- data/lib/rlang/parser/ext/integer.rb +3 -1
- data/lib/rlang/parser/ext/type.rb +30 -1
- data/lib/rlang/parser/global.rb +10 -2
- data/lib/rlang/parser/ivar.rb +3 -7
- data/lib/rlang/parser/klass.rb +8 -35
- data/lib/rlang/parser/method.rb +36 -12
- data/lib/rlang/parser/module.rb +143 -0
- data/lib/rlang/parser/wgenerator.rb +462 -168
- data/lib/rlang/parser/wnode.rb +387 -142
- data/lib/rlang/parser/wtype.rb +30 -7
- data/lib/rlang/parser.rb +506 -231
- data/lib/rlang/version.rb +1 -1
- data/lib/rlang.rb +3 -0
- data/lib/ruby/mirror/rstring.rb +16 -0
- data/lib/utils/exceptions.rb +12 -0
- data/rlang.gemspec +4 -4
- metadata +25 -13
- data/lib/rlang/lib.rb +0 -11
data/lib/rlang/lib/string.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
22
|
-
#
|
23
|
-
|
24
|
-
|
25
|
-
Memory.copy(
|
26
|
-
|
27
|
-
|
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
|
data/lib/rlang/lib/type/i32.rb
CHANGED
@@ -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
|
data/lib/rlang/lib/type/i64.rb
CHANGED
data/lib/rlang/lib/unistd.rb
CHANGED
@@ -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
|
+
|
data/lib/rlang/parser/attr.rb
CHANGED
@@ -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(
|
19
|
-
@
|
18
|
+
def initialize(klass, name, wtype=WType::DEFAULT)
|
19
|
+
@klass = klass
|
20
20
|
@name = name
|
21
|
-
@ivar =
|
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 = @
|
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
|
31
|
+
logger.debug "Getter created: #{@getter}"
|
32
32
|
@getter
|
33
33
|
end
|
34
34
|
|
35
35
|
def attr_writer
|
36
|
-
@setter = @
|
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
|
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
|
63
|
+
logger.debug "Attr/Getter/Setter/ivar wtype updated : #{@getter}"
|
68
64
|
end
|
69
65
|
|
70
66
|
def getter_name
|
data/lib/rlang/parser/const.rb
CHANGED
@@ -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
|
-
|
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
|
data/lib/rlang/parser/cvar.rb
CHANGED
@@ -12,17 +12,21 @@ require_relative './data'
|
|
12
12
|
module Rlang::Parser
|
13
13
|
class CVar
|
14
14
|
include Log
|
15
|
-
attr_reader :name, :
|
15
|
+
attr_reader :name, :klass
|
16
16
|
attr_accessor :wtype
|
17
17
|
|
18
|
-
def initialize(
|
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:
|
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
|
-
"$#{
|
41
|
+
"$#{self.class_name}::#{@name}"
|
38
42
|
end
|
39
43
|
|
40
44
|
def wasm_type
|
data/lib/rlang/parser/data.rb
CHANGED
@@ -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?
|
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
|
|
data/lib/rlang/parser/export.rb
CHANGED
@@ -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
|
-
|
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
|
-
(
|
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
|