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,1842 @@
1
+ # Rubinius WebAssembly VM
2
+ # Copyright (c) 2019, Laurent Julliard and contributors
3
+ # All rights reserved.
4
+
5
+ # Rlang parser
6
+ # Rlang is a subset of the Ruby language that can be transpiled
7
+ # to WAT and then compiled to WASM. The Rubinius WASM virtual
8
+ # machine is written in Rlang.
9
+
10
+ # TODO: write a short documentation about what subset of Ruby is
11
+ # supported in Rlang
12
+
13
+ require 'parser/current'
14
+ require 'pathname'
15
+ require_relative '../utils/log'
16
+ require_relative './parser/wtype'
17
+ require_relative './parser/wtree'
18
+ require_relative './parser/wnode'
19
+ require_relative './parser/cvar'
20
+ require_relative './parser/lvar'
21
+ require_relative './parser/marg'
22
+ require_relative './parser/global'
23
+ require_relative './parser/data'
24
+ require_relative './parser/wgenerator'
25
+
26
+ module Rlang::Parser
27
+ class Parser
28
+
29
+ include Log
30
+
31
+ ARITHMETIC_OPS = [:+, :-, :*, :/, :%, :&, :|, :^, :>>, :<<]
32
+ RELATIONAL_OPS = [:==, :!=, :>, :<, :>=, :<=, :'>s', :'<s', :'>=s', :'>=s']
33
+ UNARY_OPS = [:'!']
34
+
35
+ # Type cast order in decreading order of precedence
36
+ TYPE_CAST_PRECEDENCE = [Type::F64, Type::F32, Type::I64, Type::I32]
37
+
38
+ # WARNING!! THIS IS A **VERY** NASTY HACK PRETENDING
39
+ # THAT THIS int VALUE means NIL. It's totally unsafe
40
+ # of course as an expression could end up evaluating
41
+ # to this value and not be nil at all. But I'm using
42
+ # it for now in the xxxx_with_result_type variants of
43
+ # some parsing methods (if, while,...)
44
+ NIL = 999999999
45
+
46
+ # export toggle for method declaration
47
+ @@export = false
48
+
49
+
50
+ attr_accessor :wgenerator, :source, :config
51
+
52
+ def initialize(wgenerator)
53
+ @wgenerator = wgenerator
54
+ # LIFO of parsed files (stacked by require)
55
+ @requires = []
56
+ config_init
57
+ end
58
+
59
+ def config_init
60
+ @config = {}
61
+ @config[:LOADED_FEATURES] = []
62
+ @config[:LOAD_PATH] = ''
63
+ @config[:__FILE__] = ''
64
+ end
65
+
66
+ # Note : this method can be called recursively
67
+ # through require statements
68
+ def parse_file(file)
69
+ raise "parse_file only acccepts absolute path (got #{file})" \
70
+ unless Pathname.new(file).absolute? || file.nil?
71
+ # Already parsed. Ignore.
72
+ if self.config[:LOADED_FEATURES].include? file
73
+ logger.debug "File already loaded."
74
+ return
75
+ end
76
+
77
+ # Set file currently parsed
78
+ # Maintain a list of embedded require's
79
+ @requires << (@config[:__FILE__] = file) if file
80
+
81
+ # Parse file
82
+ source = file ? File.open(file) : STDIN
83
+ self.parse(File.read(source) )
84
+ # Ff parsing went to completion then add this
85
+ # file to the list of successfully loaded files
86
+ (@config[:LOADED_FEATURES] ||= []) << file if file
87
+ # and go back to previously parsed file
88
+ if file
89
+ @requires.pop
90
+ @config[:__FILE__] = @requires.last
91
+ end
92
+ end
93
+
94
+ def parse(source)
95
+ ast = ::Parser::CurrentRuby.parse(source)
96
+ parse_node(ast, @wgenerator.root) if ast
97
+ end
98
+
99
+ # Parse Ruby AST node and generate WAT
100
+ # code as a child of wnode
101
+ # - node: the Ruby AST node to parse
102
+ # - wnode: the parent wnode for the generated WAT code
103
+ # - keep_eval: whether to keep the value of the evaluated
104
+ # WAT expression on stock or not
105
+ def parse_node(node, wnode, keep_eval=true)
106
+ logger.debug "\n---------------------->>\n" +
107
+ "Parsing node: #{node}, wnode: #{wnode}, keep_eval: #{keep_eval}"
108
+
109
+ case node.type
110
+ when :self
111
+ wn = parse_self(node, wnode)
112
+
113
+ when :class
114
+ wn = parse_class(node, wnode)
115
+
116
+ when :defs
117
+ wn = parse_defs(node, wnode, keep_eval)
118
+
119
+ when :def
120
+ wn = parse_def(node, wnode, keep_eval)
121
+
122
+ when :begin
123
+ wn = parse_begin(node, wnode, keep_eval)
124
+
125
+ when :casgn
126
+ wn = parse_casgn(node, wnode, keep_eval)
127
+
128
+ when :cvasgn
129
+ wn = parse_cvasgn(node, wnode, keep_eval)
130
+
131
+ when :gvasgn
132
+ wn = parse_gvasgn(node, wnode, keep_eval)
133
+
134
+ when :lvasgn
135
+ wn = parse_lvasgn(node, wnode, keep_eval)
136
+
137
+ when :op_asgn
138
+ wn = parse_op_asgn(node, wnode, keep_eval)
139
+
140
+ when :lvar
141
+ wn = parse_lvar(node, wnode, keep_eval)
142
+
143
+ when :ivar, :ivasgn
144
+ raise "Instance variable not supported"
145
+
146
+ when :cvar
147
+ wn = parse_cvar(node, wnode, keep_eval)
148
+
149
+ when :gvar
150
+ wn = parse_gvar(node, wnode, keep_eval)
151
+
152
+ when :int
153
+ wn = parse_int(node, wnode, keep_eval)
154
+
155
+ when :float
156
+ raise "float instructions not supported"
157
+ #parse_float(node, wnode, keep_eval)
158
+
159
+ when :nil
160
+ raise "nil not supported"
161
+
162
+ when :const
163
+ wn = parse_const(node, wnode, keep_eval)
164
+
165
+ when :send
166
+ wn = parse_send(node, wnode, keep_eval)
167
+
168
+ when :return
169
+ wn = parse_return(node, wnode, keep_eval)
170
+
171
+ when :if
172
+ #parse_if_with_result_type(node, wnode, keep_eval)
173
+ wn = parse_if_without_result_type(node, wnode, keep_eval)
174
+
175
+ when :while
176
+ #parse_while_with_result_type(node, wnode, keep_eval)
177
+ wn = parse_while_without_result_type(node, wnode, keep_eval)
178
+
179
+ when :until
180
+ #parse_while_with_result_type(node, wnode, keep_eval)
181
+ wn = parse_while_without_result_type(node, wnode, keep_eval)
182
+
183
+ when :break
184
+ wn = parse_break(node, wnode, keep_eval)
185
+
186
+ when :next
187
+ wn = parse_next(node, wnode, keep_eval)
188
+
189
+ when :or, :and
190
+ wn = parse_logical_op(node, wnode, keep_eval)
191
+
192
+ when :true
193
+ wn = parse_true(node, wnode, keep_eval)
194
+
195
+ when :false
196
+ wn = parse_false(node, wnode, keep_eval)
197
+
198
+ else
199
+ raise "Unknown node type: #{node.type} => #{node}"
200
+ end
201
+ logger.debug "\n----------------------<<\n" +
202
+ "End parsing node: #{node}, parent wnode: #{wnode}, keep_eval: #{keep_eval}\n generated wnode #{wn}" +
203
+ "\n----------------------<<\n"
204
+ wn
205
+ end
206
+
207
+ def parse_begin(node, wnode, keep_eval)
208
+ child_count = node.children.count
209
+ logger.debug "child count: #{child_count}"
210
+ wn = nil
211
+ node.children.each_with_index do |n, idx|
212
+ logger.debug "processing begin node ##{idx}..."
213
+ # A begin block always evaluates to the value of
214
+ # its **last** child except
215
+ if (idx == child_count-1)
216
+ local_keep_eval = keep_eval
217
+ else
218
+ local_keep_eval = false
219
+ end
220
+ logger.debug "node idx: #{idx}/#{child_count-1}, wnode type: #{wnode.type}, keep_eval: #{keep_eval}, local_keep_eval: #{local_keep_eval}"
221
+ wn = parse_node(n, wnode, local_keep_eval)
222
+ logger.debug "in begin: parsing node #{n} gives wnode #{wn}"
223
+ end
224
+ return wn # return last wnode
225
+ end
226
+
227
+ # Example:
228
+ # class Stack
229
+ # ... body ...
230
+ # end
231
+ # -----
232
+ # [s(:const, nil, :Stack), nil, s(:begin ... body.... )]
233
+ #
234
+ def parse_class(node, wnode)
235
+ const_node = node.children.first
236
+ body_node = node.children.last
237
+ raise "expecting a constant for class name (got #{const_node})" \
238
+ unless const_node.type == :const
239
+
240
+ wn_class = @wgenerator.klass(wnode, const_node.children.last)
241
+ parse_node(body_node, wn_class) if body_node
242
+
243
+ # now that we finished parsing the class node
244
+ # process the class attributes if any
245
+ @wgenerator.attributes(wn_class)
246
+ return wn_class
247
+ end
248
+
249
+ # TODO: the code for op_asgn is quite murky but I thought
250
+ # we would use quite often so I implemented it. We could do
251
+ # without it though..
252
+ #
253
+ # Example (local var)
254
+ # arg1 *= 20
255
+ # ---
256
+ # (op-asgn
257
+ # (lvasgn :arg1)
258
+ # :*
259
+ # (int 20)) )
260
+ #
261
+ # Example (class var)
262
+ # @@stack_ptr -= nbytes
263
+ # ---
264
+ # s(:op_asgn,
265
+ # s(:cvasgn, :@@stack_ptr), :-, s(:lvar, :nbytes))
266
+ #
267
+ # Example (global var)
268
+ # $MYGLOBAL -= nbytes
269
+ # ---
270
+ # s(:op_asgn,
271
+ # s(:gvasgn, :$MYGLOBAL), :-, s(:lvar, :nbytes))
272
+ #
273
+ #
274
+ # Example (setter/getter)
275
+ # p.size -= nunits
276
+ # ---
277
+ # (op-asgn
278
+ # (send
279
+ # (lvar :p) :size) :-
280
+ # (lvar :nunits))
281
+ #
282
+ # **** DEPRECATED FORM OF GLOBALS *****
283
+ # Example (Global)
284
+ # Global[:$DEBUG] += 1
285
+ # ---
286
+ # (op-asgn
287
+ # (send
288
+ # (const nil :Global) :[] (sym :$DEBUG))
289
+ # :+
290
+ # (int 1)))
291
+ #
292
+ def parse_op_asgn(node, wnode, keep_eval)
293
+ logger.debug "wnode: #{wnode}, keep_eval: #{keep_eval}"
294
+
295
+ case node.children.first.type
296
+ # Global variable case
297
+ when :gvasgn
298
+ var_asgn_node, op, exp_node = *node.children
299
+ var_name = var_asgn_node.children.last
300
+
301
+ # parse the variable setting part
302
+ # Note: this will also create the variable setting
303
+ # wnode as a child of wnode
304
+ wn_var_set = parse_node(var_asgn_node, wnode, keep_eval)
305
+ gvar = Global.find(var_name)
306
+ raise "Unknown global variable #{var_name}" unless gvar
307
+
308
+ # Create the operator node (infer operator type from variable)
309
+ wn_op = @wgenerator.operator(wn_var_set, op, gvar.wtype)
310
+ # Create the var getter node as a child of operator node
311
+ wn_var_get = @wgenerator.gvar(wn_op, gvar)
312
+
313
+ # Class variable case
314
+ when :cvasgn
315
+ var_asgn_node, op, exp_node = *node.children
316
+ var_name = var_asgn_node.children.last
317
+
318
+ # parse the variable setting part
319
+ # Note: this will also create the variable setting
320
+ # wnode as a child of wnode
321
+ wn_var_set = parse_node(var_asgn_node, wnode, keep_eval)
322
+ cvar = wnode.find_cvar(var_name)
323
+ raise "Unknown class variable #{var_name}" unless cvar
324
+
325
+ # Create the operator node (infer operator type from variable)
326
+ wn_op = @wgenerator.operator(wn_var_set, op, cvar.wtype)
327
+ # Create the var getter node as a child of operator node
328
+ wn_var_get = @wgenerator.cvar(wn_op, cvar)
329
+
330
+ # Local variable case
331
+ when :lvasgn
332
+ var_asgn_node, op, exp_node = *node.children
333
+ var_name = var_asgn_node.children.last
334
+
335
+ # parse the variable setter node
336
+ # Note: this will also create the variable setting
337
+ # wnode as a child of wnode
338
+ wn_var_set = parse_node(var_asgn_node, wnode, keep_eval)
339
+ lvar = wnode.find_lvar(var_name) || wnode.find_marg(var_name)
340
+ raise "Unknown local variable #{var_name}" unless lvar
341
+
342
+ # Create the operator node (infer operator type from variable)
343
+ wn_op = @wgenerator.operator(wn_var_set, op, lvar.wtype)
344
+ # Create the var getter node as a child of operator node
345
+ wn_var_get = @wgenerator.lvar(wn_op, lvar)
346
+
347
+ # setter/getter case
348
+ # Example (setter/getter)
349
+ # p.size -= nunits
350
+ # ---
351
+ # (op-asgn
352
+ # (send
353
+ # (lvar :p) :size) :-
354
+ # (lvar :nunits))
355
+ when :send
356
+ send_node, op, exp_node = *node.children
357
+ recv_node, method_name = *send_node.children
358
+
359
+ # Parse the receiver node ((lvar :p) in the example)
360
+ # above to get its wtype
361
+ # Force keep_eval to true whatever upper level
362
+ # keep_eval says
363
+ wn_recv_node = parse_node(recv_node, wnode, true)
364
+
365
+ # Create the top level setter call
366
+ wn_var_set = @wgenerator.call(wnode, wn_recv_node.wtype.name, "#{method_name}=", :instance)
367
+
368
+ # First argument of the setter must be the recv_node
369
+ wn_recv_node.reparent_to(wn_var_set)
370
+
371
+ # Second argument of the setter is the operator wnode
372
+ # Create it with wtype :none for now. We'll fix that
373
+ # with the operands call later on
374
+ wn_op = @wgenerator.operator(wn_var_set, op)
375
+
376
+ # Parsing the send node will create the getter wnode
377
+ # this is the first argument of the operator wnode,
378
+ # the second is wn_exp below
379
+ # Force keep_eval to true whatever upper level
380
+ # keep_eval says
381
+ wn_var_get = parse_node(send_node, wn_op, true)
382
+
383
+ # If the setter returns something and last evaluated value
384
+ # must be ignored then drop it
385
+ unless keep_eval && !wn_var_set.wtype.blank?
386
+ @wgenerator.drop(wnode)
387
+ #@wgenerator.call(wnode, wn_recv_node.wtype.name, "#{method_name}", :instance)
388
+ end
389
+ else
390
+ raise "op_asgn not supported for #{node.children.first}"
391
+ end
392
+
393
+ # Finally, parse the expression node and make it
394
+ # the second child of the operator node
395
+ # Last evaluated value must be kept of course
396
+ wn_exp = parse_node(exp_node, wn_op, true)
397
+
398
+ # And process operands (cast and such...)
399
+ @wgenerator.operands(wn_op, wn_var_get, [wn_exp])
400
+
401
+ return wn_var_set
402
+ end
403
+
404
+ # Example
405
+ # MYCONST = 2000
406
+ # ---
407
+ # (casgn nil :MYCONST
408
+ # (int 2000))
409
+ def parse_casgn(node, wnode, keep_eval)
410
+ class_name_node, constant_name, exp_node = *node.children
411
+ raise "dynamic constant assignment" unless wnode.in_class_scope?
412
+ raise "constant initialization can only take a number" unless exp_node.type == :int
413
+
414
+ unless class_name_node.nil?
415
+ raise "constant assignment with class path not supported (got #{class_name_node})"
416
+ end
417
+ if wnode.find_const(constant_name)
418
+ raise "constant #{constant_name} already initialized"
419
+ end
420
+
421
+ # TODO: const are I32 hardcoded for now. Must find a way to
422
+ # initialize I64 constant too
423
+ value = exp_node.children.last
424
+ const = wnode.create_const(constant_name, nil, value, WType::DEFAULT)
425
+
426
+ wn_casgn = @wgenerator.casgn(wnode, const)
427
+ return wn_casgn
428
+ end
429
+
430
+ # Example
431
+ # $MYGLOBAL = 2000
432
+ # ---
433
+ # (gvasgn :MYGLOBAL
434
+ # (int 2000))
435
+ #
436
+ # Example with type cast
437
+ # $MYGLOBAL = 2000.to_I64
438
+ # ---
439
+ # (gvasgn :MYGLOBAL
440
+ # (send (int 2000) :to_I64))
441
+ def parse_gvasgn(node, wnode, keep_eval)
442
+ gv_name, exp_node = *node.children
443
+ gvar = Global.find(gv_name)
444
+
445
+ if wnode.in_method_scope?
446
+ # if exp_node is nil then this is the form of
447
+ # :gvasgn that comes from op_asgn
448
+ if exp_node
449
+ if gvar.nil?
450
+ # first gvar occurence
451
+ # type cast the gvar to the wtype of the expression
452
+ gvar = Global.new(gv_name)
453
+ # Do not export global for now
454
+ #gvar.export! if self.config[:export_all]
455
+ wn_gvasgn = @wgenerator.gvasgn(wnode, gvar)
456
+ wn_exp = parse_node(exp_node, wn_gvasgn)
457
+ gvar.wtype = wn_exp.wtype
458
+ else
459
+ # if gvar already exists then type cast the
460
+ # expression to the wtype of the existing gvar
461
+ wn_gvasgn = @wgenerator.gvasgn(wnode, gvar)
462
+ wn_exp = parse_node(exp_node, wn_gvasgn)
463
+ @wgenerator.cast(wn_exp, gvar.wtype, false)
464
+ end
465
+ else
466
+ raise "Global variable #{cv_name} not declared before" unless gvar
467
+ wn_gvasgn = @wgenerator.gvasgn(wnode, gvar)
468
+ end
469
+ # to mimic Ruby push the variable value on stack if needed
470
+ @wgenerator.gvar(wnode, gvar) if keep_eval
471
+ return wn_gvasgn
472
+ else
473
+ # If we are at root or in class scope
474
+ # then it is a global variable initialization
475
+ raise "Global #{gv_name} already declared" if gvar
476
+ raise "Global op_asgn can only happen in method scope" unless exp_node
477
+ # In the class or root scope
478
+ # it can only be a Global var **declaration**
479
+ # In this case the expression has to reduce
480
+ # to a const wnode that can be used as value
481
+ # in the declaration (so it could for instance
482
+ # Global[:label] = 10 or Global[:label] = 10.to_I64)
483
+ # Then remove the generated wnode because it is not for
484
+ # execution. It is just to get the init value
485
+ wn_exp = parse_node(exp_node, wnode)
486
+ raise "Global initializer can only be a straight number" \
487
+ unless wn_exp.const?
488
+ wnode.remove_child(wn_exp)
489
+ gvar = Global.new(gv_name, wn_exp.wtype, wn_exp.wargs[:value])
490
+ # Do not export global for now
491
+ #gvar.export! if self.config[:export_all]
492
+ end
493
+ end
494
+
495
+ # Example
496
+ # @@stack_ptr = 10 + nbytes
497
+ # ---
498
+ # s(:cvasgn, :@@stack_ptr, s(:send, s(:int, 10), :+, s(:lvar, :nbytes)))
499
+ def parse_cvasgn(node, wnode, keep_eval)
500
+ cv_name, exp_node = *node.children
501
+ cvar = wnode.find_cvar(cv_name)
502
+
503
+ if wnode.in_method_scope?
504
+ # if exp_node is nil then this is the form of
505
+ # :cvasgn that comes from op_asgn
506
+ if exp_node
507
+ if cvar.nil?
508
+ # first cvar occurence
509
+ # type cast the cvar to the wtype of the expression
510
+ cvar = wnode.create_cvar(cv_name)
511
+ wn_cvasgn = @wgenerator.cvasgn(wnode, cvar)
512
+ wn_exp = parse_node(exp_node, wn_cvasgn)
513
+ cvar.wtype = wn_exp.wtype
514
+ else
515
+ # if cvar already exists then type cast the
516
+ # expression to the wtype of the existing cvar
517
+ wn_cvasgn = @wgenerator.cvasgn(wnode, cvar)
518
+ wn_exp = parse_node(exp_node, wn_cvasgn)
519
+ @wgenerator.cast(wn_exp, cvar.wtype, false)
520
+ end
521
+ else
522
+ raise "Class variable #{cv_name} not declared before" unless cvar
523
+ wn_cvasgn = @wgenerator.cvasgn(wnode, cvar)
524
+ end
525
+ # to mimic Ruby push the variable value on stack if needed
526
+ @wgenerator.cvar(wnode, cvar) if keep_eval
527
+ return wn_cvasgn
528
+
529
+ elsif wnode.in_class_scope?
530
+ # If we are in class scope
531
+ # then it is a class variable initialization
532
+ raise "Class variable #{cv_name} already declared" if cvar
533
+ raise "Class variable op_asgn can only happen in method scope" unless exp_node
534
+ # Parse the expression node to see if it's a ixx.const
535
+ # in the end but get rid of it then because we are not
536
+ # executing this code. Just statically initiliazing the
537
+ #cvar with the value
538
+ wn_exp = parse_node(exp_node, wnode)
539
+ raise "Class variable initializer can only be a straight number (got #{wn_exp}" \
540
+ unless wn_exp.const?
541
+ cvar = wnode.create_cvar(cv_name, wn_exp.wargs[:value], wn_exp.wtype)
542
+ wnode.remove_child(wn_exp)
543
+ logger.debug "Class variable #{cv_name} initialzed with value #{cvar.value} and wtype #{cvar.wtype}"
544
+ else
545
+ raise "Class variable can only be defined in method or class scope"
546
+ end
547
+ end
548
+
549
+ # Regular Example
550
+ # var1 = @@stack_ptr + nbytes
551
+ # ---
552
+ # s(:lvasgn, :var1, s(:send, s(:cvar, :@@stack_ptr), :+, s(:lvar, :nbytes)))
553
+ #
554
+ # Example coming from an op_asgn node
555
+ # arg1 += 2
556
+ # ---
557
+ # s(s(lvasgn, :arg1), :+, s(int 2)))
558
+ def parse_lvasgn(node, wnode, keep_eval)
559
+ lv_name, exp_node = *node.children
560
+ lvar = wnode.find_lvar(lv_name) || wnode.find_marg(lv_name)
561
+
562
+ logger.debug "Assign to #{lv_name}, exp_node: #{exp_node}, keep_eval: #{keep_eval}"
563
+ logger.debug "lvar found: #{lvar}"
564
+
565
+ # if exp_node is nil then this is the form of
566
+ # :lvasgn that comes from op_asgn
567
+ if exp_node
568
+ if lvar.nil?
569
+ # first lvar occurence
570
+ # type cast the lvar to the wtype of the expression
571
+ lvar = wnode.create_lvar(lv_name)
572
+ wn_lvasgn = @wgenerator.lvasgn(wnode, lvar)
573
+ wn_exp = parse_node(exp_node, wn_lvasgn)
574
+ lvar.wtype = wn_exp.wtype
575
+ else
576
+ # if cvar already exists then type cast the
577
+ # expression to the wtype of the existing cvar
578
+ wn_lvasgn = @wgenerator.lvasgn(wnode, lvar)
579
+ wn_exp = parse_node(exp_node, wn_lvasgn)
580
+ @wgenerator.cast(wn_exp, lvar.wtype, false)
581
+ end
582
+ else
583
+ raise "Local variable #{cv_name} not declared before" unless lvar
584
+ wn_lvasgn = @wgenerator.lvasgn(wnode, lvar)
585
+ end
586
+ # to mimic Ruby push the variable value on stack if needed
587
+ @wgenerator.lvar(wnode, lvar) if keep_eval
588
+ return wn_lvasgn
589
+ end
590
+
591
+ # Example
592
+ # ... $MYGLOBAL
593
+ # ---
594
+ # ... s(:gvar, :$MYGLOBAL)
595
+ def parse_gvar(node, wnode, keep_eval)
596
+ gv_name, = *node.children
597
+ gvar = Global.find(gv_name)
598
+ raise "Unknown Global variable #{gv_name}" unless gvar
599
+ wn_gvar = @wgenerator.gvar(wnode, gvar)
600
+ # Drop last evaluated result if asked to
601
+ @wgenerator.drop(wnode) unless keep_eval
602
+ return wn_gvar
603
+ end
604
+
605
+ # Example
606
+ # ... @@stack_ptr
607
+ # ---
608
+ # ... s(:cvar, :@@stack_ptr)
609
+ def parse_cvar(node, wnode, keep_eval)
610
+ raise "Class variable can only be accessed in method scope" \
611
+ unless wnode.in_method_scope?
612
+ cv_name, = *node.children
613
+ if (cvar = wnode.find_cvar(cv_name))
614
+ wn_cvar = @wgenerator.cvar(wnode, cvar)
615
+ else
616
+ raise "unknown class variable #{cv_name}"
617
+ end
618
+ # Drop last evaluated result if asked to
619
+ @wgenerator.drop(wnode) unless keep_eval
620
+ return wn_cvar
621
+ end
622
+
623
+ # Example
624
+ # ... nbytes
625
+ # ---
626
+ # ... s(:lvar, :nbytes)
627
+ def parse_lvar(node, wnode, keep_eval)
628
+ logger.debug("node: #{node}, wnode: #{wnode}, keep_eval: #{keep_eval}")
629
+
630
+ lv_name, = *node.children
631
+ if (lvar = wnode.find_lvar(lv_name) || wnode.find_marg(lv_name))
632
+ wn_lvar = @wgenerator.lvar(wnode, lvar)
633
+ else
634
+ raise "unknown local variable #{lv_name}"
635
+ end
636
+ logger.debug "wnode: #{wnode}"
637
+ # Drop last evaluated result if asked to
638
+ @wgenerator.drop(wnode) unless keep_eval
639
+ return wn_lvar
640
+ end
641
+
642
+ def parse_int(node, wnode, keep_eval)
643
+ value, = *node.children
644
+ logger.debug "int: #{value} for parent wnode #{wnode} keep_eval:#{keep_eval}"
645
+ wn_int = @wgenerator.int(wnode, WType::DEFAULT, value)
646
+ # Drop last evaluated result if asked to
647
+ @wgenerator.drop(wnode) unless keep_eval
648
+
649
+ logger.debug "wn_int:#{wn_int} wtype:#{wn_int.wtype} keep_eval:#{keep_eval}"
650
+ return wn_int
651
+ end
652
+
653
+ def parse_true(node, wnode, keep_eval)
654
+ wn_true = @wgenerator.int(wnode, WType::DEFAULT, 1)
655
+ # Drop last evaluated result if asked to
656
+ @wgenerator.drop(wnode) unless keep_eval
657
+
658
+ logger.debug "wn_true:#{wn_true} wtype:#{wn_true.wtype} keep_eval:#{keep_eval}"
659
+ return wn_true
660
+ end
661
+
662
+ def parse_false(node, wnode, keep_eval)
663
+ wn_false = @wgenerator.int(wnode, WType::DEFAULT, 0)
664
+ # Drop last evaluated result if asked to
665
+ @wgenerator.drop(wnode) unless keep_eval
666
+
667
+ logger.debug "wn_false:#{wn_false} wtype:#{wn_false.wtype} keep_eval:#{keep_eval}"
668
+ return wn_false
669
+ end
670
+
671
+ # Example
672
+ # TestA::C::MYCONST
673
+ # -------
674
+ # (const (const (const nil :TESTA) :C) :MYCONST))
675
+ def parse_const(node, wnode, keep_eval)
676
+ # Build constant path from embeddec const sexp
677
+ const_path = []
678
+ n = node
679
+ while n
680
+ logger.debug "adding #{n.children.last} to constant path"
681
+ const_path.unshift(n.children.last)
682
+ n = n.children.first
683
+ end
684
+ full_const_name = const_path.join('::')
685
+ if const_path.size == 1
686
+ class_name = wnode.class_name
687
+ const_name = const_path.first
688
+ elsif const_path.size == 2
689
+ class_name, const_name = *const_path
690
+ else
691
+ raise "only constant of the form X or X::Y is supported (got #{full_const_name}"
692
+ end
693
+
694
+ # See if constant exists. It should at this point
695
+ unless (const = wnode.find_const(const_name, class_name))
696
+ raise "unknown constant #{full_const_name}"
697
+ end
698
+ wn_const = @wgenerator.const(wnode, const)
699
+
700
+ # Drop last evaluated result if asked to
701
+ @wgenerator.drop(wnode) unless keep_eval
702
+ return wn_const
703
+ end
704
+
705
+ # method arguments
706
+ def parse_args(node, wnode)
707
+ # collect method arguments
708
+ node.children.each do |arg_node|
709
+ raise "only regular method argument is supported (got #{arg_node.type})" if arg_node.type != :arg
710
+ # keep track of method arguments. Do not generate wasm code yet
711
+ # as 'arg' directives may later affect argument types (see parse_send)
712
+ wnode.create_marg(arg_node.children.last)
713
+ end
714
+ end
715
+
716
+ # class method definition
717
+ # Example
718
+ # s(:defs,
719
+ # s(:self), :push,
720
+ # s(:args,
721
+ # s(:arg, :value)),... )
722
+ #-----
723
+ # def self.push(value)
724
+ # ...
725
+ # end
726
+ def parse_defs(node, wnode, keep_eval)
727
+ logger.debug "node: #{node}\nwnode: #{wnode}"
728
+ recv_node, method_name, arg_nodes, body_node = *node.children
729
+ raise "only class method is supported. Wrong receiver at #{recv_node.loc.expression}" if recv_node.type != :self
730
+ logger.debug "recv_node: #{recv_node}\nmethod_name: #{method_name}"
731
+
732
+ # create corresponding func node
733
+ method = wnode.find_or_create_method(method_name, nil, :class)
734
+ method.export! if (@@export || self.config[:export_all])
735
+ logger.debug "Method object : #{method.inspect}"
736
+ wn_method = @wgenerator.class_method(wnode, method)
737
+ # collect method arguments
738
+ parse_args(arg_nodes, wn_method)
739
+ # Look for any result directive and parse it so
740
+ # that we know what the return type is in advance
741
+ # If :nil for instance then it may change the way
742
+ # we generate code in the body of the method
743
+ if (result_node = body_node.children.find {|n| n.respond_to?(:type) && n.type == :send && n.children[1] == :result})
744
+ logger.debug "result directive found: #{result_node}"
745
+ parse_node(result_node, wn_method, keep_eval)
746
+ end
747
+
748
+ # method body -- A method evaluates to its last
749
+ # computed value unless a result :nil directive
750
+ # is specified
751
+ logger.debug "method_name: #{method_name}, wtype: #{wn_method.wtype}"
752
+ parse_node(body_node, wn_method, !wn_method.wtype.blank?)
753
+
754
+ # Now that we have parsed the whole method we can
755
+ # prepend locals, result and method args to the
756
+ # method wnode (in that order)
757
+ @wgenerator.locals(wn_method)
758
+ @wgenerator.result(wn_method)
759
+ @wgenerator.params(wn_method)
760
+ logger.debug "Full method wnode: #{wn_method}"
761
+ # reset export toggle
762
+ @@export = false
763
+ return wn_method
764
+ end
765
+
766
+ # Instance method definition
767
+ # Example
768
+ # (def :push,
769
+ # (args,
770
+ # s(:arg, :value)),... )
771
+ #-----
772
+ # def push(value)
773
+ # ...
774
+ # end
775
+ def parse_def(node, wnode, keep_eval)
776
+ logger.debug "node: #{node}\nwnode: #{wnode}"
777
+ method_name, arg_nodes, body_node = *node.children
778
+ logger.debug "method_name: #{method_name}"
779
+
780
+ # create corresponding func node
781
+ method = wnode.find_or_create_method(method_name, nil, :instance)
782
+ method.export! if (@@export || self.config[:export_all])
783
+ logger.debug "Method object : #{method.inspect}"
784
+ wn_method = @wgenerator.instance_method(wnode, method)
785
+ # add a receiver argument
786
+
787
+ # collect method arguments
788
+ parse_args(arg_nodes, wn_method)
789
+ # Look for any result directive and parse it so
790
+ # that we know what the return type is in advance
791
+ # If :nil for instance then it may change the way
792
+ # we generate code in the body of the method
793
+ if (result_node = body_node.children.find {|n| n.respond_to?(:type) && n.type == :send && n.children[1] == :result})
794
+ logger.debug "result directive found: #{result_node}"
795
+ parse_node(result_node, wn_method, keep_eval)
796
+ end
797
+
798
+ # method body -- A method evaluates to its last
799
+ # computed value unless a result :nil directive
800
+ # is specified
801
+ logger.debug "method_name: #{method_name}, wtype: #{wn_method.wtype}"
802
+ parse_node(body_node, wn_method, !wn_method.wtype.blank?)
803
+
804
+ # Now that we have parsed the whole method we can
805
+ # prepend locals, result and method args to the
806
+ # method wnode (in that order)
807
+ @wgenerator.locals(wn_method)
808
+ @wgenerator.result(wn_method)
809
+ @wgenerator.params(wn_method)
810
+ logger.debug "Full method wnode: #{wn_method}"
811
+ # reset export toggle
812
+ @@export = false
813
+ return wn_method
814
+ end
815
+
816
+ def parse_require(wnode, file)
817
+ logger.debug "File required: #{file}"
818
+ extensions = ['', '.wat', '.rb']
819
+ full_path_file = nil
820
+ if Pathname.new(file).absolute?
821
+ logger.debug "Absolute path detected"
822
+ extensions.each do |ext|
823
+ full_path_file = file+ext
824
+ break if File.file?(full_path_file)
825
+ end
826
+ else
827
+ case file
828
+ when /^\./
829
+ # If file starts with . then look for file in pwd
830
+ load_path = [Dir.pwd]
831
+ when /^rlang/
832
+ # If it starts with rlang then look for it in the
833
+ # installed rlang gem in addition to load path
834
+ load_path = self.config[:LOAD_PATH] + $LOAD_PATH
835
+ else
836
+ load_path = self.config[:LOAD_PATH]
837
+ load_path = [Dir.pwd] if self.config[:LOAD_PATH].empty?
838
+ end
839
+ logger.debug "load_path: #{load_path} for file #{file}"
840
+
841
+ # Now try each possible extension foreach possible
842
+ # directory in the load path
843
+ load_path.each do |dir|
844
+ logger.debug "Searching in dir: #{dir}"
845
+ break unless extensions.each do |ext|
846
+ fpf = File.expand_path(File.join(dir, file+ext))
847
+ if File.file?(fpf)
848
+ logger.debug "Found required file: #{fpf}"
849
+ full_path_file = fpf; break
850
+ end
851
+ end
852
+ end
853
+ end
854
+ raise LoadError, "no such file to load: #{full_path_file}" unless full_path_file
855
+
856
+ # Now load the file
857
+ if File.extname(full_path_file) == '.wat'
858
+ wat_code = File.read(full_path_file)
859
+ @wgenerator.inline(wnode, wat_code)
860
+ else
861
+ parse_file(full_path_file)
862
+ end
863
+ end
864
+
865
+ def parse_require_relative(wnode, file)
866
+ logger.debug "Require file: #{file}...\n ...relative to #{self.config[:__FILE__]}"
867
+ full_path_file = File.expand_path(file, File.dirname(self.config[:__FILE__]))
868
+ parse_require(wnode, full_path_file)
869
+ end
870
+
871
+ # Parse the many differents forms of send
872
+ # (both compile time directives and application calls)
873
+ def parse_send(node, wnode, keep_eval)
874
+ recv_node = node.children[0]
875
+ method_name = node.children[1]
876
+ logger.debug "recv_node #{recv_node}, method_name : #{method_name}"
877
+ logger.debug "scope: #{wnode.scope}"
878
+
879
+ if recv_node.nil?
880
+ return parse_send_nil_receiver(node, wnode, keep_eval)
881
+ end
882
+
883
+ # Special case : DAta initializers
884
+ #
885
+ # Example (setting DAta address)
886
+ # DAta.current_address = 0
887
+ # ---------
888
+ # (send
889
+ # (const nil :DAta) :current_address=
890
+ # (int 0))
891
+ #
892
+ # Example (setting DAta alignment)
893
+ # DAta.align(8)
894
+ # ---------
895
+ # (send
896
+ # (const nil :DAta) :align
897
+ # (int 8))
898
+ #
899
+ # Example (value is an i32)
900
+ # DAta[:an_I32] = 3200
901
+ # ---------
902
+ # (send
903
+ # (const nil :DAta) :[]=
904
+ # (sym :an_I32)
905
+ # (int 32000))
906
+ # )
907
+ #
908
+ # Example (value is an i64)
909
+ # DAta[:an_I64] = 3200.to_I64
910
+ # ---------
911
+ # (send
912
+ # (const nil :DAta) :[]=
913
+ # (sym :an_I64)
914
+ # (send
915
+ # (int 32000) :to_Ixx))
916
+ # )
917
+ #
918
+ # Example (value is a String)
919
+ # DAta[:a_string] = "My\tLitte\tRlang\x00"
920
+ # ---------
921
+ # (send
922
+ # (const nil :Data) :[]=
923
+ # (sym :a_string)
924
+ # (str "My\tLitte\tRlang\u0000"))
925
+ #
926
+ # Example (value is a data address)
927
+ # DAta[:an_address] = DAta[:a_string]
928
+ # ---------
929
+ # (send
930
+ # (const nil :DAta) :[]=
931
+ # (sym :an_address)
932
+ # (send
933
+ # (const nil :DAta) :[]
934
+ # (sym :a_string)))
935
+ #
936
+ # Example (value is an array)
937
+ # Data[:an_array] = [ Data[:an_I64], 5, 257, "A string\n"]
938
+ # ---------
939
+ # (send
940
+ # (const nil :Data) :[]=
941
+ # (sym :an_array)
942
+ # (array
943
+ # (send
944
+ # (const nil :Data) :[]
945
+ # (sym :an_I64))
946
+ # (int 5)
947
+ # (int 257)
948
+ # (str "A string\n")))
949
+ #
950
+
951
+ if recv_node.type == :const && recv_node.children.last == :DAta
952
+ case method_name
953
+ when :current_address=
954
+ value_node = node.children[2]
955
+ raise "DAta address must be an integer" unless value_node.type == :int
956
+ DAta.current_address = value_node.children.last
957
+ when :align
958
+ value_node = node.children[2]
959
+ raise "DAta alignment argument must be an integer" unless value_node.type == :int
960
+ DAta.align(value_node.children.last)
961
+ when :[]=
962
+ if (data_label_node = node.children[2]).type == :sym
963
+ label = data_label_node.children.last
964
+ else
965
+ raise "Data label must be a symbol (got #{data_label_node}"
966
+ end
967
+ arg_node = node.children[3]
968
+ parse_data_value(label, arg_node)
969
+ else
970
+ raise "Unsupported DAta method #{method_name}"
971
+ end
972
+ return
973
+ end
974
+
975
+ # General type cast directive
976
+ # this must be processed at compile time. It's not dynamic
977
+ # An expression can be cast to any Rlang class including
978
+ # native types like :I64,:I32,...
979
+ #
980
+ # Example
981
+ # (expression).cast_to(class_name, argument)
982
+ # -----
983
+ # s(:begin,
984
+ # s(expression),
985
+ # :cast_to, s(sym, :Class_name))
986
+ # the signed argument true|false is optional and
987
+ # it defaults to false
988
+ if method_name == :cast_to
989
+ class_name_node = node.children.last
990
+ raise "cast_to expects a symbol argument (got #{class_name_node}" unless class_name_node.type == :sym
991
+ tgt_wtype = WType.new(class_name_node.children.first)
992
+ logger.debug "in cast_to: target type #{tgt_wtype}"
993
+
994
+ # Parse the expression and cast it
995
+ wn_to_cast = parse_node(recv_node, wnode)
996
+ logger.debug("wn_to_cast: #{wn_to_cast}")
997
+ wn_cast = @wgenerator.cast(wn_to_cast, tgt_wtype)
998
+ logger.debug("wn_cast: #{wn_cast}")
999
+ # Drop last evaluated result if asked to
1000
+ @wgenerator.drop(wnode) unless keep_eval
1001
+ return wn_cast
1002
+ end
1003
+
1004
+
1005
+ # Type cast directives specific for native types
1006
+ # can pass the signed argument wheres cast_to cannot
1007
+ # this must be processed at compile time. It's not dynamic
1008
+ # Example
1009
+ # (recv).to_Ixx(true|fasle) where xx is 64 or 32
1010
+ # -----
1011
+ # s(:begin,
1012
+ # s(expression),
1013
+ # :to_I64, [true|false])
1014
+ # the signed argument true|false is optional and
1015
+ # it defaults to false
1016
+ if method_name == :to_I64 || method_name == :to_I32
1017
+ tgt_wtype = (method_name == :to_I64) ? WType.new(:I64) : WType.new(:I32)
1018
+ if (cnt = node.children.count) == 3
1019
+ signed = true if node.children.last.type == :true
1020
+ elsif cnt == 2
1021
+ signed = false
1022
+ else
1023
+ raise "cast directive should have 0 or 1 argument (got #{cnt - 2})"
1024
+ end
1025
+ logger.debug "in cast section: child count #{cnt}, tgt_wtype #{tgt_wtype}, signed: #{signed}"
1026
+
1027
+ # Parse the expression and cast it
1028
+ wn_to_cast = parse_node(recv_node, wnode)
1029
+ logger.debug("wn_to_cast: #{wn_to_cast}")
1030
+ wn_cast = @wgenerator.cast(wn_to_cast, tgt_wtype, signed)
1031
+ logger.debug("wn_cast: #{wn_cast}")
1032
+ # Drop last evaluated result if asked to
1033
+ @wgenerator.drop(wnode) unless keep_eval
1034
+ return wn_cast
1035
+ end
1036
+
1037
+ # A that stage it's a method call of some sort
1038
+ # (call on class or instance)
1039
+ if ARITHMETIC_OPS.include?(method_name) ||
1040
+ RELATIONAL_OPS.include?(method_name) ||
1041
+ UNARY_OPS.include?(method_name)
1042
+ return parse_operator(node, wnode, keep_eval)
1043
+ else
1044
+ return parse_send_method_lookup(node, wnode, keep_eval)
1045
+ end
1046
+
1047
+ raise "FATAL ERROR!! Unreachable point at end of parse_send (node: #{node})"
1048
+ end
1049
+
1050
+ def parse_send_nil_receiver(node, wnode, keep_eval)
1051
+ recv_node = node.children[0]
1052
+ method_name = node.children[1]
1053
+ raise "receiver should be nil here (got #{recv_node})" \
1054
+ unless recv_node.nil?
1055
+
1056
+ if recv_node.nil? && method_name == :require
1057
+ return parse_send_require(node, wnode, keep_eval)
1058
+ end
1059
+
1060
+ if recv_node.nil? && method_name == :require_relative
1061
+ return parse_send_require_relative(node, wnode, keep_eval)
1062
+ end
1063
+
1064
+ if recv_node.nil? && method_name == :export
1065
+ return parse_send_export(node, wnode, keep_eval)
1066
+ end
1067
+
1068
+ if recv_node.nil? && method_name == :local
1069
+ return parse_send_local(node, wnode, keep_eval)
1070
+ end
1071
+
1072
+ if recv_node.nil? && method_name == :arg
1073
+ return parse_send_arg(node, wnode, keep_eval)
1074
+ end
1075
+
1076
+ if recv_node.nil? && method_name == :result
1077
+ return parse_send_result(node, wnode, keep_eval)
1078
+ end
1079
+
1080
+ if recv_node.nil? && method_name == :wattr
1081
+ return parse_send_wattr(node, wnode, keep_eval)
1082
+ end
1083
+
1084
+ if recv_node.nil? && method_name == :wattr_type
1085
+ return parse_send_wattr_type(node, wnode, keep_eval)
1086
+ end
1087
+
1088
+ if recv_node.nil? && method_name == :inline
1089
+ return parse_send_inline(node, wnode, keep_eval)
1090
+ end
1091
+
1092
+ # All other cases : it is a regular method call
1093
+ return parse_send_method_lookup(node, wnode, keep_eval)
1094
+ end
1095
+
1096
+ # Directive to require a file
1097
+ # Example
1098
+ # (send nil :require
1099
+ # (str "test5"))
1100
+ def parse_send_require(node, wnode, keep_eval)
1101
+ raise "require must be used at root level" \
1102
+ unless wnode.in_root_scope?
1103
+ file_node = node.children.last
1104
+ raise "require only accepts a string argument (got #{file_node})" \
1105
+ unless file_node.type == :str
1106
+ parse_require(wnode, file_node.children.last)
1107
+ return
1108
+ end
1109
+
1110
+ # Directive to require_a file relative to
1111
+ # current file
1112
+ # Example
1113
+ # (send nil :require_relative
1114
+ # (str "test5"))
1115
+ def parse_send_require_relative(node, wnode, keep_eval)
1116
+ raise "require_relative must be used at root level" \
1117
+ unless wnode.in_root_scope?
1118
+ file_node = node.children.last
1119
+ raise "require only accepts a string argument (got #{file_node})" \
1120
+ unless file_node.type == :str
1121
+ parse_require_relative(wnode, file_node.children.last)
1122
+ return
1123
+ end
1124
+
1125
+ # Directive to declare the current method
1126
+ # in the WASM exports
1127
+ def parse_send_export(node, wnode, keep_eval)
1128
+ raise "export must be used in class scope" unless wnode.in_class_scope?
1129
+ @@export = true
1130
+ return
1131
+ end
1132
+
1133
+ # Directive to define local variable type
1134
+ # this must be processed at compile time
1135
+ # if method name is :local then it is
1136
+ # a type definition for a local variable
1137
+ # local :value, :I64
1138
+ # ---------
1139
+ # s(:send, nil, :local,
1140
+ # (hash
1141
+ # (pair
1142
+ # s(:sym, :value)
1143
+ # s(:sym, :I64))
1144
+ # ))
1145
+ def parse_send_local(node, wnode, keep_eval)
1146
+ raise "local declaration can only be used in methods" \
1147
+ unless wnode.in_method_scope?
1148
+ hash_node = node.children.last
1149
+ local_types = parse_type_args(hash_node, :local)
1150
+ local_types.each do |name, wtype|
1151
+ lvar = wnode.find_or_create_lvar(name)
1152
+ raise "couldn't find or create local variable #{name}" unless lvar
1153
+ lvar.wtype = WType.new(wtype)
1154
+ end
1155
+ return
1156
+ end
1157
+
1158
+ # Directive to define method argument type
1159
+ # this must be processed at compile time
1160
+ # if method name is :arg then it is
1161
+ # a type definition for a method argument
1162
+ # arg value: :I64
1163
+ # ---------
1164
+ # s(:send, nil, :arg,
1165
+ # (hash
1166
+ # (pair
1167
+ # s(:sym, :value)
1168
+ # s(:sym, :I64))
1169
+ # ))
1170
+ def parse_send_arg(node, wnode, keep_eval)
1171
+ raise "arg declaration can only be used in methods" \
1172
+ unless wnode.in_method_scope?
1173
+ hash_node = node.children.last
1174
+ marg_types = parse_type_args(hash_node, :argument)
1175
+ marg_types.each do |name, wtype|
1176
+ marg = wnode.find_marg(name)
1177
+ raise "couldn't find method argument #{name}" unless marg
1178
+ marg.wtype = WType.new(wtype)
1179
+ end
1180
+ return
1181
+ end
1182
+
1183
+ # result directive in method scope
1184
+ # ======
1185
+ # Directive to define method return type
1186
+ # in the method itself
1187
+ # this must be processed at compile time
1188
+ # Supported types : :I32, :I64, :none
1189
+ # (:nil means no value is returned)
1190
+ #
1191
+ # Example
1192
+ # result :I64
1193
+ # ---------
1194
+ # s(:send, nil, :result,
1195
+ # s(:sym, :I64))
1196
+ #
1197
+ # result directive in class scope
1198
+ # ======
1199
+ # Directive to define method return type
1200
+ # at the class level. This allows to declare
1201
+ # a method type before the method is parsed
1202
+ # this must be processed at compile time
1203
+ # Supported types : :I32, :I64, :none
1204
+ # (:none means no value is returned)
1205
+ #
1206
+ # Example
1207
+ # result :class_name, :method_name, :I64
1208
+ # ---------
1209
+ # s(:send, nil, :result,
1210
+ # s(:sym, :class_name),
1211
+ # s(:sym, :method_name),
1212
+ # s(:sym, :I64))
1213
+ #
1214
+ # if name starts with # it's a n instance method,
1215
+ # otherwise a class method
1216
+ def parse_send_result(node, wnode, keep_eval)
1217
+ if wnode.in_method_scope?
1218
+ result_type, = *node.children[2]
1219
+ raise "result directive expects a symbol argument (got #{result_type})" \
1220
+ unless result_type.is_a? Symbol
1221
+ wnode.method_wnode.wtype = WType.new(result_type)
1222
+ logger.debug "result_type #{result_type} updated for method #{wnode.method_wnode.method.inspect}"
1223
+ elsif wnode.in_class_scope?
1224
+ cn_name, = *node.children[2]
1225
+ mn_name, = *node.children[3]
1226
+ result_type, = *node.children[4]
1227
+ method_type = (mn_name[0] == '#' ? :instance : :class)
1228
+ (mwn = wnode.find_or_create_method(mn_name, cn_name, method_type)).wtype = WType.new(result_type)
1229
+ logger.debug "result_type #{mwn.wtype} for method #{mwn.name}"
1230
+ else
1231
+ raise "result declaration not supported #{wn.scope} scope"
1232
+ end
1233
+ return
1234
+ end
1235
+
1236
+ # Directive to define class attributes. This defines
1237
+ # a list of getters and setters and access them in
1238
+ # memory with an offset from the base address given as
1239
+ # an argument.
1240
+ #
1241
+ # Example
1242
+ # wattr :ptr, :size
1243
+ # ---------
1244
+ # s(:send, nil, :wattr,
1245
+ # s(:sym, :ptr),
1246
+ # s(:sym, :size))
1247
+ def parse_send_wattr(node, wnode, keep_eval)
1248
+ raise "wattr directives can only happen in class scope" \
1249
+ unless wnode.in_class_scope?
1250
+ wattr_nodes = node.children[2..-1]
1251
+ wattr_nodes.each do |wan|
1252
+ logger.debug "processing wattr node #{wan}"
1253
+ raise "attribute name must be a symbol (got #{wan})" unless wan.type == :sym
1254
+ wattr_name = wan.children.last
1255
+ if (wattr = wnode.find_wattr(wattr_name))
1256
+ raise "attribute #{wattr_name} already declared" if wattr
1257
+ else
1258
+ wattr = wnode.create_wattr(wattr_name)
1259
+ wattr.getter.export! if (@@export || self.config[:export_all])
1260
+ wattr.setter.export! if (@@export || self.config[:export_all])
1261
+ end
1262
+ end
1263
+ return
1264
+ end
1265
+
1266
+ # Directive to specify wasm type of class attributes
1267
+ # in case it's not the default type
1268
+ #
1269
+ # Example
1270
+ # wattr_type ptr: :I64, size: :I32
1271
+ # ---------
1272
+ # s(:send, nil, :wattr_type,
1273
+ # (hash
1274
+ # (pair
1275
+ # s(:sym, :ptr)
1276
+ # s(:sym, :I64))
1277
+ # (pair
1278
+ # s(:sym, :size)
1279
+ # s(:sym, :I32)) ))
1280
+ #
1281
+ def parse_send_wattr_type(node, wnode, keep_eval)
1282
+ raise "wattr directives can only happen in class scope" \
1283
+ unless wnode.in_class_scope?
1284
+ hash_node = node.children.last
1285
+ wattr_types = parse_type_args(hash_node, :attribute)
1286
+ wattr_types.each do |name, wtype|
1287
+ if (wattr = wnode.find_wattr(name))
1288
+ logger.debug "Setting wattr #{name} type to #{wtype}"
1289
+ wattr.wtype = WType.new(wtype)
1290
+ else
1291
+ raise "Unknown class attribute #{name} in #{wnode}"
1292
+ end
1293
+ end
1294
+ return
1295
+ end
1296
+
1297
+ # Directive to inline WAT / Ruby code
1298
+ # the wat entry is used when the Rlang code is
1299
+ # comiled to WAT code. The Ruby entry is used
1300
+ # when the rlang code is simulated in plain Ruby
1301
+ # **CAUTION** the inline code is supposed to always
1302
+ # leave a value on the stack
1303
+ # Example
1304
+ # inline wat: '(call_indirect (type $insn_t)
1305
+ # (local.get $state)
1306
+ # (local.get $cf)
1307
+ # (local.get $opcodes)
1308
+ # (local.get $opcode) ;; instruction function pointer
1309
+ # )',
1310
+ # ruby: 'call_indirect(state, cf, opcodes, opcode)'
1311
+ #
1312
+ # ---------
1313
+ # (send, nil, :inline,
1314
+ # (hash
1315
+ # (pair
1316
+ # (sym :wat)
1317
+ # (dstr
1318
+ # (str "(call_indirect (type $insn_t) \n")
1319
+ # (str "...")
1320
+ # ...))
1321
+ # (pair)
1322
+ # (sym :wtype)
1323
+ # (sym :I64)
1324
+ # (pair
1325
+ # (sym :ruby)
1326
+ # (str "call_indirect(state, cf, opcodes, opcode)"))
1327
+ #
1328
+ def parse_send_inline(node, wnode, keep_eval)
1329
+ raise "inline can only happen in a method body or at root" \
1330
+ unless wnode.in_method_scope? || wnode.in_root_scope?
1331
+ hash_node = node.children.last
1332
+ raise "inline expects a hash argument (got #{hash_node.type}" \
1333
+ unless hash_node.type == :hash
1334
+
1335
+ # Find the :wat entry in hash
1336
+ logger.debug "Hash node: #{hash_node} "
1337
+ wat_node = hash_node.children. \
1338
+ find {|pair| sym_node, = *pair.children; sym_node.children.last == :wat}
1339
+ raise "inline has no wat: hash entry" unless wat_node
1340
+
1341
+ # Find the :wtype entry in hash if any
1342
+ wtype_node = hash_node.children. \
1343
+ find {|pair| sym_node, = *pair.children; sym_node.children.last == :wtype}
1344
+ if wtype_node
1345
+ wtype = WType.new(wtype_node.children.last.children.last)
1346
+ else
1347
+ wtype = WType::DEFAULT
1348
+ end
1349
+
1350
+ # Now extract the WAT code itself
1351
+ raise "inline has no wat: hash entry" unless wat_node
1352
+ wcode_node = wat_node.children.last
1353
+ if wcode_node.type == :dstr
1354
+ # iterate over str children
1355
+ wat_code = wcode_node.children.collect {|n| n.children.last}.join('')
1356
+ elsif wcode_node.type == :str
1357
+ wat_code = wcode_node.children.last
1358
+ else
1359
+ raise "inline WAT code must be a string (got #{wcode_node})"
1360
+ end
1361
+ wn_inline = @wgenerator.inline(wnode, wat_code, wtype)
1362
+ # Drop last evaluated result if asked to
1363
+ @wgenerator.drop(wnode) unless keep_eval
1364
+ return wn_inline
1365
+ end
1366
+
1367
+ # If receiver not self or const then it could
1368
+ # be an arithmetic or relational expression
1369
+ # a method call on class "instance"
1370
+ #
1371
+ # Example for binary op
1372
+ # 1 + 2
1373
+ # ----------
1374
+ # (send
1375
+ # (int 1) :+
1376
+ # (int 2)
1377
+ # )
1378
+ #
1379
+ # Example unary op
1380
+ # !(n==1)
1381
+ # ----------
1382
+ # (send
1383
+ # (begin
1384
+ # (send (lvar :n) :== (int 1))
1385
+ # ) :!)
1386
+ #
1387
+ def parse_operator(node, wnode, keep_eval)
1388
+ recv_node = node.children[0]
1389
+ operator = node.children[1]
1390
+ wn_op = @wgenerator.operator(wnode, operator)
1391
+ wn_recv = parse_node(recv_node, wn_op, true)
1392
+ # now process the 2nd op arguments (there should
1393
+ # be only one but do as if we had several
1394
+ arg_nodes = node.children[2..-1]
1395
+ raise "method #{operator} got #{arg_nodes.count} arguments (expected 0 or 1)" \
1396
+ unless arg_nodes.count <= 1
1397
+ wn_args = arg_nodes.collect {|n| parse_node(n, wn_op, true)}
1398
+ @wgenerator.operands(wn_op, wn_recv, wn_args)
1399
+ logger.debug " After type cast: #{wn_op} wtype: #{wn_op.wtype}, op children types: #{wn_op.children.map(&:wtype)}"
1400
+ # Drop last evaluated result if asked to
1401
+ @wgenerator.drop(wnode) unless keep_eval
1402
+ return wn_op
1403
+ end
1404
+
1405
+ # Method lookup
1406
+ # In the example below mem_size wasn' t
1407
+ # recognized as a local var because it was not
1408
+ # assigned a value before. It's recognized as
1409
+ # a tentative method call
1410
+ # Example
1411
+ # some_var = mem_size + 10
1412
+ # ------
1413
+ # (send
1414
+ # (send nil :mem_size) :+
1415
+ # (int 10))
1416
+ #
1417
+ #
1418
+ # Example for method call on class instance
1419
+ # @@cvar.x = 100
1420
+ # ----------
1421
+ # (send
1422
+ # (cvar :@@cvar) :x= (int 100)
1423
+ # )
1424
+ # TODO must guess type arg from operator type
1425
+ def parse_send_method_lookup(node, wnode, keep_eval)
1426
+ recv_node = node.children[0]
1427
+ #method_name = node.children[1]
1428
+ if wnode.in_class_scope? || wnode.in_class_method_scope?
1429
+ if recv_node.nil? || recv_node.type == :self
1430
+ return parse_send_class_method_call(node, wnode, keep_eval)
1431
+ elsif recv_node.type == :const
1432
+ const_name = recv_node.children.last
1433
+ # if this is a Constant, not a class
1434
+ # then it's actually an instance method call
1435
+ if wnode.find_const(const_name)
1436
+ return parse_send_instance_method_call(node, wnode, keep_eval)
1437
+ else
1438
+ return parse_send_class_method_call(node, wnode, keep_eval)
1439
+ end
1440
+ else
1441
+ return parse_send_instance_method_call(node, wnode, keep_eval)
1442
+ end
1443
+ elsif wnode.in_instance_method_scope?
1444
+ if recv_node&.type == :const
1445
+ const_name = recv_node.children.last
1446
+ # if this is a Constant, not a class
1447
+ # then it's actually an instance method call
1448
+ if wnode.find_const(const_name)
1449
+ return parse_send_instance_method_call(node, wnode, keep_eval)
1450
+ else
1451
+ return parse_send_class_method_call(node, wnode, keep_eval)
1452
+ end
1453
+ else
1454
+ return parse_send_instance_method_call(node, wnode, keep_eval)
1455
+ end
1456
+ else
1457
+ raise "Don't know how to call method in scope #{wnode.scope} from node #{recv_node}"
1458
+ end
1459
+ end
1460
+
1461
+ # Regular class Method call to self class
1462
+ # or another class
1463
+ # Example
1464
+ # self.m_one_arg(arg1, 200)
1465
+ # ---------
1466
+ # (send
1467
+ # (self) :m_one_arg
1468
+ # (lvar :arg1)
1469
+ # (int 200)
1470
+ # )
1471
+ # OR
1472
+ # Test.m_one_arg(arg1, 200)
1473
+ # (send
1474
+ # (const nil :Test) :m_one_arg
1475
+ # (lvar :arg1)
1476
+ # (int 200)
1477
+ # )
1478
+ #
1479
+ # OR New object instantiation
1480
+ # This is class object instantiation. Statically
1481
+ # allocated though. So it can only happen in the
1482
+ # class scope for a class variable or a constant
1483
+ # Example
1484
+ # self.new
1485
+ # ---------
1486
+ # (send
1487
+ # (self) :new) )
1488
+ #
1489
+ # OR
1490
+ # Header.new
1491
+ # ---------
1492
+ # (send
1493
+ # (const nil :Header) :new) )
1494
+ #
1495
+ def parse_send_class_method_call(node, wnode, keep_eval)
1496
+ logger.debug "Parsing class method call:..."
1497
+ recv_node = node.children[0]
1498
+ method_name = node.children[1]
1499
+ if recv_node.nil? || recv_node.type == :self
1500
+ class_name = wnode.class_name
1501
+ elsif recv_node.type == :const
1502
+ class_name = recv_node.children.last
1503
+ else
1504
+ raise "Can only call method class on self or class objects (got #{recv_node} in node #{node})"
1505
+ end
1506
+ logger.debug "...#{class_name}::#{method_name}"
1507
+ if method_name == :new
1508
+ raise "Cannot instantiate object in scope #{wnode.scope} on #{node}" \
1509
+ unless wnode.in_class_scope?
1510
+ # This is class object instantiation. Statically
1511
+ # allocated though. So it can only happen in the
1512
+ # class scope for a class variable or a constant
1513
+ # Returns a wnode with a i32.const containing the address
1514
+ wn_addr = @wgenerator.new(wnode, class_name)
1515
+ return wn_addr
1516
+ else
1517
+ wn_call = @wgenerator.call(wnode, class_name, method_name, :class)
1518
+ arg_nodes = node.children[2..-1]
1519
+ arg_nodes.each { |node| parse_node(node, wn_call) }
1520
+ # Drop last evaluated result if asked to or if
1521
+ # the method called doesn't return any value
1522
+ @wgenerator.drop(wnode) unless (keep_eval || wn_call.wtype.blank?)
1523
+ return wn_call
1524
+ end
1525
+ raise "FATAL ERROR!! Unreachable point at end of parse_send_class_method_call (node: #{node})"
1526
+ end
1527
+
1528
+ def parse_send_instance_method_call(node, wnode, keep_eval)
1529
+ recv_node = node.children[0]
1530
+ method_name = node.children[1]
1531
+ # Parse receiver node and temporarily attach it
1532
+ # to parent wnode. It will later become the first
1533
+ # argument of the method call by reparenting it
1534
+ logger.debug "Parsing instance method call #{method_name}..."
1535
+ # parse the receiver node
1536
+ # if nil it means self
1537
+ wn_recv = recv_node.nil? ? parse_self(recv_node, wnode) : parse_node(recv_node, wnode)
1538
+ logger.debug "... on #{wn_recv}"
1539
+ class_name = wn_recv.wtype.name
1540
+
1541
+ wn_call = @wgenerator.call(wnode, class_name, method_name, :instance)
1542
+ wn_recv.reparent_to(wn_call) if wn_recv
1543
+
1544
+ # Grab all arguments and add them as child of the call node
1545
+ arg_nodes = node.children[2..-1]
1546
+ arg_nodes.each do |node|
1547
+ wn = parse_node(node, wn_call)
1548
+ logger.debug "...with arg #{wn}"
1549
+ end
1550
+ logger.debug "Resulting in call node #{wn_call}"
1551
+ # Drop last evaluated result if asked to or if
1552
+ # the method called doesn't return any value
1553
+ @wgenerator.drop(wnode) unless (keep_eval || wn_call.wtype.blank?)
1554
+ return wn_call
1555
+ end
1556
+
1557
+ def parse_type_args(hash_node, entity)
1558
+ types = {}
1559
+ # Is this a hash Node ?
1560
+ unless hash_node.respond_to?(:type) && hash_node.type == :hash
1561
+ raise "#{entity} expects a hash argument (got #{hash_node}" \
1562
+ end
1563
+ logger.debug "#{entity} hash node: #{hash_node}"
1564
+ hash_node.children.each do |pair_node|
1565
+ name_node, type_node = pair_node.children
1566
+ raise "The name of an #{entity} must be a symbol (got #{name_node})" \
1567
+ unless name_node.type == :sym
1568
+ raise "The type of an #{entity} must be a symbol (got #{type_node})" \
1569
+ unless type_node.type == :sym
1570
+ name = name_node.children.last
1571
+ type = type_node.children.last
1572
+ types[name] = type
1573
+ end
1574
+ types
1575
+ end
1576
+
1577
+ # Parse self. We should ge there only
1578
+ # when sis an object instance (not a class instance)
1579
+ def parse_self(node, wnode)
1580
+ if wnode.in_instance_method_scope?
1581
+ wn = @wgenerator._self(wnode)
1582
+ logger.debug "self in instance method scope"
1583
+ elsif wnode.in_class_method_scope?
1584
+ # Nothing to do just return nil
1585
+ logger.debug "self in class method scope"
1586
+ elsif wnode.in_class_scope?
1587
+ # Nothing to do just return nil
1588
+ logger.debug "self in class definition scope"
1589
+ else
1590
+ raise "Don't know what self means in this context: #{wnode}"
1591
+ end
1592
+ wn
1593
+ end
1594
+
1595
+ # Data value node can be either of type
1596
+ # int, str, send, array
1597
+ def parse_data_value(label, node)
1598
+ case node.type
1599
+ when :int, :str
1600
+ logger.debug "in :int/:str label #{label}, value #{node.children.last}"
1601
+ DAta.append(label, node.children.last)
1602
+ when :array
1603
+ node.children.each {|n| parse_data_value(label,n)}
1604
+ when :send
1605
+ recv_node,method_name,arg_node = *node.children
1606
+ logger.debug "in send: recv_node #{recv_node}, method_name #{method_name}"
1607
+ case method_name
1608
+ when :to_I64
1609
+ raise "Data type casting can only apply to int (got #{recv_node}" \
1610
+ unless recv_node.type == :int
1611
+ value = recv_node.children.last
1612
+ DAta.append(label, value, Type::I64)
1613
+ when :[]
1614
+ raise "Initializer can only be a Data address (got #{node})" \
1615
+ unless recv_node.children.last == :DAta
1616
+ raise "Initializer expects a symbol (got #{arg_node})" \
1617
+ unless arg_node.type == :sym
1618
+ DAta.append(label, DAta[arg_node.children.last])
1619
+ else
1620
+ raise "Unknow data initializer #{node}"
1621
+ end
1622
+ else
1623
+ raise "Unknow data initializer #{node}"
1624
+ end
1625
+ end
1626
+
1627
+ def parse_return(node, wnode, keep_eval)
1628
+ ret_count = node.children.count
1629
+ raise "only one or no value can be returned (got #{ret_count})" if ret_count > 1
1630
+ exp_node = node.children.first
1631
+ wn_ret = @wgenerator.return(wnode)
1632
+ if exp_node
1633
+ wn_exp = parse_node(exp_node, wn_ret)
1634
+ wn_ret.wtype = wn_exp.wtype
1635
+ else
1636
+ wn_ret.wtype = WType.new(:none)
1637
+ end
1638
+ wn_ret
1639
+ end
1640
+
1641
+ # Process the if then else conditional statement
1642
+ # (the else clause can be absent) with a result
1643
+ # type
1644
+ # Example
1645
+ # if|unless (result type) expression
1646
+ # ...
1647
+ # else
1648
+ # ...
1649
+ # end
1650
+ # ----------
1651
+ # (if
1652
+ # (sexp)
1653
+ # (then
1654
+ # ...
1655
+ # )
1656
+ # (else
1657
+ # ...
1658
+ # )
1659
+ def parse_if_without_result_type(node, wnode, keep_eval)
1660
+ cond_node, then_node, else_node = *node.children
1661
+ # process the if clause
1662
+ # always keep eval on stack for the if statement
1663
+ wn_if = @wgenerator.if(wnode)
1664
+ parse_node(cond_node, wn_if, true)
1665
+
1666
+ # process the then clause
1667
+ # DO NOT keep the last evaluated value
1668
+ # if then clause is nil it's probably
1669
+ # because it's actually an unless statement
1670
+ wn_then = @wgenerator.then(wn_if)
1671
+ if then_node
1672
+ parse_node(then_node, wn_then, false)
1673
+ else
1674
+ @wgenerator.nop(wn_then)
1675
+ end
1676
+
1677
+ # The result type is always nil in this variant
1678
+ # of the parse_if_... method
1679
+ wn_if.wtype = WType.new(:nil); wn_then.wtype = WType.new(:nil)
1680
+
1681
+ # process the else clause if it exists
1682
+ # DO NOT keep the last evaluated value
1683
+ if else_node
1684
+ wn_else = @wgenerator.else(wn_if)
1685
+ parse_node(else_node, wn_else, false)
1686
+ wn_else.wtype = WType.new(:nil)
1687
+ end
1688
+
1689
+ # Drop last evaluated result if asked to
1690
+ # No need to drop the last evaluated value
1691
+ # here as the then and else branches do not
1692
+ # return any
1693
+ # @wgenerator.drop(wnode) unless keep_eval
1694
+ return wn_if
1695
+ end
1696
+
1697
+ # Process the if then else conditional statement
1698
+ # (the Ruby else clause is optional) with a result
1699
+ # type
1700
+ # Example
1701
+ # if (result type) expression
1702
+ # ...
1703
+ # else
1704
+ # ...
1705
+ # end
1706
+ # ----------
1707
+ # (if
1708
+ # (sexp)
1709
+ # (then
1710
+ # ...
1711
+ # )
1712
+ # (else
1713
+ # ...
1714
+ # )
1715
+ def parse_if_with_result_type(node, wnode, keep_eval)
1716
+ cond_node, then_node, else_node = *node.children
1717
+ # process the if clause
1718
+ wn_if = @wgenerator.if(wnode)
1719
+ parse_node(cond_node, wn_if, true) # always keep eval on stack
1720
+ # process the then clause
1721
+ wn_then = @wgenerator.then(wn_if)
1722
+ parse_node(then_node, wn_then, keep_eval)
1723
+
1724
+ # infer the result type of the if from the
1725
+ # the type of the then clause
1726
+ wn_then.wtype = wn_then.children.last.wtype
1727
+ wn_if.wtype = wn_then.wtype
1728
+ # Now that we know the result type
1729
+ # prepend it to the if children
1730
+ logger.debug("prepending result to wnode #{wn_if}")
1731
+ @wgenerator.result(wn_if) unless wn_if.wtype.blank?
1732
+
1733
+ # process the else clause if it exists
1734
+ wn_else = @wgenerator.else(wn_if)
1735
+ if else_node
1736
+ parse_node(else_node, wn_else, keep_eval)
1737
+ wn_else.wtype = wn_else.children.last.wtype
1738
+ if wn_then.wtype != wn_else.wtype
1739
+ raise "then and else clauses must return same wtype (got #{wn_then.wtype} and #{wn_else.wtype}"
1740
+ end
1741
+ else
1742
+ # if the else clause doesn't exist in Ruby we still
1743
+ # have to generate it in WAT because the else branch
1744
+ # **MUST** be there and return the same result type
1745
+ # as the then branch
1746
+ # In this case in Ruby the missing else clause would
1747
+ # cause the if statement to evaluate to nil if the
1748
+ # condition is false. Problem is "nil" doesn't exist in
1749
+ # Webassembly. For now let's return a "remarkable" value
1750
+ # (see NIL constant)
1751
+ # WARNING!! This is not satisfactory of course because
1752
+ # the then branch could one day return this exact same
1753
+ # value too
1754
+ #
1755
+ # A safer alternative (but not compliant with plain Ruby)
1756
+ # would be to assume that if-then-else in Rlang never
1757
+ # evaluates to a value (see method parse_if_without_result_type)
1758
+ @wgenerator.int(wn_else, wn_then.wtype, NIL)
1759
+ wn_else.wtype = wn_then.wtype
1760
+ end
1761
+
1762
+ # Drop last evaluated result if asked to
1763
+ @wgenerator.drop(wnode) unless keep_eval
1764
+ return wn_if
1765
+ end
1766
+
1767
+ # Example
1768
+ # (while (cond)
1769
+ # (body)
1770
+ # end
1771
+ # -----------
1772
+ # (block $lbl_xx
1773
+ # (loop $lbl_yy
1774
+ # (br_if $lbl_xx (ixx.eqz (cond)))
1775
+ # (body)
1776
+ # (br $lbl_yy)
1777
+ # )
1778
+ # )
1779
+ def parse_while_without_result_type(node, wnode, keep_eval)
1780
+ cond_node, body_node = *node.children
1781
+ wn_while,wn_while_cond,wn_body = @wgenerator.while(wnode)
1782
+
1783
+ # Parse the while condition...
1784
+ # Plus negate the condition if it's a while statement
1785
+ # Keep it as is for a until statement
1786
+ wn_cond_exp = parse_node(cond_node, wn_while_cond)
1787
+ if node.type == :while
1788
+ @wgenerator.while_cond(wn_while_cond, wn_cond_exp)
1789
+ end
1790
+
1791
+ # Parse the body of the while block and
1792
+ # do not keep the last evaluated expression
1793
+ parse_node(body_node, wn_body, false)
1794
+ @wgenerator.while_end(wn_body)
1795
+ return wn_while
1796
+ end
1797
+
1798
+ def parse_break(node, wnode, keep_eval)
1799
+ @wgenerator.break(wnode)
1800
+ end
1801
+
1802
+ def parse_next(node, wnode, keep_eval)
1803
+ @wgenerator.next(wnode)
1804
+ end
1805
+
1806
+ # Ruby || operator
1807
+ # Example
1808
+ # n==1 || n==2
1809
+ # ------
1810
+ # (or
1811
+ # (send (lvar :n) :== (int 1))
1812
+ # (send (lvar :n) :== (int 2))
1813
+ # )
1814
+ # Ruby && operator
1815
+ # Example
1816
+ # n==1 && m==3
1817
+ # ------
1818
+ # (and
1819
+ # (send (lvar :n) :== (int 1))
1820
+ # (send (lvar :m) :== (int 3))
1821
+ # )
1822
+ def parse_logical_op(node, wnode, keep_eval)
1823
+ logger.debug "logical operator section : #{node.type}"
1824
+ cond1_node, cond2_node = *node.children
1825
+ # Prepare the operator wnode
1826
+ wn_op = @wgenerator.operator(wnode, node.type)
1827
+ # Parse operand nodes and attach them to the
1828
+ # operator wnode
1829
+ wn_cond1 = parse_node(cond1_node, wn_op)
1830
+ wn_cond2 = parse_node(cond2_node, wn_op)
1831
+ @wgenerator.operands(wn_op, wn_cond1, [wn_cond2])
1832
+ # Drop last evaluated result if asked to
1833
+ @wgenerator.drop(wnode) unless keep_eval
1834
+ return wn_op
1835
+ end
1836
+
1837
+ def dump
1838
+ @ast
1839
+ end
1840
+ end
1841
+
1842
+ end