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,455 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+ #
5
+ # Nodes used to generate WASM code
6
+
7
+ require_relative '../../utils/log'
8
+ require_relative './wtype'
9
+ require_relative './const'
10
+ require_relative './cvar'
11
+ require_relative './lvar'
12
+ require_relative './wattr'
13
+ require_relative './method'
14
+
15
+ module Rlang::Parser
16
+ class WNode
17
+ include Log
18
+
19
+ # WASM code templates
20
+ T = {
21
+ func: 'func %{func_name}',
22
+ param: 'param %{name} %{wasm_type}',
23
+ result: 'result %{wasm_type}',
24
+ return: 'return',
25
+ local: 'local %{name} %{wasm_type}',
26
+ call: 'call %{func_name}',
27
+ store: '%{wasm_type}.store',
28
+ load: '%{wasm_type}.load',
29
+ local_get: 'local.get %{var_name}',
30
+ local_set: 'local.set %{var_name}',
31
+ global_get: 'global.get %{var_name}',
32
+ global_set: 'global.set %{var_name}',
33
+ addr: 'i32.const %{value}',
34
+ operator: '%{wasm_type}.%{operator}',
35
+ const: '%{wasm_type}.const %{value}',
36
+ drop: 'drop',
37
+ nop: 'nop',
38
+ extend_i32_u: '%{wasm_type}.extend_i32_u',
39
+ extend_i32_s: '%{wasm_type}.extend_i32_s',
40
+ wrap_i64: '%{wasm_type}.wrap_i64',
41
+ eqz: '%{wasm_type}.eqz',
42
+ if: 'if',
43
+ then: 'then',
44
+ else: 'else',
45
+ block: 'block %{label}',
46
+ loop: 'loop %{label}',
47
+ br_if: 'br_if %{label}',
48
+ br: 'br %{label}',
49
+ inline: '%{code}',
50
+ wattr_reader: %q{func %{func_name} (param $_self_ i32) (result %{wtype})
51
+ (%{wtype}.load offset=%{offset} (local.get $_self_))},
52
+ wattr_writer: %q{func %{func_name} (param $_self_ i32) (param %{wattr_name} %{wtype}) (result %{wtype})
53
+ (local.get %{wattr_name})
54
+ (%{wtype}.store offset=%{offset} (local.get $_self_) (local.get %{wattr_name}))},
55
+ class_size: %q{func %{func_name} (result %{wtype})
56
+ (%{wtype}.const %{size})}
57
+ }
58
+
59
+ attr_accessor :type, :wargs, :children, :parent, :comment, :lvars, :cvars, :margs,
60
+ :consts, :methods, :method, :template, :keep_on_stack,
61
+ :class_wnodes
62
+ attr_reader :wtype, :label, :klass_name, :klass_size, :wattrs
63
+
64
+ @@label_index = 0
65
+
66
+ def initialize(type, parent=nil, prepend=false)
67
+ @type = type # :root, :method, :class, :insn, :none
68
+ @parent = parent
69
+ @comment = nil
70
+
71
+ @wargs = {}
72
+ @template = nil
73
+ @children = []
74
+ @@root = self if type == :root
75
+ # make this wnode a child of its parent
76
+ @parent.add_child(self, prepend) if @parent
77
+
78
+ # WASM type of this node. If node is :method
79
+ # then it's the type of the return value (nil
80
+ # means no value returned)
81
+ @wtype = WType::DEFAULT
82
+
83
+ # For root wnode
84
+ @class_wnodes = [] # wnodes of classes
85
+
86
+ # For class wnode only
87
+ @klass_name = nil
88
+ @wattrs = [] # class attributes
89
+ @cvars = [] # class variables=
90
+ @consts = [] # class constants
91
+ @methods = [] # methods
92
+
93
+ # For method wnode only
94
+ @method = nil
95
+ @margs = [] # method args
96
+ @lvars = [] # local variables
97
+
98
+ # For insn wnode with
99
+ # label (.e.g block, loop)
100
+ @label = nil
101
+ end
102
+
103
+ def self.root
104
+ @@root
105
+ end
106
+
107
+ def root?
108
+ self == @@root
109
+ end
110
+
111
+ # Says whether this wnode produces a straight
112
+ # WASM const node in the end
113
+ def const?
114
+ self.template == :const || self.template == :addr
115
+ end
116
+
117
+ # set instruction template and args
118
+ def c(template, wargs = {})
119
+ raise "Error: unknown WASM code template (#{template})" unless T.has_key? template
120
+ raise "Error: this WNode is already populated with instruction #{@template}" if @template
121
+ if [:loop, :block].include? template
122
+ wargs[:label] = self.set_label
123
+ end
124
+ @template = template
125
+ @wargs = wargs
126
+ end
127
+
128
+ def wasm_code
129
+ @wargs[:wasm_type] ||= self.wasm_type
130
+ wargs = {}
131
+ @wargs.each { |k, v| wargs[k] = (v.is_a?(Proc) ? v.call : v) }
132
+ T[@template] ? T[@template] % wargs : ''
133
+ end
134
+
135
+ def wasm_type
136
+ @wtype.wasm_type
137
+ end
138
+
139
+ def set_label
140
+ @label = "$lbl_#{'%02d' % (@@label_index += 1)}"
141
+ end
142
+
143
+ # Specify the WASM type of this node
144
+ def wtype=(wtype)
145
+ raise "Expecting a WType argument (got #{wtype.inspect}" unless wtype.is_a? WType
146
+ logger.debug "Setting wtype #{wtype} for wnode #{self}"
147
+ @wtype = wtype
148
+ @method.wtype = @wtype if self.method?
149
+ # update wasm_type template arg accordingly
150
+ @wargs[:wasm_type] = @wtype.wasm_type if @wtype
151
+ logger.debug "type #{self.type} wargs #{self.wargs} wtype #{@wtype}"
152
+ @wtype
153
+ end
154
+
155
+ # Add a new child to current node at the
156
+ # end or at the beginning of the child list
157
+ def add_child(wnode, prepend=false)
158
+ #logger.debug "Adding #{wnode.object_id} to #{self.object_id} (children: #{self.children.map(&:object_id)})"
159
+ if prepend
160
+ self.children.unshift(wnode)
161
+ else
162
+ self.children << wnode
163
+ end
164
+ wnode.parent = self
165
+ #logger.debug "Added #{wnode.object_id} to #{self.object_id} (children: #{self.children.map(&:object_id)})"
166
+ #logger.debug "Parent of #{wnode.object_id} is now #{wnode.parent.object_id}"
167
+ self
168
+ end
169
+ alias :<< :add_child
170
+
171
+ # Remove child to current node
172
+ def remove_child(wnode)
173
+ logger.debug "Removing #{wnode.object_id} from #{self.children.map(&:object_id)}"
174
+ wn = self.children.delete(wnode) do
175
+ logger.error "Couldn't find wnode ID #{wnode.object_id} (#{wnode})"
176
+ raise
177
+ end
178
+ wn.parent = nil
179
+ #logger.debug "Removed #{wnode.object_id} from #{self.object_id} (children: #{self.children.map(&:object_id)})"
180
+ wn
181
+ end
182
+ alias :>> :remove_child
183
+
184
+ # Reparent self node to another wnode
185
+ def reparent_to(wnode)
186
+ return if self.parent == wnode
187
+ old_parent, new_parent = self.parent, wnode
188
+ new_parent << self
189
+ old_parent >> self if old_parent
190
+ end
191
+
192
+ # insert a blank wnode above self, so between self wnode
193
+ # and its parent (self -> parent becomes self -> wn -> parent)
194
+ def insert(wtype=:none)
195
+ wn = WNode.new(wtype, self.parent)
196
+ self.reparent_to(wn)
197
+ wn
198
+ end
199
+
200
+ # Set this node class name
201
+ def class_name=(class_name)
202
+ @klass_name = class_name
203
+ end
204
+
205
+ # Find class name in this node and up the tree
206
+ def class_name
207
+ (cn = self.class_wnode) ? cn.klass_name : nil
208
+ end
209
+
210
+ # Find class name in this node and up the tree
211
+ def class_size
212
+ (cn = self.class_wnode) ? cn.wattrs.sum(&:size) : nil
213
+ end
214
+
215
+ # Find the class wnode matching with the given
216
+ # class name
217
+ def find_class(class_name=nil)
218
+ if class_name
219
+ WNode.root.class_wnodes.find { |wn| wn.class_name == class_name }
220
+ else
221
+ self.class_wnode
222
+ end
223
+ end
224
+
225
+ # create a constant
226
+ def create_const(c_name, class_name, value, wtype)
227
+ class_name ||= self.class_name
228
+ if (cn = self.class_wnode)
229
+ cn.consts << (const = Const.new(class_name, c_name, value, wtype))
230
+ else
231
+ raise "No class found for class constant #{const}"
232
+ end
233
+ const
234
+ end
235
+
236
+ # Look for constant in the appropriate class wnode
237
+ # (it can be the current class or another class)
238
+ def find_const(c_name, class_name=nil)
239
+ wn_class = find_class(class_name)
240
+ raise "Can't find parent class for constant #{c_name}" unless wn_class
241
+ class_name = wn_class.class_name
242
+ logger.debug "looking for const #{c_name} in class #{class_name} at wnode #{self.class_wnode}..."
243
+ wn_class.consts.find { |c| c.class_name == class_name && c.name == c_name }
244
+ end
245
+
246
+ def find_or_create_const(c_name, class_name, value, wtype)
247
+ self.find_const(c_name, class_name) || self.create_const(c_name, class_name, value, wtype)
248
+ end
249
+
250
+ def find_wattr(wa_name, class_name=nil)
251
+ wn_class = find_class(class_name)
252
+ raise "Can't find parent class for wattr #{wa_name}" unless wn_class
253
+ class_name = wn_class.class_name
254
+ logger.debug "looking for wattr #{wa_name} in class #{class_name} at wnode #{self.class_wnode}..."
255
+ wn_class.wattrs.find { |wa| wa.class_name == class_name && wa.name == wa_name }
256
+ end
257
+
258
+ def create_wattr(wa_name, wtype=WType::DEFAULT)
259
+ if (cn = self.class_wnode)
260
+ logger.debug "creating wattr #{wa_name} in class #{self.class_name} at wnode #{self.class_wnode}..."
261
+ cn.wattrs << (wattr = WAttr.new(cn, wa_name, wtype))
262
+ else
263
+ raise "No class found for class attribute #{wa_name}"
264
+ end
265
+ wattr
266
+ end
267
+
268
+ def create_cvar(cv_name, value=0, wtype=WType::DEFAULT)
269
+ if (cn = self.class_wnode)
270
+ logger.debug "creating cvar #{cv_name} in class #{self.class_name} at wnode #{self.class_wnode}..."
271
+ cn.cvars << (cvar = CVar.new(cn.klass_name, cv_name, value, wtype))
272
+ else
273
+ raise "No class found for class variable #{cv_name}"
274
+ end
275
+ cvar
276
+ end
277
+
278
+ def find_cvar(cv_name)
279
+ logger.debug "looking for cvar #{cv_name} in class #{self.class_name} at wnode #{self.class_wnode}..."
280
+ self.class_wnode.cvars.find { |cv| cv.class_name == self.class_name && cv.name == cv_name }
281
+ end
282
+
283
+ def create_lvar(name)
284
+ if (mn = self.method_wnode)
285
+ mn.lvars << (lvar = LVar.new(name))
286
+ else
287
+ raise "No method found for local variable #{name}"
288
+ end
289
+ lvar
290
+ end
291
+
292
+ def find_lvar(name)
293
+ self.method_wnode.lvars.find { |lv| lv.name == name }
294
+ end
295
+
296
+ def find_or_create_lvar(name)
297
+ self.find_lvar(name) || self.create_lvar(name)
298
+ end
299
+
300
+ # add method argument
301
+ def create_marg(name)
302
+ if (mn = self.method_wnode)
303
+ mn.margs << (marg = MArg.new(name))
304
+ else
305
+ raise "No class found for class variable #{marg}"
306
+ end
307
+ marg
308
+ end
309
+
310
+ def find_marg(name)
311
+ self.method_wnode.margs.find { |ma| ma.name == name }
312
+ end
313
+
314
+ # method_type is either :instance or :class
315
+ def create_method(method_name, class_name, wtype, method_type)
316
+ raise "MEthod already exists: #{m}" \
317
+ if (m = find_method(method_name, class_name, method_type))
318
+ if (cn = self.class_wnode)
319
+ class_name ||= cn.klass_name
320
+ cn.methods << (method = MEthod.new(method_name, class_name, wtype))
321
+ else
322
+ raise "No class wnode found to create method #{method_name}"
323
+ end
324
+ method_type == :class ? method.class! : method.instance!
325
+ logger.debug "Created MEthod: #{method.inspect}"
326
+ method
327
+ end
328
+
329
+ # method_type is either :instance or :class
330
+ def find_method(method_name, class_name, method_type)
331
+ if class_name
332
+ class_wnode = find_class(class_name)
333
+ else
334
+ class_wnode = self.class_wnode
335
+ end
336
+ raise "Couldn't find class wnode for class_name #{class_name}" unless class_wnode
337
+ class_name = class_wnode.klass_name
338
+ if method_type == :class
339
+ method = class_wnode.methods.find { |m| m.name == method_name && m.class_name == class_name && m.class? }
340
+ elsif method_type == :instance
341
+ method = class_wnode.methods.find { |m| m.name == method_name && m.class_name == class_name && m.instance? }
342
+ else
343
+ raise "Unknown method type : #{method_type.inspect}"
344
+ end
345
+ if method
346
+ logger.debug "Found MEthod: #{method.inspect}"
347
+ else
348
+ logger.debug "Couldn't find MEthod: #{class_name.inspect},#{method_name.inspect}"
349
+ end
350
+ method
351
+ end
352
+
353
+ def find_or_create_method(method_name, class_name=nil, method_type=:class)
354
+ self.find_method(method_name, class_name, method_type) || \
355
+ self.create_method(method_name, class_name, WType::DEFAULT, method_type)
356
+ end
357
+
358
+ # Find block wnode up the tree
359
+ def block_wnode
360
+ if self.template == :block
361
+ self
362
+ else
363
+ @parent ? @parent.block_wnode : nil
364
+ end
365
+ end
366
+
367
+ # Find loop wnode up the tree
368
+ def loop_wnode
369
+ if self.template == :loop
370
+ self
371
+ else
372
+ @parent ? @parent.loop_wnode : nil
373
+ end
374
+ end
375
+
376
+ # Find class wnode up the tree
377
+ def class_wnode
378
+ if self.class?
379
+ self
380
+ else
381
+ @parent ? @parent.class_wnode : nil
382
+ end
383
+ end
384
+
385
+ # Find method wnode up the tree
386
+ def method_wnode
387
+ if self.method?
388
+ self
389
+ else
390
+ @parent ? @parent.method_wnode : nil
391
+ end
392
+ end
393
+
394
+ def scope
395
+ return :class_method if self.in_class_method_scope?
396
+ return :instance_method if self.in_instance_method_scope?
397
+ return :class if self.in_class_scope?
398
+ return :root if self.in_root_scope?
399
+ end
400
+
401
+ def in_method_scope?
402
+ !self.method_wnode.nil?
403
+ end
404
+
405
+ def in_class_method_scope?
406
+ !self.method_wnode.nil? && !self.method_wnode.method.instance?
407
+ end
408
+
409
+ def in_instance_method_scope?
410
+ !self.method_wnode.nil? && self.method_wnode.method.instance?
411
+ end
412
+
413
+ def in_class_scope?
414
+ !self.class_wnode.nil? && self.method_wnode.nil?
415
+ end
416
+
417
+ def in_root_scope?
418
+ self.root? || self.parent.root?
419
+ end
420
+
421
+ def method?
422
+ self.type == :method
423
+ end
424
+
425
+ def class?
426
+ self.type == :class
427
+ end
428
+
429
+ # format the wnode and tree below
430
+ # Note: this a just a tree dump. The output generated is
431
+ # not valid WAT code
432
+ def to_s(indent=0)
433
+ "\n%sw(%s:%s" % [' '*2*indent, self.type, self.wasm_code] + self.children.map { |wn| wn.to_s(indent+1) }.join('') + ')'
434
+ end
435
+
436
+ # Generate WAT code starting for this node and tree branches below
437
+ def transpile(indent=0)
438
+ case @type
439
+ when :insn, :method
440
+ if @template == :inline
441
+ "\n%s%s" % [' '*2*indent, self.wasm_code]
442
+ else
443
+ "\n%s(%s" % [' '*2*indent, self.wasm_code] + self.children.map { |wn| wn.transpile(indent+1) }.join('') + ')'
444
+ end
445
+ when :root, :class, :none
446
+ # no WAT code to generate for these nodes. Process children directly.
447
+ self.children.map { |wn| wn.transpile(indent) }.join('')
448
+ else
449
+ raise "Error: Unknown wnode type #{@type}. No WAT code generated"
450
+ end
451
+
452
+ end
453
+ end
454
+ end
455
+
@@ -0,0 +1,19 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+ #
5
+ # Node tree structure supporting the construction
6
+ # of the WASM Abstract Syntax Tree
7
+
8
+ require_relative './wnode'
9
+
10
+ module Rlang::Parser
11
+ class WTree
12
+ attr_reader :root
13
+
14
+ def initialize
15
+ @root = WNode.new(:root)
16
+ end
17
+ end
18
+ end
19
+
@@ -0,0 +1,116 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+
5
+ # WType class. This class manages the wtype
6
+ # of Wnode objects. This is a higher level
7
+ # representation of WASM types
8
+ #
9
+ # A wtype can be initialized with values like
10
+ # - :none or :nil : no type defined
11
+ # - A legit WASM type :I32, : I64,....
12
+ # - or a Rlang class name
13
+
14
+ require_relative '../../utils/log'
15
+ require_relative './ext/type'
16
+
17
+ class WType
18
+
19
+ WASM_TYPE_MAP = {
20
+ I64: Type::I64,
21
+ I32: Type::I32,
22
+ F64: Type::F64,
23
+ F32: Type::F32
24
+ }
25
+
26
+ # Implicit Type cast order in decreasing order of precedence
27
+ # Class types have precedence over default integer type
28
+ # because of pointer arithmetics
29
+ # TODO: :Class should be inserted before WType::DEFAULT
30
+ CAST_PRECEDENCE = [:F64, :F32, :I64, :Class, :I32]
31
+
32
+ attr_reader :name
33
+
34
+ def self.legit?(name)
35
+ WASM_TYPE_MAP.has_key? name
36
+ end
37
+
38
+ def self.leading(wtypes)
39
+ logger.debug "wtypes: #{wtypes}"
40
+ # The compact below is to remove the nil
41
+ # when wtype is blank or class
42
+ leading_idx = wtypes.map {|wt| wt.class? ? :Class : wt.name}. \
43
+ map {|wtn| CAST_PRECEDENCE.index(wtn) }.compact.each_with_index.min.last
44
+ wtypes[leading_idx]
45
+ end
46
+
47
+ def initialize(name)
48
+ @name = name.to_sym
49
+ raise "Invalid WType #{name.inspect}" unless self.valid?
50
+ end
51
+
52
+ def default?
53
+ @name == WType::DEFAULT.name
54
+ end
55
+
56
+ def valid?
57
+ self.blank? || self.native? || self.class?
58
+ end
59
+
60
+ def native?
61
+ WASM_TYPE_MAP.has_key? @name
62
+ end
63
+
64
+ def blank?
65
+ @name == :none || @name == :nil
66
+ end
67
+
68
+ def class?
69
+ # name starts with uppercase and is not a native type
70
+ !self.native? && ('A'..'Z').include?(@name.to_s[0])
71
+ end
72
+
73
+ def ==(other)
74
+ @name == other.name
75
+ end
76
+
77
+ def size
78
+ if self.blank?
79
+ 0
80
+ elsif self.native?
81
+ WASM_TYPE_MAP[@name].size
82
+ else
83
+ Type::DEFAULT.size
84
+ end
85
+ end
86
+
87
+ # returns a String with the proper WASM type
88
+ # that can be used for code generation
89
+ def wasm_type
90
+ if self.blank?
91
+ ''
92
+ elsif self.native?
93
+ WASM_TYPE_MAP[@name].wasm_type
94
+ elsif self.class?
95
+ # it's a class name return DEFAULT WASM
96
+ # type because this is always the class of
97
+ # amemory address in VASM VM
98
+ Type::DEFAULT.wasm_type
99
+ else
100
+ raise "Unknown WType #{self.inspect}"
101
+ end
102
+ end
103
+
104
+ def to_s
105
+ ":#{@name}"
106
+ end
107
+
108
+ def inspect
109
+ self.to_s
110
+ end
111
+
112
+ DEFAULT = self.new(WASM_TYPE_MAP.key(Type::DEFAULT))
113
+
114
+ end
115
+
116
+