rlang 0.4.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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +19 -3
  3. data/Gemfile.lock +4 -6
  4. data/README +11 -0
  5. data/README.md +18 -10
  6. data/bin/rlang +17 -5
  7. data/docs/RlangCompiler.md +5 -1
  8. data/docs/RlangManual.md +98 -20
  9. data/examples/fib/fib.rb +5 -1
  10. data/lib/builder/rlang/compiler.rb +2 -21
  11. data/lib/rlang/lib/array/array32.rb +59 -0
  12. data/lib/rlang/lib/array/array64.rb +56 -0
  13. data/lib/rlang/lib/array.rb +6 -0
  14. data/lib/rlang/lib/base64.rb +223 -0
  15. data/lib/rlang/lib/io.rb +75 -0
  16. data/lib/rlang/lib/kernel.rb +23 -0
  17. data/lib/rlang/lib/malloc.rb +12 -8
  18. data/lib/rlang/lib/memory.rb +102 -2
  19. data/lib/rlang/lib/object.rb +40 -4
  20. data/lib/rlang/lib/rlang.rb +29 -0
  21. data/lib/rlang/lib/rlang_core.rb +15 -0
  22. data/lib/rlang/lib/string.rb +106 -8
  23. data/lib/rlang/lib/type/i32.rb +43 -0
  24. data/lib/rlang/lib/type/i64.rb +2 -0
  25. data/lib/rlang/lib/unistd.rb +1 -2
  26. data/lib/rlang/lib/wasi.rb +184 -0
  27. data/lib/rlang/parser/attr.rb +9 -13
  28. data/lib/rlang/parser/const.rb +105 -1
  29. data/lib/rlang/parser/cvar.rb +11 -7
  30. data/lib/rlang/parser/data.rb +17 -6
  31. data/lib/rlang/parser/export.rb +4 -3
  32. data/lib/rlang/parser/ext/integer.rb +3 -1
  33. data/lib/rlang/parser/ext/type.rb +30 -1
  34. data/lib/rlang/parser/global.rb +10 -2
  35. data/lib/rlang/parser/ivar.rb +3 -7
  36. data/lib/rlang/parser/klass.rb +8 -35
  37. data/lib/rlang/parser/method.rb +36 -12
  38. data/lib/rlang/parser/module.rb +143 -0
  39. data/lib/rlang/parser/wgenerator.rb +462 -168
  40. data/lib/rlang/parser/wnode.rb +387 -142
  41. data/lib/rlang/parser/wtype.rb +30 -7
  42. data/lib/rlang/parser.rb +506 -231
  43. data/lib/rlang/version.rb +1 -1
  44. data/lib/rlang.rb +3 -0
  45. data/lib/ruby/mirror/rstring.rb +16 -0
  46. data/lib/utils/exceptions.rb +12 -0
  47. data/rlang.gemspec +4 -4
  48. metadata +25 -13
  49. data/lib/rlang/lib.rb +0 -11
@@ -12,6 +12,7 @@ require_relative './lvar'
12
12
  require_relative './attr'
13
13
  require_relative './method'
14
14
  require_relative './klass'
15
+ require_relative './module'
15
16
 
16
17
  module Rlang::Parser
17
18
  class WNode
@@ -20,6 +21,7 @@ module Rlang::Parser
20
21
  # WASM code templates
21
22
  T = {
22
23
  func: 'func %{func_name}',
24
+ import: 'import "%{module_name}" "%{function_name}"',
23
25
  param: 'param %{name} %{wasm_type}',
24
26
  result: 'result %{wasm_type}',
25
27
  return: 'return',
@@ -50,18 +52,22 @@ module Rlang::Parser
50
52
  br_if: 'br_if %{label}',
51
53
  br: 'br %{label}',
52
54
  inline: '%{code}',
53
- attr_getter: %q{func %{func_name} (param $_self_ i32) (result %{wtype})
54
- (%{wtype}.load offset=%{offset} (local.get $_self_))},
55
- 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})
56
58
  (local.get %{attr_name})
57
- (%{wtype}.store offset=%{offset} (local.get $_self_) (local.get %{attr_name}))},
58
- class_size: %q{func %{func_name} (result %{wtype})
59
- (%{wtype}.const %{size})}
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
+ memory: 'memory $0 %{min} %{max}',
64
+ module: 'module %{module}'
60
65
  }
61
66
 
62
67
  attr_accessor :type, :wargs, :children, :parent, :comment,
