cast 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|