rlang 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
+