63
- :method, :template, :keep_on_stack, :classes
64
- attr_reader :wtype, :label, :klass
68
+ :method, :template, :keep_on_stack, :classes,
69
+ :modules, :link
70
+ attr_reader :wtype, :label, :klass, :module
65
71
 
66
72
  @@label_index = 0
67
73
 
@@ -82,16 +88,13 @@ module Rlang::Parser
82
88
  # means no value returned)
83
89
  @wtype = WType::DEFAULT
84
90
 
85
- # For root wnode
86
- @classes = [] # classes
87
91
  # top level class needed only if const are
88
92
  # defined at top level
89
93
  # NOTE: can't use create_klass as it find_class
90
94
  # which doesn't find root class ... endless loop!!
91
95
 
92
- # For class wnode only
93
- @@klass = nil
94
- self.klass = Klass.new(:Top__) if self.root?
96
+ # For class or module wnode only
97
+ @klass = nil
95
98
 
96
99
  # For method wnode only
97
100
  @method = nil
@@ -99,6 +102,10 @@ module Rlang::Parser
99
102
  # For insn wnode with
100
103
  # label (.e.g block, loop)
101
104
  @label = nil
105
+
106
+ # link to a related node
107
+ # Semantic of the link depend on the wnode type
108
+ @link = nil
102
109
  end
103
110
 
104
111
  def self.root
@@ -119,17 +126,37 @@ module Rlang::Parser
119
126
  def c(template, wargs = {})
120
127
  raise "Error: unknown WASM code template (#{template})" unless T.has_key? template
121
128
  raise "Error: this WNode is already populated with instruction #{@template}" if @template
122
- if [:loop, :block].include? template
123
- wargs[:label] = self.set_label
124
- end
129
+ #if [:loop, :block].include? template
130
+ # wargs[:label] = self.set_label
131
+ #end
125
132
  @template = template
126
133
  @wargs = wargs
127
134
  end
128
135
 
129
136
  def wasm_code
130
- @wargs[:wasm_type] ||= self.wasm_type
137
+ return '' unless T[@template]
131
138
  wargs = {}
132
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
133
160
  T[@template] ? T[@template] % wargs : ''
134
161
  end
135
162
 
@@ -147,8 +174,13 @@ module Rlang::Parser
147
174
  logger.debug "Setting wtype #{wtype} for wnode #{self}"
148
175
  @wtype = wtype
149
176
  @method.wtype = @wtype if self.method?
150
- # update wasm_type template arg accordingly
151
- @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
152
184
  logger.debug "type #{self.type} wargs #{self.wargs} wtype #{@wtype}"
153
185
  @wtype
154
186
  end
@@ -169,7 +201,7 @@ module Rlang::Parser
169
201
  end
170
202
  alias :<< :add_child
171
203
 
172
- # Remove child to current node
204
+ # Remove child from current node
173
205
  def remove_child(wnode)
174
206
  logger.debug "Removing #{wnode.object_id} from wnodes list #{self.children.map(&:object_id)} under parent #{self.parent.object_id}"
175
207
  unless (wn = self.children.delete(wnode)) && wn == wnode
@@ -192,35 +224,48 @@ module Rlang::Parser
192
224
  self
193
225
  end
194
226
 
195
- # Reparent all children wnodes to another wnode
196
- # (in the same order)
197
- # WARNING!! Do not use self.children.each { } to
198
- # reparent because we are modifying children list
199
- # as we go
200
- def reparent_children_to(wnode)
201
- wnc = self.children
202
- wnc.count.times { wnc.first.reparent_to(wnode) }
203
- self
204
- end
227
+ # Reparent all children wnodes to another wnode
228
+ # (in the same order)
229
+ # WARNING!! Do not use self.children.each { } to
230
+ # reparent because we are modifying children list
231
+ # as we go
232
+ def reparent_children_to(wnode)
233
+ wnc = self.children
234
+ wnc.count.times { wnc.first.reparent_to(wnode) }
235
+ self
236
+ end
205
237
 
206
238
  # insert a blank wnode above self, so between self wnode
207
239
  # and its parent (self -> parent becomes self -> wn -> parent)
208
- def insert(wtype=:none)
209
- wn = WNode.new(wtype, self.parent)
240
+ def insert(type=:none)
241
+ wn = WNode.new(type, self.parent)
210
242
  self.reparent_to(wn)
211
243
  wn
212
244
  end
213
245
 
