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,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
@@ -60,8 +61,9 @@ module Rlang::Parser
60
61
  }
61
62
 
62
63
  attr_accessor :type, :wargs, :children, :parent, :comment,
63
- :method, :template, :keep_on_stack, :classes
64
- attr_reader :wtype, :label, :klass
64
+ :method, :template, :keep_on_stack, :classes,
65
+ :modules
66
+ attr_reader :wtype, :label, :klass, :module
65
67
 
66
68
  @@label_index = 0
67
69
 
@@ -82,16 +84,13 @@ module Rlang::Parser
82
84
  # means no value returned)
83
85
  @wtype = WType::DEFAULT
84
86
 
85
- # For root wnode
86
- @classes = [] # classes
87
87
  # top level class needed only if const are
88
88
  # defined at top level
89
89
  # NOTE: can't use create_klass as it find_class
90
90
  # which doesn't find root class ... endless loop!!
91
91
 
92
- # For class wnode only
93
- @@klass = nil
94
- self.klass = Klass.new(:Top__) if self.root?
92
+ # For class or module wnode only
93
+ @klass = nil
95
94
 
96
95
  # For method wnode only
97
96
  @method = nil
@@ -192,16 +191,16 @@ module Rlang::Parser
192
191
  self
193
192
  end
194
193
 
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
194
+ # Reparent all children wnodes to another wnode
195
+ # (in the same order)
196
+ # WARNING!! Do not use self.children.each { } to
197
+ # reparent because we are modifying children list
198
+ # as we go
199
+ def reparent_children_to(wnode)
200
+ wnc = self.children
201
+ wnc.count.times { wnc.first.reparent_to(wnode) }
202
+ self
203
+ end
205
204
 
206
205
  # insert a blank wnode above self, so between self wnode
207
206
  # and its parent (self -> parent becomes self -> wn -> parent)
@@ -211,16 +210,22 @@ module Rlang::Parser
211
210
  wn
212
211
  end
213
212
 
213
+ # delete current wnode (which means
214
+ # basically remove it as a child)
215
+ def delete!
216
+ return if self.root? || self.parent.nil?
217
+ self.parent.remove_child(self)
218
+ end
219
+
214
220
  def klass=(klass)
215
221
  @klass = klass
216
222
  @klass.wnode = self
217
- WNode.root.classes << klass
218
- klass
223
+ @klass
219
224
  end
220
225
 
221
226
  # Find class name in this node and up the tree
222
227
  def class_name
223
- (cn = self.class_wnode) ? cn.klass.name : nil
228
+ (cn = self.class_wnode) ? cn.klass.path_name : nil
224
229
  end
225
230
 
226
231
  # Find class size in this wnode or up the tree
@@ -228,108 +233,238 @@ module Rlang::Parser
228
233
  (cn = self.class_wnode) ? cn.klass.size : nil
229
234
  end
230
235
 
236
+ # Find the module object of the current wnode and up the tree
237
+ # if no name given
238
+ # or lookup the matching class from
239
+ # the root level if module name given
240
+ def find_module(module_path)
241
+ logger.debug "looking for #{module_path} module in scope #{self.scope}" # at wnode #{self}"
242
+ if modul = self.find_class_or_module_by_name(module_path)
243
+ logger.debug "Found module #{modul.name} / #{modul}"
244
+ else
245
+ logger.debug "Module #{module_path} not found"
246
+ end
247
+ modul
248
+ end
249
+
250
+ # Create a module object
251
+ def create_module(module_path)
252
+ # Create the constant associated to this module
253
+ klass = self.find_current_class_or_module()
254
+ logger.debug "Creating module #{module_path} in class #{klass} under wnode #{self.head}"
255
+ const = self.create_const(module_path, nil, WType.new(:Module))
256
+ modul = Module.new(const, klass)
257
+ # Add the constant to list of constants in current scope class
258
+ klass.consts << const
259
+ # Generate wnode
260
+ wnc = WNode.new(:module, self)
261
+ wnc.klass = modul
262
+ logger.debug "Created module #{modul.name}/ID:#{modul} under wnode #{wnc.parent} / ID: #{self.object_id}"
263
+ modul
264
+ end
265
+
266
+ def find_or_create_module(module_path)
267
+ self.find_module(module_path) || self.create_module(module_path)
268
+ end
269
+
270
+ # Return the first class/module up the tree
271
+ def find_current_class_or_module()
272
+ logger.debug "looking for current class in scope #{self.scope} at wnode #{self.head(3)}"
273
+ if wn = self.class_or_module_wnode
274
+ k = wn.klass
275
+ elsif self.in_root_scope?
276
+ # methods defined at root level goes to Object Class
277
+ k = self.find_class_or_module_by_name([:Object])
278
+ end
279
+ if k
280
+ logger.debug "Found class #{k.name} / #{k}"
281
+ else
282
+ logger.debug "No current class found!"
283
+ end
284
+ k
285
+ end
286
+
287
+ # Find the class by doing a lookup on the constant
288
+ def find_class_or_module_by_name(class_path)
289
+ raise "Class name argument expected" unless class_path && !class_path.empty?
290
+ logger.debug "looking for class/module constant #{class_path} in scope #{self.scope} at wnode #{self.head}"
291
+ const = self.find_const(class_path)
292
+ #raise "Class or Module #{class_path} not found!" unless const
293
+ if const
294
+ logger.debug "Found constant #{const.name} pointing to #{const.value}"
295
+ const.value
296
+ else
297
+ logger.debug "Constant #{class_path} not found"
298
+ nil
299
+ end
300
+ end
301
+
231
302
  # Find the class object of the current and up the tree
