cast 0.0.1
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.
- data/README +6 -0
- data/doc/index.html +2513 -0
- data/install.rb +14 -0
- data/lib/c.tab.rb +3433 -0
- data/lib/c.y +935 -0
- data/lib/c_nodes.rb +1301 -0
- data/lib/cast.rb +9 -0
- data/lib/node.rb +744 -0
- data/lib/node_list.rb +842 -0
- data/lib/parse.rb +233 -0
- data/lib/to_debug.rb +59 -0
- data/lib/to_s.rb +538 -0
- data/test/common.rb +174 -0
- data/test/run.rb +5 -0
- data/test/test_c_nodes.rb +160 -0
- data/test/test_node.rb +1224 -0
- data/test/test_node_list.rb +2485 -0
- data/test/test_parse.rb +2014 -0
- data/test/test_parser.rb +2925 -0
- metadata +56 -0
data/lib/cast.rb
ADDED
data/lib/node.rb
ADDED
@@ -0,0 +1,744 @@
|
|
1
|
+
###
|
2
|
+
### ##################################################################
|
3
|
+
###
|
4
|
+
### Node core functionality.
|
5
|
+
###
|
6
|
+
### ##################################################################
|
7
|
+
###
|
8
|
+
|
9
|
+
module C
|
10
|
+
###
|
11
|
+
### ================================================================
|
12
|
+
###
|
13
|
+
### Node
|
14
|
+
###
|
15
|
+
### Abstract base class for all AST nodes.
|
16
|
+
###
|
17
|
+
### ================================================================
|
18
|
+
###
|
19
|
+
class Node
|
20
|
+
###
|
21
|
+
### Called by the test suite to ensure all invariants are true.
|
22
|
+
###
|
23
|
+
def assert_invariants testcase
|
24
|
+
fields.each do |field|
|
25
|
+
if val = send(field.reader)
|
26
|
+
assert_same(self, node.parent, "field.reader is #{field.reader}")
|
27
|
+
if field.child?
|
28
|
+
assert_same(field, val.instance_variable_get(:@parent_field), "field.reader is #{field.reader}")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
###
|
34
|
+
### Like self.new, but the first argument is taken as the position
|
35
|
+
### of the Node.
|
36
|
+
###
|
37
|
+
def self.new_at pos, *args
|
38
|
+
ret = new(*args)
|
39
|
+
ret.pos = pos
|
40
|
+
return ret
|
41
|
+
end
|
42
|
+
###
|
43
|
+
### True iff both are of the same class, and all fields are #==.
|
44
|
+
###
|
45
|
+
def == other
|
46
|
+
return false if !other.is_a? self.class
|
47
|
+
|
48
|
+
fields.all? do |field|
|
49
|
+
mine = self .send(field.reader)
|
50
|
+
yours = other.send(field.reader)
|
51
|
+
mine == yours
|
52
|
+
end
|
53
|
+
end
|
54
|
+
###
|
55
|
+
### Same as #==.
|
56
|
+
###
|
57
|
+
def eql? other
|
58
|
+
return self == other
|
59
|
+
end
|
60
|
+
###
|
61
|
+
### #hash, as defined in Object.
|
62
|
+
###
|
63
|
+
def hash
|
64
|
+
fields.inject(0) do |hash, field|
|
65
|
+
val = send(field.reader)
|
66
|
+
hash ^= val.hash
|
67
|
+
end
|
68
|
+
end
|
69
|
+
###
|
70
|
+
### As defined for ::Object, but children are recursively
|
71
|
+
### `#dup'ed.
|
72
|
+
###
|
73
|
+
def dup
|
74
|
+
ret = super
|
75
|
+
ret.instance_variable_set(:@parent, nil)
|
76
|
+
fields.each do |field|
|
77
|
+
next if !field.child?
|
78
|
+
val = instance_variable_get(field.var)
|
79
|
+
val = val.dup unless val.nil?
|
80
|
+
ret.instance_variable_set(field.var, nil)
|
81
|
+
ret.send(field.writer, val)
|
82
|
+
end
|
83
|
+
return ret
|
84
|
+
end
|
85
|
+
###
|
86
|
+
### As defined for ::Object, but children are recursively
|
87
|
+
### `#clone'd.
|
88
|
+
###
|
89
|
+
def clone
|
90
|
+
ret = super
|
91
|
+
ret.instance_variable_set(:@parent, nil)
|
92
|
+
fields.each do |field|
|
93
|
+
next if !field.child?
|
94
|
+
val = instance_variable_get(field.var)
|
95
|
+
val = val.clone unless val.nil?
|
96
|
+
ret.instance_variable_set(field.var, nil)
|
97
|
+
ret.send(field.writer, val)
|
98
|
+
end
|
99
|
+
return ret
|
100
|
+
end
|
101
|
+
|
102
|
+
###
|
103
|
+
### --------------------------------------------------------------
|
104
|
+
### Tree traversal
|
105
|
+
### --------------------------------------------------------------
|
106
|
+
###
|
107
|
+
|
108
|
+
include Enumerable
|
109
|
+
|
110
|
+
###
|
111
|
+
### Yield each child in field order.
|
112
|
+
###
|
113
|
+
def each &blk
|
114
|
+
fields.each do |field|
|
115
|
+
if field.child?
|
116
|
+
val = self.send(field.reader)
|
117
|
+
yield val unless val.nil?
|
118
|
+
end
|
119
|
+
end
|
120
|
+
return self
|
121
|
+
end
|
122
|
+
###
|
123
|
+
### Yield each child in reverse field order.
|
124
|
+
###
|
125
|
+
def reverse_each &blk
|
126
|
+
fields.reverse_each do |field|
|
127
|
+
if field.child?
|
128
|
+
val = self.send(field.reader)
|
129
|
+
yield val unless val.nil?
|
130
|
+
end
|
131
|
+
end
|
132
|
+
return self
|
133
|
+
end
|
134
|
+
###
|
135
|
+
### Perform a depth-first walk of the AST, yielding on recursively
|
136
|
+
### on each child node:
|
137
|
+
###
|
138
|
+
### - (:descending, node) just prior to descending into `node'
|
139
|
+
### - (:ascending, node) just after ascending from `node'
|
140
|
+
###
|
141
|
+
### If the block throws :prune while descending, the children of
|
142
|
+
### the node that was passed to that block will not be visited.
|
143
|
+
###
|
144
|
+
def depth_first &blk
|
145
|
+
catch :prune do
|
146
|
+
yield :descending, self
|
147
|
+
each{|n| n.depth_first &blk}
|
148
|
+
end
|
149
|
+
yield :ascending, self
|
150
|
+
return self
|
151
|
+
end
|
152
|
+
###
|
153
|
+
### Perform a reverse depth-first walk of the AST, yielding on
|
154
|
+
### each node:
|
155
|
+
###
|
156
|
+
### - (:descending, node) just prior to descending into `node'
|
157
|
+
### - (:ascending, node) just after ascending from `node'
|
158
|
+
###
|
159
|
+
### If the block throws :prune while descending, the children of
|
160
|
+
### the node that was passed to that block will not be visited.
|
161
|
+
###
|
162
|
+
def reverse_depth_first &blk
|
163
|
+
catch :prune do
|
164
|
+
yield :descending, self
|
165
|
+
reverse_each{|n| n.reverse_depth_first &blk}
|
166
|
+
end
|
167
|
+
yield :ascending, self
|
168
|
+
return self
|
169
|
+
end
|
170
|
+
###
|
171
|
+
### Perform a preorder walk of the AST, yielding each node in
|
172
|
+
### turn. Return self.
|
173
|
+
###
|
174
|
+
### If the block throws :prune, the children of the node that was
|
175
|
+
### passed to that block will not be visited.
|
176
|
+
###
|
177
|
+
def preorder &blk
|
178
|
+
catch :prune do
|
179
|
+
yield self
|
180
|
+
each{|n| n.preorder &blk}
|
181
|
+
end
|
182
|
+
return self
|
183
|
+
end
|
184
|
+
###
|
185
|
+
### Perform a reverse preorder walk of the AST, yielding each node
|
186
|
+
### in turn. Return self.
|
187
|
+
###
|
188
|
+
### If the block throws :prune, the children of the node that was
|
189
|
+
### passed to that block will not be visited.
|
190
|
+
###
|
191
|
+
def reverse_preorder &blk
|
192
|
+
catch :prune do
|
193
|
+
yield self
|
194
|
+
reverse_each{|n| n.reverse_preorder &blk}
|
195
|
+
end
|
196
|
+
return self
|
197
|
+
end
|
198
|
+
###
|
199
|
+
### Perform a postorder walk of the AST, yielding each node in
|
200
|
+
### turn. Return self.
|
201
|
+
###
|
202
|
+
def postorder &blk
|
203
|
+
each{|n| n.postorder &blk}
|
204
|
+
yield self
|
205
|
+
return self
|
206
|
+
end
|
207
|
+
###
|
208
|
+
### Perform a reverse postorder walk of the AST, yielding each
|
209
|
+
### node in turn. Return self.
|
210
|
+
###
|
211
|
+
def reverse_postorder &blk
|
212
|
+
reverse_each{|n| n.reverse_postorder &blk}
|
213
|
+
yield self
|
214
|
+
return self
|
215
|
+
end
|
216
|
+
|
217
|
+
###
|
218
|
+
### --------------------------------------------------------------
|
219
|
+
### Node tree-structure methods
|
220
|
+
### --------------------------------------------------------------
|
221
|
+
###
|
222
|
+
|
223
|
+
class BadParent < StandardError; end
|
224
|
+
class NoParent < BadParent; end
|
225
|
+
|
226
|
+
###
|
227
|
+
### The Node's parent.
|
228
|
+
###
|
229
|
+
attr_accessor :parent
|
230
|
+
private :parent=
|
231
|
+
###
|
232
|
+
### The position in the source file the construct this node
|
233
|
+
### represents appears at.
|
234
|
+
###
|
235
|
+
attr_accessor :pos
|
236
|
+
###
|
237
|
+
### Return the sibling Node that comes after this in preorder
|
238
|
+
### sequence.
|
239
|
+
###
|
240
|
+
### Raises NoParent if there's no parent.
|
241
|
+
###
|
242
|
+
def next
|
243
|
+
@parent or raise NoParent
|
244
|
+
return @parent.node_after(self)
|
245
|
+
end
|
246
|
+
###
|
247
|
+
### Return the sibling Node that comes after this in the parent
|
248
|
+
### NodeList.
|
249
|
+
###
|
250
|
+
### Raises:
|
251
|
+
### -- NoParent if there's no parent
|
252
|
+
### -- BadParent if the parent is otherwise not a NodeList
|
253
|
+
###
|
254
|
+
def list_next
|
255
|
+
@parent or raise NoParent
|
256
|
+
@parent.NodeList? or raise BadParent
|
257
|
+
return @parent.node_after(self)
|
258
|
+
end
|
259
|
+
###
|
260
|
+
### Return the sibling Node that comes before this in preorder
|
261
|
+
### sequence.
|
262
|
+
###
|
263
|
+
### Raises NoParent if there's no parent.
|
264
|
+
###
|
265
|
+
def prev
|
266
|
+
@parent or raise NoParent
|
267
|
+
return @parent.node_before(self)
|
268
|
+
end
|
269
|
+
###
|
270
|
+
### Return the sibling Node that comes before this in the parent
|
271
|
+
### NodeList.
|
272
|
+
###
|
273
|
+
### Raises:
|
274
|
+
### -- NoParent if there's no parent
|
275
|
+
### -- BadParent if the parent is otherwise not a NodeList
|
276
|
+
###
|
277
|
+
def list_prev
|
278
|
+
@parent or raise NoParent
|
279
|
+
@parent.NodeList? or raise BadParent
|
280
|
+
return @parent.node_before(self)
|
281
|
+
end
|
282
|
+
###
|
283
|
+
### Detach this Node from the tree and return it.
|
284
|
+
###
|
285
|
+
### Raises NoParent if there's no parent.
|
286
|
+
###
|
287
|
+
def detach
|
288
|
+
@parent or raise NoParent
|
289
|
+
@parent.remove_node(self)
|
290
|
+
return self
|
291
|
+
end
|
292
|
+
###
|
293
|
+
### Replace this Node in the tree with the given node(s). Return
|
294
|
+
### this node.
|
295
|
+
###
|
296
|
+
### Raises:
|
297
|
+
### -- NoParent if there's no parent
|
298
|
+
### -- BadParent if the parent is otherwise not a NodeList, and
|
299
|
+
### more than one node is given.
|
300
|
+
###
|
301
|
+
def replace_with *nodes
|
302
|
+
@parent or raise NoParent
|
303
|
+
@parent.replace_node(self, *nodes)
|
304
|
+
return self
|
305
|
+
end
|
306
|
+
###
|
307
|
+
### Swap this node with `node' in their trees. If either node is
|
308
|
+
### detached, the other will become detached as a result of
|
309
|
+
### calling this method.
|
310
|
+
###
|
311
|
+
def swap_with node
|
312
|
+
return self if node.equal? self
|
313
|
+
if self.attached?
|
314
|
+
if node.attached?
|
315
|
+
## both attached -- use placeholder
|
316
|
+
placeholder = Default.new
|
317
|
+
my_parent = @parent
|
318
|
+
my_parent.replace_node(self, placeholder)
|
319
|
+
node.parent.replace_node(node, self)
|
320
|
+
my_parent.replace_node(placeholder, node)
|
321
|
+
else
|
322
|
+
## only `self' attached
|
323
|
+
@parent.replace_node(self, node)
|
324
|
+
end
|
325
|
+
else
|
326
|
+
if node.attached?
|
327
|
+
## only `node' attached
|
328
|
+
node.parent.replace_node(node, self)
|
329
|
+
else
|
330
|
+
## neither attached -- nothing to do
|
331
|
+
end
|
332
|
+
end
|
333
|
+
return self
|
334
|
+
end
|
335
|
+
###
|
336
|
+
### Insert `newnodes' before this node. Return this node.
|
337
|
+
###
|
338
|
+
### Raises:
|
339
|
+
### -- NoParent if there's no parent
|
340
|
+
### -- BadParent if the parent is otherwise not a NodeList
|
341
|
+
###
|
342
|
+
def insert_prev *newnodes
|
343
|
+
@parent or raise NoParent
|
344
|
+
@parent.NodeList? or raise BadParent
|
345
|
+
@parent.insert_before(self, *newnodes)
|
346
|
+
return self
|
347
|
+
end
|
348
|
+
###
|
349
|
+
### Insert `newnodes' after this node. Return this node.
|
350
|
+
###
|
351
|
+
### Raises:
|
352
|
+
### -- NoParent if there's no parent
|
353
|
+
### -- BadParent if the parent is otherwise not a NodeList
|
354
|
+
###
|
355
|
+
def insert_next *newnodes
|
356
|
+
@parent or raise NoParent
|
357
|
+
@parent.NodeList? or raise BadParent
|
358
|
+
@parent.insert_after(self, *newnodes)
|
359
|
+
return self
|
360
|
+
end
|
361
|
+
###
|
362
|
+
### Return true if this Node is detached (i.e., #parent is nil),
|
363
|
+
### false otherwise.
|
364
|
+
###
|
365
|
+
### This is equal to !attached?
|
366
|
+
###
|
367
|
+
def detached?
|
368
|
+
@parent.nil?
|
369
|
+
end
|
370
|
+
###
|
371
|
+
### Return true if this Node is attached (i.e., #parent is
|
372
|
+
### nonnil), false otherwise.
|
373
|
+
###
|
374
|
+
### This is equal to !detached?
|
375
|
+
###
|
376
|
+
def attached?
|
377
|
+
!@parent.nil?
|
378
|
+
end
|
379
|
+
|
380
|
+
###
|
381
|
+
### --------------------------------------------------------------
|
382
|
+
### Subclass management
|
383
|
+
### --------------------------------------------------------------
|
384
|
+
###
|
385
|
+
|
386
|
+
###
|
387
|
+
### The direct subclasses of this class (an Array of Class).
|
388
|
+
###
|
389
|
+
attr_reader :subclasses
|
390
|
+
###
|
391
|
+
### Return all classes which have this class somewhere in its
|
392
|
+
### ancestry (an Array of Class).
|
393
|
+
###
|
394
|
+
def self.subclasses_recursive
|
395
|
+
ret = @subclasses.dup
|
396
|
+
@subclasses.each{|c| ret.concat(c.subclasses_recursive)}
|
397
|
+
return ret
|
398
|
+
end
|
399
|
+
###
|
400
|
+
### Callback defined in Class.
|
401
|
+
###
|
402
|
+
def self.inherited klass
|
403
|
+
@subclasses << klass
|
404
|
+
klass.instance_variable_set(:@subclasses, [])
|
405
|
+
klass.instance_variable_set(:@fields , [])
|
406
|
+
end
|
407
|
+
###
|
408
|
+
### Declare this class as abstract.
|
409
|
+
###
|
410
|
+
def self.abstract
|
411
|
+
end
|
412
|
+
|
413
|
+
## set the instance vars for Node
|
414
|
+
@subclasses = []
|
415
|
+
@fields = []
|
416
|
+
|
417
|
+
###
|
418
|
+
### --------------------------------------------------------------
|
419
|
+
###
|
420
|
+
### Fields
|
421
|
+
###
|
422
|
+
### Fields are interesting attributes, that are, e.g., compared in
|
423
|
+
### `==', and copied in `dup' and `clone'. "Child" fields are
|
424
|
+
### also yielded in a traversal. For each field, a setter and
|
425
|
+
### getter is created, and the corresponding instance variable is
|
426
|
+
### set in `initialize'.
|
427
|
+
###
|
428
|
+
### Child fields are declared using Node.child; other fields are
|
429
|
+
### declared using Node.field.
|
430
|
+
###
|
431
|
+
### --------------------------------------------------------------
|
432
|
+
###
|
433
|
+
|
434
|
+
private # -------------------------------------------------------
|
435
|
+
|
436
|
+
###
|
437
|
+
### Add the Field `newfield' to the list of fields for this class.
|
438
|
+
###
|
439
|
+
def self.add_field newfield
|
440
|
+
## add the newfield to @fields, and set the index
|
441
|
+
fields = @fields
|
442
|
+
newfield.index = fields.length
|
443
|
+
fields << newfield
|
444
|
+
## getter
|
445
|
+
define_method(newfield.reader) do
|
446
|
+
instance_variable_get(newfield.var)
|
447
|
+
end
|
448
|
+
## setter
|
449
|
+
if newfield.child?
|
450
|
+
define_method(newfield.writer) do |val|
|
451
|
+
old = send(newfield.reader)
|
452
|
+
return if val.equal? old
|
453
|
+
## detach the old Node
|
454
|
+
old = self.send(newfield.reader)
|
455
|
+
unless old.nil?
|
456
|
+
old.instance_variable_set(:@parent, nil)
|
457
|
+
end
|
458
|
+
## copy val if needed
|
459
|
+
val = val.clone if !val.nil? && val.attached?
|
460
|
+
## set
|
461
|
+
self.instance_variable_set(newfield.var, val)
|
462
|
+
## attach the new Node
|
463
|
+
unless val.nil?
|
464
|
+
val.instance_variable_set(:@parent, self)
|
465
|
+
val.instance_variable_set(:@parent_field, newfield)
|
466
|
+
end
|
467
|
+
end
|
468
|
+
else
|
469
|
+
define_method(newfield.writer) do |val|
|
470
|
+
instance_variable_set(newfield.var, val)
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
def self.fields
|
475
|
+
@fields
|
476
|
+
end
|
477
|
+
###
|
478
|
+
### The args are the fields/children in the order they were
|
479
|
+
### declared, all optional. If the last argument is a hash, field
|
480
|
+
### and child values are looked up in there. If a value is given
|
481
|
+
### by both argument and hash, the behaviour is undefined.
|
482
|
+
###
|
483
|
+
|
484
|
+
###
|
485
|
+
### Define an initialize method. The initialize method sets the
|
486
|
+
### fields named in `syms' from the arguments given to it. The
|
487
|
+
### initialize method also takes named parameters (i.e., an
|
488
|
+
### optional Hash as the last argument), which may be used to set
|
489
|
+
### fields not even named in `syms'. The syms in the optional
|
490
|
+
### Hash are the values of the `init_key' members of the
|
491
|
+
### corresponding Field objects.
|
492
|
+
###
|
493
|
+
### As an example for this Node class:
|
494
|
+
###
|
495
|
+
### class X < Node
|
496
|
+
### field :x
|
497
|
+
### field :y
|
498
|
+
### child :z
|
499
|
+
### initializer :z, :y
|
500
|
+
### end
|
501
|
+
###
|
502
|
+
### ...X.new can be called in any of these ways:
|
503
|
+
###
|
504
|
+
### X.new # all fields set to default
|
505
|
+
### X.new(1) # .z = 1
|
506
|
+
### X.new(1, 2) # .z = 1, .y = 2
|
507
|
+
### X.new(:x = 1, :y => 2, :z => 3) # .x = 1, .y = 2, .z = 3
|
508
|
+
### X.new(1, :x => 2) # .z = 1, .x = 2
|
509
|
+
### X.new(1, :z => 2) # undefined behaviour!
|
510
|
+
### ...etc.
|
511
|
+
###
|
512
|
+
def self.initializer *syms
|
513
|
+
define_method(:initialize) do |*args|
|
514
|
+
## pop off the opts hash
|
515
|
+
opts = args.last.is_a?(::Hash) ? args.pop : {}
|
516
|
+
|
517
|
+
## add positional args to opts
|
518
|
+
args.each_with_index do |arg, i|
|
519
|
+
opts[syms[i]] = arg
|
520
|
+
end
|
521
|
+
|
522
|
+
## set field values
|
523
|
+
fields.each do |field|
|
524
|
+
key = field.init_key
|
525
|
+
if opts.key?(key)
|
526
|
+
send(field.writer, opts[key])
|
527
|
+
else
|
528
|
+
send(field.writer, field.make_default)
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
## pos, parent
|
533
|
+
@pos = nil
|
534
|
+
@parent = nil
|
535
|
+
end
|
536
|
+
end
|
537
|
+
|
538
|
+
public # --------------------------------------------------------
|
539
|
+
|
540
|
+
###
|
541
|
+
### Declare a field with the given name and default value.
|
542
|
+
###
|
543
|
+
def self.field name, default=:'no default'
|
544
|
+
if default == :'no default'
|
545
|
+
if name.to_s[-1] == ??
|
546
|
+
default = false
|
547
|
+
else
|
548
|
+
default = nil
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
## if the field exists, just update its default, otherwise, add
|
553
|
+
## a new field
|
554
|
+
self.fields.each do |field|
|
555
|
+
if field.reader == name
|
556
|
+
field.default = default
|
557
|
+
return
|
558
|
+
end
|
559
|
+
end
|
560
|
+
add_field Field.new(name, default)
|
561
|
+
end
|
562
|
+
###
|
563
|
+
### Declare a child with the given name and default value. The
|
564
|
+
### default value is cloned when used (unless cloning is
|
565
|
+
### unnecessary).
|
566
|
+
###
|
567
|
+
def self.child name, default=nil
|
568
|
+
field = Field.new(name, default)
|
569
|
+
field.child = true
|
570
|
+
add_field field
|
571
|
+
end
|
572
|
+
###
|
573
|
+
### Return the list of fields this object has. Don't modify the
|
574
|
+
### returned array!
|
575
|
+
###
|
576
|
+
def fields
|
577
|
+
self.class.fields
|
578
|
+
end
|
579
|
+
|
580
|
+
def method_missing meth, *args, &blk
|
581
|
+
## respond to `Module?'
|
582
|
+
methstr = meth.to_s
|
583
|
+
if methstr =~ /^([A-Z].*)\?$/ && C.const_defined?($1)
|
584
|
+
klass = C.const_get($1)
|
585
|
+
if klass.is_a?(::Module)
|
586
|
+
return self.is_a?(klass)
|
587
|
+
end
|
588
|
+
end
|
589
|
+
|
590
|
+
begin
|
591
|
+
super
|
592
|
+
rescue NoMethodError => e
|
593
|
+
e.set_backtrace(caller)
|
594
|
+
raise e
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
###
|
599
|
+
### ==============================================================
|
600
|
+
###
|
601
|
+
### Child management
|
602
|
+
###
|
603
|
+
### ==============================================================
|
604
|
+
###
|
605
|
+
|
606
|
+
public # --------------------------------------------------------
|
607
|
+
|
608
|
+
###
|
609
|
+
### Return the Node that comes after the given Node in tree
|
610
|
+
### preorder.
|
611
|
+
###
|
612
|
+
def node_after node
|
613
|
+
node.parent.equal? self or
|
614
|
+
raise ArgumentError, "node is not a child"
|
615
|
+
fields = self.fields
|
616
|
+
i = node.instance_variable_get(:@parent_field).index + 1
|
617
|
+
(i...fields.length).each do |i|
|
618
|
+
f = fields[i]
|
619
|
+
if f.child? && (val = self.send(f.reader))
|
620
|
+
return val
|
621
|
+
end
|
622
|
+
end
|
623
|
+
return nil
|
624
|
+
end
|
625
|
+
###
|
626
|
+
### Return the Node that comes before the given Node in tree
|
627
|
+
### preorder.
|
628
|
+
###
|
629
|
+
def node_before node
|
630
|
+
node.parent.equal? self or
|
631
|
+
raise ArgumentError, "node is not a child"
|
632
|
+
fields = self.fields
|
633
|
+
i = node.instance_variable_get(:@parent_field).index - 1
|
634
|
+
i.downto(0) do |i|
|
635
|
+
f = fields[i]
|
636
|
+
if f.child? && (val = self.send(f.reader))
|
637
|
+
return val
|
638
|
+
end
|
639
|
+
end
|
640
|
+
return nil
|
641
|
+
end
|
642
|
+
###
|
643
|
+
### Remove the given Node.
|
644
|
+
###
|
645
|
+
def remove_node node
|
646
|
+
node.parent.equal? self or
|
647
|
+
raise ArgumentError, "node is not a child"
|
648
|
+
field = node.instance_variable_get(:@parent_field)
|
649
|
+
node.instance_variable_set(:@parent, nil)
|
650
|
+
node.instance_variable_set(:@parent_field, nil)
|
651
|
+
self.instance_variable_set(field.var, nil)
|
652
|
+
return self
|
653
|
+
end
|
654
|
+
###
|
655
|
+
### Replace `node' with `newnode'.
|
656
|
+
###
|
657
|
+
def replace_node node, newnode=nil
|
658
|
+
node.parent.equal? self or
|
659
|
+
raise ArgumentError, "node is not a child"
|
660
|
+
field = node.instance_variable_get(:@parent_field)
|
661
|
+
self.send(field.writer, newnode)
|
662
|
+
return self
|
663
|
+
end
|
664
|
+
|
665
|
+
###
|
666
|
+
### ==============================================================
|
667
|
+
###
|
668
|
+
### Node::Field
|
669
|
+
###
|
670
|
+
### ==============================================================
|
671
|
+
###
|
672
|
+
|
673
|
+
private # -------------------------------------------------------
|
674
|
+
|
675
|
+
class Field
|
676
|
+
attr_accessor :var, :reader, :writer, :init_key, :index,
|
677
|
+
:default
|
678
|
+
|
679
|
+
###
|
680
|
+
### True if this field is a child field, false otherwise.
|
681
|
+
###
|
682
|
+
attr_writer :child
|
683
|
+
def child?
|
684
|
+
@child
|
685
|
+
end
|
686
|
+
###
|
687
|
+
### Create a default value for this field. This differs from
|
688
|
+
### #default in that if it's a Proc, it is called and the result
|
689
|
+
### returned.
|
690
|
+
###
|
691
|
+
def make_default
|
692
|
+
if @default.respond_to? :call
|
693
|
+
@default.call
|
694
|
+
else
|
695
|
+
@default
|
696
|
+
end
|
697
|
+
end
|
698
|
+
|
699
|
+
def initialize name, default
|
700
|
+
name = name.to_s
|
701
|
+
|
702
|
+
@child = false
|
703
|
+
@reader = name.to_sym
|
704
|
+
@default = default
|
705
|
+
|
706
|
+
if name[-1] == ?? then name[-1] = '' end
|
707
|
+
@init_key = name.to_sym
|
708
|
+
@var = "@#{name}".to_sym
|
709
|
+
@writer = "#{name}=".to_sym
|
710
|
+
end
|
711
|
+
end
|
712
|
+
|
713
|
+
###
|
714
|
+
### ==============================================================
|
715
|
+
###
|
716
|
+
### Node::Pos
|
717
|
+
###
|
718
|
+
### A position in a source file. All Nodes may have one in
|
719
|
+
### their #pos attribute.
|
720
|
+
###
|
721
|
+
### ==============================================================
|
722
|
+
###
|
723
|
+
|
724
|
+
public # -------------------------------------------------------
|
725
|
+
|
726
|
+
class Pos
|
727
|
+
attr_accessor :filename, :line_num, :col_num
|
728
|
+
def initialize filename, line_num, col_num
|
729
|
+
@filename = filename
|
730
|
+
@line_num = line_num
|
731
|
+
@col_num = col_num
|
732
|
+
end
|
733
|
+
def to_s
|
734
|
+
(@filename ? "#@filename:" : '') << "#@line_num:#@col_num"
|
735
|
+
end
|
736
|
+
def <=> x
|
737
|
+
return nil if self.filename != x.filename
|
738
|
+
return (self.line_num <=> x.line_num).nonzero? ||
|
739
|
+
self.col_num <=> x.col_num
|
740
|
+
end
|
741
|
+
include Comparable
|
742
|
+
end
|
743
|
+
end
|
744
|
+
end
|