246
+ # delete current wnode (which means
247
+ # basically remove it as a child)
248
+ def delete!
249
+ return if self.root? || self.parent.nil?
250
+ self.parent.remove_child(self)
251
+ end
252
+
253
+ # Silence the current wnode (which means
254
+ # inserting a silent type wnode between this
255
+ # and its parent)
256
+ def silence!
257
+ self.insert(:silent)
258
+ end
259
+
214
260
  def klass=(klass)
215
261
  @klass = klass
216
262
  @klass.wnode = self
217
- WNode.root.classes << klass
218
- klass
263
+ @klass
219
264
  end
220
265
 
221
266
  # Find class name in this node and up the tree
222
267
  def class_name
223
- (cn = self.class_wnode) ? cn.klass.name : nil
268
+ (cn = self.class_wnode) ? cn.klass.path_name : nil
224
269
  end
225
270
 
226
271
  # Find class size in this wnode or up the tree
@@ -228,108 +273,238 @@ module Rlang::Parser
228
273
  (cn = self.class_wnode) ? cn.klass.size : nil
229
274
  end
230
275
 
276
+ # Find the module object of the current wnode and up the tree
277
+ # if no name given
278
+ # or lookup the matching class from
279
+ # the root level if module name given
280
+ def find_module(module_path)
281
+ logger.debug "looking for #{module_path} module in scope #{self.scope}" # at wnode #{self}"
282
+ if modul = self.find_class_or_module_by_name(module_path)
283
+ logger.debug "Found module #{modul.name} / #{modul}"
284
+ else
285
+ logger.debug "Module #{module_path} not found"
286
+ end
287
+ modul
288
+ end
289
+
290
+ # Create a module object
291
+ def create_module(module_path)
292
+ # Create the constant associated to this module
293
+ klass = self.find_current_class_or_module()
294
+ logger.debug "Creating module #{module_path} in class #{klass} under wnode #{self.head}"
295
+ const = self.create_const(module_path, nil, WType.new(:Module))
296
+ modul = Module.new(const, klass)
297
+ # Add the constant to list of constants in current scope class
298
+ klass.consts << const
299
+ # Generate wnode
300
+ wnc = WNode.new(:module, self)
301
+ wnc.klass = modul
302
+ logger.debug "Created module #{modul.name}/ID:#{modul} under wnode #{wnc.parent} / ID: #{self.object_id}"
303
+ modul
304
+ end
305
+
306
+ def find_or_create_module(module_path)
307
+ self.find_module(module_path) || self.create_module(module_path)
308
+ end
309
+
310
+ # Return the first class/module up the tree
311
+ def find_current_class_or_module()
312
+ logger.debug "looking for current class in scope #{self.scope} at wnode #{self.head(3)}"
313
+ if wn = self.class_or_module_wnode
314
+ k = wn.klass
315
+ elsif self.in_root_scope?
316
+ # methods defined at root level goes to Object Class
317
+ k = self.find_class_or_module_by_name([:Object])
318
+ end
319
+ if k
320
+ logger.debug "Found class #{k.name} / #{k}"
321
+ else
322
+ logger.debug "No current class found!"
323
+ end
324
+ k
325
+ end
326
+
327
+ # Find the class by doing a lookup on the constant
328
+ def find_class_or_module_by_name(class_path)
329
+ raise "Class name argument expected" unless class_path && !class_path.empty?
330
+ logger.debug "looking for class/module constant #{class_path} in scope #{self.scope} at wnode #{self.head}"
331
+ const = self.find_const(class_path)
332
+ #raise "Class or Module #{class_path} not found!" unless const
333
+ if const
334
+ logger.debug "Found constant #{const.name} pointing to #{const.value}"
335
+ const.value
336
+ else
337
+ logger.debug "Constant #{class_path} not found"
338
+ nil
339
+ end
340
+ end
341
+
231
342
  # Find the class object of the current and up the tree
232
343
  # if no name given or lookup the matching class from
233
344
  # the root level if class name given
234
- def find_class(class_name)
235
- logger.debug "looking for class #{class_name ? class_name : 'current'}
236
- in scope #{self.scope} at wnode #{self}"
237
- if class_name
238
- c = WNode.root.classes.find { |c|
239
- logger.debug "**** looking for class #{class_name} in class object #{c} / #{c.name}"; c.name == class_name }
345
+ # class_path can be passed either as in a Symbol (e.g. :"A::B")
346
+ # or as an array of symbols (e.g. [:A, :B])
347
+ def find_class_or_module(class_path)
348
+ logger.debug "looking for #{class_path} class in scope #{self.scope} at wnode #{self.head}"
349
+ # turn the symbol form of class_path into the array form
350
+ if class_path.is_a? Symbol
351
+ class_path = class_path.to_s.split('::').map(&:to_sym)
352
+ end
353
+
354
+ if class_path.empty?
355
+ k = self.find_current_class_or_module()
240
356
  else
