rlang 0.4.1 → 0.5.0

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