rltk 2.2.1 → 3.0.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.
- checksums.yaml +7 -0
- data/LICENSE +12 -12
- data/README.md +458 -285
- data/Rakefile +99 -92
- data/lib/rltk/ast.rb +221 -126
- data/lib/rltk/cfg.rb +218 -239
- data/lib/rltk/cg/basic_block.rb +1 -1
- data/lib/rltk/cg/bindings.rb +9 -26
- data/lib/rltk/cg/builder.rb +40 -8
- data/lib/rltk/cg/context.rb +1 -1
- data/lib/rltk/cg/contractor.rb +51 -0
- data/lib/rltk/cg/execution_engine.rb +45 -8
- data/lib/rltk/cg/function.rb +12 -2
- data/lib/rltk/cg/generated_bindings.rb +2541 -575
- data/lib/rltk/cg/generic_value.rb +2 -2
- data/lib/rltk/cg/instruction.rb +104 -83
- data/lib/rltk/cg/llvm.rb +44 -3
- data/lib/rltk/cg/memory_buffer.rb +22 -5
- data/lib/rltk/cg/module.rb +85 -36
- data/lib/rltk/cg/old_generated_bindings.rb +6152 -0
- data/lib/rltk/cg/pass_manager.rb +87 -43
- data/lib/rltk/cg/support.rb +2 -4
- data/lib/rltk/cg/target.rb +158 -28
- data/lib/rltk/cg/triple.rb +8 -8
- data/lib/rltk/cg/type.rb +69 -25
- data/lib/rltk/cg/value.rb +107 -66
- data/lib/rltk/cg.rb +16 -17
- data/lib/rltk/lexer.rb +21 -11
- data/lib/rltk/lexers/calculator.rb +1 -1
- data/lib/rltk/lexers/ebnf.rb +8 -7
- data/lib/rltk/parser.rb +300 -247
- data/lib/rltk/parsers/infix_calc.rb +1 -1
- data/lib/rltk/parsers/postfix_calc.rb +2 -2
- data/lib/rltk/parsers/prefix_calc.rb +2 -2
- data/lib/rltk/token.rb +1 -2
- data/lib/rltk/version.rb +3 -3
- data/lib/rltk.rb +6 -6
- 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 +105 -8
- data/test/tc_cfg.rb +63 -48
- data/test/tc_lexer.rb +84 -96
- data/test/tc_parser.rb +224 -52
- data/test/tc_token.rb +6 -6
- data/test/ts_rltk.rb +12 -15
- metadata +149 -75
- data/lib/rltk/cg/generated_extended_bindings.rb +0 -287
- data/lib/rltk/util/abstract_class.rb +0 -25
- data/lib/rltk/util/monkeys.rb +0 -129
data/lib/rltk/ast.rb
CHANGED
@@ -7,54 +7,62 @@
|
|
7
7
|
# Requires #
|
8
8
|
############
|
9
9
|
|
10
|
-
#
|
11
|
-
require '
|
10
|
+
# Gems
|
11
|
+
require 'filigree/abstract_class'
|
12
|
+
require 'filigree/class'
|
13
|
+
require 'filigree/match'
|
14
|
+
require 'filigree/types'
|
12
15
|
|
13
16
|
#######################
|
14
17
|
# Classes and Modules #
|
15
18
|
#######################
|
16
19
|
|
17
|
-
module RLTK
|
18
|
-
# A TypeMismatch is raised when an object being set as a child or value of
|
19
|
-
# an ASTNode is of the wrong type.
|
20
|
-
class TypeMismatch < StandardError
|
21
|
-
|
22
|
-
# Instantiates a new TypeMismatch object.
|
23
|
-
#
|
24
|
-
# @param [Class] expected Expected type.
|
25
|
-
# @param [Klass] actual Actual type of object.
|
26
|
-
def initialize(expected, actual)
|
27
|
-
@expected = expected
|
28
|
-
@actual = actual
|
29
|
-
end
|
30
|
-
|
31
|
-
# @return [String] String representation of the error.
|
32
|
-
def to_s
|
33
|
-
"Type Mismatch: Expected #{@expected} but received #{@actual}."
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
20
|
+
module RLTK
|
37
21
|
# This class is a good start for all your abstract syntax tree node needs.
|
38
22
|
class ASTNode
|
39
|
-
|
23
|
+
|
24
|
+
extend Filigree::AbstractClass
|
25
|
+
extend Filigree::Destructurable
|
26
|
+
|
27
|
+
# @return [ASTNode] Reference to the parent node.
|
40
28
|
attr_accessor :parent
|
41
29
|
|
30
|
+
# @return [Hash] The notes hash for this node.
|
31
|
+
attr_reader :notes
|
32
|
+
|
42
33
|
#################
|
43
34
|
# Class Methods #
|
44
35
|
#################
|
45
36
|
|
46
37
|
class << self
|
47
38
|
|
39
|
+
# Check to make sure a name isn't re-defining a value or child.
|
40
|
+
#
|
41
|
+
# @raise [ArgumentError] Raised if the name is already used for an existing value or child
|
42
|
+
def check_odr(name)
|
43
|
+
if @child_names.include? name
|
44
|
+
raise ArgumentError, "Class #{self} or one of its superclasses already defines a child named #{name}"
|
45
|
+
end
|
46
|
+
|
47
|
+
if @value_names.include?(name)
|
48
|
+
raise ArgumentError, "Class #{self} or one of its superclasses already defines a value named #{name}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
48
52
|
# Installs instance class varialbes into a class.
|
49
53
|
#
|
50
54
|
# @return [void]
|
51
55
|
def install_icvars
|
52
56
|
if self.superclass == ASTNode
|
53
57
|
@child_names = Array.new
|
58
|
+
@child_types = Array.new
|
54
59
|
@value_names = Array.new
|
60
|
+
@value_types = Array.new
|
55
61
|
else
|
56
62
|
@child_names = self.superclass.child_names.clone
|
63
|
+
@child_types = self.superclass.child_types.clone
|
57
64
|
@value_names = self.superclass.value_names.clone
|
65
|
+
@value_types = self.superclass.value_types.clone
|
58
66
|
end
|
59
67
|
end
|
60
68
|
protected :install_icvars
|
@@ -62,7 +70,7 @@ module RLTK # :nodoc:
|
|
62
70
|
# Called when the Lexer class is sub-classed, it installes
|
63
71
|
# necessary instance class variables.
|
64
72
|
#
|
65
|
-
# @param [Class]
|
73
|
+
# @param [Class] klass The class is inheriting from this class.
|
66
74
|
#
|
67
75
|
# @return [void]
|
68
76
|
def inherited(klass)
|
@@ -74,11 +82,13 @@ module RLTK # :nodoc:
|
|
74
82
|
# methods that include type checking. The type of this
|
75
83
|
# child must be a subclass of the ASTNode class.
|
76
84
|
#
|
77
|
-
# @param [String, Symbol]
|
78
|
-
# @param [Class]
|
85
|
+
# @param [String, Symbol] name Name of child node.
|
86
|
+
# @param [Class] type Type of child node. Must be a subclass of ASTNode.
|
79
87
|
#
|
80
88
|
# @return [void]
|
81
89
|
def child(name, type)
|
90
|
+
check_odr(name)
|
91
|
+
|
82
92
|
if type.is_a?(Array) and type.length == 1
|
83
93
|
t = type.first
|
84
94
|
|
@@ -96,20 +106,26 @@ module RLTK # :nodoc:
|
|
96
106
|
end
|
97
107
|
|
98
108
|
@child_names << name
|
109
|
+
@child_types << type
|
99
110
|
define_accessor(name, type, true)
|
100
111
|
end
|
101
112
|
|
102
|
-
# @return [Array<Symbol>]
|
113
|
+
# @return [Array<Symbol>] Array of the names of this node class's children
|
103
114
|
def child_names
|
104
115
|
@child_names
|
105
116
|
end
|
106
117
|
|
118
|
+
# @return [Array] Array of types of this node class's children
|
119
|
+
def child_types
|
120
|
+
@child_types
|
121
|
+
end
|
122
|
+
|
107
123
|
# This method defines a type checking accessor named *name*
|
108
124
|
# with type *type*.
|
109
125
|
#
|
110
|
-
# @param [String, Symbol]
|
111
|
-
# @param [Class]
|
112
|
-
# @param [Boolean]
|
126
|
+
# @param [String, Symbol] name Name of accessor
|
127
|
+
# @param [Class] type Class used for type checking
|
128
|
+
# @param [Boolean] set_parent Set the parent variable or not
|
113
129
|
#
|
114
130
|
# @return [void]
|
115
131
|
def define_accessor(name, type, set_parent = false)
|
@@ -122,48 +138,26 @@ module RLTK # :nodoc:
|
|
122
138
|
if type.is_a?(Class)
|
123
139
|
if set_parent
|
124
140
|
define_method((name.to_s + '=').to_sym) do |value|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
value.parent = self if value
|
129
|
-
else
|
130
|
-
raise TypeMismatch.new(type, value.class)
|
131
|
-
end
|
141
|
+
self.instance_variable_set(ivar_name, check_type(value, type, nil, true))
|
142
|
+
value.parent = self if value
|
132
143
|
end
|
133
144
|
|
134
145
|
else
|
135
146
|
define_method((name.to_s + '=').to_sym) do |value|
|
136
|
-
|
137
|
-
self.instance_variable_set(ivar_name, value)
|
138
|
-
|
139
|
-
else
|
140
|
-
raise TypeMismatch.new(type, value.class)
|
141
|
-
end
|
147
|
+
self.instance_variable_set(ivar_name, check_type(value, type, nil, true))
|
142
148
|
end
|
143
149
|
end
|
144
150
|
|
145
151
|
else
|
146
|
-
type = type.first
|
147
|
-
|
148
152
|
if set_parent
|
149
153
|
define_method((name.to_s + '=').to_sym) do |value|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
value.each { |c| c.parent = self }
|
154
|
-
else
|
155
|
-
raise TypeMismatch.new(type, value.class)
|
156
|
-
end
|
154
|
+
self.instance_variable_set(ivar_name, check_array_type(value, type.first, nil, true))
|
155
|
+
value.each { |c| c.parent = self }
|
157
156
|
end
|
158
157
|
|
159
158
|
else
|
160
159
|
define_method((name.to_s + '=').to_sym) do |value|
|
161
|
-
|
162
|
-
self.instance_variable_set(ivar_name, value)
|
163
|
-
|
164
|
-
else
|
165
|
-
raise TypeMismatch.new(type, value.class)
|
166
|
-
end
|
160
|
+
self.instance_variable_set(ivar_name, check_array_type(value, type.first, nil, true))
|
167
161
|
end
|
168
162
|
end
|
169
163
|
end
|
@@ -172,14 +166,15 @@ module RLTK # :nodoc:
|
|
172
166
|
|
173
167
|
# Defined a value for this AST class and its subclasses.
|
174
168
|
# The name of the value will be used to define accessor
|
175
|
-
# methods that include type checking.
|
176
|
-
# value must NOT be a subclass of the ASTNode class.
|
169
|
+
# methods that include type checking.
|
177
170
|
#
|
178
|
-
# @param [String, Symbol]
|
179
|
-
# @param [Class]
|
171
|
+
# @param [String, Symbol] name Name of value
|
172
|
+
# @param [Class] type Type of value
|
180
173
|
#
|
181
174
|
# @return [void]
|
182
175
|
def value(name, type)
|
176
|
+
check_odr(name)
|
177
|
+
|
183
178
|
if type.is_a?(Array) and type.length == 1
|
184
179
|
t = type.first
|
185
180
|
|
@@ -190,20 +185,20 @@ module RLTK # :nodoc:
|
|
190
185
|
raise 'Child and Value types must be a class name or an array with a single class name element.'
|
191
186
|
end
|
192
187
|
|
193
|
-
# Check to make sure that type is NOT a subclass of
|
194
|
-
# ASTNode.
|
195
|
-
if t.subclass_of?(ASTNode)
|
196
|
-
raise "A value's type specification must NOT be a subclass of ASTNode."
|
197
|
-
end
|
198
|
-
|
199
188
|
@value_names << name
|
189
|
+
@value_types << type
|
200
190
|
define_accessor(name, type)
|
201
191
|
end
|
202
192
|
|
203
|
-
# @return [Array<Symbol>]
|
193
|
+
# @return [Array<Symbol>] Array of the names of this node class's values
|
204
194
|
def value_names
|
205
195
|
@value_names
|
206
196
|
end
|
197
|
+
|
198
|
+
# @return [Array<Symbol>] Array of the types of this node class's values
|
199
|
+
def value_types
|
200
|
+
@value_types
|
201
|
+
end
|
207
202
|
end
|
208
203
|
|
209
204
|
####################
|
@@ -214,14 +209,14 @@ module RLTK # :nodoc:
|
|
214
209
|
# nodes are of the same class and all of their values and children
|
215
210
|
# are equal.
|
216
211
|
#
|
217
|
-
# @param [ASTNode]
|
212
|
+
# @param [ASTNode] other The ASTNode to compare to
|
218
213
|
#
|
219
214
|
# @return [Boolean]
|
220
215
|
def ==(other)
|
221
216
|
self.class == other.class and self.values == other.values and self.children == other.children
|
222
217
|
end
|
223
218
|
|
224
|
-
# @return [Object]
|
219
|
+
# @return [Object] Note with the name *key*
|
225
220
|
def [](key)
|
226
221
|
@notes[key]
|
227
222
|
end
|
@@ -231,31 +226,71 @@ module RLTK # :nodoc:
|
|
231
226
|
@notes[key] = value
|
232
227
|
end
|
233
228
|
|
234
|
-
#
|
235
|
-
def
|
236
|
-
|
229
|
+
# This method allows ASTNodes to be destructured for pattern matching.
|
230
|
+
def call(arity)
|
231
|
+
if arity == self.values.length
|
232
|
+
self.values
|
233
|
+
else
|
234
|
+
[*self.values, *self.children]
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# @param [Class] as The type that should be returned by the method. Must be either Array or hash.
|
239
|
+
#
|
240
|
+
# @return [Array<ASTNode>, Hash{Symbol => ASTNode}] Array or Hash of this node's children.
|
241
|
+
def children(as = Array)
|
242
|
+
if as == Array
|
243
|
+
self.class.child_names.map { |name| self.send(name) }
|
244
|
+
|
245
|
+
elsif as == Hash
|
246
|
+
self.class.child_names.inject(Hash.new) { |h, name| h[name] = self.send(name); h }
|
247
|
+
|
248
|
+
else
|
249
|
+
raise 'Children can only be returned as an Array or a Hash.'
|
250
|
+
end
|
237
251
|
end
|
238
252
|
|
239
|
-
# Assigns an array of AST nodes as the children of this node.
|
253
|
+
# Assigns an array or hash of AST nodes as the children of this node.
|
254
|
+
# If a hash is provided as an argument the key is used as the name of
|
255
|
+
# the child a object should be assigned to.
|
240
256
|
#
|
241
|
-
# @param [Array<ASTNode
|
257
|
+
# @param [Array<ASTNode>, Hash{Symbol => ASTNode}] children Children to be assigned to this node.
|
242
258
|
#
|
243
259
|
# @return [void]
|
244
260
|
def children=(children)
|
245
|
-
|
246
|
-
|
247
|
-
|
261
|
+
case children
|
262
|
+
when Array
|
263
|
+
if children.length != self.class.child_names.length
|
264
|
+
raise 'Wrong number of children specified.'
|
265
|
+
end
|
248
266
|
|
249
|
-
|
250
|
-
|
267
|
+
self.class.child_names.each_with_index do |name, i|
|
268
|
+
self.send((name.to_s + '=').to_sym, children[i])
|
269
|
+
end
|
270
|
+
|
271
|
+
when Hash
|
272
|
+
children.each do |name, val|
|
273
|
+
if self.class.child_names.include?(name)
|
274
|
+
self.send((name.to_s + '=').to_sym, val)
|
275
|
+
else
|
276
|
+
raise "ASTNode subclass #{self.class.name} does not have a child named #{name}."
|
277
|
+
end
|
278
|
+
end
|
251
279
|
end
|
252
280
|
end
|
253
281
|
|
282
|
+
# Produce an exact copy of this tree.
|
283
|
+
#
|
284
|
+
# @return [ASTNode] A copy of the tree.
|
285
|
+
def copy
|
286
|
+
self.map { |c| c }
|
287
|
+
end
|
288
|
+
|
254
289
|
# Removes the note *key* from this node. If the *recursive* argument
|
255
290
|
# is true it will also remove the note from the node's children.
|
256
291
|
#
|
257
|
-
# @param [Object]
|
258
|
-
# @param [Boolean]
|
292
|
+
# @param [Object] key The key of the note to remove
|
293
|
+
# @param [Boolean] recursive Do a recursive removal or not
|
259
294
|
def delete_note(key, recursive = true)
|
260
295
|
if recursive
|
261
296
|
self.children.each do |child|
|
@@ -276,16 +311,17 @@ module RLTK # :nodoc:
|
|
276
311
|
# to serialize an AST. You can use Marshal.load to reconstruct a
|
277
312
|
# serialized AST.
|
278
313
|
#
|
279
|
-
# @param [nil, IO, String]
|
280
|
-
#
|
314
|
+
# @param [nil, IO, String] dest Where the serialized version of the AST will end up. If nil,
|
315
|
+
# this method will return the AST as a string.
|
316
|
+
# @param [Fixnum] limit Recursion depth. If -1 is specified there is no limit on the recursion depth.
|
281
317
|
#
|
282
|
-
# @return [void, String]
|
318
|
+
# @return [void, String] String if *dest* is nil, void otherwise.
|
283
319
|
def dump(dest = nil, limit = -1)
|
284
320
|
case dest
|
285
|
-
when nil
|
286
|
-
when String
|
287
|
-
when IO
|
288
|
-
else
|
321
|
+
when nil then Marshal.dump(self, limit)
|
322
|
+
when String then File.open(dest, 'w') { |f| Marshal.dump(self, f, limit) }
|
323
|
+
when IO then Marshal.dump(self, dest, limit)
|
324
|
+
else raise TypeError, "AST#dump expects nil, a String, or an IO object for the dest parameter."
|
289
325
|
end
|
290
326
|
end
|
291
327
|
|
@@ -296,16 +332,18 @@ module RLTK # :nodoc:
|
|
296
332
|
# * Post-order (:post)
|
297
333
|
# * Level-order (:level)
|
298
334
|
#
|
335
|
+
# @param [:pre, :post, :level] order The order in which to iterate over the tree
|
336
|
+
#
|
299
337
|
# @return [void]
|
300
338
|
def each(order = :pre, &block)
|
301
339
|
case order
|
302
340
|
when :pre
|
303
341
|
yield self
|
304
342
|
|
305
|
-
self.children.compact.each { |c| c.each(:pre, &block) }
|
343
|
+
self.children.flatten.compact.each { |c| c.each(:pre, &block) }
|
306
344
|
|
307
345
|
when :post
|
308
|
-
self.children.compact.each { |c| c.each(:post, &block) }
|
346
|
+
self.children.flatten.compact.each { |c| c.each(:post, &block) }
|
309
347
|
|
310
348
|
yield self
|
311
349
|
|
@@ -315,7 +353,7 @@ module RLTK # :nodoc:
|
|
315
353
|
while node = level_queue.shift
|
316
354
|
yield node
|
317
355
|
|
318
|
-
level_queue += node.children.compact
|
356
|
+
level_queue += node.children.flatten.compact
|
319
357
|
end
|
320
358
|
end
|
321
359
|
end
|
@@ -334,25 +372,31 @@ module RLTK # :nodoc:
|
|
334
372
|
# the order they were declared).
|
335
373
|
#
|
336
374
|
# If a node has 2 values and 2 children and is passed only a single
|
337
|
-
# value the remaining values and children are assumed to be nil
|
375
|
+
# value the remaining values and children are assumed to be nil or
|
376
|
+
# empty arrays, depending on the declared type of the value or
|
377
|
+
# child.
|
338
378
|
#
|
339
|
-
#
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
379
|
+
# If a block is passed to initialize the block will be executed in
|
380
|
+
# the conext of the new object.
|
381
|
+
#
|
382
|
+
# @param [Array<Object>] objects Values and children of this node
|
383
|
+
def initialize(*objects, &block)
|
384
|
+
@notes = Hash.new()
|
385
|
+
@parent = nil
|
386
|
+
|
387
|
+
# Pad out the objects array with nil values and empty
|
388
|
+
# arrays.
|
389
|
+
all_types = self.class.value_types + self.class.child_types
|
390
|
+
remaining_types = all_types[objects.length..-1]
|
391
|
+
|
392
|
+
objects += remaining_types.map { |type| type.is_a?(Array) ? [] : nil }
|
393
|
+
|
394
|
+
pivot = self.class.value_names.length
|
395
|
+
|
396
|
+
self.values = objects[0...pivot]
|
397
|
+
self.children = objects[pivot..-1]
|
398
|
+
|
399
|
+
self.instance_exec(&block) if not block.nil?
|
356
400
|
end
|
357
401
|
|
358
402
|
# Create a new tree by using the provided Proc object to map the
|
@@ -362,10 +406,21 @@ module RLTK # :nodoc:
|
|
362
406
|
#
|
363
407
|
# @note This does not modify the current tree.
|
364
408
|
#
|
365
|
-
# @return [Object]
|
409
|
+
# @return [Object] Result of calling the given block on the root node
|
366
410
|
def map(&block)
|
367
|
-
|
368
|
-
|
411
|
+
new_values = self.values.map { |v| v.clone }
|
412
|
+
|
413
|
+
new_children =
|
414
|
+
self.children.map do |c0|
|
415
|
+
case c0
|
416
|
+
when Array then c0.map { |c1| c1.map(&block) }
|
417
|
+
when ASTNode then c0.map(&block)
|
418
|
+
when NilClass then nil
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
new_node = self.class.new(*new_values, *new_children)
|
423
|
+
new_node.notes = self.notes
|
369
424
|
|
370
425
|
block.call(new_node)
|
371
426
|
end
|
@@ -378,33 +433,73 @@ module RLTK # :nodoc:
|
|
378
433
|
# calling the provided block on the root node is used as the
|
379
434
|
# return value.
|
380
435
|
#
|
381
|
-
# @return [Object]
|
436
|
+
# @return [Object] Result of calling the given block on the root node
|
382
437
|
def map!(&block)
|
383
|
-
self.children =
|
438
|
+
self.children =
|
439
|
+
self.children.map do |c0|
|
440
|
+
case c0
|
441
|
+
when Array then c0.map { |c1| c1.map!(&block) }
|
442
|
+
when ASTNode then c0.map!(&block)
|
443
|
+
when NilClass then nil
|
444
|
+
end
|
445
|
+
end
|
384
446
|
|
385
447
|
block.call(self)
|
386
448
|
end
|
387
449
|
|
450
|
+
# Set the notes for this node from a given hash.
|
451
|
+
#
|
452
|
+
# @param [Hash] new_notes The new notes for this node.
|
453
|
+
#
|
454
|
+
# @return [void]
|
455
|
+
def notes=(new_notes)
|
456
|
+
@notes = new_notes.clone
|
457
|
+
end
|
458
|
+
|
388
459
|
# @return [ASTNode] Root of the abstract syntax tree.
|
389
460
|
def root
|
390
461
|
if @parent then @parent.root else self end
|
391
462
|
end
|
392
463
|
|
393
|
-
# @
|
394
|
-
|
395
|
-
|
464
|
+
# @param [Class] as The type that should be returned by the method. Must be either Array or hash.
|
465
|
+
#
|
466
|
+
# @return [Array<Object>, Hash{Symbol => Object}] Array or Hash of this node's values.
|
467
|
+
def values(as = Array)
|
468
|
+
if as == Array
|
469
|
+
self.class.value_names.map { |name| self.send(name) }
|
470
|
+
|
471
|
+
elsif as == Hash
|
472
|
+
self.class.value_names.inject(Hash.new) { |h, name| h[name] = self.send(name); h }
|
473
|
+
|
474
|
+
else
|
475
|
+
raise 'Values can only be returned as an Array or a Hash.'
|
476
|
+
end
|
396
477
|
end
|
397
478
|
|
398
|
-
# Assigns an array of objects as the values of this node.
|
479
|
+
# Assigns an array or hash of objects as the values of this node. If
|
480
|
+
# a hash is provided as an argument the key is used as the name of
|
481
|
+
# the value an object should be assigned to.
|
399
482
|
#
|
400
|
-
# @param [Array<Object
|
483
|
+
# @param [Array<Object>, Hash{Symbol => Object}] values The values to be assigned to this node.
|
401
484
|
def values=(values)
|
402
|
-
|
403
|
-
|
404
|
-
|
485
|
+
case values
|
486
|
+
when Array
|
487
|
+
if values.length != self.class.value_names.length
|
488
|
+
raise 'Wrong number of values specified.'
|
489
|
+
end
|
405
490
|
|
406
|
-
|
407
|
-
|
491
|
+
self.class.value_names.each_with_index do |name, i|
|
492
|
+
self.send((name.to_s + '=').to_sym, values[i])
|
493
|
+
end
|
494
|
+
|
495
|
+
when Hash
|
496
|
+
values.each do |name, val|
|
497
|
+
if self.class.value_names.include?(name)
|
498
|
+
self.send((name.to_s + '=').to_sym, val)
|
499
|
+
else
|
500
|
+
raise "ASTNode subclass #{self.class.name} does not have a value named #{name}."
|
501
|
+
end
|
502
|
+
end
|
408
503
|
end
|
409
504
|
end
|
410
505
|
end
|