241
- if self.in_root_scope?
242
- # if at root level and no class name given
243
- # then it's the top level class
244
- c = self.class.root.klass
245
- else
246
- logger.debug "Looking for class wnode from wnode #{self} / ID: #{self.object_id} /
247
- type: #{self.type} / class_wnode ID #{self.class_wnode.object_id} /
248
- class_wnode #{self.class_wnode} /
249
- self klass : #{self.klass} /
250
- self klass wtype : #{self.klass&.wtype} / "
251
- c = self.class_wnode.klass
252
- end
253
- end
254
- if c
255
- logger.debug "Found class #{c.name} / #{c}"
357
+ k = self.find_class_or_module_by_name(class_path)
358
+ end
359
+ if k
360
+ logger.debug "Found class #{k.name} / #{k}"
256
361
  else
257
- logger.debug "Class #{class_name} not found"
362
+ logger.debug "Class #{class_path} not found!"
363
+ end
364
+ k
365
+ end
366
+
367
+ # Create a Class object. The code below assumes
368
+ # the class doesn't exist
369
+ def create_class(class_path, super_class_path)
370
+ # check that super class exists
371
+ super_class = nil
372
+ unless super_class_path.empty?
373
+ super_class_const = self.find_const(super_class_path)
374
+ raise NameError, "uninitialized constant #{super_class_path}" \
375
+ unless super_class_const
376
+ super_class = super_class_const.scope_class
258
377
  end
259
- c
260
- end
261
378
 
262
- # Create a Class object. **NOTE** the self
263
- # wnode must be the parent of the new class
264
- def create_class(class_name)
265
- wnc = WNode.new(:class, self)
266
- wnc.klass = Klass.new(class_name)
267
- logger.debug "Created class #{wnc.klass} under wnode #{self} / id: #{self.object_id}"
268
- wnc.klass
269
- end
379
+ # Find current class or module (lexical scope)
380
+ # special case for Object class
381
+ if class_path == [:Object] && self.in_root_scope?
382
+ scope_class = nil
383
+ else
384
+ scope_class = self.find_current_class_or_module()
385
+ end
386
+
387
+ # Create the constant associated to this class
388
+ # the class itself
389
+ const = self.create_const(class_path, nil, WType.new(:Class))
390
+ k = Klass.new(const, scope_class, super_class)
391
+
392
+ # special case to bootstrap Object class
393
+ if class_path == [:Object] && self.in_root_scope?
394
+ const.scope_class = k
395
+ end
270
396
 
271
- def find_or_create_class(class_name)
272
- self.find_class(class_name) || self.create_class(class_name)
397
+ # create class wnode
398
+ wnc = WNode.new(:class, self)
399
+ wnc.klass = k
400
+ k.wnode = wnc
401
+ logger.debug "Created class #{k.name}/ID: #{k} under wnode #{self}/ ID: #{self.object_id}"
402
+ k
273
403
  end
274
404
 
275
- # create a constant
276
- def create_const(c_name, class_name, value, wtype)
277
- k = find_class(class_name)
278
- logger.debug "Creating constant #{c_name} in class #{k&.name} / wtype: #{wtype} at wnode #{self.class_wnode}..."
279
- if (cn = self.class_wnode)
280
- cn.klass.consts << (const = Const.new(k.name, c_name, value, wtype))
405
+ def find_or_create_class(class_path, super_class_path)
406
+ logger.debug "Find/Create class: #{class_path}"
407
+ if (km = self.find_class_or_module(class_path))
408
+ raise TypeError, "#{class_path} is not a class" unless km.const.class?
281
409
  else
