rlang 0.4.0 → 0.4.1

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.
@@ -9,61 +9,64 @@ require_relative './ivar'
9
9
  require_relative './wtype'
10
10
 
11
11
  module Rlang::Parser
12
- class WAttr
12
+ class Attr
13
13
  include Log
14
- attr_reader :name, :wtype, :getter, :setter, :ivar
14
+ attr_reader :name, :getter, :setter, :ivar
15
15
 
16
16
  # The name argument can either be the attribute name
17
17
  # (e.g. :size) or an ivar name (e.g. :@size)
18
18
  def initialize(class_wnode, name, wtype=WType::DEFAULT)
19
19
  @class_wnode = class_wnode
20
- if name.to_s[0] == '@'
21
- @ivar = IVar.new(@class_wnode, name, wtype)
22
- @name = @ivar.wattr_name
23
- else
24
- @name = name
25
- @ivar = IVar.new(@class_wnode, self.ivar_name, wtype)
26
- end
27
- @wtype = wtype
28
- # Also create the corresponding getter and setter
29
- # method objects (with default WType - wattr_type
30
- # directives might later change this wtype)
31
- # Don't generate WAT code yet
20
+ @name = name
21
+ @ivar = class_wnode.create_ivar(:"@#{name}", wtype)
22
+ @getter = nil
23
+ @setter = nil
24
+ @export = false
25
+ logger.debug "Class attribute #{name} created"
26
+ end
27
+
28
+ def attr_reader
32
29
  @getter = @class_wnode.find_or_create_method(self.getter_name, nil, wtype, :instance)
33
- @getter.instance!
30
+ @getter.export! if @export
34
31
  logger.debug "Getter created: #{@getter.inspect}"
32
+ @getter
33
+ end
35
34
 
35
+ def attr_writer
36
36
  @setter = @class_wnode.find_or_create_method(self.setter_name, nil, wtype, :instance)
37
- @setter.instance!
37
+ @setter.export! if @export
38
38
  logger.debug "Setter created: #{@setter.inspect}"
39
- logger.debug "Class attribute #{name} created"
39
+ @setter
40
40
  end
41
41
 
42
- def class_name
43
- @class_wnode.class_name
42
+ def attr_accessor
43
+ [self.attr_reader, self.attr_writer]
44
44
  end
45
45
 
46
- def wtype=(wtype)
47
- @wtype = wtype
48
- # Adjust corresponding method objects wtype accordingly
49
- @getter.wtype = wtype
50
- @setter.wtype = wtype
51
- @ivar.wtype = wtype
52
- logger.debug "WAttr/Getter/Setter/ivar wtype updated : #{@getter.inspect}"
46
+ def export!
47
+ @export = true
53
48
  end
54
49
 
55
- def size
56
- @wtype.size
50
+ def class_name
51
+ @class_wnode.class_name
57
52
  end
58
53
 
59
- def ivar_name
60
- @ivar ? @ivar.name : :"@#{name}"
54
+ def wtype
55
+ @ivar.wtype
61
56
  end
62
57
 
63
- def wasm_name
64
- "$#{@name}"
58
+ def offset
59
+ @ivar.offset
65
60
  end
66
61
 
62
+ def wtype=(wtype)
63
+ # Adjust getter/setter and ivar wtype accordingly
64
+ @getter.wtype = wtype if @getter
65
+ @setter.wtype = wtype if @setter
66
+ @ivar.wtype = wtype
67
+ logger.debug "Attr/Getter/Setter/ivar wtype updated : #{@getter.inspect}"
68
+ end
69
+
67
70
  def getter_name
68
71
  @name
69
72
  end
@@ -71,8 +74,13 @@ module Rlang::Parser
71
74
  def setter_name
72
75
  "#{@name}=".to_sym
73
76
  end
77
+
78
+ def wasm_name
79
+ "$#{@name}"
80
+ end
81
+
74
82
  def wasm_type
