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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +25 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/rubinius/ast/constants.rb +324 -0
- data/lib/rubinius/ast/control_flow.rb +698 -0
- data/lib/rubinius/ast/data.rb +30 -0
- data/lib/rubinius/ast/definitions.rb +1134 -0
- data/lib/rubinius/ast/encoding.rb +26 -0
- data/lib/rubinius/ast/exceptions.rb +545 -0
- data/lib/rubinius/ast/file.rb +18 -0
- data/lib/rubinius/ast/grapher.rb +89 -0
- data/lib/rubinius/ast/literals.rb +555 -0
- data/lib/rubinius/ast/node.rb +389 -0
- data/lib/rubinius/ast/operators.rb +394 -0
- data/lib/rubinius/ast/self.rb +25 -0
- data/lib/rubinius/ast/sends.rb +1028 -0
- data/lib/rubinius/ast/transforms.rb +371 -0
- data/lib/rubinius/ast/values.rb +182 -0
- data/lib/rubinius/ast/variables.rb +842 -0
- data/lib/rubinius/ast/version.rb +5 -0
- data/lib/rubinius/ast.rb +18 -0
- data/rubinius-ast.gemspec +22 -0
- metadata +96 -0
@@ -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
|