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