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