232
303
  # if no name given or lookup the matching class from
233
304
  # 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 }
305
+ # class_path can be passed either as in a Symbol (e.g. :"A::B")
306
+ # or as an array of symbols (e.g. [:A, :B])
307
+ def find_class_or_module(class_path)
308
+ logger.debug "looking for #{class_path} class in scope #{self.scope} at wnode #{self.head}"
309
+ # turn the symbol form of class_path into the array form
310
+ if class_path.is_a? Symbol
311
+ class_path = class_path.to_s.split('::').map(&:to_sym)
312
+ end
313
+
314
+ if class_path.empty?
315
+ k = self.find_current_class_or_module()
240
316
  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}"
317
+ k = self.find_class_or_module_by_name(class_path)
318
+ end
319
+ if k
320
+ logger.debug "Found class #{k.name} / #{k}"
256
321
  else
257
- logger.debug "Class #{class_name} not found"
322
+ logger.debug "Class #{class_path} not found!"
323
+ end
324
+ k
325
+ end
326
+
327
+ # Create a Class object. The code below assumes
328
+ # the class doesn't exist
329
+ def create_class(class_path, super_class_path)
330
+ # check that super class exists
331
+ super_class = nil
332
+ unless super_class_path.empty?
333
+ super_class_const = self.find_const(super_class_path)
334
+ raise NameError, "uninitialized constant #{super_class_path}" \
335
+ unless super_class_const
336
+ super_class = super_class_const.scope_class
258
337
  end
259
- c
260
- end
261
338
 
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
339
+ # Find current class or module (lexical scope)
340
+ # special case for Object class
341
+ if class_path == [:Object] && self.in_root_scope?
342
+ scope_class = nil
343
+ else
344
+ scope_class = self.find_current_class_or_module()
345
+ end
270
346
 
271
- def find_or_create_class(class_name)
272
- self.find_class(class_name) || self.create_class(class_name)
347
+ # Create the constant associated to this class
348
+ # the class itself
349
+ const = self.create_const(class_path, nil, WType.new(:Class))
350
+ k = Klass.new(const, scope_class, super_class)
351
+
352
+ # special case to bootstrap Object class
353
+ if class_path == [:Object] && self.in_root_scope?
354
+ const.scope_class = k
355
+ end
356
+
357
+ # create class wnode
358
+ wnc = WNode.new(:class, self)
359
+ wnc.klass = k
360
+ k.wnode = wnc
361
+ logger.debug "Created class #{k.name}/ID: #{k} under wnode #{self}/ ID: #{self.object_id}"
362
+ k
273
363
  end
274
364
 
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))
365
+ def find_or_create_class(class_path, super_class_path)
366
+ logger.debug "Find/Create class: #{class_path}"
367
+ if (km = self.find_class_or_module(class_path))
368
+ raise TypeError, "#{class_path} is not a class" unless km.const.class?
281
369
  else
