rubinius-ast 1.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.
@@ -0,0 +1,389 @@
1
+ # -*- encoding: us-ascii -*-
2
+
3
+ module Rubinius::ToolSet.current::TS
4
+ module AST
5
+ class Node
6
+ attr_accessor :line
7
+
8
+ def self.transform(category, name, comment)
9
+ Transforms.register category, name, self
10
+ @transform_name = name
11
+ @transform_comment = comment
12
+ @transform_kind = :call
13
+ end
14
+
15
+ def self.transform_name
16
+ @transform_name
17
+ end
18
+
19
+ def self.transform_comment
20
+ @transform_comment
21
+ end
22
+
23
+ def self.transform_kind
24
+ @transform_kind
25
+ end
26
+
27
+ def self.transform_kind=(k)
28
+ @transform_kind = k
29
+ end
30
+
31
+ def self.match_send?(node, receiver, method, name)
32
+ node.kind_of? ConstantAccess and
33
+ node.name == receiver and
34
+ method == name
35
+ end
36
+
37
+ def self.match_arguments?(arguments, count)
38
+ case arguments
39
+ when ArrayLiteral
40
+ arguments.body.size == count
41
+ when nil
42
+ count == 0
43
+ else
44
+ false
45
+ end
46
+ end
47
+
48
+ def initialize(line)
49
+ @line = line
50
+ end
51
+
52
+ def pos(g)
53
+ g.set_line @line
54
+ end
55
+
56
+ def new_block_generator(g, arguments)
57
+ blk = g.class.new
58
+ blk.name = g.state.name || :__block__
59
+ blk.file = g.file
60
+ blk.for_block = true
61
+
62
+ blk.required_args = arguments.required_args
63
+ blk.post_args = arguments.post_args
64
+ blk.total_args = arguments.total_args
65
+ blk.splat_index = arguments.splat_index
66
+ blk.block_index = arguments.block_index
67
+
68
+ blk
69
+ end
70
+
71
+ def new_generator(g, name, arguments=nil)
72
+ meth = g.class.new
73
+ meth.name = name
74
+ meth.file = g.file
75
+
76
+ if arguments
77
+ meth.required_args = arguments.required_args
78
+ meth.post_args = arguments.post_args
79
+ meth.total_args = arguments.total_args
80
+ meth.splat_index = arguments.splat_index
81
+ meth.block_index = arguments.block_index
82
+ end
83
+
84
+ meth
85
+ end
86
+
87
+ def bytecode(g)
88
+ end
89
+
90
+ def defined(g)
91
+ g.push_rubinius
92
+ g.push_scope
93
+ g.send :active_path, 0
94
+ g.push @line
95
+ g.send :unrecognized_defined, 2
96
+ g.pop
97
+ g.push :nil
98
+ end
99
+
100
+ def value_defined(g, f)
101
+ bytecode(g)
102
+ end
103
+
104
+ # This method implements a sort of tree iterator, yielding each Node
105
+ # instance to the provided block with the first argument to #walk. If
106
+ # the block returns a non-true value, the walk is terminated.
107
+ #
108
+ # This method is really an iterator, not a Visitor pattern.
109
+ def walk(arg=true, &block)
110
+ children do |child|
111
+ if ch_arg = block.call(arg, child)
112
+ child.walk(ch_arg, &block)
113
+ end
114
+ end
115
+ end
116
+
117
+ def ascii_graph
118
+ AsciiGrapher.new(self).print
119
+ end
120
+
121
+ # Called if used as the lhs of an ||=. Expected to yield if the
122
+ # value was not found, so the bytecode for it to be emitted.
123
+ def or_bytecode(g)
124
+ found = g.new_label
125
+ bytecode(g)
126
+ g.dup
127
+ g.git found
128
+ g.pop
129
+ yield
130
+ found.set!
131
+ end
132
+
133
+ def to_sexp
134
+ [:node, self.class.name]
135
+ end
136
+
137
+ # Yields each child of this Node to the block. Additionally, for any
138
+ # attribute that is an Array, yields each element that is a Node.
139
+ def children
140
+ instance_variables.each do |var|
141
+ child = instance_variable_get var
142
+ if child.kind_of? Node
143
+ yield child
144
+ elsif child.kind_of? Array
145
+ child.each { |x| yield x if x.kind_of? Node }
146
+ end
147
+ end
148
+ end
149
+
150
+ # The equivalent of Some::Module.demodulize.underscore in ActiveSupport.
151
+ # The code is shamelessly borrowed as well.
152
+ def node_name
153
+ name = self.class.name.gsub(/^.*::/, '')
154
+ name.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
155
+ name.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
156
+ name.downcase!
157
+ name
158
+ end
159
+
160
+ # Supports the Visitor pattern on a tree of Nodes. The +visitor+ should
161
+ # be an object that responds to methods named after the Node subclasses.
162
+ # The method called is determined by the #node_name method. Passes both
163
+ # the node and its parent so that the visitor can maintain nesting
164
+ # information if desired.
165
+ #
166
+ # The #visit implements a read-only traversal of the tree. To modify the
167
+ # tree, see the #transform methed.
168
+ def visit(visitor, parent=nil)
169
+ visitor.__send__ self.node_name, self, parent
170
+ children { |c| c.visit visitor, self }
171
+ end
172
+
173
+ # Called by #transform to update the child of a Node instance. The
174
+ # default just calls the attr_accessor for the child. However, Node
175
+ # subclasses that must synchronize other internal state can override
176
+ # this method.
177
+ def set_child(name, node)
178
+ send :"#{name}=", node
179
+ end
180
+
181
+ # Yields each attribute and its name to the block.
182
+ def attributes
183
+ instance_variables.each do |var|
184
+ child = instance_variable_get var
185
+ name = var.to_s[1..-1]
186
+ yield child, name
187
+ end
188
+ end
189
+
190
+ # A fixed-point algorithm for transforming an AST with a visitor. The
191
+ # traversal is top-down. The visitor object's method corresponding to
192
+ # each node (see #node_name) is called for each node, passing the node
193
+ # and its parent.
194
+ #
195
+ # To replace the node in the tree, the visitor method should return a
196
+ # new node; otherwise, return the existing node. The visitor is free to
197
+ # change values in the node, but substituting a node causes the entire
198
+ # tree to be walked repeatedly until no modifications are made.
199
+ def transform(visitor, parent=nil, state=nil)
200
+ state ||= TransformState.new
201
+
202
+ node = visitor.send :"node_#{node_name}", self, parent
203
+ state.modify unless equal? node
204
+
205
+ node.attributes do |attr, name|
206
+ if attr.kind_of? Node
207
+ child = attr.transform visitor, node, state
208
+ unless attr.equal? child
209
+ state.modify
210
+ node.set_child name, child
211
+ end
212
+ elsif attr.kind_of? Array
213
+ attr.each_with_index do |x, i|
214
+ if x.kind_of? Node
215
+ child = x.transform visitor, node, state
216
+ unless x.equal? child
217
+ state.modify
218
+ attr[i] = child
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
224
+
225
+ # Repeat the walk until the tree is not modified.
226
+ if parent.nil? and state.modified?
227
+ state.unmodify
228
+ node = transform visitor, nil, state
229
+ end
230
+
231
+ node
232
+ end
233
+
234
+ # Manage the state of the #transform method.
235
+ class TransformState
236
+ def initialized
237
+ @modified = false
238
+ end
239
+
240
+ def modified?
241
+ @modified
242
+ end
243
+
244
+ def modify
245
+ @modified = true
246
+ end
247
+
248
+ def unmodify
249
+ @modified = false
250
+ end
251
+ end
252
+ end
253
+
254
+ # In a perfect world, each AST node would fully encapsulate its state. But
255
+ # in the real world, some state exists across the AST rather than just in
256
+ # a node. For example, some nodes need to emit different bytecode when in
257
+ # a rescue.
258
+ #
259
+ # This class maintains state needed as the AST is walked to generate
260
+ # bytecode. An instance of State is pushed onto a stack in the Generator
261
+ # instance as each ClosedScope or Iter is entered, and popped when left.
262
+ class State
263
+ attr_reader :scope, :super, :eval
264
+ attr_accessor :check_for_locals
265
+
266
+ class << self
267
+ attr_accessor :flip_flops
268
+ end
269
+
270
+ self.flip_flops ||= 0
271
+
272
+ def initialize(scope)
273
+ @scope = scope
274
+ @ensure = 0
275
+ @block = 0
276
+ @masgn = 0
277
+ @loop = 0
278
+ @op_asgn = 0
279
+ @rescue = []
280
+ @name = []
281
+ @check_for_locals = true
282
+ end
283
+
284
+ def push_name(name)
285
+ @name.push name
286
+ end
287
+
288
+ def pop_name
289
+ @name.pop
290
+ end
291
+
292
+ def name
293
+ @name.last
294
+ end
295
+
296
+ def push_rescue(val)
297
+ @rescue.push(val)
298
+ end
299
+
300
+ def pop_rescue
301
+ @rescue.pop if rescue?
302
+ end
303
+
304
+ def rescue?
305
+ @rescue.last
306
+ end
307
+
308
+ def push_ensure
309
+ @ensure += 1
310
+ end
311
+
312
+ def pop_ensure
313
+ @ensure -= 1 if ensure?
314
+ end
315
+
316
+ def ensure?
317
+ @ensure > 0
318
+ end
319
+
320
+ def push_block
321
+ @block += 1
322
+ end
323
+
324
+ def pop_block
325
+ @block -= 1 if block?
326
+ end
327
+
328
+ def block?
329
+ @block > 0
330
+ end
331
+
332
+ def flip_flops
333
+ State.flip_flops
334
+ end
335
+
336
+ def push_flip_flop
337
+ State.flip_flops += 1
338
+ end
339
+
340
+ def push_masgn
341
+ @masgn += 1
342
+ end
343
+
344
+ def pop_masgn
345
+ @masgn -= 1 if masgn?
346
+ end
347
+
348
+ def masgn?
349
+ @masgn > 0
350
+ end
351
+
352
+ def push_op_asgn
353
+ @op_asgn += 1
354
+ end
355
+
356
+ def pop_op_asgn
357
+ @op_asgn -= 1 if op_asgn?
358
+ end
359
+
360
+ def op_asgn?
361
+ @op_asgn > 0
362
+ end
363
+
364
+ def push_super(scope)
365
+ @super = scope
366
+ end
367
+
368
+ alias_method :super?, :super
369
+
370
+ def push_eval(scope)
371
+ @eval = scope
372
+ end
373
+
374
+ alias_method :eval?, :eval
375
+
376
+ def push_loop
377
+ @loop += 1
378
+ end
379
+
380
+ def pop_loop
381
+ @loop -= 1 if loop?
382
+ end
383
+
384
+ def loop?
385
+ @loop > 0
386
+ end
387
+ end
388
+ end
389
+ end
@@ -0,0 +1,394 @@
1
+ # -*- encoding: us-ascii -*-
2
+
3
+ module Rubinius::ToolSet.current::TS
4
+ module AST
5
+ class And < Node
6
+ attr_accessor :left, :right
7
+
8
+ def initialize(line, left, right)
9
+ @line = line
10
+ @left = left
11
+ @right = right
12
+ end
13
+
14
+ def bytecode(g, use_gif=true)
15
+ @left.bytecode(g)
16
+ g.dup
17
+ lbl = g.new_label
18
+
19
+ if use_gif
20
+ g.gif lbl
21
+ else
22
+ g.git lbl
23
+ end
24
+
25
+ g.pop
26
+ @right.bytecode(g)
27
+ lbl.set!
28
+ end
29
+
30
+ def defined(g)
31
+ t = g.new_label
32
+ f = g.new_label
33
+ done = g.new_label
34
+
35
+ case @left
36
+ when GlobalVariableAccess, InstanceVariableAccess
37
+ g.goto t
38
+ else
39
+ @left.value_defined(g, f)
40
+ g.pop
41
+ end
42
+
43
+ case @right
44
+ when GlobalVariableAccess, InstanceVariableAccess
45
+ g.goto t
46
+ else
47
+ @right.value_defined(g, f)
48
+ g.pop
49
+ end
50
+
51
+ t.set!
52
+ g.push_literal "expression"
53
+ g.string_dup
54
+ g.goto done
55
+
56
+ f.set!
57
+ g.push :nil
58
+
59
+ done.set!
60
+ end
61
+
62
+ def sexp_name
63
+ :and
64
+ end
65
+
66
+ def to_sexp
67
+ [sexp_name, @left.to_sexp, @right.to_sexp]
68
+ end
69
+ end
70
+
71
+ class Or < And
72
+ def bytecode(g)
73
+ super(g, false)
74
+ end
75
+
76
+ def sexp_name
77
+ :or
78
+ end
79
+ end
80
+
81
+ class Not < Node
82
+ attr_accessor :value
83
+
84
+ def initialize(line, value)
85
+ @line = line
86
+ @value = value
87
+ end
88
+
89
+ def bytecode(g)
90
+ true_label = g.new_label
91
+ end_label = g.new_label
92
+
93
+ @value.bytecode(g)
94
+ g.git true_label
95
+
96
+ g.push :true
97
+ g.goto end_label
98
+
99
+ true_label.set!
100
+ g.push :false
101
+ end_label.set!
102
+ end
103
+
104
+ def defined(g)
105
+ t = g.new_label
106
+ f = g.new_label
107
+ done = g.new_label
108
+
109
+ case @value
110
+ when GlobalVariableAccess, InstanceVariableAccess
111
+ g.goto t
112
+ else
113
+ @value.value_defined(g, f)
114
+ g.pop
115
+ end
116
+
117
+ t.set!
118
+ g.push_literal "expression"
119
+ g.goto done
120
+
121
+ f.set!
122
+ g.push :nil
123
+
124
+ done.set!
125
+ end
126
+
127
+ def to_sexp
128
+ [:not, @value.to_sexp]
129
+ end
130
+ end
131
+
132
+ class OpAssign1 < Node
133
+ attr_accessor :receiver, :op, :arguments, :value
134
+
135
+ def initialize(line, receiver, arguments, op, value)
136
+ @line = line
137
+ @receiver = receiver
138
+ @op = op
139
+ arguments = nil if arguments.is_a?(EmptyArray)
140
+ @arguments = ActualArguments.new line, arguments
141
+ @value = value
142
+ end
143
+
144
+ def bytecode(g)
145
+ pos(g)
146
+
147
+ # X: Snippet used for explanation: h[:a] += 3
148
+ # X: given h = { :a => 2 }
149
+ # X: Pull h onto the stack
150
+ @receiver.bytecode(g)
151
+ # X: Pull :a in
152
+ @arguments.bytecode(g)
153
+ recv_stack = @arguments.stack_size + 1
154
+
155
+ # Dup the receiver and arguments to use later
156
+ g.dup_many recv_stack
157
+
158
+ #
159
+ # X: Call [](:a) on h
160
+ #
161
+ # @arguments.size will be 1
162
+
163
+ if @arguments.splat?
164
+ g.push :nil
165
+ g.send_with_splat :[], @arguments.size
166
+ else
167
+ g.send :[], @arguments.size
168
+ end
169
+
170
+ # X: 2 is now on the top of the stack (TOS)
171
+
172
+ # A special case, where we use the value as boolean
173
+ if @op == :or or @op == :and
174
+ fnd = g.new_label
175
+ fin = g.new_label
176
+
177
+ # We dup the value from [] to leave it as the value of the
178
+ # expression
179
+
180
+ g.dup
181
+ if @op == :or
182
+ g.git fnd
183
+ else
184
+ g.gif fnd
185
+ end
186
+
187
+ # Ok, take the extra copy off and pull the value onto the stack
188
+ g.pop
189
+
190
+ # The receiver and arguments are still on the stack
191
+
192
+ @value.bytecode(g)
193
+
194
+ # retain the rhs as the expression value
195
+ g.dup
196
+ g.move_down recv_stack + 1
197
+
198
+ if @arguments.splat?
199
+ g.send :push, 1
200
+ g.push :nil
201
+ g.send_with_splat :[]=, @arguments.size
202
+ else
203
+ g.send :[]=, @arguments.size + 1
204
+ end
205
+ g.pop
206
+
207
+ # Leaves the value we moved down the stack on the top
208
+ g.goto fin
209
+
210
+ fnd.set!
211
+
212
+ # Clean up the stack but retain return value from :[]
213
+ g.move_down recv_stack
214
+ g.pop_many recv_stack
215
+
216
+ fin.set!
217
+ else
218
+ # @op is something like + or -
219
+ # We pull in @value to the stack
220
+ @value.bytecode(g)
221
+ # X: 3 TOS
222
+
223
+ # ... then call it as an argument to @or, called on the return
224
+ # from [].
225
+ # X: 2 + 3
226
+
227
+ g.send @op, 1
228
+ # X: 5 TOS
229
+
230
+ # The new value is on the stack now. It is the last argument to the call
231
+ # to []= because your dupd versions of recv and arguments are still on the stack.
232
+
233
+ # retain the rhs as the expression value
234
+ g.dup
235
+ g.move_down recv_stack + 1
236
+
237
+ # X: Call []=(:a, 5) on h
238
+ if @arguments.splat?
239
+ g.send :push, 1
240
+ g.push :nil
241
+ g.send_with_splat :[]=, @arguments.size
242
+ else
243
+ g.send :[]=, @arguments.size + 1
244
+ end
245
+ g.pop
246
+ end
247
+ end
248
+
249
+ def to_sexp
250
+ arguments = [:arglist] + @arguments.to_sexp
251
+ op = @op == :or ? :"||" : :"&&"
252
+ [:op_asgn1, @receiver.to_sexp, arguments, op, @value.to_sexp]
253
+ end
254
+ end
255
+
256
+ class OpAssign2 < Node
257
+ attr_accessor :receiver, :name, :assign, :op, :value
258
+
259
+ def initialize(line, receiver, name, op, value)
260
+ @line = line
261
+ @receiver = receiver
262
+ @name = name
263
+ @op = op
264
+ @value = value
265
+ @assign = name.to_s[-1] == ?= ? name : :"#{name}="
266
+ end
267
+
268
+ def bytecode(g)
269
+ pos(g)
270
+
271
+ # X: h[:a] += 3, given h.a == 2
272
+ @receiver.bytecode(g)
273
+ # X: TOS = h
274
+ g.dup
275
+ g.send @name, 0
276
+ # X: TOS = 2
277
+
278
+ if @op == :or or @op == :and
279
+ fnd = g.new_label
280
+ fin = g.new_label
281
+
282
+ g.dup
283
+ if @op == :or
284
+ g.git fnd
285
+ else
286
+ g.gif fnd
287
+ end
288
+
289
+ # Remove the copy of 2 and push @value on the stack
290
+ g.pop
291
+ @value.bytecode(g)
292
+
293
+ # Retain the this value to use as the expression value
294
+ g.dup
295
+ g.move_down 2
296
+
297
+ # Call the assignement method, passing @value as the argument
298
+ g.send @assign, 1
299
+ g.pop
300
+
301
+ g.goto fin
302
+
303
+ fnd.set!
304
+
305
+ # Clean up the stack
306
+ g.swap
307
+ g.pop
308
+
309
+ fin.set!
310
+ else
311
+ @value.bytecode(g)
312
+ # X: TOS = 3
313
+ # X: 2 + 3
314
+ g.send @op, 1
315
+
316
+ # Retain the this value to use as the expression value
317
+ g.dup
318
+ g.move_down 2
319
+ # X: TOS = 5
320
+ g.send @assign, 1
321
+ # X: TOS = 5 (or whatever a=() returns)
322
+
323
+ # Discard the methods return value
324
+ g.pop
325
+ end
326
+ end
327
+
328
+ def to_sexp
329
+ op = @op == :or ? :"||" : :"&&"
330
+ [:op_asgn2, @receiver.to_sexp, :"#{@name}=", op, @value.to_sexp]
331
+ end
332
+ end
333
+
334
+ class OpAssignAnd < Node
335
+ attr_accessor :left, :right
336
+
337
+ def initialize(line, left, right)
338
+ @line = line
339
+ @left = left
340
+ @right = right
341
+ end
342
+
343
+ def bytecode(g)
344
+ pos(g)
345
+
346
+ @left.bytecode(g)
347
+ lbl = g.new_label
348
+ g.dup
349
+ g.gif lbl
350
+ g.pop
351
+ @right.bytecode(g)
352
+ lbl.set!
353
+ end
354
+
355
+ def defined(g)
356
+ g.push_literal "assignment"
357
+ end
358
+
359
+ def sexp_name
360
+ :op_asgn_and
361
+ end
362
+
363
+ def to_sexp
364
+ [sexp_name, @left.to_sexp, @right.to_sexp]
365
+ end
366
+ end
367
+
368
+ class OpAssignOr < OpAssignAnd
369
+ def bytecode(g)
370
+ pos(g)
371
+
372
+ @left.or_bytecode(g) do
373
+ @right.bytecode(g)
374
+ end
375
+ end
376
+
377
+ def sexp_name
378
+ :op_asgn_or
379
+ end
380
+ end
381
+
382
+ class OpAssignOr19 < OpAssignOr
383
+ def bytecode(g)
384
+ pos(g)
385
+
386
+ g.state.push_op_asgn
387
+ @left.or_bytecode(g) do
388
+ g.state.pop_op_asgn
389
+ @right.bytecode(g)
390
+ end
391
+ end
392
+ end
393
+ end
394
+ end