282
- raise "No class found for class constant #{const}"
410
+ km = self.create_class(class_path, super_class_path)
411
+ end
412
+ km
413
+ end
414
+
415
+ # create a constant, relative to the current wnode
416
+ # the constant is assumed to not exist already
417
+ def create_const(c_path, value, wtype)
418
+ logger.debug "Creating constant #{c_path} / wtype: #{wtype} at wnode #{self.class_wnode.head}..."
419
+ raise "Dynamic constant assignment. Constant #{name} cannot be created in scope #{cmn.scope}" \
420
+ if self.in_method_scope?
421
+
422
+ # if const_path has more than one element then check
423
+ # that all element but last already exist
424
+ !(c_prefix = c_path[0..-2]).empty? && self.find_const(c_prefix)
425
+ c_name = c_path.last
426
+ const = Const.new(c_name, value, wtype)
427
+ end
428
+
429
+ # Look for constant from where we are in wtree
430
+ # For a Ruby implementation of the constant lookup
431
+ # algo, see https://cirw.in/blog/constant-lookup
432
+ # - c_path is an array of constant name elements
433
+ # e.g. for constant A::B::C constant_path is [A, B, C]
434
+ def find_const(c_path)
435
+ logger.debug "looking for constant #{c_path}...from wnode #{self.head}..."
436
+ wn = self; idx = 0; count = c_path.size
437
+ while idx < count
438
+ const = wn._const_lookup(c_path[idx])
439
+ if const && (idx < count-1) && (const.class? || const.module?) && const.scope_class
440
+ wn = const.value.wnode
441
+ else
442
+ raise NameError, "uninitialized constant #{c_path.join('::')}" unless idx == count-1
443
+ end
444
+ idx += 1
283
445
  end
284
446
  const
285
447
  end
286
448
 
287
- # Look for constant
288
- def find_const(c_name, class_name=nil)
289
- logger.debug "looking for constant #{c_name} in class #{class_name ? class_name : 'current'} from wnode #{self}..."
290
- k = find_class(class_name)
291
- # Look for the constant both in current class and a roor class level
292
- const = [k, @@root.klass].map(&:consts).flatten.find do |c|
293
- logger.debug "exploring constant #{c} / name: #{c.name} / class_name: #{c.class_name}";
294
- c.name == c_name
449
+ def _const_lookup(name)
450
+ # build constant lookup path: lexical scope first
451
+ # excluding the
452
+ mn = self.find_current_class_or_module()&.nesting
453
+ return nil unless mn
454
+ # do not use find_class_... to find the Object class
455
+ # This is to avoid and endless loop
456
+ oc = WNode.root.klass
457
+ #oc = self.find_class_or_module_by_name([:Object])
458
+ logger.debug "Module/Class nesting: #{mn.map(&:name)}"
459
+ # and ancestors second
460
+ lookup_path = mn + (mn.first || oc).ancestors
461
+ lookup_path += oc.ancestors if (oc && mn.first.const.module?)
462
+ logger.debug "searching constant #{name} in path #{lookup_path.map(&:name)}..."
463
+ const = nil
464
+ lookup_path.find do |mod|
465
+ logger.debug "++ looking for const #{name} in #{mod.name}"
466
+ const = mod.const_get(name)
295
467
  end
296
468
  if const
297
- logger.debug "Constant #{c_name} found in class #{k.name} at wnode #{k.wnode}..."
469
+ logger.debug "... found! in class #{const.scope_class&.name}"
298
470
  else
299
- logger.debug "Constant #{c_name} not found in class #{k.name} or at top level..."
471
+ logger.debug "Constant #{name} not found in lookup path #{lookup_path.map(&:name)}..." \
300
472
  end
301
473
  const
302
474
  end
303
475
 
304
- def find_or_create_const(c_name, class_name, value, wtype)
305
- self.find_const(c_name, class_name) || self.create_const(c_name, class_name, value, wtype)
476
+ # find or create constant, relative to current wnode
477
+ def find_or_create_const(c_path, class_name, value, wtype)
478
+ self.find_const(c_path) || self.create_const(c_path, value, wtype)
306
479
  end
307
480
 
308
- def find_attr(name, class_name=nil)
309
- k = find_class(class_name)
481
+ # find attr in current class
482
+ def find_attr(name)
483
+ k = self.find_current_class_or_module()
310
484
  raise "Can't find parent class for attr #{name}" unless k
311
485
  logger.debug "looking for attr #{name} in class #{k.name} at wnode #{self.class_wnode}..."
312
- k.attrs.find { |a| a.class_name == k.name && a.name == name }
486
+ k.attrs.find { |a| a.klass == k && a.name == name }
313
487
  end
314
488
 
315
489
  def create_attr(name, wtype=WType::DEFAULT)
316
- if (cn = self.class_wnode)
317
- logger.debug "creating attr #{name} in class #{self.class_name} at wnode #{self.class_wnode}..."
318
- cn.klass.attrs << (_attr = Attr.new(cn, name, wtype))
490
+ if (k = self.find_current_class_or_module())
491
+ logger.debug "creating attr #{name} in class #{k.name} at wnode #{k.wnode}..."
492
+ k.attrs << (_attr = Attr.new(k, name, wtype))
319
493
  else
