rlang 0.5.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 +10 -0
- data/Gemfile.lock +3 -5
- data/README +11 -0
- data/bin/rlang +4 -0
- data/docs/RlangCompiler.md +2 -0
- data/docs/RlangManual.md +37 -8
- data/lib/rlang/lib/array/array32.rb +13 -4
- data/lib/rlang/lib/array/array64.rb +11 -3
- data/lib/rlang/lib/base64.rb +223 -0
- data/lib/rlang/lib/io.rb +7 -2
- data/lib/rlang/lib/malloc.rb +5 -3
- data/lib/rlang/lib/object.rb +23 -0
- data/lib/rlang/lib/rlang_core.rb +2 -1
- data/lib/rlang/lib/string.rb +95 -11
- data/lib/rlang/lib/type/i32.rb +30 -12
- data/lib/rlang/lib/type/i64.rb +0 -2
- data/lib/rlang/lib/wasi.rb +57 -6
- data/lib/rlang/parser/data.rb +5 -0
- data/lib/rlang/parser/ext/integer.rb +3 -1
- data/lib/rlang/parser/ext/type.rb +30 -1
- data/lib/rlang/parser/global.rb +7 -0
- data/lib/rlang/parser/wgenerator.rb +221 -83
- data/lib/rlang/parser/wnode.rb +41 -14
- data/lib/rlang/parser/wtype.rb +12 -6
- data/lib/rlang/parser.rb +182 -130
- data/lib/rlang/version.rb +1 -1
- data/lib/ruby/mirror/rstring.rb +16 -0
- data/lib/utils/exceptions.rb +12 -0
- data/rlang.gemspec +4 -4
- metadata +16 -12
data/lib/rlang/lib/string.rb
CHANGED
@@ -11,34 +11,118 @@ class String
|
|
11
11
|
# ptr is a simple memory address of type
|
12
12
|
# :I32 (see it as the equivalent of a
|
13
13
|
# char * in C)
|
14
|
-
|
14
|
+
#
|
15
15
|
# There are 3 ways to initialize a new String object
|
16
16
|
# * with a string literal (e.g. mystring = "Hello World!")
|
17
17
|
# * by pointing at an existing memory location (e.g. String.new(ptr, length))
|
18
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
|
19
20
|
def initialize(ptr, length)
|
20
21
|
result :none
|
21
22
|
if ptr == 0
|
22
|
-
|
23
|
+
if length == 0
|
24
|
+
@ptr = 0
|
25
|
+
else
|
26
|
+
@ptr = Malloc.malloc(length)
|
27
|
+
end
|
23
28
|
else
|
24
29
|
@ptr = ptr
|
25
30
|
end
|
26
31
|
@length = length
|
27
32
|
end
|
28
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
|
+
|
29
46
|
def +(stg)
|
30
47
|
arg stg: :String
|
31
48
|
result :String
|
32
|
-
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
Memory.copy(
|
37
|
-
|
38
|
-
|
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
|
39
56
|
end
|
40
57
|
|
41
|
-
def
|
42
|
-
|
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)
|
126
|
+
end
|
43
127
|
|
44
128
|
end
|
data/lib/rlang/lib/type/i32.rb
CHANGED
@@ -4,29 +4,47 @@
|
|
4
4
|
#
|
5
5
|
# Integer 32 methods
|
6
6
|
|
7
|
-
|
7
|
+
require_relative '../string'
|
8
8
|
|
9
9
|
class I32
|
10
|
-
|
10
|
+
|
11
|
+
DIGITS = "0123456789ABCDEF"
|
11
12
|
|
12
13
|
def self.size; 4; end
|
13
14
|
|
14
|
-
|
15
|
+
# convert an integer to its string representation in a given base
|
16
|
+
def self.itoa(x,base)
|
15
17
|
result :String
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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]
|
23
33
|
end
|
24
|
-
|
34
|
+
result.reverse!
|
25
35
|
end
|
26
36
|
|
27
37
|
def to_s
|
28
38
|
result :String
|
29
|
-
|
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
|
30
48
|
end
|
31
49
|
|
32
50
|
end
|
data/lib/rlang/lib/type/i64.rb
CHANGED
data/lib/rlang/lib/wasi.rb
CHANGED
@@ -9,12 +9,15 @@ require_relative './string'
|
|
9
9
|
|
10
10
|
ARGC = 0
|
11
11
|
ARGV = 0.cast_to(:Array32)
|
12
|
+
ENV = 0.cast_to(:Array32)
|
12
13
|
|
13
14
|
class WASI
|
14
15
|
STDIN_FD = 0
|
15
16
|
STDOUT_FD = 1
|
16
17
|
STDERR_FD = 2
|
17
18
|
@@argv_buf_size = 0
|
19
|
+
@@environc = 0
|
20
|
+
@@environ_buf_size = 0
|
18
21
|
|
19
22
|
class CIOVec
|
20
23
|
attr_reader :ciovs
|
@@ -81,9 +84,15 @@ class WASI
|
|
81
84
|
# Import WASI functions
|
82
85
|
import :wasi_unstable, :args_sizes_get
|
83
86
|
def self.args_sizes_get(argc, args_size); end
|
84
|
-
|
87
|
+
|
85
88
|
import :wasi_unstable, :args_get
|
86
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
|
87
96
|
|
88
97
|
import :wasi_unstable, :fd_write
|
89
98
|
def self.fd_write(fd, iovs, iovs_count, nwritten_ptr); end
|
@@ -94,10 +103,8 @@ class WASI
|
|
94
103
|
import :wasi_unstable, :proc_exit
|
95
104
|
def self.proc_exit(exitcode); result :none; end
|
96
105
|
|
97
|
-
|
98
|
-
|
99
|
-
def self.init
|
100
|
-
local argv: :Array32
|
106
|
+
def self.argv_init
|
107
|
+
local argv: :Array32, environ: :Array32
|
101
108
|
|
102
109
|
# Get number of arguments and their total size
|
103
110
|
errno = WASI.args_sizes_get(ARGC.addr, @@argv_buf_size.addr)
|
@@ -108,7 +115,7 @@ class WASI
|
|
108
115
|
#
|
109
116
|
# Setup an extra slot in argv array to simplify the
|
110
117
|
# loop below
|
111
|
-
argv = Array32.new(ARGC+1) #
|
118
|
+
argv = Array32.new(ARGC+1) # Assuming I32 for pointers
|
112
119
|
argv_buf = Malloc.malloc(@@argv_buf_size)
|
113
120
|
errno = WASI.args_get(argv.ptr, argv_buf)
|
114
121
|
|
@@ -124,10 +131,54 @@ class WASI
|
|
124
131
|
while i < ARGC
|
125
132
|
length = argv[i+1] - argv[i] - 1 # -1 because of null terminated
|
126
133
|
ARGV[i] = String.new(argv[i], length)
|
134
|
+
# Nullify argv[i] so that String is not freed
|
135
|
+
argv[i] = 0
|
127
136
|
i += 1
|
128
137
|
end
|
138
|
+
argv.free
|
129
139
|
return errno
|
130
140
|
end
|
131
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
|
+
|
132
183
|
end
|
133
184
|
|
data/lib/rlang/parser/data.rb
CHANGED
@@ -38,6 +38,8 @@ module Rlang::Parser
|
|
38
38
|
@@current_address = 0
|
39
39
|
end
|
40
40
|
|
41
|
+
# Append a value to the DAta object
|
42
|
+
# and return this DAta object
|
41
43
|
def append_value(value, wtype)
|
42
44
|
logger.warn "Data type #{@wtype} misaligned!!! (Data[:#{@label}] value #{value} at address #{@address}" \
|
43
45
|
unless self.aligned?
|
@@ -49,6 +51,7 @@ module Rlang::Parser
|
|
49
51
|
end
|
50
52
|
# Always make sure current address is aligned
|
51
53
|
self.class.align(WType::DEFAULT.size)
|
54
|
+
self
|
52
55
|
end
|
53
56
|
|
54
57
|
def aligned?
|
@@ -64,6 +67,8 @@ module Rlang::Parser
|
|
64
67
|
@@label_table[label].address
|
65
68
|
end
|
66
69
|
|
70
|
+
# Append a value to the DAta object identified by the
|
71
|
+
# label and return this DAta object object
|
67
72
|
def self.append(label, value, wtype=WType::DEFAULT)
|
68
73
|
logger.debug "appending #{value} to DAta[#{label}]"
|
69
74
|
if self.exist? label
|
@@ -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
|
@@ -2,6 +2,18 @@ module Type
|
|
2
2
|
|
3
3
|
class I64 < Numeric
|
4
4
|
MAX = 2**64 - 1
|
5
|
+
MIN = 0
|
6
|
+
def self.signed?; true; end
|
7
|
+
def size; 8; end # size in bytes
|
8
|
+
def self.size; 8; end # size in bytes
|
9
|
+
def wasm_type; 'i64'; end
|
10
|
+
def self.wasm_type; 'i64'; end
|
11
|
+
end
|
12
|
+
|
13
|
+
class UI64 < Numeric
|
14
|
+
MAX = 2**63 - 1
|
15
|
+
MIN = -2**63
|
16
|
+
def self.signed?; false; end
|
5
17
|
def size; 8; end # size in bytes
|
6
18
|
def self.size; 8; end # size in bytes
|
7
19
|
def wasm_type; 'i64'; end
|
@@ -9,9 +21,23 @@ module Type
|
|
9
21
|
end
|
10
22
|
|
11
23
|
class I32 < Numeric
|
12
|
-
MAX = 2**
|
24
|
+
MAX = 2**31 - 1
|
25
|
+
MIN = -2**31
|
26
|
+
def initialize(v); @v = Integer(v); end
|
27
|
+
def +(v); I32.new(v + @v); end
|
28
|
+
def self.signed?; true; end
|
29
|
+
def size; 4; end # size in bytes
|
30
|
+
def self.size; 4; end # size in bytes
|
31
|
+
def wasm_type; 'i32'; end
|
32
|
+
def self.wasm_type; 'i32'; end
|
33
|
+
end
|
34
|
+
|
35
|
+
class UI32 < Numeric
|
36
|
+
MAX = 2**32 - 1
|
37
|
+
MIN = 0
|
13
38
|
def initialize(v); @v = Integer(v); end
|
14
39
|
def +(v); I32.new(v + @v); end
|
40
|
+
def self.signed?; false; end
|
15
41
|
def size; 4; end # size in bytes
|
16
42
|
def self.size; 4; end # size in bytes
|
17
43
|
def wasm_type; 'i32'; end
|
@@ -20,6 +46,7 @@ module Type
|
|
20
46
|
|
21
47
|
class F64 < Numeric
|
22
48
|
MAX = 1.7976931348623158E308
|
49
|
+
def self.signed?; true; end
|
23
50
|
def size; 8; end # size in bytes
|
24
51
|
def wasm_type; 'f64'; end
|
25
52
|
def self.size; 8; end # size in bytes
|
@@ -29,6 +56,7 @@ module Type
|
|
29
56
|
class F32 < Numeric
|
30
57
|
MAX = 3.4028235E38
|
31
58
|
# size in bytes
|
59
|
+
def self.signed?; true; end
|
32
60
|
def size; 4; end
|
33
61
|
def wasm_type; 'f32'; end
|
34
62
|
def self.size; 4; end
|
@@ -60,5 +88,6 @@ module Type
|
|
60
88
|
end
|
61
89
|
|
62
90
|
DEFAULT = Type::I32
|
91
|
+
UNSIGNED_DEFAULT = Type::UI32
|
63
92
|
|
64
93
|
end
|
data/lib/rlang/parser/global.rb
CHANGED
@@ -53,6 +53,13 @@ module Rlang::Parser
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def self.transpile(depth)
|
56
|
+
# TODO : we should probably do that in a less "hardcoded" way
|
57
|
+
# Adjust the Heap base address to start after the static DAta
|
58
|
+
# section
|
59
|
+
g_heap = Global.find(:$HEAP)
|
60
|
+
g_heap.value = [DAta.align(8), g_heap.value].max if g_heap
|
61
|
+
|
62
|
+
# Go generate code now
|
56
63
|
indent = ' ' * depth * 2
|
57
64
|
output = []
|
58
65
|
@@globals.each do |g|
|