rlang 0.4.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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