rlang 0.4.1 → 0.5.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.
@@ -12,6 +12,9 @@
12
12
 
13
13
  require_relative '../../utils/log'
14
14
  require_relative './wnode'
15
+ require_relative './klass'
16
+ require_relative './module'
17
+ require_relative './module'
15
18
 
16
19
  module Rlang::Parser
17
20
 
@@ -67,10 +70,10 @@ module Rlang::Parser
67
70
 
68
71
  # new template when object size > 0
69
72
  NEW_TMPL = %q{
70
- result :Object, :alloc, :%{default_wtype}
73
+ result :Object, :allocate, :%{default_wtype}
71
74
  def self.new(%{margs})
72
- result :%{class_name}
73
- object_ptr = Object.alloc(%{class_name}._size_).cast_to(:%{class_name})
75
+ result :"%{class_name}"
76
+ object_ptr = Object.allocate(%{class_name}._size_).cast_to(:"%{class_name}")
74
77
  object_ptr.initialize(%{margs})
75
78
  return object_ptr
76
79
  end
@@ -80,14 +83,13 @@ module Rlang::Parser
80
83
  # use 0 as the _self_ address in memory. It should never
81
84
  # be used anyway
82
85
  NEW_ZERO_TMPL = %q{
83
- result :Object, :alloc, :%{default_wtype}
84
- def self.new(%{margs})
85
- result :%{class_name}
86
- object_ptr = 0.cast_to(:%{class_name})
87
- object_ptr.initialize(%{margs})
88
- return object_ptr
89
- end
90
- }
86
+ def self.new(%{margs})
87
+ result :"%{class_name}"
88
+ object_ptr = 0.cast_to(:"%{class_name}")
89
+ object_ptr.initialize(%{margs})
90
+ return object_ptr
91
+ end
92
+ }
91
93
 
92
94
  # Do nothing initialize method