75
- @wtype.wasm_type
83
+ self.wtype.wasm_type
76
84
  end
77
85
  end
78
86
  end
@@ -17,6 +17,9 @@ module Rlang::Parser
17
17
 
18
18
  attr_reader :label, :wtype, :address, :value
19
19
 
20
+ # NOTE: new and append only takes individual DAta values
21
+ # not an array of values
22
+ # TODO: fix DAta.new and DAta.append not accepting an array of values
20
23
  def initialize(label, value, wtype=WType::DEFAULT)
21
24
  raise "Data label '#{label}' already initialized" \
22
25
  if self.class.exist? label
@@ -2,7 +2,7 @@
2
2
  # Copyright (c) 2019, Laurent Julliard and contributors
3
3
  # All rights reserved.
4
4
  #
5
- # Instance variables
5
+ # Instance variables class
6
6
  #
7
7
  require_relative '../../utils/log'
8
8
  require_relative './wtype'
@@ -11,22 +11,31 @@ require_relative './data'
11
11
  module Rlang::Parser
12
12
  class IVar
13
13
  include Log
14
- attr_reader :name, :class_wnode
15
- attr_accessor :wtype
14
+ attr_reader :name
15
+ attr_accessor :wtype, :offset
16
16
 
17
17
  def initialize(class_wnode, name, wtype=WType::DEFAULT)
18
- @name = name
19
18
  @class_wnode = class_wnode
19
+ @name = name
20
20
  @wtype = wtype
21
- logger.debug "creating instance variable #{name} in class #{self.class_name} / wtype #{wtype}"
21
+ # this is the offset of the instance variable
22
+ # in memory in the order they are declared
23
+ # It is computed at the end of a class
24
+ # definition
25
+ @offset = nil
26
+ logger.debug "Instance variable #{name} created"
22
27
  end
23
28
 
24
29
  def class_name
25
30
  @class_wnode.class_name
26
31
  end
27
32
 
28
- def wattr_name
29
- @name.to_s.tr('@','').to_sym
33
+ def size
34
+ @wtype.size
35
+ end
36
+
37
+ def wasm_name
38
+ "$#{@name}"
30
39
  end
31
40
 
32
41
  def wasm_type
@@ -13,7 +13,8 @@ module Rlang::Parser
13
13
  include Log
14
14
 
15
15
  attr_reader :wtype
16
- attr_accessor :name, :wnode, :wattrs, :cvars, :consts, :methods
16
+ attr_accessor :name, :wnode, :attrs, :ivars, :cvars,
17
+ :consts, :methods, :offset
17
18
 
18
19
  def initialize(name)
19
20
  @name = name
@@ -23,14 +24,16 @@ module Rlang::Parser
23
24
  # the wnode implementing the code of the class
24
25
  @wnode = nil
25
26
  logger.debug "Klass created #{self.inspect}"
26
- @wattrs = [] # class attributes
27
+ @attrs = [] # class attributes
28
+ @ivars = [] # instance variables
27
29
  @cvars = [] # class variables
28
30
  @consts = [] # class constants
29
31
  @methods = [] # methods
32
+ @offset = 0 # offset of the next class attribute in memory
30
33
  end
31
34
 
32
35
  def size
33
- self.wattrs.sum(&:size)
36
+ @offset
34
37
  end
35
38
 
36
39
  def wtype=(wtype)
@@ -16,32 +16,40 @@ module Rlang::Parser
16
16
  attr_reader :name, :wtype
17
17
  attr_accessor :class_name, :margs, :lvars, :wnode
18
18
 
19
- def initialize(name, class_name, wtype=WType::DEFAULT)
19
+ METHOD_TYPES = [:class, :instance]
20
+
21
+ def initialize(name, class_name, wtype, method_type)
20
22
  raise "Wrong method wtype argument: #{wtype.inspect}" unless wtype.is_a? WType
21
23
  @name = name