320
494
  raise "No class found for class attribute #{name}"
321
495
  end
322
496
  _attr
323
497
  end
324
498
 
325
- def find_or_create_attr(name, class_name=nil, wtype=WType::DEFAULT)
326
- find_attr(name, class_name) || create_attr(name, wtype)
499
+ # find or create attr in current class
500
+ def find_or_create_attr(name, wtype=WType::DEFAULT)
501
+ find_attr(name) || create_attr(name, wtype)
327
502
  end
328
503
 
329
504
  def create_ivar(iv_name, wtype=WType::DEFAULT)
330
- if (cn = self.class_wnode)
505
+ if (k = self.find_current_class_or_module())
331
506
  logger.debug "creating ivar #{iv_name} in class #{self.class_name} at wnode #{self.class_wnode}..."
332
- cn.klass.ivars << (ivar = IVar.new(cn, iv_name, wtype))
507
+ k.ivars << (ivar = IVar.new(k, iv_name, wtype))
333
508
  else
334
509
  raise "No class found for instance variable #{iv_name}"
335
510
  end
@@ -337,10 +512,10 @@ module Rlang::Parser
337
512
  end
338
513
 
339
514
  def find_ivar(iv_name, class_name=nil)
340
- klass = find_class(class_name)
341
- raise "Can't find parent class for ivar #{iv_name}" unless klass
342
- logger.debug "looking for ivar #{iv_name} in class #{class_name} at wnode #{self.class_wnode}..."
343
- self.class_wnode.klass.ivars.find { |iv| iv.class_name == klass.name && iv.name == iv_name }
515
+ k = self.find_current_class_or_module()
516
+ raise "Can't find parent class for ivar #{iv_name}" unless k
517
+ logger.debug "looking for ivar #{iv_name} in class #{k.name} at wnode #{self.class_wnode}..."
518
+ self.class_wnode.klass.ivars.find { |iv| iv.klass == k && iv.name == iv_name }
344
519
  end
345
520
 
346
521
  def find_or_create_ivar(iv_name)
@@ -350,7 +525,7 @@ module Rlang::Parser
350
525
  def create_cvar(cv_name, value=0, wtype=WType::DEFAULT)
351
526
  if (cn = self.class_wnode)
352
527
  logger.debug "creating cvar #{cv_name} in class #{self.class_name} at wnode #{self.class_wnode}..."
353
- cn.klass.cvars << (cvar = CVar.new(cn.klass.name, cv_name, value, wtype))
528
+ cn.klass.cvars << (cvar = CVar.new(cn.klass, cv_name, value, wtype))
354
529
  else
355
530
  raise "No class found for class variable #{cv_name}"
356
531
  end
@@ -384,7 +559,7 @@ module Rlang::Parser
384
559
  if (mn = self.method_wnode)
385
560
  mn.method.margs << (marg = MArg.new(name))
386
561
  else
387
- raise "No class found for class variable #{marg}"
562
+ raise "No class found for method argument #{marg}"
388
563
  end
389
564
  marg
390
565
  end
@@ -394,44 +569,51 @@ module Rlang::Parser
394
569
  end
395
570
 
396
571
  # method_type is either :instance or :class
397
- def create_method(method_name, class_name, wtype, method_type)
398
- if (m = find_method(method_name, class_name, method_type))
399
- raise "MEthod already exists: #{m.inspect}"
400
- end
401
- if (cn = self.class_wnode)
402
- class_name ||= cn.klass.name
403
- cn.klass.methods << (method = MEthod.new(method_name, class_name, wtype, method_type))
404
- else
405
- raise "No class wnode found to create method #{method_name}"
406
- end
407
- logger.debug "Created MEthod: #{method}"
408
- method
572
+ def create_method(klass, method_name, method_type, wtype, local=false)
573
+ logger.debug "Create #{method_type} method #{method_name} in class #{class_name || 'current'} / wtype: #{wtype}"
574
+ wtype ||= WType::DEFAULT
575
+
576
+ # see if method already created
577
+ m = find_method(klass, method_name, method_type, local)
578
+ raise "Method already exists: #{class_name},#{m.name} / ID: #{m}" if m
579
+
580
+ # Go create method
581
+ km = klass || self.find_current_class_or_module()
582
+ km.methods << (m = MEthod.new(method_name, km, wtype, method_type))
583
+ logger.debug "++++ adding #{method_type} method #{m.name}/ID:#{m} in class #{km.name}/ID:#{km}"
584
+ logger.debug "#{km.methods.count} methods in class #{km.name}/#{km}"
585
+ m
409
586
  end
