rltk3 3.0.2
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.
- checksums.yaml +7 -0
- data/AUTHORS +1 -0
- data/LICENSE +27 -0
- data/README.md +852 -0
- data/Rakefile +197 -0
- data/lib/rltk/ast.rb +573 -0
- data/lib/rltk/cfg.rb +683 -0
- data/lib/rltk/cg/basic_block.rb +157 -0
- data/lib/rltk/cg/bindings.rb +151 -0
- data/lib/rltk/cg/builder.rb +1127 -0
- data/lib/rltk/cg/context.rb +48 -0
- data/lib/rltk/cg/contractor.rb +51 -0
- data/lib/rltk/cg/execution_engine.rb +194 -0
- data/lib/rltk/cg/function.rb +237 -0
- data/lib/rltk/cg/generated_bindings.rb +8118 -0
- data/lib/rltk/cg/generic_value.rb +95 -0
- data/lib/rltk/cg/instruction.rb +519 -0
- data/lib/rltk/cg/llvm.rb +150 -0
- data/lib/rltk/cg/memory_buffer.rb +75 -0
- data/lib/rltk/cg/module.rb +451 -0
- data/lib/rltk/cg/pass_manager.rb +252 -0
- data/lib/rltk/cg/support.rb +29 -0
- data/lib/rltk/cg/target.rb +230 -0
- data/lib/rltk/cg/triple.rb +58 -0
- data/lib/rltk/cg/type.rb +554 -0
- data/lib/rltk/cg/value.rb +1272 -0
- data/lib/rltk/cg.rb +32 -0
- data/lib/rltk/lexer.rb +372 -0
- data/lib/rltk/lexers/calculator.rb +44 -0
- data/lib/rltk/lexers/ebnf.rb +38 -0
- data/lib/rltk/parser.rb +1702 -0
- data/lib/rltk/parsers/infix_calc.rb +43 -0
- data/lib/rltk/parsers/postfix_calc.rb +34 -0
- data/lib/rltk/parsers/prefix_calc.rb +34 -0
- data/lib/rltk/token.rb +90 -0
- data/lib/rltk/version.rb +11 -0
- data/lib/rltk.rb +16 -0
- data/test/cg/tc_basic_block.rb +83 -0
- data/test/cg/tc_control_flow.rb +191 -0
- data/test/cg/tc_function.rb +54 -0
- data/test/cg/tc_generic_value.rb +33 -0
- data/test/cg/tc_instruction.rb +256 -0
- data/test/cg/tc_llvm.rb +25 -0
- data/test/cg/tc_math.rb +88 -0
- data/test/cg/tc_module.rb +89 -0
- data/test/cg/tc_transforms.rb +68 -0
- data/test/cg/tc_type.rb +69 -0
- data/test/cg/tc_value.rb +151 -0
- data/test/cg/ts_cg.rb +23 -0
- data/test/tc_ast.rb +332 -0
- data/test/tc_cfg.rb +164 -0
- data/test/tc_lexer.rb +216 -0
- data/test/tc_parser.rb +711 -0
- data/test/tc_token.rb +34 -0
- data/test/ts_rltk.rb +47 -0
- metadata +317 -0
data/lib/rltk/ast.rb
ADDED
@@ -0,0 +1,573 @@
|
|
1
|
+
# Author: Chris Wailes <chris.wailes@gmail.com>
|
2
|
+
# Project: Ruby Language Toolkit
|
3
|
+
# Date: 2011/01/19
|
4
|
+
# Description: This file provides a base Node class for ASTs.
|
5
|
+
|
6
|
+
############
|
7
|
+
# Requires #
|
8
|
+
############
|
9
|
+
|
10
|
+
# Gems
|
11
|
+
require 'filigree/abstract_class'
|
12
|
+
require 'filigree/class'
|
13
|
+
require 'filigree/match'
|
14
|
+
require 'filigree/types'
|
15
|
+
require 'filigree/visitor'
|
16
|
+
|
17
|
+
#######################
|
18
|
+
# Classes and Modules #
|
19
|
+
#######################
|
20
|
+
|
21
|
+
module RLTK
|
22
|
+
# This class is a good start for all your abstract syntax tree node needs.
|
23
|
+
class ASTNode
|
24
|
+
|
25
|
+
include Filigree::Visitable
|
26
|
+
|
27
|
+
extend Filigree::AbstractClass
|
28
|
+
extend Filigree::Destructurable
|
29
|
+
|
30
|
+
# @return [ASTNode] Reference to the parent node.
|
31
|
+
attr_accessor :parent
|
32
|
+
|
33
|
+
# @return [Hash] The notes hash for this node.
|
34
|
+
attr_reader :notes
|
35
|
+
|
36
|
+
#################
|
37
|
+
# Class Methods #
|
38
|
+
#################
|
39
|
+
|
40
|
+
class << self
|
41
|
+
|
42
|
+
# @return [Array<Symbol>] List of members (children and values) that have array types
|
43
|
+
def array_members
|
44
|
+
@array_members
|
45
|
+
end
|
46
|
+
|
47
|
+
# Check to make sure a name isn't re-defining a value or child.
|
48
|
+
#
|
49
|
+
# @raise [ArgumentError] Raised if the name is already used for an existing value or child
|
50
|
+
def check_odr(name)
|
51
|
+
if @child_names.include? name
|
52
|
+
raise ArgumentError, "Class #{self} or one of its superclasses already defines a child named #{name}"
|
53
|
+
end
|
54
|
+
|
55
|
+
if @value_names.include?(name)
|
56
|
+
raise ArgumentError, "Class #{self} or one of its superclasses already defines a value named #{name}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Installs instance class variables into a class.
|
61
|
+
#
|
62
|
+
# @return [void]
|
63
|
+
def install_icvars
|
64
|
+
if self.superclass == ASTNode
|
65
|
+
@child_names = Array.new
|
66
|
+
@value_names = Array.new
|
67
|
+
@array_members = Array.new
|
68
|
+
|
69
|
+
@member_order = :values
|
70
|
+
@def_order = Array.new
|
71
|
+
@inc_children = Array.new
|
72
|
+
@inc_values = Array.new
|
73
|
+
else
|
74
|
+
@child_names = self.superclass.child_names.clone
|
75
|
+
@value_names = self.superclass.value_names.clone
|
76
|
+
@array_members = self.superclass.array_members.clone
|
77
|
+
|
78
|
+
@member_order = (v = self.superclass.member_order).is_a?(Symbol) ? v : v.clone
|
79
|
+
@def_order = self.superclass.def_order.clone
|
80
|
+
@inc_children = self.superclass.inc_children.clone
|
81
|
+
@inc_values = self.superclass.inc_values.clone
|
82
|
+
end
|
83
|
+
end
|
84
|
+
protected :install_icvars
|
85
|
+
|
86
|
+
# Called when the Lexer class is sub-classed, it installes
|
87
|
+
# necessary instance class variables.
|
88
|
+
#
|
89
|
+
# @param [Class] klass The class is inheriting from this class.
|
90
|
+
#
|
91
|
+
# @return [void]
|
92
|
+
def inherited(klass)
|
93
|
+
klass.install_icvars
|
94
|
+
end
|
95
|
+
|
96
|
+
# Defined a child for this AST class and its subclasses.
|
97
|
+
# The name of the child will be used to define accessor
|
98
|
+
# methods that include type checking. The type of this
|
99
|
+
# child must be a subclass of the ASTNode class.
|
100
|
+
#
|
101
|
+
# @param [String, Symbol] name Name of child node
|
102
|
+
# @param [Class] type Type of child node. Must be a subclass of ASTNode.
|
103
|
+
# @param [Boolean] omit Include the child in the constructor or not
|
104
|
+
#
|
105
|
+
# @return [void]
|
106
|
+
def child(name, type, omit = false)
|
107
|
+
check_odr(name)
|
108
|
+
|
109
|
+
if type.is_a?(Array) and type.length == 1
|
110
|
+
t = type.first
|
111
|
+
|
112
|
+
elsif type.is_a?(Class)
|
113
|
+
t = type
|
114
|
+
|
115
|
+
else
|
116
|
+
raise 'Child and Value types must be a class name or an array with a single class name element.'
|
117
|
+
end
|
118
|
+
|
119
|
+
# Check to make sure that type is a subclass of ASTNode.
|
120
|
+
if not t < ASTNode
|
121
|
+
raise "A child's type specification must be a subclass of ASTNode."
|
122
|
+
end
|
123
|
+
|
124
|
+
@child_names << name
|
125
|
+
@array_members << name if type.is_a?(Array)
|
126
|
+
|
127
|
+
if not omit
|
128
|
+
@def_order << name
|
129
|
+
@inc_children << name
|
130
|
+
end
|
131
|
+
|
132
|
+
define_accessor(name, type, true)
|
133
|
+
end
|
134
|
+
|
135
|
+
# @return [Array<Symbol>] Array of the names of this node class's children
|
136
|
+
def child_names
|
137
|
+
@child_names
|
138
|
+
end
|
139
|
+
|
140
|
+
# @return [Array<Symbol>] Array of names of values/children in the order they were defined
|
141
|
+
def def_order
|
142
|
+
@def_order
|
143
|
+
end
|
144
|
+
|
145
|
+
# This method defines a type checking accessor named *name*
|
146
|
+
# with type *type*.
|
147
|
+
#
|
148
|
+
# @param [String, Symbol] name Name of accessor
|
149
|
+
# @param [Class] type Class used for type checking
|
150
|
+
# @param [Boolean] set_parent Set the parent variable or not
|
151
|
+
#
|
152
|
+
# @return [void]
|
153
|
+
def define_accessor(name, type, set_parent = false)
|
154
|
+
ivar_name = ('@' + name.to_s).to_sym
|
155
|
+
|
156
|
+
define_method(name) do
|
157
|
+
self.instance_variable_defined?(ivar_name) ? self.instance_variable_get(ivar_name) : nil
|
158
|
+
end
|
159
|
+
|
160
|
+
if type.is_a?(Class)
|
161
|
+
if set_parent
|
162
|
+
define_method((name.to_s + '=').to_sym) do |value|
|
163
|
+
self.instance_variable_set(ivar_name, check_type(value, type, nil, true))
|
164
|
+
value.parent = self if value
|
165
|
+
end
|
166
|
+
|
167
|
+
else
|
168
|
+
define_method((name.to_s + '=').to_sym) do |value|
|
169
|
+
self.instance_variable_set(ivar_name, check_type(value, type, nil, true))
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
else
|
174
|
+
if set_parent
|
175
|
+
define_method((name.to_s + '=').to_sym) do |value|
|
176
|
+
self.instance_variable_set(ivar_name, check_array_type(value, type.first, nil, true))
|
177
|
+
value.each { |c| c.parent = self }
|
178
|
+
end
|
179
|
+
|
180
|
+
else
|
181
|
+
define_method((name.to_s + '=').to_sym) do |value|
|
182
|
+
self.instance_variable_set(ivar_name, check_array_type(value, type.first, nil, true))
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
private :define_accessor
|
188
|
+
|
189
|
+
# Define a custom ordering for the class to use when building the
|
190
|
+
# default constructor and destructurer.
|
191
|
+
#
|
192
|
+
# @param [Array<Symbol>] members List of member names
|
193
|
+
#
|
194
|
+
# @return [void]
|
195
|
+
def custom_order(*members)
|
196
|
+
@member_order = members
|
197
|
+
end
|
198
|
+
|
199
|
+
# @return [Array<Symbol>] Array of the names of children that should be included in the constructor
|
200
|
+
def inc_children
|
201
|
+
@inc_children
|
202
|
+
end
|
203
|
+
|
204
|
+
# @return [Array<Symbol>] Array of the names of values that should be included in the constructor
|
205
|
+
def inc_values
|
206
|
+
@inc_values
|
207
|
+
end
|
208
|
+
|
209
|
+
# A getter and setter for a class's initialization order. If the
|
210
|
+
# order value is `:values` the constructor will expect all of the
|
211
|
+
# values and then the children. If it is `:children` then the
|
212
|
+
# constructor expects children and then values. If it is `:def`
|
213
|
+
# the constructor expects to values and children in the order that
|
214
|
+
# they were defined. If val is nil the current value will be
|
215
|
+
# returned.
|
216
|
+
#
|
217
|
+
# The default ordering is `:values`, which matches the behavior of
|
218
|
+
# previous versions of RLTK.
|
219
|
+
#
|
220
|
+
# @param [:values, :children, :def] val The new initialization order
|
221
|
+
#
|
222
|
+
# @return [:values, :children, :def] The current initialization order
|
223
|
+
def member_order(val = nil)
|
224
|
+
if val
|
225
|
+
@member_order = val
|
226
|
+
else
|
227
|
+
@member_order
|
228
|
+
end
|
229
|
+
end
|
230
|
+
alias :order :member_order
|
231
|
+
|
232
|
+
# Defined a value for this AST class and its subclasses.
|
233
|
+
# The name of the value will be used to define accessor
|
234
|
+
# methods that include type checking.
|
235
|
+
#
|
236
|
+
# @param [String, Symbol] name Name of value
|
237
|
+
# @param [Class] type Type of value
|
238
|
+
# @param [Boolean] omit Include the value in the constructor or not
|
239
|
+
#
|
240
|
+
# @return [void]
|
241
|
+
def value(name, type, omit = false)
|
242
|
+
check_odr(name)
|
243
|
+
|
244
|
+
if not (type.is_a?(Class) or (type.is_a?(Array) and type.length == 1))
|
245
|
+
raise 'Child and Value types must be a class name or an array with a single class name element.'
|
246
|
+
end
|
247
|
+
|
248
|
+
@value_names << name
|
249
|
+
@array_members << name if type.is_a?(Array)
|
250
|
+
|
251
|
+
if not omit
|
252
|
+
@def_order << name
|
253
|
+
@inc_values << name
|
254
|
+
end
|
255
|
+
|
256
|
+
define_accessor(name, type)
|
257
|
+
end
|
258
|
+
|
259
|
+
# @return [Array<Symbol>] Array of the names of this node class's values
|
260
|
+
def value_names
|
261
|
+
@value_names
|
262
|
+
end
|
263
|
+
end
|
264
|
+
|
265
|
+
####################
|
266
|
+
# Instance Methods #
|
267
|
+
####################
|
268
|
+
|
269
|
+
# Used for AST comparison, this function will return true if the two
|
270
|
+
# nodes are of the same class and all of their values and children
|
271
|
+
# are equal.
|
272
|
+
#
|
273
|
+
# @param [ASTNode] other The ASTNode to compare to
|
274
|
+
#
|
275
|
+
# @return [Boolean]
|
276
|
+
def ==(other)
|
277
|
+
self.class == other.class and self.values == other.values and self.children == other.children
|
278
|
+
end
|
279
|
+
|
280
|
+
# @return [Object] Note with the name *key*
|
281
|
+
def [](key)
|
282
|
+
@notes[key]
|
283
|
+
end
|
284
|
+
|
285
|
+
# Sets the note named *key* to *value*.
|
286
|
+
def []=(key, value)
|
287
|
+
@notes[key] = value
|
288
|
+
end
|
289
|
+
|
290
|
+
# This method allows ASTNodes to be destructured for pattern matching.
|
291
|
+
def destructure(arity)
|
292
|
+
case self.class.member_order
|
293
|
+
when :values then (self.class.inc_values + self.class.inc_children)
|
294
|
+
when :children then (self.class.inc_children + self.class.inc_values)
|
295
|
+
when :def then self.class.def_order
|
296
|
+
when Array then self.class.member_order
|
297
|
+
end.map { |m| self.send m }
|
298
|
+
end
|
299
|
+
|
300
|
+
# @param [Class] as The type that should be returned by the method. Must be either Array or hash.
|
301
|
+
#
|
302
|
+
# @return [Array<ASTNode>, Hash{Symbol => ASTNode}] Array or Hash of this node's children.
|
303
|
+
def children(as = Array)
|
304
|
+
if as == Array
|
305
|
+
self.class.child_names.map { |name| self.send(name) }
|
306
|
+
|
307
|
+
elsif as == Hash
|
308
|
+
self.class.child_names.inject(Hash.new) { |h, name| h[name] = self.send(name); h }
|
309
|
+
|
310
|
+
else
|
311
|
+
raise 'Children can only be returned as an Array or a Hash.'
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
# Assigns an array or hash of AST nodes as the children of this node.
|
316
|
+
# If a hash is provided as an argument the key is used as the name of
|
317
|
+
# the child a object should be assigned to.
|
318
|
+
#
|
319
|
+
# @param [Array<ASTNode>, Hash{Symbol => ASTNode}] children Children to be assigned to this node.
|
320
|
+
#
|
321
|
+
# @return [void]
|
322
|
+
def children=(children)
|
323
|
+
case children
|
324
|
+
when Array
|
325
|
+
if children.length != self.class.child_names.length
|
326
|
+
raise 'Wrong number of children specified.'
|
327
|
+
end
|
328
|
+
|
329
|
+
self.class.child_names.each_with_index do |name, i|
|
330
|
+
self.send((name.to_s + '=').to_sym, children[i])
|
331
|
+
end
|
332
|
+
|
333
|
+
when Hash
|
334
|
+
children.each do |name, val|
|
335
|
+
if self.class.child_names.include?(name)
|
336
|
+
self.send((name.to_s + '=').to_sym, val)
|
337
|
+
else
|
338
|
+
raise "ASTNode subclass #{self.class.name} does not have a child named #{name}."
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
# Produce an exact copy of this tree.
|
345
|
+
#
|
346
|
+
# @return [ASTNode] A copy of the tree.
|
347
|
+
def copy
|
348
|
+
self.map { |c| c }
|
349
|
+
end
|
350
|
+
|
351
|
+
# Removes the note *key* from this node. If the *recursive* argument
|
352
|
+
# is true it will also remove the note from the node's children.
|
353
|
+
#
|
354
|
+
# @param [Object] key The key of the note to remove
|
355
|
+
# @param [Boolean] recursive Do a recursive removal or not
|
356
|
+
def delete_note(key, recursive = true)
|
357
|
+
if recursive
|
358
|
+
self.children.each do |child|
|
359
|
+
next if not child
|
360
|
+
|
361
|
+
if child.is_a?(Array)
|
362
|
+
child.each { |c| c.delete_note(key, true) }
|
363
|
+
else
|
364
|
+
child.delete_note(key, true)
|
365
|
+
end
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
@notes.delete(key)
|
370
|
+
end
|
371
|
+
|
372
|
+
# This method is a simple wrapper around Marshal.dump, and is used
|
373
|
+
# to serialize an AST. You can use Marshal.load to reconstruct a
|
374
|
+
# serialized AST.
|
375
|
+
#
|
376
|
+
# @param [nil, IO, String] dest Where the serialized version of the AST will end up. If nil,
|
377
|
+
# this method will return the AST as a string.
|
378
|
+
# @param [Fixnum] limit Recursion depth. If -1 is specified there is no limit on the recursion depth.
|
379
|
+
#
|
380
|
+
# @return [void, String] String if *dest* is nil, void otherwise.
|
381
|
+
def dump(dest = nil, limit = -1)
|
382
|
+
case dest
|
383
|
+
when nil then Marshal.dump(self, limit)
|
384
|
+
when String then File.open(dest, 'w') { |f| Marshal.dump(self, f, limit) }
|
385
|
+
when IO then Marshal.dump(self, dest, limit)
|
386
|
+
else raise TypeError, "AST#dump expects nil, a String, or an IO object for the dest parameter."
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
# An iterator over the node's children. The AST may be traversed in
|
391
|
+
# the following orders:
|
392
|
+
#
|
393
|
+
# * Pre-order (:pre)
|
394
|
+
# * Post-order (:post)
|
395
|
+
# * Level-order (:level)
|
396
|
+
#
|
397
|
+
# @param [:pre, :post, :level] order The order in which to iterate over the tree
|
398
|
+
#
|
399
|
+
# @return [void]
|
400
|
+
def each(order = :pre, &block)
|
401
|
+
case order
|
402
|
+
when :pre
|
403
|
+
yield self
|
404
|
+
|
405
|
+
self.children.flatten.compact.each { |c| c.each(:pre, &block) }
|
406
|
+
|
407
|
+
when :post
|
408
|
+
self.children.flatten.compact.each { |c| c.each(:post, &block) }
|
409
|
+
|
410
|
+
yield self
|
411
|
+
|
412
|
+
when :level
|
413
|
+
level_queue = [self]
|
414
|
+
|
415
|
+
while node = level_queue.shift
|
416
|
+
yield node
|
417
|
+
|
418
|
+
level_queue += node.children.flatten.compact
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
# Tests to see if a note named *key* is present at this node.
|
424
|
+
def has_note?(key)
|
425
|
+
@notes.has_key?(key)
|
426
|
+
end
|
427
|
+
alias :'note?' :'has_note?'
|
428
|
+
|
429
|
+
# Instantiates a new ASTNode object. The arguments to this method are
|
430
|
+
# split into two lists: the set of values for this node and a list of
|
431
|
+
# its children. If the node has 2 values and 3 children you would
|
432
|
+
# pass the values in as the first two arguments (in the order they
|
433
|
+
# were declared) and then the children as the remaining arguments (in
|
434
|
+
# the order they were declared).
|
435
|
+
#
|
436
|
+
# If a node has 2 values and 2 children and is passed only a single
|
437
|
+
# value the remaining values and children are assumed to be nil or
|
438
|
+
# empty arrays, depending on the declared type of the value or
|
439
|
+
# child.
|
440
|
+
#
|
441
|
+
# If a block is passed to initialize the block will be executed in
|
442
|
+
# the conext of the new object.
|
443
|
+
#
|
444
|
+
# @param [Array<Object>] objects Values and children of this node
|
445
|
+
def initialize(*objects, &block)
|
446
|
+
@notes = Hash.new()
|
447
|
+
@parent = nil
|
448
|
+
|
449
|
+
pairs =
|
450
|
+
case self.class.member_order
|
451
|
+
when :values then (self.class.inc_values + self.class.inc_children)
|
452
|
+
when :children then (self.class.inc_children + self.class.inc_values)
|
453
|
+
when :def then self.class.def_order
|
454
|
+
when Array then self.class.member_order
|
455
|
+
end.zip(objects).first(objects.length)
|
456
|
+
|
457
|
+
pairs.each do |name, value|
|
458
|
+
self.send("#{name}=", value)
|
459
|
+
end
|
460
|
+
|
461
|
+
self.class.array_members.each do |member|
|
462
|
+
ivar_name = '@' + member.to_s
|
463
|
+
self.instance_variable_set(ivar_name, []) if self.instance_variable_get(ivar_name).nil?
|
464
|
+
end
|
465
|
+
|
466
|
+
self.instance_exec(&block) if not block.nil?
|
467
|
+
end
|
468
|
+
|
469
|
+
# Create a new tree by using the provided Proc object to map the
|
470
|
+
# nodes of this tree to new nodes. This is always done in
|
471
|
+
# post-order, meaning that all children of a node are visited before
|
472
|
+
# the node itself.
|
473
|
+
#
|
474
|
+
# @note This does not modify the current tree.
|
475
|
+
#
|
476
|
+
# @return [Object] Result of calling the given block on the root node
|
477
|
+
def map(&block)
|
478
|
+
new_values = self.values.map { |v| v.clone }
|
479
|
+
|
480
|
+
new_children =
|
481
|
+
self.children.map do |c0|
|
482
|
+
case c0
|
483
|
+
when Array then c0.map { |c1| c1.map(&block) }
|
484
|
+
when ASTNode then c0.map(&block)
|
485
|
+
when NilClass then nil
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
new_node = self.class.new(*new_values, *new_children)
|
490
|
+
new_node.notes = self.notes
|
491
|
+
|
492
|
+
block.call(new_node)
|
493
|
+
end
|
494
|
+
|
495
|
+
# Map the nodes in an AST to new nodes using the provided Proc
|
496
|
+
# object. This is always done in post-order, meaning that all
|
497
|
+
# children of a node are visited before the node itself.
|
498
|
+
#
|
499
|
+
# @note The root node can not be replaced and as such the result of
|
500
|
+
# calling the provided block on the root node is used as the
|
501
|
+
# return value.
|
502
|
+
#
|
503
|
+
# @return [Object] Result of calling the given block on the root node
|
504
|
+
def map!(&block)
|
505
|
+
self.children =
|
506
|
+
self.children.map do |c0|
|
507
|
+
case c0
|
508
|
+
when Array then c0.map { |c1| c1.map!(&block) }
|
509
|
+
when ASTNode then c0.map!(&block)
|
510
|
+
when NilClass then nil
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
block.call(self)
|
515
|
+
end
|
516
|
+
|
517
|
+
# Set the notes for this node from a given hash.
|
518
|
+
#
|
519
|
+
# @param [Hash] new_notes The new notes for this node.
|
520
|
+
#
|
521
|
+
# @return [void]
|
522
|
+
def notes=(new_notes)
|
523
|
+
@notes = new_notes.clone
|
524
|
+
end
|
525
|
+
|
526
|
+
# @return [ASTNode] Root of the abstract syntax tree.
|
527
|
+
def root
|
528
|
+
if @parent then @parent.root else self end
|
529
|
+
end
|
530
|
+
|
531
|
+
# @param [Class] as The type that should be returned by the method. Must be either Array or hash.
|
532
|
+
#
|
533
|
+
# @return [Array<Object>, Hash{Symbol => Object}] Array or Hash of this node's values.
|
534
|
+
def values(as = Array)
|
535
|
+
if as == Array
|
536
|
+
self.class.value_names.map { |name| self.send(name) }
|
537
|
+
|
538
|
+
elsif as == Hash
|
539
|
+
self.class.value_names.inject(Hash.new) { |h, name| h[name] = self.send(name); h }
|
540
|
+
|
541
|
+
else
|
542
|
+
raise 'Values can only be returned as an Array or a Hash.'
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
# Assigns an array or hash of objects as the values of this node. If
|
547
|
+
# a hash is provided as an argument the key is used as the name of
|
548
|
+
# the value an object should be assigned to.
|
549
|
+
#
|
550
|
+
# @param [Array<Object>, Hash{Symbol => Object}] values The values to be assigned to this node.
|
551
|
+
def values=(values)
|
552
|
+
case values
|
553
|
+
when Array
|
554
|
+
if values.length != self.class.value_names.length
|
555
|
+
raise 'Wrong number of values specified.'
|
556
|
+
end
|
557
|
+
|
558
|
+
self.class.value_names.each_with_index do |name, i|
|
559
|
+
self.send((name.to_s + '=').to_sym, values[i])
|
560
|
+
end
|
561
|
+
|
562
|
+
when Hash
|
563
|
+
values.each do |name, val|
|
564
|
+
if self.class.value_names.include?(name)
|
565
|
+
self.send((name.to_s + '=').to_sym, val)
|
566
|
+
else
|
567
|
+
raise "ASTNode subclass #{self.class.name} does not have a value named #{name}."
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end
|
571
|
+
end
|
572
|
+
end
|
573
|
+
end
|