22
24
  @class_name = class_name
23
- @wtype = wtype
24
- @instance = false
25
+ @wtype = wtype || WType::DEFAULT
26
+ @method_type = method_type
27
+ raise "Unknown method type: #{method_type}" unless METHOD_TYPES.include? @method_type
25
28
  @wnode = nil # wnode where method is implemented
26
29
  logger.debug "Method created #{self.inspect}"
27
30
  @margs = [] # method args
28
31
  @lvars = [] # local variables
29
32
  end
30
33
 
34
+ def implemented?
35
+ !@wnode.nil?
36
+ end
37
+
31
38
  def instance!
32
- @instance = true
39
+ @method_type = :instance
33
40
  end
34
41
 
35
42
  def instance?
36
- @instance
43
+ @method_type == :instance
44
+
37
45
  end
38
46
 
39
47
  def class!
40
- @instance = false
48
+ @method_type = :class
41
49
  end
42
50
 
43
51
  def class?
44
- !@instance
52
+ @method_type == :class
45
53
  end
46
54
 
47
55
  def wtype=(wtype)
@@ -50,7 +58,7 @@ module Rlang::Parser
50
58
  end
51
59
 
52
60
  def wasm_name
53
- if @instance
61
+ if self.instance?
54
62
  "$#{@class_name}##{@name}"
55
63
  else
56
64
  "$#{@class_name}::#{@name}"
@@ -62,7 +70,7 @@ module Rlang::Parser
62
70
  end
63
71
 
64
72
  def export_name
65
- if @instance
73
+ if self.instance?
66
74
  "#{@class_name.downcase}_i_#{@name}"
67
75
  else
68
76
  "#{@class_name.downcase}_c_#{@name}"
@@ -50,6 +50,8 @@ module Rlang::Parser
50
50
  :'!' => :eqz
51
51
  }
52
52
 
53
+ ALL_OPS_MAP = [*ARITHMETIC_OPS_MAP, *RELATIONAL_OPS_MAP, *BOOLEAN_OPS_MAP, *UNARY_OPS_MAP].to_h
54
+
53
55
  # Matrix of how to cast a WASM type to another
54
56
  CAST_OPS = {
55
57
  I32: { I32: :cast_nope, I64: :cast_extend, F32: :cast_notyet, F64: :cast_notyet, Class: :cast_wtype, none: :cast_error},
@@ -60,6 +62,9 @@ module Rlang::Parser
60
62
  none: { I32: :cast_error, I64: :cast_error, F32: :cast_error, F64: :cast_error, Class: :cast_error, none: :cast_error},
61
63
  }
62
64
 
65
+ # Rlang class size method name
66
+ SIZE_METHOD = :_size_
67
+
63
68
  # new template when object size > 0
64
69
  NEW_TMPL = %q{
65
70
  result :Object, :alloc, :%{default_wtype}
@@ -71,7 +76,7 @@ module Rlang::Parser
71
76
  end
72
77
  }
73
78
 
74
- # new template when object size iz 0 (no instance var)
79
+ # new template when object size is 0 (no instance var)
75
80
  # use 0 as the _self_ address in memory. It should never
76
81
  # be used anyway
77
82
  NEW_ZERO_TMPL = %q{
@@ -91,6 +96,11 @@ module Rlang::Parser
91
96
  end
92
97
  }
93
98
 
99
+ # Dynamically allocate a string object
100
+ STRING_NEW_TMPL = %q{
101
+ String.new(%{ptr}, %{length})
102
+ }
103
+
94
104
  # Generate the wasm nodes and tree structure
95
105
  # ***IMPORTANT NOTE***
96
106
  # Unless otherwise stated all methods receive
@@ -106,6 +116,7 @@ module Rlang::Parser
106
116
  @parser = parser
107
117
  @root = WTree.new().root
108
118
  @new_count = 0
119
+ @static_count = 0
109
120
  end
