rlang 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -22,26 +22,29 @@ module Rlang::Parser
22
22
  :+ => :add,
23
23
  :- => :sub,
24
24
  :* => :mul,
25
- :/ => :div_u,
26
- :% => :rem_u,
27
25
  :& => :and,
28
26
  :| => :or,
29
27
  :^ => :xor,
30
- :>> => :shr_u,
31
28
  :<< => :shl
32
29
  }
33
30
 
31
+ ARITHMETIC_SIGNED_OPS_MAP = {
32
+ :/ => :div_x,
33
+ :% => :rem_x,
34
+ :>> => :shr_x
35
+ }
36
+
34
37
  RELATIONAL_OPS_MAP = {
35
38
  :== => :eq,
36
39
  :!= => :ne,
37
- :'<s' => :lt_s,
38
- :< => :lt_u,
39
- :'>s' => :gt_s,
40
- :> => :gt_u,
41
- :'<=s' => :le_s,
42
- :<= => :le_u,
43
- :'>=s' => :ge_s,
44
- :>= => :ge_u
40
+
41
+ }
42
+
43
+ RELATIONAL_SIGNED_OPS_MAP = {
44
+ :< => :lt_x,
45
+ :> => :gt_x,
46
+ :<= => :le_x,
47
+ :>= => :ge_x
45
48
  }
46
49
 
47
50
  BOOLEAN_OPS_MAP = {
@@ -50,19 +53,40 @@ module Rlang::Parser
50
53
  }
51
54
 
52
55
  UNARY_OPS_MAP = {
53
- :'!' => :eqz
56
+ :'!' => :eqz,
57
+ :'-@' => :sub # special case for unary - turned into (sub 0 x)
54
58
  }
55
59
 
56
- ALL_OPS_MAP = [*ARITHMETIC_OPS_MAP, *RELATIONAL_OPS_MAP, *BOOLEAN_OPS_MAP, *UNARY_OPS_MAP].to_h
60
+ SIGNED_OPS = {
61
+ signed: {div_x: :div_s, rem_x: :rem_s, shr_x: :shr_s, lt_x: :lt_s, gt_x: :gt_s,
62
+ le_x: :le_s, ge_x: :ge_s},
63
+ unsigned: {div_x: :div_u, rem_x: :rem_u, shr_x: :shr_u, lt_x: :lt_u, gt_x: :gt_u,
64
+ le_x: :le_u, ge_x: :ge_u},
65
+ }
66
+
67
+ # Operators that can be legally used when doing
68
+ # arithmetic on class instance (= object) pointers
69
+ # Only unsigned relational operators make sense as pointers
70
+ # are by nature unsigned integers
71
+ LEGAL_CLASS_WASM_OPS = [:eq, :ne, :lt_u, :gt_u, :le_u, :ge_u, :add, :sub]
72
+
73
+ # All operators with signed / unsigned variants
74
+ ALL_SIGNED_OPS_MAP = [*ARITHMETIC_SIGNED_OPS_MAP, *RELATIONAL_SIGNED_OPS_MAP].to_h
75
+
76
+ # All operators in on hash
77
+ ALL_OPS_MAP = [*ARITHMETIC_OPS_MAP, *ARITHMETIC_SIGNED_OPS_MAP, *RELATIONAL_OPS_MAP, *RELATIONAL_SIGNED_OPS_MAP,
78
+ *BOOLEAN_OPS_MAP, *UNARY_OPS_MAP].to_h
57
79
 
58
80
  # Matrix of how to cast a WASM type to another