93
95
  DUMB_INIT_TMPL = %q{
@@ -117,18 +119,122 @@ module Rlang::Parser
117
119
  @root = WTree.new().root
118
120
  @new_count = 0
119
121
  @static_count = 0
120
- end
121
122
 
122
- def klass(wnode, class_name)
123
+ # define Object class and Kernel modules
124
+ # and include Kernel in Object
125
+ wn_object_class = self.klass(@root, [:Object], [])
126
+ @object_class = wn_object_class.klass
127
+ @root.klass = @object_class
128
+ wn_kernel_module = self.module(@root, [:Kernel])
129
+ self.include(wn_object_class, [:Kernel])
130
+
131
+ # Create Class and Module classes
132
+ # And Class inherits from module
133
+ self.klass(@root, [:Module], [:Object])
134
+ self.klass(@root, [:Class], [:Module])
135
+ end
136
+
137
+ # Create class and its basic methods (new, initialize and _size_)
138
+ def klass(wnode, class_path, super_class_path)
139
+ logger.debug "Defining klass #{class_path} < #{super_class_path}"
140
+ # See if class already created
141
+ if (k = wnode.find_class_or_module(class_path))
142
+ return k.wnode
143
+ end
144
+
145
+ # Make sure super class is known like Ruby does
146
+ if super_class_path.empty?
147
+ # special case to bootstrap Object class
148
+ if (class_path == [:Object] && wnode.in_root_scope?)
149
+ sk = nil
150
+ else
151
+ sk = @object_class
152
+ super_class_path << sk.path_name
153
+ end
154
+ else
155
+ sk = wnode.find_class_or_module(super_class_path)
156
+ raise "Unknown super class #{super_class_path}" unless sk
157
+ end
123
158
  # Create class object and class wnode if it doesn't exist yet
124
- k = wnode.find_or_create_class(class_name)
125
- # Create the Class.new method object too (not
126
- # the code yet in case the end user code defines
127
- # its own implementation in the class body)
128
- k.wnode.find_or_create_method(:new, k.name, k.wtype, :class)
159
+ # only one level deep class for now so scope class is always
160
+ # Object class
161
+ if (class_path == [:Object] && wnode.in_root_scope?)
162
+ k = wnode.create_class(class_path, super_class_path)
163
+ else
164
+ k = wnode.find_or_create_class(class_path, super_class_path)
165
+ end
166
+ # make sure the super class is correct in case class
167
+ # was previously declared in a result directive where
168
+ # no super class can be specified
169
+ k.super_class = sk if sk
170
+ # Create methods Class::new, Class#initialize and Class::_size_
171
+ # (do not generate the code yet as the end user code may
172
+ # define its own implementation in the class body)
173
+ k.wnode.find_or_create_method(k, :new, :class, k.wtype, true)
174
+ k.wnode.find_or_create_method(k, :_size_, :class, WType::DEFAULT, true)
175
+ k.wnode.find_or_create_method(k, :initialize, :instance, WType.new(:none), true)
129
176
  k.wnode
130
177
  end
131
178
 
179
+ # Create module object and module wnode
180
+ # if it doesn't exist yet
181
+ def module(wnode, module_path)
182
+ m = wnode.find_or_create_module(module_path)
183
+ m.wnode
184
+ end
185
+
186
+ def include(wnode, module_path)
187
+ m = wnode.find_module(module_path)
188
+ raise "Unknown module #{module_path}. Please require module first." \
189
+ unless m
190
+ wnc = wnode.class_or_module_wnode
191
+ raise "Cannot find scope class/module for included module!!" unless wnc
192
+ wnc.klass.include(m)
193
+ m.included!
194
+ wnc
195
+ end
196
+
197
+ def prepend(wnode, module_path)
198
+ m = wnode.find_module(module_path)
199
+ raise "Unknown module #{module_path}. Please require module first." \
200
+ unless m
201
+ wnc = wnode.class_or_module_wnode
202
+ raise "Cannot find scope class/module for prepended module!!" unless wnc
203
+ wnc.klass.prepend(m)
204
+ m.prepended!
205
+ wnc
206
+ end
207
+
208
+ def extend(wnode, module_path)
209
+ m = wnode.find_module(module_path)
210
+ raise "Cannot find module #{module_path}. Please require module first." \
211
+ unless m
212
+ wnc = wnode.class_or_module_wnode
213
+ raise "Cannot find scope class/module for included module!!" unless wnc
214
+ wnc.klass.extend(m)
215
+ m.extended!
216
+ wnc
217
+ end
218
+
219
+ # Ahead of time method declaration and return type
220
+ # Create corresponding classes and method objects as we known we'll
221
+ # be calling them later on
222
+ def declare_method(wnode, wtype, method_name, result_type)
223
+ class_path = wtype.class_path
224
+ logger.debug "Declaring method #{method_name} in class #{class_path}"
225
+ klass = WNode.root.find_or_create_class(class_path, [])
226
+ if method_name[0] == '#'
227
+ method_type = :instance
228
+ mth_name = method_name[1..-1].to_sym
229
+ else
230
+ method_type = :class
231
+ mth_name = method_name.to_sym
232
+ end
233
+ (m = wnode.find_or_create_method(klass, mth_name, method_type, nil)).wtype = WType.new(result_type)
234
+ logger.debug "Declared #{method_type} method #{m.name} in class #{m.klass.name} with wtype #{m.wtype.name}"
235
+ m
236
+ end
237
+
132
238
  # Postprocess ivars
133
239
  # (called at end of class parsing)
134
240
  def ivars_setup(wnode)
@@ -147,11 +253,12 @@ module Rlang::Parser
147
253
  # generate code for class attributes
148
254
  # (called at end of class parsing)
149
255
  def def_attr(wnode)
150
- wnc = wnode.class_wnode
256
+ klass = wnode.find_current_class_or_module()
257
+ wnc = klass.wnode
151
258
  raise "Cannot find class for attributes definition!!" unless wnc
152
259
  # Process each declared class attribute
153
- wnc.klass.attrs.each do |attr|
154
- logger.debug("Generating accessors for attribute #{wnc.klass.name}\##{attr.name}")
260
+ klass.attrs.each do |attr|
261
+ logger.debug("Generating accessors for attribute #{klass.name}\##{attr.name}")
155
262
  # Generate getter and setter methods wnode
156
263
  # unless method already implemented by user
157
264
  if attr.setter
@@ -173,9 +280,9 @@ module Rlang::Parser
173
280
  # Also generate the Class::_size_ method
174
281
  # (needed for dynamic memory allocation
175
282
  # by Object.allocate)
176
- size_method = wnc.find_or_create_method(SIZE_METHOD, wnc.klass.name, WType::DEFAULT, :class)
283
+ size_method = wnc.find_or_create_method(klass, SIZE_METHOD, :class, WType::DEFAULT)
177
284
  unless size_method.wnode
178
- logger.debug("Generating #{size_method.class_name}\##{size_method.name}")
285
+ logger.debug("Generating #{size_method.klass.name}\##{size_method.name}")
179
286
  wns = WNode.new(:insn, wnc, true)
180
287
  wns.wtype = WType::DEFAULT
181
288
  wns.c(:class_size, func_name: size_method.wasm_name,
@@ -204,28 +311,24 @@ module Rlang::Parser
204
311
  wn_get
205
312
  end
206
313
 
207
- def instance_method(wnode, method)
208
- logger.debug("Generating wnode for instance method #{method.inspect}")
209
- wn = WNode.new(:method, wnode)
210
- method.wnode = wn
211
- wn.method = method # must be set before calling func_name
212
- wn.wtype = method.wtype
213
- wn.c(:func, func_name: wn.method.wasm_name)
214
- # Also declare a "hidden" parameter representing the
215
- # pointer to the instance (always default wtype)
216
- wn.create_marg(:_self_)
217
- logger.debug("Building instance method: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
218
- wn
219
- end
220
-
221
- def class_method(wnode, method)
222
- logger.debug("Generating wnode for class method #{method}")
314
+ def def_method(wnode, method_name, method_type)
315
+ logger.debug("Defining #{method_type} method #{method_name}...")
316
+ if (method = wnode.find_method(nil, method_name, method_type, true))
317
+ logger.warn "Redefining #{method.klass.name},#{method_name}" if method.wnode
318
+ else
319
+ method = wnode.create_method(nil, method_name, method_type, nil, true)
320
+ end
321
+ # Generate method definition wnode
322
+ logger.debug("Generating wnode for #{method_type} method #{method_name}")
223
323
  wn = WNode.new(:method, wnode)
224
324
  method.wnode = wn
225
- wn.method = method # must be set before calling func_name
226
325
  wn.wtype = method.wtype
227
326
  wn.c(:func, func_name: wn.method.wasm_name)
228
- logger.debug("Building class method: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
327
+ # On instance method also declare a first parameter
328
+ # called _self_ representing the pointer to the
329
+ # object instance
330
+ wn.create_marg(:_self_) if method.instance?
331
+ logger.debug("Building #{method_type} method: wn.wtype #{wn.wtype}, wn.method #{wn.method}")
229
332
  wn
230
333
  end
231
334
 
@@ -273,7 +376,7 @@ module Rlang::Parser
273
376
  wn
274
377
  end
275
378
 
276
- # Get class variable
379
+ # Get constant
277
380
  def const(wnode, const)
278
381
  (wn = WNode.new(:insn, wnode)).wtype = const.wtype
279
382
  wn.c(:load, wtype: const.wtype, var_name: const.wasm_name)
@@ -298,7 +401,7 @@ module Rlang::Parser
298
401
  # Call setter (on attr or instance variable)
299
402
  # This is the same as calling the corresponding setter
300
403
  def call_setter(wnode, wnode_recv, attr)
301
- wn = self.call(wnode, wnode_recv.wtype.name, attr.setter_name, :instance)
404
+ wn = self.send_method(wnode, wnode_recv.wtype, attr.setter_name, :instance)
302
405
  # First argument of the setter must be the receiver
303
406
  wnode_recv.reparent_to(wn)
304
407
  wn
@@ -307,7 +410,7 @@ module Rlang::Parser
307
410
  # Call getter (on attr or instance variable)
308
411
  # This is the same as calling the corresponding getter
309
412
  def call_getter(wnode, wnode_recv, attr)
310
- wn = self.call(wnode, wnode_recv.wtype.name, attr.getter_name, :instance)
413
+ wn = self.send_method(wnode, wnode_recv.wtype, attr.getter_name, :instance)
311
414
  # First argument of the getter must always be the receiver
312
415
  wnode_recv.reparent_to(wn)
313
416
  wn
@@ -402,7 +505,7 @@ module Rlang::Parser
402
505
 
403
506
  # Static new string object
404
507
  def string_static_new(wnode, string)
405
- klass = wnode.find_class(nil)
508
+ klass = wnode.find_current_class_or_module()
406
509
  data_label = "#{klass.name}_string_#{@static_count += 1}"
407
510
  # Statically
408
511
  data_stg = self.string_static(string, data_label)
@@ -418,16 +521,16 @@ module Rlang::Parser
418
521
 
419
522
  # Dynamic new string object
420
523
  def string_dynamic_new(wnode, string)
421
- klass = wnode.find_class(nil)
524
+ klass = wnode.find_current_class_or_module()
422
525
  data_label = "#{klass.name}_string_#{@static_count += 1}"
423
526
  data_stg = self.string_static(string, data_label)
424
527
  string_new_source = STRING_NEW_TMPL % {
425
- string: string,
426
528
  ptr: data_stg.address,
427
529
  length: string.length
428
530
  }
429
531
  #puts string_new_source;exit
430
532
  wn_string = self.parser.parse(string_new_source, wnode)
533
+ #puts wn_string; exit
431
534
  end
432
535
 
433
536
  # All the cast_xxxx methods below returns
@@ -503,25 +606,6 @@ module Rlang::Parser
503
606
  wn_cast_op
504
607
  end
505
608
 
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
-
525
609
  # just create a wnode for the WASM operator
526
610
  # Do not set wtype or a code template yet,
527
611
  # wait until operands type is known (see
@@ -592,10 +676,10 @@ module Rlang::Parser
592
676
 
593
677
  # Statically allocate an object in data segment
594
678
  # with the size of the class
595
- def static_new(wnode, class_name)
596
- klass = wnode.find_class(class_name)
679
+ def static_new(wnode, class_path)
680
+ klass = wnode.find_class_or_module(class_path)
597
681
  if klass.size > 0
598
- data_label = "#{class_name}_new_#{@new_count += 1}"
682
+ data_label = "#{klass.path_name}_new_#{@new_count += 1}"
599
683
  data = DAta.new(data_label.to_sym, "\x00"*klass.wnode.class_size)
600
684
  address = data.address
601
685
  else
@@ -606,50 +690,66 @@ module Rlang::Parser
606
690
  end
607
691
  (wn_object_addr = WNode.new(:insn, wnode)).c(:addr, value: address)
608
692
  # VERY IMPORTANT the wtype of this node is the Class name !!!
609
- wn_object_addr.wtype = WType.new(class_name.to_sym)
693
+ wn_object_addr.wtype = WType.new(klass.path_name)
610
694
  wn_object_addr
611
695
  end
612
696
 
613
697
  # Create the dynamic new method. It allocates memory
614
698
  # for the object created and calls initialize
615
699
  def def_new(wnode_class)
616
- # no new method for native types
700
+ k = wnode_class.find_current_class_or_module()
701
+ logger.debug "Defining new method for #{k.name}"
702
+ # no need to define new method for native types
617
703
  return if wnode_class.klass.wtype.native?
618
- new_method = wnode_class.find_method(:new, wnode_class.class_name, :class)
619
- return if new_method.wnode # already implemented
620
-
621
- init_method = wnode_class.find_method(:initialize, wnode_class.class_name, :instance)
622
- logger.debug "Creating code for #{wnode_class.class_name}.new"
704
+ if (new_mth = wnode_class.find_method(k, :new, :class, true))
705
+ return if new_mth.wnode # already implemented
706
+ end
707
+
708
+ logger.debug "Creating code for #{k.name}.new"
709
+ # Find initialize method and use the same method args for new
710
+ init_method = wnode_class.find_method(k, :initialize, :instance, true)
623
711
  new_tmpl = wnode_class.class_size.zero? ? NEW_ZERO_TMPL : NEW_TMPL
624
712
  new_source = new_tmpl % {
625
713
  default_wtype: WType::DEFAULT.name,
626
- class_name: wnode_class.class_name,
714
+ class_name: k.path_name,
627
715
  # Do not pass _self_ argument to the new method of course !!
628
716
  margs: init_method.margs.reject {|ma| ma._self_?}.map(&:name).join(', '),
629
717
  class_size: wnode_class.class_size
630
718
  }
631
- new_method.wnode = self.parser.parse(new_source, wnode_class)
719
+ new_mth.wnode = self.parser.parse(new_source, wnode_class)
632
720
  end
633
721
 
634
722
  # Define a dumb initialize method if not implemented
635
723
  # already in user code
636
724
  def def_initialize(wnode_class)
725
+ k = wnode_class.find_current_class_or_module()
637
726
  # no new/initialize method for native types
638
- return if WType.new(wnode_class.class_name).native?
727
+ return if WType.new(k.path_name).native?
639
728
  # generate code for a dumb initialize method if not defined
640
729
  # in user code
641
- unless wnode_class.find_method(:initialize, wnode_class.class_name, :instance)
642
- logger.debug "Creating MEthod and code for #{wnode_class.class_name}#initialize"
643
- init_source = DUMB_INIT_TMPL
644
- self.parser.parse(init_source, wnode_class)
730
+ if (init_mth = wnode_class.find_method(k, :initialize, :instance, true))
731
+ return if init_mth.wnode # already implemented
732
+ end
733
+ logger.debug "Creating MEthod and code for #{k.name}#initialize"
734
+ init_source = DUMB_INIT_TMPL
735
+ init_mth.wnode = self.parser.parse(init_source, wnode_class)
736
+ end
737
+
738
+ # generate code for method call
739
+ def send_method(wnode, class_path, method_name, method_type)
740
+ logger.debug "In call generator for #{class_path}::#{method_name}"
741
+ k = wnode.find_class_or_module(class_path)
742
+ if k && (method = wnode.find_method(k, method_name, method_type))
743
+ logger.debug "Found method #{method.name} in class #{method.klass.name}"
744
+ (wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
745
+ wn_call.wtype = method.wtype
746
+ wn_call
747
+ elsif ALL_OPS_MAP.has_key? method_name
748
+ # it's a native Wasm operator
749
+ wn_call = self.native_operator(wnode, method_name, WType.new(class_path))
750
+ else
751
+ raise "Unknown method '#{method_name}' in class #{class_path}"
645
752
  end
646
- end
647
-
648
- def call(wnode, class_name, method_name, method_type)
649
- method = wnode.find_or_create_method(method_name, class_name, nil, method_type)
650
- logger.debug "found method #{method}"
651
- (wn_call = WNode.new(:insn, wnode)).c(:call, func_name: method.wasm_name)
652
- wn_call.wtype = method.wtype
653
753
  wn_call
654
754
  end
655
755