110
121
 
111
122
  def klass(wnode, class_name)
@@ -118,22 +129,51 @@ module Rlang::Parser
118
129
  k.wnode
119
130
  end
120
131
 
121
- def def_wattr(wnode)
132
+ # Postprocess ivars
133
+ # (called at end of class parsing)
134
+ def ivars_setup(wnode)
122
135
  wnc = wnode.class_wnode
123
- # Process each declared attribute
124
- offset = 0
125
- wnc.klass.wattrs.each do |wa|
126
- logger.debug("Generating accessors for attribute #{wnc.klass.name}\##{wa.name}")
136
+ raise "Cannot find class for attributes definition!!" unless wnc
137
+ klass = wnc.klass
138
+ logger.debug "Postprocessing ivars for class #{klass.name}..."
139
+ klass.ivars.each do |iv|
140
+ iv.offset = klass.offset
141
+ logger.debug "... ivar #{iv.name} has offset #{iv.offset}"
142
+ # Update offset for next ivar
143
+ klass.offset += iv.size
144
+ end
145
+ end
146
+
147
+ # generate code for class attributes
148
+ # (called at end of class parsing)
149
+ def def_attr(wnode)
150
+ wnc = wnode.class_wnode
151
+ raise "Cannot find class for attributes definition!!" unless wnc
152
+ # Process each declared class attribute
153
+ wnc.klass.attrs.each do |attr|
154
+ logger.debug("Generating accessors for attribute #{wnc.klass.name}\##{attr.name}")
127
155
  # Generate getter and setter methods wnode
128
- (wa.setter.wnode = wattr_setter(wnc, wa, offset)) unless wa.setter.wnode
129
- (wa.getter.wnode = wattr_getter(wnc, wa, offset)) unless wa.getter.wnode
130
- # Update offset
131
- offset += wa.wtype.size
156
+ # unless method already implemented by user
157
+ if attr.setter
158
+ unless attr.setter.implemented?
159
+ attr.setter.wnode = self.attr_setter(wnc, attr)
160
+ else
161
+ logger.debug "Attribute setter #{attr.setter.name} already defined. Skipping"
162
+ end
163
+ end
164
+ if attr.getter
165
+ unless attr.getter.implemented?
166
+ attr.getter.wnode = self.attr_getter(wnc, attr)
167
+ else
168
+ logger.debug "Attribute getter #{attr.getter.name} already defined. Skipping"
169
+ end
170
+ end
132
171
  end
133
172
 
134
173
  # Also generate the Class::_size_ method
135
- # always (needed by Object.allocate)
136
- size_method = wnc.find_or_create_method(:_size_, wnc.klass.name, WType::DEFAULT, :class )
174
+ # (needed for dynamic memory allocation
175
+ # by Object.allocate)
176
+ size_method = wnc.find_or_create_method(SIZE_METHOD, wnc.klass.name, WType::DEFAULT, :class)
137
177
  unless size_method.wnode
138
178
  logger.debug("Generating #{size_method.class_name}\##{size_method.name}")
139
179
  wns = WNode.new(:insn, wnc, true)
@@ -145,22 +185,22 @@ module Rlang::Parser
145
185
  end
146
186
 
147
187
  # Generate attribute setter method wnode
148
- def wattr_setter(wnode, wattr, offset)
188
+ def attr_setter(wnode, attr)
149
189
  wnc = wnode.class_wnode
150
190
  wn_set = WNode.new(:insn, wnc, true)
151
- wn_set.c(:wattr_writer, func_name: wattr.setter.wasm_name,
152
- wattr_name: wattr.wasm_name, wtype: wattr.wasm_type,
153
- offset: offset)
191
+ wn_set.c(:attr_setter, func_name: attr.setter.wasm_name,
192
+ attr_name: attr.wasm_name, wtype: attr.wasm_type,
193
+ offset: attr.offset)
154
194
  wn_set
155
195
  end