282
- raise "No class found for class constant #{const}"
370
+ km = self.create_class(class_path, super_class_path)
371
+ end
372
+ km
373
+ end
374
+
375
+ # create a constant, relative to the current wnode
376
+ # the constant is assumed to not exist already
377
+ def create_const(c_path, value, wtype)
378
+ logger.debug "Creating constant #{c_path} / wtype: #{wtype} at wnode #{self.class_wnode.head}..."
379
+ raise "Dynamic constant assignment. Constant #{name} cannot be created in scope #{cmn.scope}" \
380
+ if self.in_method_scope?
381
+
382
+ # if const_path has more than one element then check
383
+ # that all element but last already exist
384
+ !(c_prefix = c_path[0..-2]).empty? && self.find_const(c_prefix)
385
+ c_name = c_path.last
386
+ const = Const.new(c_name, value, wtype)
387
+ end
388
+
389
+ # Look for constant from where we are in wtree
390
+ # For a Ruby implementation of the constant lookup
391
+ # algo, see https://cirw.in/blog/constant-lookup
392
+ # - c_path is an array of constant name elements
393
+ # e.g. for constant A::B::C constant_path is [A, B, C]
394
+ def find_const(c_path)
395
+ logger.debug "looking for constant #{c_path}...from wnode #{self.head}..."
396
+ wn = self; idx = 0; count = c_path.size
397
+ while idx < count
398
+ const = wn._const_lookup(c_path[idx])
399
+ if const && (idx < count-1) && (const.class? || const.module?) && const.scope_class
400
+ wn = const.value.wnode
401
+ else
402
+ raise NameError, "uninitialized constant #{c_path.join('::')}" unless idx == count-1
403
+ end
404
+ idx += 1
283
405
  end
284
406
  const
285
407
  end
286
408
 
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
409
+ def _const_lookup(name)
410
+ # build constant lookup path: lexical scope first
411
+ # excluding the
412
+ mn = self.find_current_class_or_module()&.nesting
413
+ return nil unless mn
414
+ # do not use find_class_... to find the Object class
415
+ # This is to avoid and endless loop
416
+ oc = WNode.root.klass
417
+ #oc = self.find_class_or_module_by_name([:Object])
418
+ logger.debug "Module/Class nesting: #{mn.map(&:name)}"
419
+ # and ancestors second
420
+ lookup_path = mn + (mn.first || oc).ancestors
421
+ lookup_path += oc.ancestors if (oc && mn.first.const.module?)
422
+ logger.debug "searching constant #{name} in path #{lookup_path.map(&:name)}..."
423
+ const = nil
424
+ lookup_path.find do |mod|
425
+ logger.debug "++ looking for const #{name} in #{mod.name}"
426
+ const = mod.const_get(name)
295
427
  end
296
428
  if const
297
- logger.debug "Constant #{c_name} found in class #{k.name} at wnode #{k.wnode}..."
429
+ logger.debug "... found! in class #{const.scope_class&.name}"
298
430
  else
299
- logger.debug "Constant #{c_name} not found in class #{k.name} or at top level..."
431
+ logger.debug "Constant #{name} not found in lookup path #{lookup_path.map(&:name)}..." \
300
432
  end
301
433
  const
302
434
  end
303
435
 
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)
436
+ # find or create constant, relative to current wnode
437
+ def find_or_create_const(c_path, class_name, value, wtype)
438
+ self.find_const(c_path) || self.create_const(c_path, value, wtype)
306
439
  end
307
440
 
308
- def find_attr(name, class_name=nil)
309
- k = find_class(class_name)
441
+ # find attr in current class
442
+ def find_attr(name)
443
+ k = self.find_current_class_or_module()
310
444
  raise "Can't find parent class for attr #{name}" unless k
311
445
  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 }
446
+ k.attrs.find { |a| a.klass == k && a.name == name }
313
447
  end
314
448
 
315
449
  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))
450
+ if (k = self.find_current_class_or_module())
451
+ logger.debug "creating attr #{name} in class #{k.name} at wnode #{k.wnode}..."
452
+ k.attrs << (_attr = Attr.new(k, name, wtype))
319
453
  else
320
454
  raise "No class found for class attribute #{name}"
321
455
  end
322
456
  _attr
323
457
  end
324
458
 
325
- def find_or_create_attr(name, class_name=nil, wtype=WType::DEFAULT)
326
- find_attr(name, class_name) || create_attr(name, wtype)
459
+ # find or create attr in current class
460
+ def find_or_create_attr(name, wtype=WType::DEFAULT)
461
+ find_attr(name) || create_attr(name, wtype)
327
462
  end
328
463
 
329
464
  def create_ivar(iv_name, wtype=WType::DEFAULT)
330
- if (cn = self.class_wnode)
465
+ if (k = self.find_current_class_or_module())
331
466
  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))
467
+ k.ivars << (ivar = IVar.new(k, iv_name, wtype))
333
468
  else
334
469
  raise "No class found for instance variable #{iv_name}"