410
587
 
411
588
  # method_type is either :instance or :class
412
- def find_method(method_name, class_name, method_type)
413
- logger.debug "looking for #{method_type} method '#{method_name}' in class name '#{class_name}' from wnode #{self}"
414
- k = self.find_class(class_name)
415
- return nil unless k
416
- if method_type == :class
417
- method = k.methods.find { |m| m.name == method_name && m.class_name == k.name && m.class? }
418
- elsif method_type == :instance
419
- method = k.methods.find { |m| m.name == method_name && m.class_name == k.name && m.instance? }
420
- else
421
- raise "Unknown method type : #{method_type.inspect}"
589
+ # if local is true look for method in the current class only
590
+ def find_method(klass, method_name, method_type, local=false)
591
+ logger.debug "looking #{local ? 'locally' : 'globally'} for #{method_type} method #{method_name} in class #{klass}" #from wnode #{self.head(2)}"
592
+ km = klass || self.find_current_class_or_module()
593
+ raise "Couldn't find scope class/module where to search for method #{method_name}" unless km
594
+
595
+ class_hierarchy = (local ? [km] : km.ancestors)
596
+ logger.debug "searching #{method_type} method #{method_name} in ancestors #{class_hierarchy.map(&:name)}..."
597
+ method = nil
598
+ class_hierarchy.each do |k|
599
+ logger.debug "Currently #{k.methods.count} method(s) in class #{k.name}/#{k}"
600
+ method = k.methods.find do |m|
601
+ logger.debug "++ looking for #{method_type} method #{k.name}/#{method_name} in #{m.klass.name}/#{m.name}/#{m.method_type}"
602
+ m.name == method_name && m.klass == k && m.method_type == method_type
603
+ end
604
+ break if method
422
605
  end
423
606
  if method
424
- logger.debug "Found #{method_type} MEthod: #{k.name},#{method}"
607
+ logger.debug "Found #{method_type} method: #{km.name},#{method.name} in #{method.klass.name}"
425
608
  else
426
- logger.debug "Couldn't find #{method_type} MEthod: #{k.name},#{method_name}"
609
+ logger.debug "Couldn't find #{method_type} method: #{km.name},#{method_name}"
427
610
  end
428
611
  method
429
612
  end
430
613
 
431
- def find_or_create_method(method_name, class_name, wtype, method_type)
432
- wtype ||= WType::DEFAULT
433
- self.find_method(method_name, class_name, method_type) || \
434
- self.create_method(method_name, class_name, wtype, method_type)
614
+ def find_or_create_method(klass, method_name, method_type, wtype, local=false)
615
+ self.find_method(klass, method_name, method_type, local) ||
616
+ self.create_method(klass, method_name, method_type, wtype, local)
435
617
  end
436
618
 
437
619
  # Find block wnode up the tree
@@ -452,6 +634,15 @@ module Rlang::Parser
452
634
  end
453
635
  end
454
636
 
637
+ # Find module wnode up the tree
638
+ def module_wnode
639
+ if self.module?
640
+ self
641
+ else
642
+ @parent ? @parent.module_wnode : nil
643
+ end
644
+ end
645
+
455
646
  # Find class wnode up the tree
456
647
  def class_wnode
457
648
  if self.class?
@@ -461,6 +652,16 @@ module Rlang::Parser
461
652
  end
462
653
  end
463
654
 
655
+ # Find class or module wnode up the tree
656
+ # which ever come first
657
+ def class_or_module_wnode
658
+ if self.class? || self.module?
659
+ self
660
+ else
661
+ @parent ? @parent.class_or_module_wnode : nil
662
+ end
663
+ end
664
+
464
665
  # Find method wnode up the tree
465
666
  def method_wnode
466
667
  if self.method?
@@ -474,6 +675,7 @@ module Rlang::Parser
474
675
  return :class_method if self.in_class_method_scope?
475
676
  return :instance_method if self.in_instance_method_scope?
476
677
  return :class if self.in_class_scope?
678
+ return :module if self.in_module_scope?
477
679
  return :root if self.in_root_scope?
478
680
  end
479
681
 
@@ -482,19 +684,27 @@ module Rlang::Parser
482
684
  end