156
196
 
157
197
  # Generate attribute getter method wnode
158
- def wattr_getter(wnode, wattr, offset)
198
+ def attr_getter(wnode, attr)
159
199
  wnc = wnode.class_wnode
160
200
  wn_get = WNode.new(:insn, wnc, true)
161
- wn_get.c(:wattr_reader, func_name: wattr.getter.wasm_name,
162
- wattr_name: wattr.wasm_name, wtype: wattr.wasm_type,
163
- offset: offset)
201
+ wn_get.c(:attr_getter, func_name: attr.getter.wasm_name,
202
+ attr_name: attr.wasm_name, wtype: attr.wasm_type,
203
+ offset: attr.offset)
164
204
  wn_get
165
205
  end
166
206
 
@@ -225,11 +265,12 @@ module Rlang::Parser
225
265
  wn
226
266
  end
227
267
 
228
- # Set class variable
229
- # Constant assignment doesn't generate any code
230
- # A Data object is instantiated and initialized
231
- # when the Const object is created in parser
268
+ # Set constant
232
269
  def casgn(wnode, const)
270
+ (wn = WNode.new(:insn, wnode)).wtype = const.wtype
271
+ wn.c(:store, wtype: const.wtype)
272
+ WNode.new(:insn, wn).c(:addr, value: const.address)
273
+ wn
233
274
  end
234
275
 
235
276
  # Get class variable
@@ -254,34 +295,38 @@ module Rlang::Parser
254
295
  wn
255
296
  end
256
297
 
257
- # Call setter (on wattr or instance variable)
298
+ # Call setter (on attr or instance variable)
258
299
  # This is the same as calling the corresponding setter
259
- def call_setter(wnode, wnode_recv, wattr)
260
- wn = self.call(wnode, wnode_recv.wtype.name, wattr.setter_name, :instance)
300
+ def call_setter(wnode, wnode_recv, attr)
301
+ wn = self.call(wnode, wnode_recv.wtype.name, attr.setter_name, :instance)
261
302
  # First argument of the setter must be the receiver
262
303
  wnode_recv.reparent_to(wn)
263
304
  wn
264
305
  end
265
306
 
266
- # Call getter (on wattr or instance variable)
307
+ # Call getter (on attr or instance variable)
267
308
  # This is the same as calling the corresponding getter
268
- def call_getter(wnode, wnode_recv, wattr)
269
- wn = self.call(wnode, wnode_recv.wtype.name, wattr.getter_name, :instance)
309
+ def call_getter(wnode, wnode_recv, attr)
310
+ wn = self.call(wnode, wnode_recv.wtype.name, attr.getter_name, :instance)
270
311
  # First argument of the getter must always be the receiver
271
312
  wnode_recv.reparent_to(wn)
272
313
  wn
273
314
  end
274
315
 
275
316
  # Set instance variable