335
470
  end
@@ -337,10 +472,10 @@ module Rlang::Parser
337
472
  end
338
473
 
339
474
  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 }
475
+ k = self.find_current_class_or_module()
476
+ raise "Can't find parent class for ivar #{iv_name}" unless k
477
+ logger.debug "looking for ivar #{iv_name} in class #{k.name} at wnode #{self.class_wnode}..."
478
+ self.class_wnode.klass.ivars.find { |iv| iv.klass == k && iv.name == iv_name }
344
479
  end
345
480
 
346
481
  def find_or_create_ivar(iv_name)
@@ -350,7 +485,7 @@ module Rlang::Parser
350
485
  def create_cvar(cv_name, value=0, wtype=WType::DEFAULT)
351
486
  if (cn = self.class_wnode)
352
487
  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))
488
+ cn.klass.cvars << (cvar = CVar.new(cn.klass, cv_name, value, wtype))
354
489
  else
355
490
  raise "No class found for class variable #{cv_name}"
356
491
  end
@@ -394,44 +529,51 @@ module Rlang::Parser
394
529
  end
395
530
 
396
531
  # 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
532
+ def create_method(klass, method_name, method_type, wtype, local=false)
533
+ logger.debug "Create #{method_type} method #{method_name} in class #{class_name || 'current'} / wtype: #{wtype}"
534
+ wtype ||= WType::DEFAULT
535
+
536
+ # see if method already created
537
+ m = find_method(klass, method_name, method_type, local)
538
+ raise "Method already exists: #{class_name},#{m.name} / ID: #{m}" if m
539
+
540
+ # Go create method
541
+ km = klass || self.find_current_class_or_module()
542
+ km.methods << (m = MEthod.new(method_name, km, wtype, method_type))
543
+ logger.debug "++++ adding #{method_type} method #{m.name}/ID:#{m} in class #{km.name}/ID:#{km}"
544
+ logger.debug "#{km.methods.count} methods in class #{km.name}/#{km}"
545
+ m
409
546
  end
410
547
 
411
548
  # 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}"
549
+ # if local is true look for method in the current class only
550
+ def find_method(klass, method_name, method_type, local=false)
551
+ logger.debug "looking #{local ? 'locally' : 'globally'} for #{method_type} method #{method_name} in class #{klass}" #from wnode #{self.head(2)}"
552
+ km = klass || self.find_current_class_or_module()
553
+ raise "Couldn't find scope class/module where to search for method #{method_name}" unless km
554
+
555
+ class_hierarchy = (local ? [km] : km.ancestors)
556
+ logger.debug "searching #{method_type} method #{method_name} in ancestors #{class_hierarchy.map(&:name)}..."
557
+ method = nil
558
+ class_hierarchy.each do |k|
559
+ logger.debug "Currently #{k.methods.count} method(s) in class #{k.name}/#{k}"
560
+ method = k.methods.find do |m|
561
+ logger.debug "++ looking for #{method_type} method #{k.name}/#{method_name} in #{m.klass.name}/#{m.name}/#{m.method_type}"
562
+ m.name == method_name && m.klass == k && m.method_type == method_type
563
+ end
564
+ break if method
422
565
  end
423
566
  if method
424
- logger.debug "Found #{method_type} MEthod: #{k.name},#{method}"
567
+ logger.debug "Found #{method_type} method: #{km.name},#{method.name} in #{method.klass.name}"
425
568
  else
426
- logger.debug "Couldn't find #{method_type} MEthod: #{k.name},#{method_name}"
569
+ logger.debug "Couldn't find #{method_type} method: #{km.name},#{method_name}"
427
570
  end
428
571
  method
429
572
  end
430
573
 
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)
574
+ def find_or_create_method(klass, method_name, method_type, wtype, local=false)
575
+ self.find_method(klass, method_name, method_type, local) ||
576
+ self.create_method(klass, method_name, method_type, wtype, local)
435
577
  end
436
578
 
437
579
  # Find block wnode up the tree
@@ -452,6 +594,15 @@ module Rlang::Parser
452
594
  end
453
595
  end
454
596
 
597
+ # Find module wnode up the tree
598
+ def module_wnode
599
+ if self.module?
600
+ self
601
+ else
602
+ @parent ? @parent.module_wnode : nil
603
+ end
604
+ end
605
+
455
606
  # Find class wnode up the tree
456
607
  def class_wnode
457
608
  if self.class?
@@ -461,6 +612,16 @@ module Rlang::Parser
461
612
  end
462
613
  end
463
614
 