483
685
 
484
686
  def in_class_method_scope?
485
- !self.method_wnode.nil? && !self.method_wnode.method.instance?
687
+ self.in_method_scope? && self.method_wnode.method.class?
486
688
  end
487
689
 
488
690
  def in_instance_method_scope?
489
- !self.method_wnode.nil? && self.method_wnode.method.instance?
691
+ self.in_method_scope? && self.method_wnode.method.instance?
490
692
  end
491
693
 
492
694
  def in_class_scope?
493
- !self.class_wnode.nil? && self.method_wnode.nil?
695
+ !self.class_wnode.nil? && !self.in_method_scope?
696
+ end
697
+
698
+ def in_module_scope?
699
+ !self.module_wnode.nil? && !self.in_method_scope?
700
+ end
701
+
702
+ def in_class_or_module_scope?
703
+ self.in_class_scope? || self.in_module_scope?
494
704
  end
495
705
 
496
706
  def in_root_scope?
497
- self.root? || (self.parent.root? && !in_class_scope?)
707
+ self.root? || (self.parent.root? && !self.in_class_scope?)
498
708
  end
499
709
 
500
710
  def method?
@@ -502,9 +712,14 @@ module Rlang::Parser
502
712
  end
503
713
 
504
714
  def class?
715
+ # root always has the Object class associated
505
716
  self.type == :class || self.type == :root
506
717
  end
507
718
 
719
+ def module?
720
+ self.type == :module
721
+ end
722
+
508
723
  # format the wnode and tree below
509
724
  # Note: this a just a tree dump. The output generated is
510
725
  # not valid WAT code
@@ -512,18 +727,48 @@ module Rlang::Parser
512
727
  "\n%sw(%s:%s" % [' '*2*indent, self.type, self.wasm_code] + self.children.map { |wn| wn.to_s(indent+1) }.join('') + ')'
513
728
  end
514
729
 
730
+ def head(n=5)
731
+ (self.to_s.lines[0,n] << "...\n").join('')
732
+ end
733
+
515
734
  # Generate WAT code starting for this node and tree branches below
516
- def transpile(indent=0)
735
+ def transpile(depth=0)
736
+ # follow children first and then go on with
737
+ # the wnode link if it exits
738
+ children = self.children + (self.link ? [self.link] : [])
739
+ indent = ' ' * depth * 2
740
+ logger.debug "children: #{self} / #{children.map(&:head)}" if self.link
741
+
517
742
  case @type
518
- when :insn, :method
743
+ # Section nodes
744
+ when :comment
745
+ "\n%s%s" % [indent, self.wasm_code]
746
+ when :imports
747
+ "\n%s;;============= %s SECTION ===============\n" % [indent, @type.to_s.upcase] +
748
+ children.map { |wn| wn.transpile(depth) }.join('')
749
+ when :data
750
+ "\n\n%s;;============= %s SECTION ===============\n" % [indent, @type.to_s.upcase] +
751
+ DAta.transpile(depth)
752
+ when :globals
753
+ "\n\n%s;;============= %s SECTION ===============\n" % [indent, @type.to_s.upcase] +
754
+ Global.transpile(depth)
755
+ when :exports
756
+ "\n\n%s;;============= %s SECTION ===============\n" % [indent, @type.to_s.upcase] +
757
+ "%s(export \"memory\" (memory $0))\n" % [indent] +
758
+ Export.transpile(depth)
759
+ when :insn, :method, :root
519
760
  if @template == :inline
520
- "\n%s%s" % [' '*2*indent, self.wasm_code]
761
+ "\n%s%s" % [indent, self.wasm_code]
521
762
  else
522
- "\n%s(%s" % [' '*2*indent, self.wasm_code] + self.children.map { |wn| wn.transpile(indent+1) }.join('') + ')'
763
+ "\n%s(%s" % [indent, self.wasm_code] + children.map { |wn| wn.transpile(depth+1) }.join('') + ')'
523
764
  end
524
- when :root, :class, :none
765
+ when :class, :module, :none, :memory
525
766
  # no WAT code to generate for these nodes. Process children directly.
526
- self.children.map { |wn| wn.transpile(indent) }.join('')
767
+ children.map { |wn| wn.transpile(depth) }.join('')
768
+ when :silent
769
+ # Do not generate any WAT code for a silent node and
770
+ # and its children
771
+ ''
527
772
  else
528
773
  raise "Error: Unknown wnode type #{@type}. No WAT code generated"
529
774
  end