59
81
  CAST_OPS = {
60
- I32: { I32: :cast_nope, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
61
- I64: { I32: :cast_wrap, I64: :cast_nope, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
62
- F32: { I32: :cast_notyet, I64: :cast_notyet, F32: :cast_nope, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
63
- F64: { I32: :cast_notyet, I64: :cast_notyet, F32: :cast_notyet, F64: :cast_nope, Class: :cast_error, none: :cast_error},
64
- Class: { I32: :cast_wtype, I64: :cast_extend, F32: :cast_error, F64: :cast_error, Class: :cast_wtype, none: :cast_error},
65
- none: { I32: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
82
+ UI32: { UI32: :cast_nope, I32: :cast_wtype, UI64: :cast_extend, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
83
+ I32: { UI32: :cast_wtype, I32: :cast_nope, UI64: :cast_extend, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
84
+ UI64: { UI32: :cast_wrap, I32: :cast_wrap, UI64: :cast_nope, I64: :cast_wtype, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
85
+ I64: { UI32: :cast_wrap, I32: :cast_wrap, UI64: :cast_wtype, I64: :cast_nope, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
86
+ F32: { UI32: :cast_notyet, I32: :cast_notyet, UI64: :cast_notyet, I64: :cast_notyet, F32: :cast_nope, F64: :cast_notyet, Class: :cast_error, none: :cast_error},
87
+ F64: { UI32: :cast_notyet, I32: :cast_notyet, UI64: :cast_notyet, I64: :cast_notyet, F32: :cast_notyet, F64: :cast_nope, Class: :cast_error, none: :cast_error},
88
+ Class: { UI32: :cast_wtype, I32: :cast_wtype, UI64: :cast_extend, I64: :cast_extend, F32: :cast_error, F64: :cast_error, Class: :cast_wtype, none: :cast_error},
89
+ none: { UI32: :cast_error, I32: :cast_error, UI64: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
66
90
  }
67
91
 
68
92
  # Rlang class size method name
@@ -103,6 +127,11 @@ module Rlang::Parser
103
127
  String.new(%{ptr}, %{length})
104
128
  }
105
129
 
130
+ # Dynamically allocate a string object
131
+ ARRAY_NEW_TMPL = %q{
132
+ Array%{elt_size_in_bits}.new(%{ptr}, %{length})
133
+ }
134
+
106
135
  # Generate the wasm nodes and tree structure
107
136
  # ***IMPORTANT NOTE***
108
137
  # Unless otherwise stated all methods receive
@@ -190,6 +219,13 @@ module Rlang::Parser
190
219
  k.wnode
191
220
  end
192
221
 
222
+ def comments(wnode, comments)
223
+ # The gsub below is to handle =begin...=end block comments
224
+ comments.each do |c|
225
+ WNode.new(:comment, wnode).c(:comment, text: c.text.sub(/^\s*#/,'').gsub("\n", "\n;;"))
226
+ end
227
+ end
228
+
193
229
  # Create module object and module wnode
194
230
  # if it doesn't exist yet
195
231
  def module(wnode, module_path)
@@ -305,9 +341,9 @@ module Rlang::Parser
305
341
  unless size_method.wnode
306
342
  logger.debug("Generating #{size_method.klass.name}\##{size_method.name}")
307
343
  wns = WNode.new(:insn, wnc)
308
- wns.wtype = WType::DEFAULT
344
+ wns.wtype = WType::DEFAULT
309
345
  wns.c(:class_size, func_name: size_method.wasm_name,
310
- wtype: wns.wasm_type, size: wnc.class_size)
346
+ wasm_type: wns.wasm_type, size: wnc.class_size)
311
347
  size_method.wnode = wns
312
348
  end
313
349
  end
@@ -317,7 +353,7 @@ module Rlang::Parser
317
353
  wnc = wnode.class_wnode
318
354
  wn_set = WNode.new(:insn, wnc, true)
319
355
  wn_set.c(:attr_setter, func_name: attr.setter.wasm_name,
320
- attr_name: attr.wasm_name, wtype: attr.wasm_type,
356
+ attr_name: attr.wasm_name, wasm_type: attr.wasm_type,
321
357
  offset: attr.offset)
322
358
  wn_set
323
359
  end
@@ -327,8 +363,7 @@ module Rlang::Parser
327
363
  wnc = wnode.class_wnode
328
364
  wn_get = WNode.new(:insn, wnc, true)
329
365
  wn_get.c(:attr_getter, func_name: attr.getter.wasm_name,
330
- attr_name: attr.wasm_name, wtype: attr.wasm_type,
331
- offset: attr.offset)
366
+ wasm_type: attr.wasm_type, offset: attr.offset)
332
367
  wn_get
333
368
  end
334
369
 
@@ -381,7 +416,7 @@ module Rlang::Parser
381
416
  logger.debug("Prepending param #{marg}")
382
417
  wn = WNode.new(:insn, wnm, true)
383
418
  wn.wtype = marg.wtype
384
- wn.c(:param, name: marg.wasm_name)
419
+ wn.c(:param, name: marg.wasm_name, wasm_type: wn.wasm_type)
385
420
  end
386
421
  end
387
422
 
@@ -389,7 +424,7 @@ module Rlang::Parser
389
424
  unless wnode.wtype.blank?
390
425
  wn = WNode.new(:insn, wnode, true)
391
426
  wn.wtype = wnode.wtype
392
- wn.c(:result)
427
+ wn.c(:result, wasm_type: wn.wasm_type)
393
428
  end
394
429
  end
395
430
 
@@ -399,7 +434,7 @@ module Rlang::Parser
399
434
  logger.debug("Prepending local #{lvar.inspect}")
400
435
  wn = WNode.new(:insn, wnm, true)
401
436
  wn.wtype = lvar.wtype
402
- wn.c(:local, name: lvar.wasm_name)
437
+ wn.c(:local, name: lvar.wasm_name, wasm_type: wn.wasm_type)
403
438
  end
404
439
  end
405
440
 
@@ -413,7 +448,7 @@ module Rlang::Parser
413
448
  # Set constant
414
449
  def casgn(wnode, const)
415
450
  (wn = WNode.new(:insn, wnode)).wtype = const.wtype
416
- wn.c(:store, wtype: const.wtype)
451
+ wn.c(:store, wasm_type: const.wtype)
417
452
  WNode.new(:insn, wn).c(:addr, value: const.address)
418
453
  wn
419
454
  end
@@ -421,7 +456,7 @@ module Rlang::Parser
421
456
  # Get constant
422
457
  def const(wnode, const)
423
458
  (wn = WNode.new(:insn, wnode)).wtype = const.wtype
424
- wn.c(:load, wtype: const.wtype, var_name: const.wasm_name)
459
+ wn.c(:load, wasm_type: const.wasm_type)
425
460
  WNode.new(:insn, wn).c(:addr, value: const.address)
426
461
  wn
427
462
  end
@@ -468,7 +503,7 @@ module Rlang::Parser
468
503
  # Set instance variable
469
504
  def ivasgn(wnode, ivar)
470
505
  (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
471
- wn.c(:store_offset, wtype: ivar.wasm_type, offset: lambda { ivar.offset })
506
+ wn.c(:store_offset, wasm_type: ivar.wasm_type, offset: lambda { ivar.offset })
472
507
  self._self_(wn)
473
508
  wn
474
509
  end
@@ -476,7 +511,7 @@ module Rlang::Parser
476
511
  # Get instance variable.
477
512
  def ivar(wnode, ivar)
478
513
  (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
479
- wn.c(:load_offset, wtype: ivar.wasm_type, offset: lambda { ivar.offset })
514
+ wn.c(:load_offset, wasm_type: ivar.wasm_type, offset: lambda { ivar.offset })
480
515
  self._self_(wn)
481
516
  wn
482
517
  end
@@ -486,7 +521,7 @@ module Rlang::Parser
486
521
  # an empty expression node to populate later
487
522
  def cvasgn(wnode, cvar)
488
523
  (wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
489
- wn.c(:store, wtype: cvar.wtype)
524
+ wn.c(:store, wasm_type: cvar.wasm_type)
490
525
  WNode.new(:insn, wn).c(:addr, value: cvar.address)
491
526
  wn
492
527
  end
@@ -494,7 +529,7 @@ module Rlang::Parser
494
529
  # Get class variable
495
530
  def cvar(wnode, cvar)
496
531
  (wn = WNode.new(:insn, wnode)).wtype = cvar.wtype
497
- wn.c(:load, wtype: cvar.wtype, var_name: cvar.wasm_name)
532
+ wn.c(:load, wasm_type: cvar.wasm_type)
498
533
  WNode.new(:insn, wn).c(:addr, value: cvar.address)
499
534
  wn
500
535
  end
@@ -510,14 +545,14 @@ module Rlang::Parser
510
545
  # Create the local variable storage node
511
546
  def lvasgn(wnode, lvar)
512
547
  (wn = WNode.new(:insn, wnode)).wtype = lvar.wtype
513
- wn.c(:local_set, wtype: lvar.wtype, var_name: lvar.wasm_name)
548
+ wn.c(:local_set, var_name: lvar.wasm_name)
514
549
  wn
515
550
  end
516
551
 
517
552
  # Read local variable
518
553
  def lvar(wnode, lvar)
519
554
  (wn = WNode.new(:insn, wnode)).wtype = lvar.wtype
520
- wn.c(:local_get, wtype: lvar.wtype, var_name: lvar.wasm_name)
555
+ wn.c(:local_get, var_name: lvar.wasm_name)
521
556
  wn
522
557
  end
523
558
 
@@ -534,13 +569,13 @@ module Rlang::Parser
534
569
 
535
570
  def int(wnode, wtype, value)
536
571
  (wn = WNode.new(:insn, wnode)).wtype = wtype
537
- wn.c(:const, wtype: wtype, value: value)
572
+ wn.c(:const, wasm_type: wn.wasm_type, value: value)
538
573
  wn
539
574
  end
540
575
 
541
576
  def float(wnode, wtype, value)
542
577
  (wn = WNode.new(:insn, wnode)).wtype = wtype
543
- wn.c(:const, wtype: wtype, value: value)
578
+ wn.c(:const, wasm_type: wn.wasm_type, value: value)
544
579
  wn
545
580
  end
546
581
 
@@ -551,24 +586,25 @@ module Rlang::Parser
551
586
  WNode.new(type, wnode)
552
587
  end
553
588
 
554
- # Static string allocation
555
- def string_static(string, data_label)
556
- # Allocate string itself and the attributes
557
- # of String object pointing to that string
558
- data_stg = DAta.append(data_label.to_sym, string)
559
- data_stg
589
+ # Static string data allocation
590
+ def allocate_string_static_data(string, data_label)
591
+ # if string is empty do not allocate any memory space
592
+ DAta.append(data_label.to_sym, string) unless string.empty?
560
593
  end
561
594
 
562
595
  # Static new string object
563
596
  def string_static_new(wnode, string)
564
597
  klass = wnode.find_current_class_or_module()
565
598
  data_label = "#{klass.name}_string_#{@static_count += 1}"
566
- # Statically
567
- data_stg = self.string_static(string, data_label)
599
+ # Allocate string data statically
600
+ # Note : data_stg is nil if string is empty
601
+ data_stg = self.allocate_string_static_data(string, data_label)
568
602
  # align on :I32 boundary
603
+ # then allocate the String object attributes
604
+ # and set them up
569
605
  DAta.align(4)
570
606
  data_len = DAta.append("#{data_label}_len".to_sym, string.length, WType::DEFAULT)
571
- data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_stg.address, WType::DEFAULT)
607
+ data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_stg ? data_stg.address : 0, WType::DEFAULT)
572
608
  # Generate address wnode
573
609
  (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_len.address)
574
610
  wn_object_addr.wtype = WType.new(:String)
@@ -579,25 +615,73 @@ module Rlang::Parser
579
615
  def string_dynamic_new(wnode, string)
580
616
  klass = wnode.find_current_class_or_module()
581
617
  data_label = "#{klass.name}_string_#{@static_count += 1}"
582
- data_stg = self.string_static(string, data_label)
618
+ # Note : data_stg is nil if string is empty
619
+ data_stg = self.allocate_string_static_data(string, data_label)
583
620
  string_new_source = STRING_NEW_TMPL % {
584
- ptr: data_stg.address,
621
+ ptr: data_stg ? data_stg.address : 0,
585
622
  length: string.length
586
623
  }
587
624
  #puts string_new_source;exit
588
- wn_string = self.parser.parse(string_new_source, wnode)
589
- #puts wn_string; exit
625
+ self.parser.parse(string_new_source, wnode)
626
+ end
627
+
628
+ # Static array data allocation
629
+ def allocate_array_static_data(array, data_label)
630
+ # Append each array element to the same data section
631
+ label = data_label.to_sym
632
+ data_arr = nil
633
+ # Do not allocate memory space if array is empty
634
+ array.each { |elt| data_arr = DAta.append(label, elt) }
635
+ data_arr
636
+ end
637
+
638
+ # Static new array object
639
+ def array_static_new(wnode, array)
640
+ klass = wnode.find_current_class_or_module()
641
+ data_label = "#{klass.name}_array_#{@static_count += 1}"
642
+ # Allocate array data statically
643
+ # Note : data_arr is nil if string is empty
644
+ data_arr = self.allocate_array_static_data(array, data_label)
645
+ # align on :I32 boundary
646
+ # then allocate the Array object attributes
647
+ # and set them up
648
+ DAta.align(4)
649
+ data_count = DAta.append("#{data_label}_count".to_sym, array.length, WType::DEFAULT)
650
+ data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_arr ? data_arr.address : 0, WType::DEFAULT)
651
+ # Generate address wnode
652
+ (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_count.address)
653
+ wn_object_addr.wtype = WType.new(:Array32)
654
+ wn_object_addr
655
+ end
656
+
657
+ # Dynamic new array object
658
+ def array_dynamic_new(wnode, array)
659
+ klass = wnode.find_current_class_or_module()
660
+ data_label = "#{klass.name}_array_#{@static_count += 1}"
661
+ # Note : data_arr is nil if string is empty
662
+ data_arr = self.allocate_array_static_data(array, data_label)
663
+ array_new_source = ARRAY_NEW_TMPL % {
664
+ elt_size_in_bits: WTYPE::DEFAULT.size * 8,
665
+ ptr: data_arr ? data_arr.address : 0,
666
+ count: array.length
667
+ }
668
+ #puts array_new_source;exit
669
+ self.parser.parse(array_new_source, wnode)
590
670
  end
591
671
 
672
+ # TYPE CASTING methods
592
673
  # All the cast_xxxx methods below returns
593
674
  # the new wnode doing the cast operation
594
675
  # or the same wnode if there is no additional code
595
676
  # for the cast operation
677
+
678
+ # No casting. Return node as is.
596
679
  def cast_nope(wnode, wtype, signed)
597
- # Do nothing
598
680
  wnode
599
681
  end
600
682
 
683
+ # Cast by extending to a wtype of larger bit size
684
+ # (e.g. I32 to I64)
601
685
  def cast_extend(wnode, wtype, signed)
602
686
  if (wnode.template == :const)
603
687
  # it's a WASM const, simply change the wtype
@@ -606,15 +690,19 @@ module Rlang::Parser
606
690
  else
607
691
  wn_cast_op = wnode.insert(:insn)
608
692
  wn_cast_op.wtype = wtype
609
- wn_cast_op.c(signed ? :extend_i32_s : :extend_i32_u , wtype: wtype)
693
+ wn_cast_op.c(signed ? :extend_i32_s : :extend_i32_u , wasm_type: wn_cast_op.wasm_type)
610
694
  end
611
695
  wn_cast_op
612
696
  end
613
697
 
698
+ # Cast by simply changing the node wtype
699
+ # No change in native WASM type
700
+ # (e.g. casting an object pointer to I32)
614
701
  def cast_wtype(wnode, wtype, signed)
615
- if (wnode.wtype.default? && wtype.class?) ||
616
- (wnode.wtype.class? && wtype.default?) ||
617
- (wnode.wtype.class? && wtype.class?)
702
+ # Don't cast blindly. Check that source and target
703
+ # have the same bit size (e.g. Object pointers, I32, UI32
704
+ # are the same size, )
705
+ if wnode.wtype.size == wtype.size
618
706
  wnode.wtype = wtype
619
707
  else
620
708
  cast_error(wnode, wtype, signed)
@@ -622,6 +710,8 @@ module Rlang::Parser
622
710
  wnode
623
711
  end
624
712
 
713
+ # Cast by wraping a wtype to a smaller size
714
+ # (e.g. I64 to I32)
625
715
  def cast_wrap(wnode, wtype, signed)
626
716
  if (wnode.template == :const)
627
717
  # it's a WASM const, simply change the wtype
@@ -630,17 +720,19 @@ module Rlang::Parser
630
720
  else
631
721
  wn_cast_op = wnode.insert(:insn)
632
722
  wn_cast_op.wtype = wtype
633
- wn_cast_op.c(:wrap_i64, wtype: wtype)
723
+ wn_cast_op.c(:wrap_i64, wasm_type: wn_cast_op.wasm_type)
634
724
  end
635
725
  wn_cast_op
636
726
  end
637
727
 
728
+ # Cast operation not yet supported
638
729
  def cast_notyet(wnode, wtype, signed)
639
730
  raise "Type cast from #{wnode.wtype} to #{wtype} not supported yet"
640
731
  end
641
732
 
733
+ # Cast operation is invalid
642
734
  def cast_error(wnode, wtype, signed)
643
- raise "Cannot cast type #{src} to #{dest}. Time to fix your code :-)"
735
+ raise "Cannot cast type #{wnode.wtype} to #{wtype}. Time to fix your code :-)"
644
736
  end
645
737
 
646
738
  # cast an expression to a different type
@@ -668,17 +760,20 @@ module Rlang::Parser
668
760
  # operands below)
669
761
  def native_operator(wnode, operator, wtype=WType.new(:none))
670
762
  if (op = ALL_OPS_MAP[operator])
671
- (wn_op = WNode.new(:insn, wnode)).c(:operator, operator: op)
672
- wn_op.wtype = wtype
763
+ (wn_op = WNode.new(:insn, wnode)).wtype = wtype
764
+ wn_op.c(:operator, wasm_type: wn_op.wasm_type, operator: op)
673
765
  logger.debug "Creating operator #{operator} wnode: #{wn_op}"
766
+ # special case for - unary operator transformed into (0 - x)
767
+ WNode.new(:insn, wn_op).c(:const, wasm_type: wn_op.wasm_type, value: 0) if operator == :-@
674
768
  wn_op
675
769
  else
676
770
  raise "operator '#{operator}' not supported"
677
771
  end
678
772
  end
679
773
 
680
- # finish the setting of the operator node and
681
- # attach operands
774
+ # Finish the setting of the operator node,
775
+ # attach operands and see if they need implicit
776
+ # type casting
682
777
  def operands(wnode_op, wnode_recv, wnode_args)
683
778
  logger.debug "Processing operands in operator wnode: #{wnode_op}..."
684
779
  # Do not post process operands if the operator
@@ -696,26 +791,48 @@ module Rlang::Parser
696
791
  #wnode_recv = wnode_op.children[0]
697
792
  #wnode_args = wnode_op.children[1..-1]
698
793
  # First find out the wtype that has precedence
699
- wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
794
+ leading_wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
700
795
 
701
- wnode_op.wtype = wtype
702
- logger.debug "leading type cast: #{wtype}"
796
+ wnode_op.wtype = leading_wtype
797
+ logger.debug "leading type cast: #{leading_wtype}"
703
798
 
704
799
  # Attach receiver and argument to the operator wnode
705
- # type casting them if necessary
706
- self.cast(wnode_recv, wtype).reparent_to(wnode_op)
707
- self.cast(wnode_args.first, wtype).reparent_to(wnode_op) unless wnode_args.empty?
800
+ # type casting them if necessary
801
+ # Note : normally for an operator there is only one argument
802
+ # but process args as if they were many one day.
803
+ logger.debug "Perform implicit type casting of receviver and operator arg(s)"
804
+ self.cast(wnode_recv, leading_wtype).reparent_to(wnode_op)
805
+ wnode_args.each do |wna|
806
+ self.cast(wna, leading_wtype).reparent_to(wnode_op)
807
+ end
808
+
809
+ # Once operands casting is done, see if we need the signed or unsigned
810
+ # version of the native operator
811
+ # NOTE : At this stage, after the operands casting both of them
812
+ # should either be signed or unsigned, hence the XNOR sanity check
813
+ # below
814
+ if ALL_SIGNED_OPS_MAP.values.include? op
815
+ if !((signed = wnode_recv.wtype.signed?) ^ (wnode_args.empty? ? true : wnode_args.first.wtype.signed?))
816
+ wnode_op.wargs[:operator] = SIGNED_OPS[signed ? :signed : :unsigned][op]
817
+ logger.debug "Receiver has wtype #{wnode_recv.wtype} / Argument has wtype #{wnode_args.first.wtype}"
818
+ logger.debug "Replacing #{op} operator with #{wnode_op.wargs[:operator]}"
819
+ op = wnode_op.wargs[:operator]
820
+ else
821
+ raise "Type mismatch between operands. Receiver is #{wnode_recv.wtype} and argument is #{wnode_args.first.wtype}"
822
+ end
823
+ end
708
824
 
709
825
  # if the receiver is a class object and not
710
826
  # a native integer then pointer arithmetic
711
827
  # applies (like in C)
712
828
  if wnode_recv.wtype.class?
713
- legal_ops = RELATIONAL_OPS_MAP.values + [:add, :sub]
714
- raise "Only #{legal_ops.join(', ')} operators are supported on objects (got #{op} in #{wnode_op})" \
715
- unless legal_ops.include?(op)
716
- # if + or - operator then multiply arg by size of object
717
- if [:add, :sub].include? wnode_op.wargs[:operator]
718
- (wn_mulop = WNode.new(:insn, wnode_op)).c(:operator, operator: :mul)
829
+ raise "Only #{LEGAL_CLASS_WASM_OPS.join(', ')} operators are supported on objects (got #{op} in #{wnode_op})" \
830
+ unless LEGAL_CLASS_WASM_OPS.include?(op)
831
+ # if :add or :sub operator then multiply arg by size of object
832
+ # like in C
833
+ if [:add, :sub].include? op
834
+ (wn_mulop = WNode.new(:insn, wnode_op)).wtype = WType::DEFAULT
835
+ wn_mulop.c(:operator, wasm_type: wn_mulop.wasm_type, operator: :mul)
719
836
  WNode.new(:insn, wn_mulop).c(:call, func_name: "$#{wnode_recv.wtype.name}::#{SIZE_METHOD}")
720
837
  wnode_args.first.reparent_to(wn_mulop)
721
838
  else
@@ -794,14 +911,35 @@ module Rlang::Parser
794
911
  # generate code for method call
795
912
  def send_method(wnode, class_path, method_name, method_type)
796
913
  logger.debug "In call generator for #{class_path}::#{method_name}"
797
- k = wnode.find_class_or_module(class_path)
798
- if k && (method = wnode.find_method(k, method_name, method_type))
799
- logger.debug "Found method #{method.name} in class #{method.klass.name}"
800
- (wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
801
- wn_call.wtype = method.wtype
802
- wn_call
914
+ if k = wnode.find_class_or_module(class_path)
915
+ method = wnode.find_method(k, method_name, method_type)
916
+ if k.wtype.native? && ALL_OPS_MAP.has_key?(method_name)
917
+ # An Rlang method exist for this class but methods corresponding to
918
+ # Webassembly native operators applied to native Webassembly types
919
+ # (I32, I64, F32, F64) **cannot** be overriden by instance methods
920
+ # in Rlang code
921
+ if method
922
+ logger.warn "Rlang #{class_path}::#{method_name} method ignored. Native operator has precedence"
923
+ end
924
+ logger.debug "Apply native operator #{method_name} to native wtype #{class_path}"
925
+ wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
926
+ elsif !k.wtype.native? && ALL_OPS_MAP.has_key?(method_name) && method.nil?
927
+ # Similarly if the Class is not a native type (a regular class)
928
+ # and the Class doesn't provide its own method implementation of the native
929
+ # operator then apply the native operands
930
+ logger.debug "Apply native operator #{method_name} to class found : #{class_path}"
931
+ wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
932
+ elsif method
933
+ logger.debug "Found method #{method.name} in class #{method.klass.name}"
934
+ (wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
935
+ wn_call.wtype = method.wtype
936
+ wn_call
937
+ else
938
+ raise "Unknown method '#{method_name}' in class #{class_path}"
939
+ end
803
940
  elsif ALL_OPS_MAP.has_key? method_name
804
- # it's a native Wasm operator
941
+ # It is a native Wasm operator
942
+ logger.debug "Native operator found : #{class_path}::#{method_name}"
805
943
  wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
806
944
  else
807
945
  raise "Unknown method '#{method_name}' in class #{class_path}"
@@ -838,8 +976,8 @@ module Rlang::Parser
838
976
  end
839
977
 
840
978
  def while(wnode)
841
- (wnb = WNode.new(:insn, wnode)).c(:block)
842
- (wnl = WNode.new(:insn, wnb)).c(:loop)
979
+ (wnb = WNode.new(:insn, wnode)).c(:block, label: wnb.set_label)
980
+ (wnl = WNode.new(:insn, wnb)).c(:loop, label: wnl.set_label)
843
981
  (wnbi = WNode.new(:insn, wnl)).c(:br_if, label: wnb.label)
844
982
  return wnb,wnbi,wnl
845
983
  end
@@ -849,7 +987,7 @@ module Rlang::Parser
849
987
  # negate the original while condition
850
988
  def while_cond(wnode, wnode_cond_exp)
851
989
  wn_eqz = WNode.new(:insn, wnode)
852
- wn_eqz.c(:eqz, wtype: wnode_cond_exp.wtype)
990
+ wn_eqz.c(:eqz, wasm_type: wnode_cond_exp.wasm_type)
853
991
  wnode_cond_exp.reparent_to(wn_eqz)
854
992
  wn_eqz
855
993
  end
@@ -52,14 +52,14 @@ module Rlang::Parser
52
52
  br_if: 'br_if %{label}',
53
53
  br: 'br %{label}',
54
54
  inline: '%{code}',
55
- attr_getter: %q{func %{func_name} (param $_self_ i32) (result %{wtype})
56
- (%{wtype}.load offset=%{offset} (local.get $_self_))},
57
- attr_setter: %q{func %{func_name} (param $_self_ i32) (param %{attr_name} %{wtype}) (result %{wtype})
55
+ attr_getter: %q{func %{func_name} (param $_self_ i32) (result %{wasm_type})
56
+ (%{wasm_type}.load offset=%{offset} (local.get $_self_))},
57
+ attr_setter: %q{func %{func_name} (param $_self_ i32) (param %{attr_name} %{wasm_type}) (result %{wasm_type})
58
58
  (local.get %{attr_name})
59
- (%{wtype}.store offset=%{offset} (local.get $_self_) (local.get %{attr_name}))},
60
- class_size: %q{func %{func_name} (result %{wtype})
61
- (%{wtype}.const %{size})},
62
- comment: ';; %{comment}',
59
+ (%{wasm_type}.store offset=%{offset} (local.get $_self_) (local.get %{attr_name}))},
60
+ class_size: %q{func %{func_name} (result %{wasm_type})
61
+ (%{wasm_type}.const %{size})},
62
+ comment: ';; %{text}',
63
63
  memory: 'memory $0 %{min} %{max}',
64
64
  module: 'module %{module}'
65
65
  }
@@ -126,17 +126,37 @@ module Rlang::Parser
126
126
  def c(template, wargs = {})
127
127
  raise "Error: unknown WASM code template (#{template})" unless T.has_key? template
128
128
  raise "Error: this WNode is already populated with instruction #{@template}" if @template
129
- if [:loop, :block].include? template
130
- wargs[:label] = self.set_label
131
- end
129
+ #if [:loop, :block].include? template
130
+ # wargs[:label] = self.set_label
131
+ #end
132
132
  @template = template
133
133
  @wargs = wargs
134
134
  end
135
135
 
136
136
  def wasm_code
137
- @wargs[:wasm_type] ||= self.wasm_type
137
+ return '' unless T[@template]
138
138
  wargs = {}
139
139
  @wargs.each { |k, v| wargs[k] = (v.is_a?(Proc) ? v.call : v) }
140
+ # Because WNode#to_s generate wasm code from sometimes incomplete
141
+ # information, we must add the wasm_type key if needed by the template
142
+ if T[@template].index("%{wasm_type}")
143
+ puts "*** adding wasm_type to #{T[@template]}" unless wargs.has_key? :wasm_type
144
+ wargs[:wasm_type] ||= self.wasm_type
145
+ end
146
+
147
+ # debug code to activate if you get
148
+ # a Ruby warning on too many or too few arguments
149
+ # wargs.each do |k, v|
150
+ # if T[@template].index("%{#{k.to_s}}").nil?
151
+ # puts "**** Error wargs missing keys in wargs : template: #{@template} / wargs: #{wargs.inspect}"
152
+ # end
153
+ # end
154
+ #T[@template].scan(/%{([^}]+)}/).flatten.each do |k|
155
+ # unless wargs.has_key? k.to_sym
156
+ # puts "**** Error wargs missing keys in template: template: #{@template} / wargs: #{wargs.inspect}"
157
+ # end
158
+ # end
159
+ #logger.debug "code template : #{@template} / T : #{T[@template]} / wargs : #{wargs.inspect}" if @template
140
160
  T[@template] ? T[@template] % wargs : ''
141
161
  end
142
162
 
@@ -154,8 +174,13 @@ module Rlang::Parser
154
174
  logger.debug "Setting wtype #{wtype} for wnode #{self}"
155
175
  @wtype = wtype
156
176
  @method.wtype = @wtype if self.method?
157
- # update wasm_type template arg accordingly
158
- @wargs[:wasm_type] = @wtype.wasm_type if @wtype
177
+ # update wasm_type template arg accordingly if ti is already set
178
+ # (this is needed in some cases - e.g. operands - where node wtype
179
+ # is known once the wtypes of the children nodes are known )
180
+ if @wtype && @wargs.has_key?(:wasm_type)
181
+ logger.debug "Updating @wargs[:wasm_type] from '#{@wargs[:wasm_type]}' to '#{@wtype.wasm_type}'"
182
+ @wargs[:wasm_type] = @wtype.wasm_type
183
+ end
159
184
  logger.debug "type #{self.type} wargs #{self.wargs} wtype #{@wtype}"
160
185
  @wtype
161
186
  end
@@ -715,7 +740,9 @@ module Rlang::Parser
715
740
  logger.debug "children: #{self} / #{children.map(&:head)}" if self.link
716
741
 
717
742
  case @type
718
- # Section nodes
743
+ # Section nodes
744
+ when :comment
745
+ "\n%s%s" % [indent, self.wasm_code]
719
746
  when :imports
720
747
  "\n%s;;============= %s SECTION ===============\n" % [indent, @type.to_s.upcase] +
721
748
  children.map { |wn| wn.transpile(depth) }.join('')