615
+ # Find class or module wnode up the tree
616
+ # which ever come first
617
+ def class_or_module_wnode
618
+ if self.class? || self.module?
619
+ self
620
+ else
621
+ @parent ? @parent.class_or_module_wnode : nil
622
+ end
623
+ end
624
+
464
625
  # Find method wnode up the tree
465
626
  def method_wnode
466
627
  if self.method?
@@ -474,6 +635,7 @@ module Rlang::Parser
474
635
  return :class_method if self.in_class_method_scope?
475
636
  return :instance_method if self.in_instance_method_scope?
476
637
  return :class if self.in_class_scope?
638
+ return :module if self.in_module_scope?
477
639
  return :root if self.in_root_scope?
478
640
  end
479
641
 
@@ -482,19 +644,23 @@ module Rlang::Parser
482
644
  end
483
645
 
484
646
  def in_class_method_scope?
485
- !self.method_wnode.nil? && !self.method_wnode.method.instance?
647
+ self.in_method_scope? && self.method_wnode.method.class?
486
648
  end
487
649
 
488
650
  def in_instance_method_scope?
489
- !self.method_wnode.nil? && self.method_wnode.method.instance?
651
+ self.in_method_scope? && self.method_wnode.method.instance?
490
652
  end
491
653
 
492
654
  def in_class_scope?
493
- !self.class_wnode.nil? && self.method_wnode.nil?
655
+ !self.class_wnode.nil? && !self.in_method_scope?
656
+ end
657
+
658
+ def in_module_scope?
659
+ !self.module_wnode.nil? && !self.in_method_scope?
494
660
  end
495
661
 
496
662
  def in_root_scope?
497
- self.root? || (self.parent.root? && !in_class_scope?)
663
+ self.root? || (self.parent.root? && !self.in_class_scope?)
498
664
  end
499
665
 
500
666
  def method?
@@ -502,9 +668,14 @@ module Rlang::Parser
502
668
  end
503
669
 
504
670
  def class?
671
+ # root always has the Object class associated
505
672
  self.type == :class || self.type == :root
506
673
  end
507
674
 
675
+ def module?
676
+ self.type == :module
677
+ end
678
+
508
679
  # format the wnode and tree below
509
680
  # Note: this a just a tree dump. The output generated is
510
681
  # not valid WAT code
@@ -512,6 +683,10 @@ module Rlang::Parser
512
683
  "\n%sw(%s:%s" % [' '*2*indent, self.type, self.wasm_code] + self.children.map { |wn| wn.to_s(indent+1) }.join('') + ')'
513
684
  end
514
685
 
686
+ def head(n=5)
687
+ (self.to_s.lines[0,n] << "...\n").join('')
688
+ end
689
+
515
690
  # Generate WAT code starting for this node and tree branches below
516
691
  def transpile(indent=0)
517
692
  case @type
@@ -521,7 +696,7 @@ module Rlang::Parser
521
696
  else
522
697
  "\n%s(%s" % [' '*2*indent, self.wasm_code] + self.children.map { |wn| wn.transpile(indent+1) }.join('') + ')'
523
698
  end
524
- when :root, :class, :none
699
+ when :root, :class, :module, :none
525
700
  # no WAT code to generate for these nodes. Process children directly.
526
701
  self.children.map { |wn| wn.transpile(indent) }.join('')
527
702
  else
@@ -45,11 +45,28 @@ class WType
45
45
  wtypes[leading_idx]
46
46
  end
47
47
 
48
+ # Name is a symbol of the form :A or :"A::B" or :"A::B::C"
49
+ # or a string "A" or "A::B" or "A::B::C"
50
+ # or it can also be an array of symbols [:A], [:A, :B] or [:A, :B, :C]
51
+ # (It is not a class object)
48
52
  def initialize(name)
49
- @name = name.to_sym
53
+ if name.is_a? Symbol
54
+ @name = name
55
+ elsif name.is_a? String
56
+ @name = name.to_sym
57
+ elsif name.is_a? Array
58
+ @name = name.map(&:to_s).join('::').to_sym
59
+ else
60
+ raise "Unknown type for WType name (got #{name}, class: #{name.class}"
61
+ end
50
62
  raise "Invalid WType #{name.inspect}" unless self.valid?
51
63
  end
52
64
 
65
+ def class_path
66
+ return [] if self.blank?
67
+ name.to_s.split('::').map(&:to_sym)
68
+ end
69
+
53
70
  def default?
54
71
  @name == WType::DEFAULT.name
55
72
  end