276
- # This is the same as calling the corresponding setter
277
- def ivasgn(wnode, wnode_recv, wattr)
278
- self.call_setter(wnode, wnode_recv, wattr)
317
+ def ivasgn(wnode, ivar)
318
+ (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
319
+ wn.c(:store_offset, wtype: ivar.wasm_type, offset: lambda { ivar.offset })
320
+ self._self_(wn)
321
+ wn
279
322
  end
280
323
 
281
324
  # Get instance variable.
282
- # This is the same as calling the corresponding getter
283
- def ivar(wnode, wnode_recv, wattr)
284
- self.call_getter(wnode, wnode_recv, wattr)
325
+ def ivar(wnode, ivar)
326
+ (wn = WNode.new(:insn, wnode)).wtype = ivar.wtype
327
+ wn.c(:load_offset, wtype: ivar.wasm_type, offset: lambda { ivar.offset })
328
+ self._self_(wn)
329
+ wn
285
330
  end
286
331
 
287
332
  # Set class variable
@@ -339,6 +384,52 @@ module Rlang::Parser
339
384
  wn
340
385
  end
341
386
 
387
+ # Generate a phony node (generally used to create
388
+ # a wnode subtree under the phony node and later
389
+ # reparent it to the proper place in the wtree)
390
+ def phony(wnode)
391
+ phony = WNode.new(:none, wnode)
392
+ phony
393
+ end
394
+
395
+ # Static string allocation
396
+ def string_static(string, data_label)
397
+ # Allocate string itself and the attributes
398
+ # of String object pointing to that string
399
+ data_stg = DAta.append(data_label.to_sym, string)
400
+ data_stg
401
+ end
402
+
403
+ # Static new string object
404
+ def string_static_new(wnode, string)
405
+ klass = wnode.find_class(nil)
406
+ data_label = "#{klass.name}_string_#{@static_count += 1}"
407
+ # Statically
408
+ data_stg = self.string_static(string, data_label)
409
+ # align on :I32 boundary
410
+ DAta.align(4)
411
+ data_len = DAta.append("#{data_label}_len".to_sym, string.length, WType::DEFAULT)
412
+ data_ptr = DAta.append("#{data_label}_ptr".to_sym, data_stg.address, WType::DEFAULT)
413
+ # Generate address wnode
414
+ (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: data_len.address)
415
+ wn_object_addr.wtype = WType.new(:String)
416
+ wn_object_addr
417
+ end
418
+
419
+ # Dynamic new string object
420
+ def string_dynamic_new(wnode, string)
421
+ klass = wnode.find_class(nil)
422
+ data_label = "#{klass.name}_string_#{@static_count += 1}"
423
+ data_stg = self.string_static(string, data_label)
424
+ string_new_source = STRING_NEW_TMPL % {
425
+ string: string,
426
+ ptr: data_stg.address,
427
+ length: string.length
428
+ }
429
+ #puts string_new_source;exit
430
+ wn_string = self.parser.parse(string_new_source, wnode)
431
+ end
432
+
342
433
  # All the cast_xxxx methods below returns
343
434
  # the new wnode doing the cast operation
344
435
  # or the same wnode if there is no additional code
@@ -402,27 +493,44 @@ module Rlang::Parser
402
493
  # TODO: simplify this complex method (possibly by using
403
494
  # a conversion table source type -> destination type)
404
495
  def cast(wnode, wtype, signed=false)
405
- logger.debug "wnode: #{wnode}, wtype: #{wtype}"
496
+ logger.debug "Casting wnode: #{wnode}, wtype: #{wtype}, wnode ID: #{wnode.object_id}"
406
497
  src_type = (wnode.wtype.native? ? wnode.wtype.name : :Class)
407
498
  dest_type = (wtype.native? ? wtype.name : :Class)
408
499
  cast_method = CAST_OPS[src_type] && CAST_OPS[src_type][dest_type] || :cast_error
409
-
500
+ logger.debug "Calling cast method: #{cast_method}"
410
501
  wn_cast_op = self.send(cast_method, wnode, wtype, signed)
411
- logger.debug "After type cast: wnode: #{wn_cast_op}, wtype: #{wtype}"
502
+ logger.debug "After type cast: wnode: #{wn_cast_op}, wtype: #{wtype}, wnode ID: #{wn_cast_op.object_id}"
412
503
  wn_cast_op
413
504
  end
414
505
 
506
+ # before generating native operator Wasm code check
507
+ # if the operator was overloaded
508
+ def send_method(wnode, method_name, wtype=WType.new(:none))
509
+ class_name = wtype.name
510
+ if wnode.find_method(method_name, class_name, :instance)
511
+ # it's an instance method call
512
+ logger.debug "Overloaded operator #{method_name} found in class #{class_name}"
513
+ wn_op = self.call(wnode, class_name, method_name, :instance)
514
+ else
515
+ # it's a native Wasm operator
516
+ if ALL_OPS_MAP.has_key? method_name
517
+ wn_op = self.native_operator(wnode, method_name, wtype)
518
+ else
519
+ raise "Unknown method '#{method_name}' in class #{class_name}"
520
+ end
521
+ end
522
+ wn_op
523
+ end
524
+
415
525
  # just create a wnode for the WASM operator
416
526
  # Do not set wtype or a code template yet,
417
527
  # wait until operands type is known (see
418
528
  # operands below)
419
- def operator(wnode, operator, wtype=WType.new(:none))
420
- if (op = (ARITHMETIC_OPS_MAP[operator] ||
421
- RELATIONAL_OPS_MAP[operator] ||
422
- BOOLEAN_OPS_MAP[operator] ||
423
- UNARY_OPS_MAP[operator] ))
529
+ def native_operator(wnode, operator, wtype=WType.new(:none))
530
+ if (op = ALL_OPS_MAP[operator])
424
531
  (wn_op = WNode.new(:insn, wnode)).c(:operator, operator: op)
425
532
  wn_op.wtype = wtype
533
+ logger.debug "Creating operator #{operator} wnode: #{wn_op}"
426
534
  wn_op
427
535
  else
428
536
  raise "operator '#{operator}' not supported"
@@ -432,8 +540,21 @@ module Rlang::Parser
432
540
  # finish the setting of the operator node and
433
541
  # attach operands
434
542
  def operands(wnode_op, wnode_recv, wnode_args)
543
+ logger.debug "Processing operands in operator wnode: #{wnode_op}..."
544
+ # Do not post process operands if the operator
545
+ # wnode is a call (= overloaded operator)
546
+ # and not a native operand
547
+ if wnode_op.template == :call
548
+ logger.debug "Doing nothing because it's a func call..."
549
+ return wnode_op
550
+ end
551
+
552
+ # A native operator only expects 0 (unary) or 1 (binary)
553
+ # argument in addition to the receiver
435
554
  raise "only 0 or 1 operand expected (got #{wnode_args.count})" if wnode_args.count > 1
436
555
  op = wnode_op.wargs[:operator]
556
+ #wnode_recv = wnode_op.children[0]
557
+ #wnode_args = wnode_op.children[1..-1]
437
558
  # First find out the wtype that has precedence
438
559
  wtype = self.class.leading_wtype(wnode_recv, *wnode_args)
439
560
 
@@ -441,7 +562,7 @@ module Rlang::Parser
441
562
  logger.debug "leading type cast: #{wtype}"
442
563
 
443
564
  # Attach receiver and argument to the operator wnode
444
- # type casting them if necessary
565
+ # type casting them if necessary
445
566
  self.cast(wnode_recv, wtype).reparent_to(wnode_op)
446
567
  self.cast(wnode_args.first, wtype).reparent_to(wnode_op) unless wnode_args.empty?
447
568
 
@@ -455,8 +576,7 @@ module Rlang::Parser
455
576
  # if + or - operator then multiply arg by size of object
456
577
  if [:add, :sub].include? wnode_op.wargs[:operator]
457
578
  (wn_mulop = WNode.new(:insn, wnode_op)).c(:operator, operator: :mul)
458
- WNode.new(:insn, wn_mulop).c(:const,
459
- value: lambda { wnode_recv.find_class(wnode_recv.wtype.name).size })
579
+ WNode.new(:insn, wn_mulop).c(:call, func_name: "$#{wnode_recv.wtype.name}::#{SIZE_METHOD}")
460
580
  wnode_args.first.reparent_to(wn_mulop)
461
581
  else
462
582
  # It's a relational operator. In this case
@@ -466,6 +586,7 @@ module Rlang::Parser
466
586
  wnode_op.wtype = WType::DEFAULT
467
587
  end
468
588
  end
589
+ logger.debug "Operands in operator wnode after postprocessing: #{wnode_op}..."
469
590
  wnode_op
470
591
  end
471
592