Mercurial > hg > graal-compiler
annotate graal/com.oracle.truffle.ruby.parser/src/com/oracle/truffle/ruby/parser/Translator.java @ 13569:1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
author | Michael Van De Vanter <michael.van.de.vanter@oracle.com> |
---|---|
date | Wed, 08 Jan 2014 14:03:36 -0800 |
parents | 0fbee3eb71f0 |
children | d7af2296cebb |
rev | line source |
---|---|
13514 | 1 /* |
13569
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
2 * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This |
13514 | 3 * code is released under a tri EPL/GPL/LGPL license. You can use it, |
4 * redistribute it and/or modify it under the terms of the: | |
5 * | |
6 * Eclipse Public License version 1.0 | |
7 * GNU General Public License version 2 | |
8 * GNU Lesser General Public License version 2.1 | |
9 */ | |
10 package com.oracle.truffle.ruby.parser; | |
11 | |
12 import java.math.*; | |
13 import java.util.*; | |
14 import java.util.regex.*; | |
15 | |
16 import com.oracle.truffle.api.*; | |
17 import com.oracle.truffle.api.frame.*; | |
18 import com.oracle.truffle.api.impl.*; | |
13569
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
19 import com.oracle.truffle.api.nodes.instrument.*; |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
20 import com.oracle.truffle.api.nodes.instrument.InstrumentationProbeNode.ProbeChain; |
13514 | 21 import com.oracle.truffle.ruby.nodes.*; |
22 import com.oracle.truffle.ruby.nodes.call.*; | |
23 import com.oracle.truffle.ruby.nodes.cast.*; | |
24 import com.oracle.truffle.ruby.nodes.constants.*; | |
25 import com.oracle.truffle.ruby.nodes.control.*; | |
26 import com.oracle.truffle.ruby.nodes.core.*; | |
27 import com.oracle.truffle.ruby.nodes.debug.*; | |
28 import com.oracle.truffle.ruby.nodes.literal.*; | |
29 import com.oracle.truffle.ruby.nodes.literal.array.*; | |
30 import com.oracle.truffle.ruby.nodes.methods.*; | |
31 import com.oracle.truffle.ruby.nodes.methods.locals.*; | |
32 import com.oracle.truffle.ruby.nodes.objects.*; | |
33 import com.oracle.truffle.ruby.nodes.objects.instancevariables.*; | |
34 import com.oracle.truffle.ruby.nodes.yield.*; | |
35 import com.oracle.truffle.ruby.runtime.*; | |
36 import com.oracle.truffle.ruby.runtime.core.*; | |
37 import com.oracle.truffle.ruby.runtime.core.range.*; | |
38 import com.oracle.truffle.ruby.runtime.debug.*; | |
39 import com.oracle.truffle.ruby.runtime.methods.*; | |
40 | |
41 /** | |
42 * A JRuby parser node visitor which translates JRuby AST nodes into our Ruby nodes, implementing a | |
43 * Ruby parser. Therefore there is some namespace contention here! We make all references to JRuby | |
44 * explicit. This is the only place though - it doesn't leak out elsewhere. | |
45 */ | |
46 public class Translator implements org.jrubyparser.NodeVisitor { | |
47 | |
48 protected final Translator parent; | |
49 | |
50 protected final RubyContext context; | |
51 protected final TranslatorEnvironment environment; | |
52 protected final Source source; | |
53 | |
54 private boolean translatingForStatement = false; | |
55 | |
56 private static final Map<Class, String> nodeDefinedNames = new HashMap<>(); | |
57 | |
58 static { | |
59 nodeDefinedNames.put(org.jrubyparser.ast.SelfNode.class, "self"); | |
60 nodeDefinedNames.put(org.jrubyparser.ast.NilNode.class, "nil"); | |
61 nodeDefinedNames.put(org.jrubyparser.ast.TrueNode.class, "true"); | |
62 nodeDefinedNames.put(org.jrubyparser.ast.FalseNode.class, "false"); | |
63 nodeDefinedNames.put(org.jrubyparser.ast.LocalAsgnNode.class, "assignment"); | |
64 nodeDefinedNames.put(org.jrubyparser.ast.DAsgnNode.class, "assignment"); | |
65 nodeDefinedNames.put(org.jrubyparser.ast.GlobalAsgnNode.class, "assignment"); | |
66 nodeDefinedNames.put(org.jrubyparser.ast.InstAsgnNode.class, "assignment"); | |
67 nodeDefinedNames.put(org.jrubyparser.ast.ClassVarAsgnNode.class, "assignment"); | |
68 nodeDefinedNames.put(org.jrubyparser.ast.OpAsgnAndNode.class, "assignment"); | |
69 nodeDefinedNames.put(org.jrubyparser.ast.OpAsgnOrNode.class, "assignment"); | |
70 nodeDefinedNames.put(org.jrubyparser.ast.OpAsgnNode.class, "assignment"); | |
71 nodeDefinedNames.put(org.jrubyparser.ast.OpElementAsgnNode.class, "assignment"); | |
72 nodeDefinedNames.put(org.jrubyparser.ast.MultipleAsgnNode.class, "assignment"); | |
73 nodeDefinedNames.put(org.jrubyparser.ast.GlobalVarNode.class, "global-variable"); | |
74 nodeDefinedNames.put(org.jrubyparser.ast.StrNode.class, "expression"); | |
75 nodeDefinedNames.put(org.jrubyparser.ast.DStrNode.class, "expression"); | |
76 nodeDefinedNames.put(org.jrubyparser.ast.FixnumNode.class, "expression"); | |
77 nodeDefinedNames.put(org.jrubyparser.ast.BignumNode.class, "expression"); | |
78 nodeDefinedNames.put(org.jrubyparser.ast.FloatNode.class, "expression"); | |
79 nodeDefinedNames.put(org.jrubyparser.ast.RegexpNode.class, "expression"); | |
80 nodeDefinedNames.put(org.jrubyparser.ast.DRegexpNode.class, "expression"); | |
81 nodeDefinedNames.put(org.jrubyparser.ast.ArrayNode.class, "expression"); | |
82 nodeDefinedNames.put(org.jrubyparser.ast.HashNode.class, "expression"); | |
83 nodeDefinedNames.put(org.jrubyparser.ast.SymbolNode.class, "expression"); | |
84 nodeDefinedNames.put(org.jrubyparser.ast.DotNode.class, "expression"); | |
85 nodeDefinedNames.put(org.jrubyparser.ast.NotNode.class, "expression"); | |
86 nodeDefinedNames.put(org.jrubyparser.ast.AndNode.class, "expression"); | |
87 nodeDefinedNames.put(org.jrubyparser.ast.OrNode.class, "expression"); | |
88 nodeDefinedNames.put(org.jrubyparser.ast.LocalVarNode.class, "local-variable"); | |
89 nodeDefinedNames.put(org.jrubyparser.ast.DVarNode.class, "local-variable"); | |
90 } | |
91 | |
13569
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
92 private static final Set<String> debugIgnoredCalls = new HashSet<>(); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
93 |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
94 static { |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
95 debugIgnoredCalls.add("downto"); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
96 debugIgnoredCalls.add("each"); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
97 debugIgnoredCalls.add("times"); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
98 debugIgnoredCalls.add("upto"); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
99 } |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
100 |
13514 | 101 /** |
102 * Global variables which in common usage have frame local semantics. | |
103 */ | |
104 public static final Set<String> FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$_")); | |
105 | |
106 public Translator(RubyContext context, Translator parent, TranslatorEnvironment environment, Source source) { | |
107 this.context = context; | |
108 this.parent = parent; | |
109 this.environment = environment; | |
110 this.source = source; | |
111 } | |
112 | |
113 @Override | |
114 public Object visitAliasNode(org.jrubyparser.ast.AliasNode node) { | |
115 final SourceSection sourceSection = translate(node.getPosition()); | |
116 | |
117 final org.jrubyparser.ast.LiteralNode oldName = (org.jrubyparser.ast.LiteralNode) node.getOldName(); | |
118 final org.jrubyparser.ast.LiteralNode newName = (org.jrubyparser.ast.LiteralNode) node.getNewName(); | |
119 | |
120 final ClassNode classNode = new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); | |
121 | |
122 return new AliasNode(context, sourceSection, classNode, newName.getName(), oldName.getName()); | |
123 } | |
124 | |
125 @Override | |
126 public Object visitAndNode(org.jrubyparser.ast.AndNode node) { | |
127 final SourceSection sourceSection = translate(node.getPosition()); | |
128 | |
129 RubyNode x; | |
130 | |
131 if (node.getFirst() == null) { | |
132 x = new NilNode(context, sourceSection); | |
133 } else { | |
134 x = (RubyNode) node.getFirst().accept(this); | |
135 } | |
136 | |
137 RubyNode y; | |
138 | |
139 if (node.getSecond() == null) { | |
140 y = new NilNode(context, sourceSection); | |
141 } else { | |
142 y = (RubyNode) node.getSecond().accept(this); | |
143 } | |
144 | |
145 return AndNodeFactory.create(context, sourceSection, x, y); | |
146 } | |
147 | |
148 @Override | |
149 public Object visitArgsCatNode(org.jrubyparser.ast.ArgsCatNode node) { | |
150 final List<org.jrubyparser.ast.Node> nodes = new ArrayList<>(); | |
151 collectArgsCatNodes(nodes, node); | |
152 | |
153 final List<RubyNode> translatedNodes = new ArrayList<>(); | |
154 | |
155 for (org.jrubyparser.ast.Node catNode : nodes) { | |
156 translatedNodes.add((RubyNode) catNode.accept(this)); | |
157 } | |
158 | |
159 return new ArrayConcatNode(context, translate(node.getPosition()), translatedNodes.toArray(new RubyNode[translatedNodes.size()])); | |
160 } | |
161 | |
162 // ArgsCatNodes can be nested - this collects them into a flat list of children | |
163 private void collectArgsCatNodes(List<org.jrubyparser.ast.Node> nodes, org.jrubyparser.ast.ArgsCatNode node) { | |
164 if (node.getFirst() instanceof org.jrubyparser.ast.ArgsCatNode) { | |
165 collectArgsCatNodes(nodes, (org.jrubyparser.ast.ArgsCatNode) node.getFirst()); | |
166 } else { | |
167 nodes.add(node.getFirst()); | |
168 } | |
169 | |
170 if (node.getSecond() instanceof org.jrubyparser.ast.ArgsCatNode) { | |
171 collectArgsCatNodes(nodes, (org.jrubyparser.ast.ArgsCatNode) node.getSecond()); | |
172 } else { | |
173 nodes.add(node.getSecond()); | |
174 } | |
175 } | |
176 | |
177 @Override | |
178 public Object visitArgsNode(org.jrubyparser.ast.ArgsNode node) { | |
179 return unimplemented(node); | |
180 } | |
181 | |
182 @Override | |
183 public Object visitArgsPushNode(org.jrubyparser.ast.ArgsPushNode node) { | |
184 return new ArrayPushNode(context, translate(node.getPosition()), (RubyNode) node.getFirstNode().accept(this), (RubyNode) node.getSecondNode().accept(this)); | |
185 } | |
186 | |
187 @Override | |
188 public Object visitArrayNode(org.jrubyparser.ast.ArrayNode node) { | |
189 final List<org.jrubyparser.ast.Node> values = node.childNodes(); | |
190 | |
191 final RubyNode[] translatedValues = new RubyNode[values.size()]; | |
192 | |
193 for (int n = 0; n < values.size(); n++) { | |
194 translatedValues[n] = (RubyNode) values.get(n).accept(this); | |
195 } | |
196 | |
197 return new UninitialisedArrayLiteralNode(context, translate(node.getPosition()), translatedValues); | |
198 } | |
199 | |
200 @Override | |
201 public Object visitAttrAssignNode(org.jrubyparser.ast.AttrAssignNode node) { | |
202 return visitAttrAssignNodeExtraArgument(node, null); | |
203 } | |
204 | |
205 /** | |
206 * See translateDummyAssignment to understand what this is for. | |
207 */ | |
208 public RubyNode visitAttrAssignNodeExtraArgument(org.jrubyparser.ast.AttrAssignNode node, RubyNode extraArgument) { | |
209 final org.jrubyparser.ast.CallNode callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), node.getReceiver(), node.getName(), node.getArgs()); | |
210 return visitCallNodeExtraArgument(callNode, extraArgument); | |
211 } | |
212 | |
213 @Override | |
214 public Object visitBackRefNode(org.jrubyparser.ast.BackRefNode node) { | |
215 return unimplemented(node); | |
216 } | |
217 | |
218 @Override | |
219 public Object visitBeginNode(org.jrubyparser.ast.BeginNode node) { | |
220 return node.getBody().accept(this); | |
221 } | |
222 | |
223 @Override | |
224 public Object visitBignumNode(org.jrubyparser.ast.BignumNode node) { | |
225 return new BignumLiteralNode(context, translate(node.getPosition()), node.getValue()); | |
226 } | |
227 | |
228 @Override | |
229 public Object visitBlockArg18Node(org.jrubyparser.ast.BlockArg18Node node) { | |
230 return unimplemented(node); | |
231 } | |
232 | |
233 @Override | |
234 public Object visitBlockArgNode(org.jrubyparser.ast.BlockArgNode node) { | |
235 return unimplemented(node); | |
236 } | |
237 | |
238 @Override | |
239 public Object visitBlockNode(org.jrubyparser.ast.BlockNode node) { | |
240 final List<org.jrubyparser.ast.Node> children = node.childNodes(); | |
241 | |
242 final List<RubyNode> translatedChildren = new ArrayList<>(); | |
243 | |
244 for (int n = 0; n < children.size(); n++) { | |
245 final RubyNode translatedChild = (RubyNode) children.get(n).accept(this); | |
246 | |
247 if (!(translatedChild instanceof DeadNode)) { | |
248 translatedChildren.add(translatedChild); | |
249 } | |
250 } | |
251 | |
252 if (translatedChildren.size() == 1) { | |
253 return translatedChildren.get(0); | |
254 } else { | |
255 return new SequenceNode(context, translate(node.getPosition()), translatedChildren.toArray(new RubyNode[translatedChildren.size()])); | |
256 } | |
257 } | |
258 | |
259 @Override | |
260 public Object visitBlockPassNode(org.jrubyparser.ast.BlockPassNode node) { | |
261 return unimplemented(node); | |
262 } | |
263 | |
264 @Override | |
265 public Object visitBreakNode(org.jrubyparser.ast.BreakNode node) { | |
266 final SourceSection sourceSection = translate(node.getPosition()); | |
267 | |
268 RubyNode resultNode; | |
269 | |
270 if (node.getValueNode() == null) { | |
271 resultNode = new NilNode(context, sourceSection); | |
272 } else { | |
273 resultNode = (RubyNode) node.getValueNode().accept(this); | |
274 } | |
275 | |
276 return new BreakNode(context, sourceSection, resultNode); | |
277 } | |
278 | |
279 @Override | |
280 public Object visitCallNode(org.jrubyparser.ast.CallNode node) { | |
281 return visitCallNodeExtraArgument(node, null); | |
282 } | |
283 | |
284 /** | |
285 * See translateDummyAssignment to understand what this is for. | |
286 */ | |
287 public RubyNode visitCallNodeExtraArgument(org.jrubyparser.ast.CallNode node, RubyNode extraArgument) { | |
288 final SourceSection sourceSection = translate(node.getPosition()); | |
289 | |
290 final RubyNode receiverTranslated = (RubyNode) node.getReceiver().accept(this); | |
291 | |
292 org.jrubyparser.ast.Node args = node.getArgs(); | |
293 org.jrubyparser.ast.Node block = node.getIter(); | |
294 | |
295 if (block == null && args instanceof org.jrubyparser.ast.IterNode) { | |
296 final org.jrubyparser.ast.Node temp = args; | |
297 args = block; | |
298 block = temp; | |
299 } | |
300 | |
301 final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, block, args, extraArgument); | |
302 | |
13569
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
303 RubyNode translated = new CallNode(context, sourceSection, node.getName(), receiverTranslated, argumentsAndBlock.getBlock(), argumentsAndBlock.isSplatted(), argumentsAndBlock.getArguments()); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
304 |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
305 if (context.getConfiguration().getDebug()) { |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
306 final CallNode callNode = (CallNode) translated; |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
307 if (!debugIgnoredCalls.contains(callNode.getName())) { |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
308 |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
309 final RubyProxyNode proxy = new RubyProxyNode(context, translated); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
310 proxy.markAs(NodePhylum.CALL); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
311 proxy.getProbeChain().appendProbe(new RubyCallProbe(context, node.getName())); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
312 translated = proxy; |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
313 } |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
314 } |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
315 |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
316 return translated; |
13514 | 317 } |
318 | |
319 protected class ArgumentsAndBlockTranslation { | |
320 | |
321 private final RubyNode block; | |
322 private final RubyNode[] arguments; | |
323 private final boolean isSplatted; | |
324 | |
325 public ArgumentsAndBlockTranslation(RubyNode block, RubyNode[] arguments, boolean isSplatted) { | |
326 super(); | |
327 this.block = block; | |
328 this.arguments = arguments; | |
329 this.isSplatted = isSplatted; | |
330 } | |
331 | |
332 public RubyNode getBlock() { | |
333 return block; | |
334 } | |
335 | |
336 public RubyNode[] getArguments() { | |
337 return arguments; | |
338 } | |
339 | |
340 public boolean isSplatted() { | |
341 return isSplatted; | |
342 } | |
343 | |
344 } | |
345 | |
346 protected ArgumentsAndBlockTranslation translateArgumentsAndBlock(SourceSection sourceSection, org.jrubyparser.ast.Node iterNode, org.jrubyparser.ast.Node argsNode, RubyNode extraArgument) { | |
347 assert !(argsNode instanceof org.jrubyparser.ast.IterNode); | |
348 | |
349 final List<org.jrubyparser.ast.Node> arguments = new ArrayList<>(); | |
350 org.jrubyparser.ast.Node blockPassNode = null; | |
351 | |
352 boolean isSplatted = false; | |
353 | |
354 if (argsNode instanceof org.jrubyparser.ast.ListNode) { | |
355 arguments.addAll(((org.jrubyparser.ast.ListNode) argsNode).childNodes()); | |
356 } else if (argsNode instanceof org.jrubyparser.ast.BlockPassNode) { | |
357 final org.jrubyparser.ast.BlockPassNode blockPass = (org.jrubyparser.ast.BlockPassNode) argsNode; | |
358 | |
359 final org.jrubyparser.ast.Node blockPassArgs = blockPass.getArgs(); | |
360 | |
361 if (blockPassArgs instanceof org.jrubyparser.ast.ListNode) { | |
362 arguments.addAll(((org.jrubyparser.ast.ListNode) blockPassArgs).childNodes()); | |
363 } else if (blockPassArgs instanceof org.jrubyparser.ast.ArgsCatNode) { | |
364 arguments.add(blockPassArgs); | |
365 } else if (blockPassArgs != null) { | |
366 throw new UnsupportedOperationException("Don't know how to block pass " + blockPassArgs); | |
367 } | |
368 | |
369 blockPassNode = blockPass.getBody(); | |
370 } else if (argsNode instanceof org.jrubyparser.ast.SplatNode) { | |
371 isSplatted = true; | |
372 arguments.add(argsNode); | |
373 } else if (argsNode instanceof org.jrubyparser.ast.ArgsCatNode) { | |
374 isSplatted = true; | |
375 arguments.add(argsNode); | |
376 } else if (argsNode != null) { | |
377 isSplatted = true; | |
378 arguments.add(argsNode); | |
379 } | |
380 | |
381 RubyNode blockTranslated; | |
382 | |
383 if (blockPassNode != null && iterNode != null) { | |
384 throw new UnsupportedOperationException("Don't know how to pass both an block and a block-pass argument"); | |
385 } else if (iterNode != null) { | |
386 blockTranslated = (BlockDefinitionNode) iterNode.accept(this); | |
387 } else if (blockPassNode != null) { | |
388 blockTranslated = ProcCastNodeFactory.create(context, sourceSection, (RubyNode) blockPassNode.accept(this)); | |
389 } else { | |
390 blockTranslated = null; | |
391 } | |
392 | |
393 final List<RubyNode> argumentsTranslated = new ArrayList<>(); | |
394 | |
395 for (org.jrubyparser.ast.Node argument : arguments) { | |
396 argumentsTranslated.add((RubyNode) argument.accept(this)); | |
397 } | |
398 | |
399 if (extraArgument != null) { | |
400 argumentsTranslated.add(extraArgument); | |
401 } | |
402 | |
403 final RubyNode[] argumentsTranslatedArray = argumentsTranslated.toArray(new RubyNode[argumentsTranslated.size()]); | |
404 | |
405 return new ArgumentsAndBlockTranslation(blockTranslated, argumentsTranslatedArray, isSplatted); | |
406 } | |
407 | |
408 @Override | |
409 public Object visitCaseNode(org.jrubyparser.ast.CaseNode node) { | |
410 final SourceSection sourceSection = translate(node.getPosition()); | |
411 | |
412 RubyNode elseNode; | |
413 | |
414 if (node.getElse() != null) { | |
415 elseNode = (RubyNode) node.getElse().accept(this); | |
416 } else { | |
417 elseNode = new NilNode(context, sourceSection); | |
418 } | |
419 | |
420 /* | |
421 * There are two sorts of case - one compares a list of expressions against a value, the | |
422 * other just checks a list of expressions for truth. | |
423 */ | |
424 | |
425 if (node.getCase() != null) { | |
426 // Evaluate the case expression and store it in a local | |
427 | |
428 final String tempName = environment.allocateLocalTemp(); | |
429 | |
430 final RubyNode readTemp = environment.findLocalVarNode(tempName, sourceSection); | |
431 | |
432 final RubyNode assignTemp = ((ReadNode) readTemp).makeWriteNode((RubyNode) node.getCase().accept(this)); | |
433 | |
434 /* | |
435 * Build an if expression from the whens and else. Work backwards because the first if | |
436 * contains all the others in its else clause. | |
437 */ | |
438 | |
439 for (int n = node.getCases().size() - 1; n >= 0; n--) { | |
440 final org.jrubyparser.ast.WhenNode when = (org.jrubyparser.ast.WhenNode) node.getCases().get(n); | |
441 | |
442 // Make a condition from the one or more expressions combined in an or expression | |
443 | |
444 final List<org.jrubyparser.ast.Node> expressions; | |
445 | |
446 if (when.getExpression() instanceof org.jrubyparser.ast.ListNode) { | |
447 expressions = ((org.jrubyparser.ast.ListNode) when.getExpression()).childNodes(); | |
448 } else { | |
449 expressions = Arrays.asList(when.getExpression()); | |
450 } | |
451 | |
452 final List<RubyNode> comparisons = new ArrayList<>(); | |
453 | |
454 for (org.jrubyparser.ast.Node expressionNode : expressions) { | |
455 final RubyNode rubyExpression = (RubyNode) expressionNode.accept(this); | |
456 | |
457 final CallNode comparison = new CallNode(context, sourceSection, "===", rubyExpression, null, false, new RubyNode[]{environment.findLocalVarNode(tempName, sourceSection)}); | |
458 | |
459 comparisons.add(comparison); | |
460 } | |
461 | |
462 RubyNode conditionNode = comparisons.get(comparisons.size() - 1); | |
463 | |
464 // As with the if nodes, we work backwards to make it left associative | |
465 | |
466 for (int i = comparisons.size() - 2; i >= 0; i--) { | |
467 conditionNode = OrNodeFactory.create(context, sourceSection, comparisons.get(i), conditionNode); | |
468 } | |
469 | |
470 // Create the if node | |
471 | |
472 final BooleanCastNode conditionCastNode = BooleanCastNodeFactory.create(context, sourceSection, conditionNode); | |
473 | |
474 RubyNode thenNode; | |
475 | |
476 if (when.getBody() == null) { | |
477 thenNode = new NilNode(context, sourceSection); | |
478 } else { | |
479 thenNode = (RubyNode) when.getBody().accept(this); | |
480 } | |
481 | |
482 final IfNode ifNode = new IfNode(context, sourceSection, conditionCastNode, thenNode, elseNode); | |
483 | |
484 // This if becomes the else for the next if | |
485 | |
486 elseNode = ifNode; | |
487 } | |
488 | |
489 final RubyNode ifNode = elseNode; | |
490 | |
491 // A top-level block assigns the temp then runs the if | |
492 | |
493 return new SequenceNode(context, sourceSection, assignTemp, ifNode); | |
494 } else { | |
495 for (int n = node.getCases().size() - 1; n >= 0; n--) { | |
496 final org.jrubyparser.ast.WhenNode when = (org.jrubyparser.ast.WhenNode) node.getCases().get(n); | |
497 | |
498 // Make a condition from the one or more expressions combined in an or expression | |
499 | |
500 final List<org.jrubyparser.ast.Node> expressions; | |
501 | |
502 if (when.getExpression() instanceof org.jrubyparser.ast.ListNode) { | |
503 expressions = ((org.jrubyparser.ast.ListNode) when.getExpression()).childNodes(); | |
504 } else { | |
505 expressions = Arrays.asList(when.getExpression()); | |
506 } | |
507 | |
508 final List<RubyNode> tests = new ArrayList<>(); | |
509 | |
510 for (org.jrubyparser.ast.Node expressionNode : expressions) { | |
511 final RubyNode rubyExpression = (RubyNode) expressionNode.accept(this); | |
512 tests.add(rubyExpression); | |
513 } | |
514 | |
515 RubyNode conditionNode = tests.get(tests.size() - 1); | |
516 | |
517 // As with the if nodes, we work backwards to make it left associative | |
518 | |
519 for (int i = tests.size() - 2; i >= 0; i--) { | |
520 conditionNode = OrNodeFactory.create(context, sourceSection, tests.get(i), conditionNode); | |
521 } | |
522 | |
523 // Create the if node | |
524 | |
525 final BooleanCastNode conditionCastNode = BooleanCastNodeFactory.create(context, sourceSection, conditionNode); | |
526 | |
527 final RubyNode thenNode = (RubyNode) when.getBody().accept(this); | |
528 | |
529 final IfNode ifNode = new IfNode(context, sourceSection, conditionCastNode, thenNode, elseNode); | |
530 | |
531 // This if becomes the else for the next if | |
532 | |
533 elseNode = ifNode; | |
534 } | |
535 | |
536 return elseNode; | |
537 } | |
538 } | |
539 | |
540 @Override | |
541 public Object visitClassNode(org.jrubyparser.ast.ClassNode node) { | |
542 final SourceSection sourceSection = translate(node.getPosition()); | |
543 | |
544 final String name = node.getCPath().getName(); | |
545 | |
546 final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, | |
547 new UniqueMethodIdentifier()); | |
548 final ModuleTranslator classTranslator = new ModuleTranslator(context, this, newEnvironment, source); | |
549 | |
550 final MethodDefinitionNode definitionMethod = classTranslator.compileClassNode(node.getPosition(), node.getCPath().getName(), node.getBody()); | |
551 | |
552 /* | |
553 * See my note in visitDefnNode about where the class gets defined - the same applies here. | |
554 */ | |
555 | |
556 RubyNode superClass; | |
557 | |
558 if (node.getSuper() != null) { | |
559 superClass = (RubyNode) node.getSuper().accept(this); | |
560 } else { | |
561 superClass = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getObjectClass()); | |
562 } | |
563 | |
564 final DefineOrGetClassNode defineOrGetClass = new DefineOrGetClassNode(context, sourceSection, name, getModuleToDefineModulesIn(sourceSection), superClass); | |
565 | |
566 return new OpenModuleNode(context, sourceSection, defineOrGetClass, definitionMethod); | |
567 } | |
568 | |
569 protected RubyNode getModuleToDefineModulesIn(SourceSection sourceSection) { | |
570 return new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); | |
571 } | |
572 | |
573 @Override | |
574 public Object visitClassVarAsgnNode(org.jrubyparser.ast.ClassVarAsgnNode node) { | |
575 final SourceSection sourceSection = translate(node.getPosition()); | |
576 | |
577 final RubyNode receiver = new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); | |
578 | |
579 final RubyNode rhs = (RubyNode) node.getValue().accept(this); | |
580 | |
581 return new WriteClassVariableNode(context, sourceSection, node.getName(), receiver, rhs); | |
582 } | |
583 | |
584 @Override | |
585 public Object visitClassVarDeclNode(org.jrubyparser.ast.ClassVarDeclNode node) { | |
586 return unimplemented(node); | |
587 } | |
588 | |
589 @Override | |
590 public Object visitClassVarNode(org.jrubyparser.ast.ClassVarNode node) { | |
591 final SourceSection sourceSection = translate(node.getPosition()); | |
592 return new ReadClassVariableNode(context, sourceSection, node.getName(), new SelfNode(context, sourceSection)); | |
593 } | |
594 | |
595 @Override | |
596 public Object visitColon2Node(org.jrubyparser.ast.Colon2Node node) { | |
597 final RubyNode lhs = (RubyNode) node.getLeftNode().accept(this); | |
598 | |
599 return new UninitializedReadConstantNode(context, translate(node.getPosition()), node.getName(), lhs); | |
600 } | |
601 | |
602 @Override | |
603 public Object visitColon3Node(org.jrubyparser.ast.Colon3Node node) { | |
604 // Colon3 means the root namespace, as in ::Foo | |
605 | |
606 final SourceSection sourceSection = translate(node.getPosition()); | |
607 | |
608 final ObjectLiteralNode root = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getMainObject()); | |
609 | |
610 return new UninitializedReadConstantNode(context, sourceSection, node.getName(), root); | |
611 } | |
612 | |
613 @Override | |
614 public Object visitConstDeclNode(org.jrubyparser.ast.ConstDeclNode node) { | |
615 final SourceSection sourceSection = translate(node.getPosition()); | |
616 | |
617 final ClassNode classNode = new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); | |
618 | |
619 return new WriteConstantNode(context, sourceSection, node.getName(), classNode, (RubyNode) node.getValue().accept(this)); | |
620 } | |
621 | |
622 @Override | |
623 public Object visitConstNode(org.jrubyparser.ast.ConstNode node) { | |
624 final SourceSection sourceSection = translate(node.getPosition()); | |
625 | |
626 return new UninitializedReadConstantNode(context, sourceSection, node.getName(), new SelfNode(context, sourceSection)); | |
627 } | |
628 | |
629 @Override | |
630 public Object visitDAsgnNode(org.jrubyparser.ast.DAsgnNode node) { | |
631 return new org.jrubyparser.ast.LocalAsgnNode(node.getPosition(), node.getName(), node.getDepth(), node.getValue()).accept(this); | |
632 } | |
633 | |
634 @Override | |
635 public Object visitDRegxNode(org.jrubyparser.ast.DRegexpNode node) { | |
636 SourceSection sourceSection = translate(node.getPosition()); | |
637 | |
638 final RubyNode stringNode = translateInterpolatedString(sourceSection, node.childNodes()); | |
639 | |
640 return StringToRegexpNodeFactory.create(context, sourceSection, stringNode); | |
641 } | |
642 | |
643 @Override | |
644 public Object visitDStrNode(org.jrubyparser.ast.DStrNode node) { | |
645 return translateInterpolatedString(translate(node.getPosition()), node.childNodes()); | |
646 } | |
647 | |
648 @Override | |
649 public Object visitDSymbolNode(org.jrubyparser.ast.DSymbolNode node) { | |
650 SourceSection sourceSection = translate(node.getPosition()); | |
651 | |
652 final RubyNode stringNode = translateInterpolatedString(sourceSection, node.childNodes()); | |
653 | |
654 return StringToSymbolNodeFactory.create(context, sourceSection, stringNode); | |
655 } | |
656 | |
657 private RubyNode translateInterpolatedString(SourceSection sourceSection, List<org.jrubyparser.ast.Node> childNodes) { | |
658 final List<RubyNode> children = new ArrayList<>(); | |
659 | |
660 for (org.jrubyparser.ast.Node child : childNodes) { | |
661 children.add((RubyNode) child.accept(this)); | |
662 } | |
663 | |
664 return new InterpolatedStringNode(context, sourceSection, children.toArray(new RubyNode[children.size()])); | |
665 } | |
666 | |
667 @Override | |
668 public Object visitDVarNode(org.jrubyparser.ast.DVarNode node) { | |
669 RubyNode readNode = environment.findLocalVarNode(node.getName(), translate(node.getPosition())); | |
670 | |
671 if (readNode == null) { | |
672 context.implementationMessage("can't find variable %s at %s, using noop", node.getName(), node.getPosition()); | |
673 readNode = new NilNode(context, translate(node.getPosition())); | |
674 } | |
675 | |
676 return readNode; | |
677 } | |
678 | |
679 @Override | |
680 public Object visitDXStrNode(org.jrubyparser.ast.DXStrNode node) { | |
681 SourceSection sourceSection = translate(node.getPosition()); | |
682 | |
683 final RubyNode string = translateInterpolatedString(sourceSection, node.childNodes()); | |
684 | |
685 return new SystemNode(context, sourceSection, string); | |
686 } | |
687 | |
688 @Override | |
689 public Object visitDefinedNode(org.jrubyparser.ast.DefinedNode node) { | |
690 final SourceSection sourceSection = translate(node.getPosition()); | |
691 | |
692 org.jrubyparser.ast.Node expressionNode = node.getExpression(); | |
693 | |
694 while (expressionNode instanceof org.jrubyparser.ast.NewlineNode) { | |
695 expressionNode = ((org.jrubyparser.ast.NewlineNode) expressionNode).getNextNode(); | |
696 } | |
697 | |
698 final String name = nodeDefinedNames.get(expressionNode.getClass()); | |
699 | |
700 if (name != null) { | |
701 final StringLiteralNode literal = new StringLiteralNode(context, sourceSection, name); | |
702 return literal; | |
703 } | |
704 | |
705 return new DefinedNode(context, sourceSection, (RubyNode) node.getExpression().accept(this)); | |
706 } | |
707 | |
708 @Override | |
709 public Object visitDefnNode(org.jrubyparser.ast.DefnNode node) { | |
710 final SourceSection sourceSection = translate(node.getPosition()); | |
711 final ClassNode classNode = new ClassNode(context, sourceSection, new SelfNode(context, sourceSection)); | |
712 return translateMethodDefinition(sourceSection, classNode, node.getName(), node.getArgs(), node.getBody()); | |
713 } | |
714 | |
715 @Override | |
716 public Object visitDefsNode(org.jrubyparser.ast.DefsNode node) { | |
717 final SourceSection sourceSection = translate(node.getPosition()); | |
718 | |
719 final RubyNode objectNode = (RubyNode) node.getReceiver().accept(this); | |
720 | |
721 final SingletonClassNode singletonClassNode = new SingletonClassNode(context, sourceSection, objectNode); | |
722 | |
723 return translateMethodDefinition(sourceSection, singletonClassNode, node.getName(), node.getArgs(), node.getBody()); | |
724 } | |
725 | |
726 private RubyNode translateMethodDefinition(SourceSection sourceSection, RubyNode classNode, String methodName, org.jrubyparser.ast.ArgsNode argsNode, org.jrubyparser.ast.Node bodyNode) { | |
727 final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, | |
728 new UniqueMethodIdentifier()); | |
729 | |
730 // ownScopeForAssignments is the same for the defined method as the current one. | |
731 | |
732 final MethodTranslator methodCompiler = new MethodTranslator(context, this, newEnvironment, false, source); | |
733 | |
734 final MethodDefinitionNode functionExprNode = methodCompiler.compileFunctionNode(sourceSection, methodName, argsNode, bodyNode); | |
735 | |
736 /* | |
737 * In the top-level, methods are defined in the class of the main object. This is | |
738 * counter-intuitive - I would have expected them to be defined in the singleton class. | |
739 * Apparently this is a design decision to make top-level methods sort of global. | |
740 * | |
741 * http://stackoverflow.com/questions/1761148/where-are-methods-defined-at-the-ruby-top-level | |
742 */ | |
743 | |
744 return new AddMethodNode(context, sourceSection, classNode, functionExprNode); | |
745 } | |
746 | |
747 @Override | |
748 public Object visitDotNode(org.jrubyparser.ast.DotNode node) { | |
749 final RubyNode begin = (RubyNode) node.getBegin().accept(this); | |
750 final RubyNode end = (RubyNode) node.getEnd().accept(this); | |
751 SourceSection sourceSection = translate(node.getPosition()); | |
752 | |
753 if (begin instanceof FixnumLiteralNode && end instanceof FixnumLiteralNode) { | |
754 final int beginValue = ((FixnumLiteralNode) begin).getValue(); | |
755 final int endValue = ((FixnumLiteralNode) end).getValue(); | |
756 | |
757 return new ObjectLiteralNode(context, sourceSection, new FixnumRange(context.getCoreLibrary().getRangeClass(), beginValue, endValue, node.isExclusive())); | |
758 } | |
759 // See RangeNode for why there is a node specifically for creating this one type | |
760 return RangeLiteralNodeFactory.create(context, sourceSection, node.isExclusive(), begin, end); | |
761 } | |
762 | |
763 @Override | |
764 public Object visitEncodingNode(org.jrubyparser.ast.EncodingNode node) { | |
765 return unimplemented(node); | |
766 } | |
767 | |
768 @Override | |
769 public Object visitEnsureNode(org.jrubyparser.ast.EnsureNode node) { | |
770 final RubyNode tryPart = (RubyNode) node.getBody().accept(this); | |
771 final RubyNode ensurePart = (RubyNode) node.getEnsure().accept(this); | |
772 return new EnsureNode(context, translate(node.getPosition()), tryPart, ensurePart); | |
773 } | |
774 | |
775 @Override | |
776 public Object visitEvStrNode(org.jrubyparser.ast.EvStrNode node) { | |
777 return node.getBody().accept(this); | |
778 } | |
779 | |
780 @Override | |
781 public Object visitFCallNode(org.jrubyparser.ast.FCallNode node) { | |
782 final org.jrubyparser.ast.Node receiver = new org.jrubyparser.ast.SelfNode(node.getPosition()); | |
783 final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), receiver, node.getName(), node.getArgs(), node.getIter()); | |
784 | |
785 return callNode.accept(this); | |
786 } | |
787 | |
788 @Override | |
789 public Object visitFalseNode(org.jrubyparser.ast.FalseNode node) { | |
790 return new BooleanLiteralNode(context, translate(node.getPosition()), false); | |
791 } | |
792 | |
793 @Override | |
794 public Object visitFixnumNode(org.jrubyparser.ast.FixnumNode node) { | |
795 final long value = node.getValue(); | |
796 | |
797 if (value >= RubyFixnum.MIN_VALUE && value <= RubyFixnum.MAX_VALUE) { | |
798 return new FixnumLiteralNode(context, translate(node.getPosition()), (int) value); | |
799 } | |
800 return new BignumLiteralNode(context, translate(node.getPosition()), BigInteger.valueOf(value)); | |
801 } | |
802 | |
803 @Override | |
804 public Object visitFlipNode(org.jrubyparser.ast.FlipNode node) { | |
805 final SourceSection sourceSection = translate(node.getPosition()); | |
806 | |
807 final RubyNode begin = (RubyNode) node.getBegin().accept(this); | |
808 final RubyNode end = (RubyNode) node.getEnd().accept(this); | |
809 | |
810 final BooleanCastNode beginCast = BooleanCastNodeFactory.create(context, sourceSection, begin); | |
811 final BooleanCastNode endCast = BooleanCastNodeFactory.create(context, sourceSection, end); | |
812 final FlipFlopStateNode stateNode = createFlipFlopState(sourceSection, 0); | |
813 | |
814 return new FlipFlopNode(context, sourceSection, beginCast, endCast, stateNode, node.isExclusive()); | |
815 } | |
816 | |
817 protected FlipFlopStateNode createFlipFlopState(SourceSection sourceSection, int depth) { | |
818 final FrameSlot frameSlot = environment.declareVar(environment.allocateLocalTemp()); | |
819 environment.getFlipFlopStates().add(frameSlot); | |
820 | |
821 if (depth == 0) { | |
822 return new LocalFlipFlopStateNode(sourceSection, frameSlot); | |
823 } else { | |
824 return new LevelFlipFlopStateNode(sourceSection, depth, frameSlot); | |
825 } | |
826 } | |
827 | |
828 @Override | |
829 public Object visitFloatNode(org.jrubyparser.ast.FloatNode node) { | |
830 return new FloatLiteralNode(context, translate(node.getPosition()), node.getValue()); | |
831 } | |
832 | |
833 @Override | |
834 public Object visitForNode(org.jrubyparser.ast.ForNode node) { | |
835 /** | |
836 * A Ruby for-loop, such as: | |
837 * | |
838 * <pre> | |
839 * for x in y | |
840 * z = x | |
841 * puts z | |
842 * end | |
843 * </pre> | |
844 * | |
845 * naively desugars to: | |
846 * | |
847 * <pre> | |
848 * y.each do |x| | |
849 * z = x | |
850 * puts z | |
851 * end | |
852 * </pre> | |
853 * | |
854 * The main difference is that z is always going to be local to the scope outside the block, | |
855 * so it's a bit more like: | |
856 * | |
857 * <pre> | |
858 * z = nil unless z is already defined | |
859 * y.each do |x| | |
860 * z = x | |
861 * puts x | |
862 * end | |
863 * </pre> | |
864 * | |
865 * Which forces z to be defined in the correct scope. The parser already correctly calls z a | |
866 * local, but then that causes us a problem as if we're going to translate to a block we | |
867 * need a formal parameter - not a local variable. My solution to this is to add a | |
868 * temporary: | |
869 * | |
870 * <pre> | |
871 * z = nil unless z is already defined | |
872 * y.each do |temp| | |
873 * x = temp | |
874 * z = x | |
875 * puts x | |
876 * end | |
877 * </pre> | |
878 * | |
879 * We also need that temp because the expression assigned in the for could be index | |
880 * assignment, multiple assignment, or whatever: | |
881 * | |
882 * <pre> | |
883 * for x[0] in y | |
884 * z = x[0] | |
885 * puts z | |
886 * end | |
887 * </pre> | |
888 * | |
889 * http://blog.grayproductions.net/articles/the_evils_of_the_for_loop | |
890 * http://stackoverflow.com/questions/3294509/for-vs-each-in-ruby | |
891 * | |
892 * The other complication is that normal locals should be defined in the enclosing scope, | |
893 * unlike a normal block. We do that by setting a flag on this translator object when we | |
894 * visit the new iter, translatingForStatement, which we recognise when visiting an iter | |
895 * node. | |
896 * | |
897 * Finally, note that JRuby's terminology is strange here. Normally 'iter' is a different | |
898 * term for a block. Here, JRuby calls the object being iterated over the 'iter'. | |
899 */ | |
900 | |
901 final String temp = environment.allocateLocalTemp(); | |
902 | |
903 final org.jrubyparser.ast.Node receiver = node.getIter(); | |
904 | |
905 /* | |
906 * The x in for x in ... is like the nodes in multiple assignment - it has a dummy RHS which | |
907 * we need to replace with our temp. Just like in multiple assignment this is really awkward | |
908 * with the JRuby AST. | |
909 */ | |
910 | |
911 final org.jrubyparser.ast.LocalVarNode readTemp = new org.jrubyparser.ast.LocalVarNode(node.getPosition(), 0, temp); | |
912 final org.jrubyparser.ast.Node forVar = node.getVar(); | |
913 final org.jrubyparser.ast.Node assignTemp = setRHS(forVar, readTemp); | |
914 | |
915 final org.jrubyparser.ast.BlockNode bodyWithTempAssign = new org.jrubyparser.ast.BlockNode(node.getPosition()); | |
916 bodyWithTempAssign.add(assignTemp); | |
917 bodyWithTempAssign.add(node.getBody()); | |
918 | |
919 final org.jrubyparser.ast.ArgumentNode blockVar = new org.jrubyparser.ast.ArgumentNode(node.getPosition(), temp); | |
920 final org.jrubyparser.ast.ListNode blockArgsPre = new org.jrubyparser.ast.ListNode(node.getPosition(), blockVar); | |
921 final org.jrubyparser.ast.ArgsNode blockArgs = new org.jrubyparser.ast.ArgsNode(node.getPosition(), blockArgsPre, null, null, null, null, null, null); | |
922 final org.jrubyparser.ast.IterNode block = new org.jrubyparser.ast.IterNode(node.getPosition(), blockArgs, node.getScope(), bodyWithTempAssign); | |
923 | |
924 final org.jrubyparser.ast.CallNode callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), receiver, "each", null, block); | |
925 | |
926 translatingForStatement = true; | |
927 final RubyNode translated = (RubyNode) callNode.accept(this); | |
928 translatingForStatement = false; | |
929 | |
930 return translated; | |
931 } | |
932 | |
933 private static org.jrubyparser.ast.Node setRHS(org.jrubyparser.ast.Node node, org.jrubyparser.ast.Node rhs) { | |
934 if (node instanceof org.jrubyparser.ast.LocalAsgnNode) { | |
935 final org.jrubyparser.ast.LocalAsgnNode localAsgnNode = (org.jrubyparser.ast.LocalAsgnNode) node; | |
936 return new org.jrubyparser.ast.LocalAsgnNode(node.getPosition(), localAsgnNode.getName(), 0, rhs); | |
937 } else if (node instanceof org.jrubyparser.ast.DAsgnNode) { | |
938 final org.jrubyparser.ast.DAsgnNode dAsgnNode = (org.jrubyparser.ast.DAsgnNode) node; | |
939 return new org.jrubyparser.ast.DAsgnNode(node.getPosition(), dAsgnNode.getName(), 0, rhs); | |
940 } else if (node instanceof org.jrubyparser.ast.MultipleAsgnNode) { | |
941 final org.jrubyparser.ast.MultipleAsgnNode multAsgnNode = (org.jrubyparser.ast.MultipleAsgnNode) node; | |
942 return new org.jrubyparser.ast.MultipleAsgnNode(node.getPosition(), multAsgnNode.getPre(), multAsgnNode.getRest(), multAsgnNode.getPost()); | |
943 } else if (node instanceof org.jrubyparser.ast.InstAsgnNode) { | |
944 final org.jrubyparser.ast.InstAsgnNode instAsgnNode = (org.jrubyparser.ast.InstAsgnNode) node; | |
945 return new org.jrubyparser.ast.InstAsgnNode(node.getPosition(), instAsgnNode.getName(), rhs); | |
946 } else if (node instanceof org.jrubyparser.ast.ClassVarAsgnNode) { | |
947 final org.jrubyparser.ast.ClassVarAsgnNode instAsgnNode = (org.jrubyparser.ast.ClassVarAsgnNode) node; | |
948 return new org.jrubyparser.ast.ClassVarAsgnNode(node.getPosition(), instAsgnNode.getName(), rhs); | |
949 } else if (node instanceof org.jrubyparser.ast.ConstDeclNode) { | |
950 final org.jrubyparser.ast.ConstDeclNode constDeclNode = (org.jrubyparser.ast.ConstDeclNode) node; | |
951 return new org.jrubyparser.ast.ConstDeclNode(node.getPosition(), constDeclNode.getName(), (org.jrubyparser.ast.INameNode) constDeclNode.getConstNode(), rhs); | |
952 } else { | |
953 throw new UnsupportedOperationException("Don't know how to set the RHS of a " + node.getClass().getName()); | |
954 } | |
955 } | |
956 | |
957 @Override | |
958 public Object visitGlobalAsgnNode(org.jrubyparser.ast.GlobalAsgnNode node) { | |
959 final SourceSection sourceSection = translate(node.getPosition()); | |
960 | |
961 final String name = "$" + node.getName(); | |
962 final RubyNode rhs = (RubyNode) node.getValue().accept(this); | |
963 | |
964 if (FRAME_LOCAL_GLOBAL_VARIABLES.contains(name)) { | |
965 context.implementationMessage("Assigning to frame local global variables not implemented at %s", node.getPosition()); | |
966 | |
967 return rhs; | |
968 } else { | |
969 final ObjectLiteralNode globalVariablesObjectNode = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getGlobalVariablesObject()); | |
970 | |
971 return new UninitializedWriteInstanceVariableNode(context, sourceSection, name, globalVariablesObjectNode, rhs); | |
972 } | |
973 } | |
974 | |
975 @Override | |
976 public Object visitGlobalVarNode(org.jrubyparser.ast.GlobalVarNode node) { | |
977 final String name = "$" + node.getName(); | |
978 final SourceSection sourceSection = translate(node.getPosition()); | |
979 | |
980 if (FRAME_LOCAL_GLOBAL_VARIABLES.contains(name)) { | |
981 // Assignment is implicit for many of these, so we need to declare when we use | |
982 | |
983 environment.declareVar(name); | |
984 | |
985 final RubyNode readNode = environment.findLocalVarNode(name, sourceSection); | |
986 | |
987 return readNode; | |
988 } else { | |
989 final ObjectLiteralNode globalVariablesObjectNode = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getGlobalVariablesObject()); | |
990 | |
991 return new UninitializedReadInstanceVariableNode(context, sourceSection, name, globalVariablesObjectNode); | |
992 } | |
993 } | |
994 | |
995 @Override | |
996 public Object visitHashNode(org.jrubyparser.ast.HashNode node) { | |
997 final SourceSection sourceSection = translate(node.getPosition()); | |
998 | |
999 final List<RubyNode> keys = new ArrayList<>(); | |
1000 final List<RubyNode> values = new ArrayList<>(); | |
1001 | |
1002 final org.jrubyparser.ast.ListNode entries = node.getListNode(); | |
1003 | |
1004 assert entries.size() % 2 == 0; | |
1005 | |
1006 for (int n = 0; n < entries.size(); n += 2) { | |
1007 if (entries.get(n) == null) { | |
1008 final NilNode nilNode = new NilNode(context, sourceSection); | |
1009 keys.add(nilNode); | |
1010 } else { | |
1011 keys.add((RubyNode) entries.get(n).accept(this)); | |
1012 } | |
1013 | |
1014 if (entries.get(n + 1) == null) { | |
1015 final NilNode nilNode = new NilNode(context, sourceSection); | |
1016 values.add(nilNode); | |
1017 } else { | |
1018 values.add((RubyNode) entries.get(n + 1).accept(this)); | |
1019 } | |
1020 } | |
1021 | |
1022 return new HashLiteralNode(translate(node.getPosition()), keys.toArray(new RubyNode[keys.size()]), values.toArray(new RubyNode[values.size()]), context); | |
1023 } | |
1024 | |
1025 @Override | |
1026 public Object visitIfNode(org.jrubyparser.ast.IfNode node) { | |
1027 final SourceSection sourceSection = translate(node.getPosition()); | |
1028 | |
1029 org.jrubyparser.ast.Node thenBody = node.getThenBody(); | |
1030 | |
1031 if (thenBody == null) { | |
1032 thenBody = new org.jrubyparser.ast.NilNode(node.getPosition()); | |
1033 } | |
1034 | |
1035 org.jrubyparser.ast.Node elseBody = node.getElseBody(); | |
1036 | |
1037 if (elseBody == null) { | |
1038 elseBody = new org.jrubyparser.ast.NilNode(node.getPosition()); | |
1039 } | |
1040 | |
1041 RubyNode condition; | |
1042 | |
1043 if (node.getCondition() == null) { | |
1044 condition = new NilNode(context, sourceSection); | |
1045 } else { | |
1046 condition = (RubyNode) node.getCondition().accept(this); | |
1047 } | |
1048 | |
1049 final BooleanCastNode conditionCast = BooleanCastNodeFactory.create(context, sourceSection, condition); | |
1050 | |
1051 final RubyNode thenBodyTranslated = (RubyNode) thenBody.accept(this); | |
1052 final RubyNode elseBodyTranslated = (RubyNode) elseBody.accept(this); | |
1053 | |
1054 return new IfNode(context, sourceSection, conditionCast, thenBodyTranslated, elseBodyTranslated); | |
1055 } | |
1056 | |
1057 @Override | |
1058 public Object visitInstAsgnNode(org.jrubyparser.ast.InstAsgnNode node) { | |
1059 final SourceSection sourceSection = translate(node.getPosition()); | |
1060 final String nameWithoutSigil = node.getName(); | |
1061 | |
1062 final RubyNode receiver = new SelfNode(context, sourceSection); | |
1063 | |
1064 RubyNode rhs; | |
1065 | |
1066 if (node.getValue() == null) { | |
1067 rhs = new DeadNode(context, sourceSection); | |
1068 } else { | |
1069 rhs = (RubyNode) node.getValue().accept(this); | |
1070 } | |
1071 | |
1072 return new UninitializedWriteInstanceVariableNode(context, sourceSection, nameWithoutSigil, receiver, rhs); | |
1073 } | |
1074 | |
1075 @Override | |
1076 public Object visitInstVarNode(org.jrubyparser.ast.InstVarNode node) { | |
1077 final SourceSection sourceSection = translate(node.getPosition()); | |
1078 final String nameWithoutSigil = node.getName(); | |
1079 | |
1080 final RubyNode receiver = new SelfNode(context, sourceSection); | |
1081 | |
1082 return new UninitializedReadInstanceVariableNode(context, sourceSection, nameWithoutSigil, receiver); | |
1083 } | |
1084 | |
1085 @Override | |
1086 public Object visitIterNode(org.jrubyparser.ast.IterNode node) { | |
1087 /* | |
1088 * In a block we do NOT allocate a new return ID - returns will return from the method, not | |
1089 * the block (in the general case, see Proc and the difference between Proc and Lambda for | |
1090 * specifics). | |
1091 */ | |
1092 | |
1093 final boolean hasOwnScope = !translatingForStatement; | |
1094 | |
1095 // Unset this flag for any for any blocks within the for statement's body | |
1096 | |
1097 translatingForStatement = false; | |
1098 | |
1099 final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getReturnID(), hasOwnScope, false, | |
1100 new UniqueMethodIdentifier()); | |
1101 final MethodTranslator methodCompiler = new MethodTranslator(context, this, newEnvironment, true, source); | |
1102 | |
1103 org.jrubyparser.ast.ArgsNode argsNode; | |
1104 | |
1105 if (node.getVar() instanceof org.jrubyparser.ast.ArgsNode) { | |
1106 argsNode = (org.jrubyparser.ast.ArgsNode) node.getVar(); | |
1107 } else if (node.getVar() instanceof org.jrubyparser.ast.DAsgnNode) { | |
1108 final org.jrubyparser.ast.ArgumentNode arg = new org.jrubyparser.ast.ArgumentNode(node.getPosition(), ((org.jrubyparser.ast.DAsgnNode) node.getVar()).getName()); | |
1109 final org.jrubyparser.ast.ListNode preArgs = new org.jrubyparser.ast.ArrayNode(node.getPosition(), arg); | |
1110 argsNode = new org.jrubyparser.ast.ArgsNode(node.getPosition(), preArgs, null, null, null, null, null, null); | |
1111 } else if (node.getVar() == null) { | |
1112 argsNode = null; | |
1113 } else { | |
1114 throw new UnsupportedOperationException(); | |
1115 } | |
1116 | |
1117 return methodCompiler.compileFunctionNode(translate(node.getPosition()), "(block)", argsNode, node.getBody()); | |
1118 } | |
1119 | |
1120 @Override | |
1121 public Object visitLiteralNode(org.jrubyparser.ast.LiteralNode node) { | |
1122 return unimplemented(node); | |
1123 } | |
1124 | |
1125 @Override | |
1126 public Object visitLocalAsgnNode(org.jrubyparser.ast.LocalAsgnNode node) { | |
1127 | |
1128 final SourceSection sourceSection = translate(node.getPosition()); | |
1129 | |
1130 if (environment.getNeverAssignInParentScope()) { | |
1131 environment.declareVar(node.getName()); | |
1132 } | |
1133 | |
1134 RubyNode lhs = environment.findLocalVarNode(node.getName(), sourceSection); | |
1135 | |
1136 if (lhs == null) { | |
1137 if (environment.hasOwnScopeForAssignments()) { | |
1138 environment.declareVar(node.getName()); | |
1139 } | |
1140 | |
1141 TranslatorEnvironment environmentToDeclareIn = environment; | |
1142 | |
1143 while (!environmentToDeclareIn.hasOwnScopeForAssignments()) { | |
1144 environmentToDeclareIn = environmentToDeclareIn.getParent(); | |
1145 } | |
1146 | |
1147 environmentToDeclareIn.declareVar(node.getName()); | |
1148 lhs = environment.findLocalVarNode(node.getName(), sourceSection); | |
1149 | |
1150 if (lhs == null) { | |
1151 throw new RuntimeException("shoudln't be here"); | |
1152 } | |
1153 } | |
1154 | |
1155 RubyNode rhs; | |
1156 | |
1157 if (node.getValue() == null) { | |
1158 rhs = new DeadNode(context, sourceSection); | |
1159 } else { | |
1160 rhs = (RubyNode) node.getValue().accept(this); | |
1161 } | |
1162 | |
1163 RubyNode translated = ((ReadNode) lhs).makeWriteNode(rhs); | |
1164 | |
1165 if (context.getConfiguration().getDebug()) { | |
1166 final UniqueMethodIdentifier methodIdentifier = environment.findMethodForLocalVar(node.getName()); | |
1167 | |
1168 RubyProxyNode proxy; | |
1169 if (translated instanceof RubyProxyNode) { | |
1170 proxy = (RubyProxyNode) translated; | |
1171 } else { | |
1172 proxy = new RubyProxyNode(context, translated); | |
1173 } | |
13569
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
1174 proxy.markAs(NodePhylum.ASSIGNMENT); |
13514 | 1175 context.getDebugManager().registerLocalDebugProxy(methodIdentifier, node.getName(), proxy.getProbeChain()); |
1176 | |
1177 translated = proxy; | |
1178 } | |
1179 | |
1180 return translated; | |
1181 } | |
1182 | |
1183 @Override | |
1184 public Object visitLocalVarNode(org.jrubyparser.ast.LocalVarNode node) { | |
1185 final SourceSection sourceSection = translate(node.getPosition()); | |
1186 | |
1187 final String name = node.getName(); | |
1188 | |
1189 RubyNode readNode = environment.findLocalVarNode(name, sourceSection); | |
1190 | |
1191 if (readNode == null) { | |
1192 context.implementationMessage("Local variable found by parser but not by translator - " + name + " at " + node.getPosition()); | |
1193 readNode = environment.findLocalVarNode(environment.allocateLocalTemp(), sourceSection); | |
1194 } | |
1195 | |
1196 return readNode; | |
1197 } | |
1198 | |
1199 @Override | |
1200 public Object visitMatch2Node(org.jrubyparser.ast.Match2Node node) { | |
1201 final org.jrubyparser.ast.Node argsNode = buildArrayNode(node.getPosition(), node.getValue()); | |
1202 final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), node.getReceiver(), "=~", argsNode, null); | |
1203 return callNode.accept(this); | |
1204 } | |
1205 | |
1206 @Override | |
1207 public Object visitMatch3Node(org.jrubyparser.ast.Match3Node node) { | |
1208 final org.jrubyparser.ast.Node argsNode = buildArrayNode(node.getPosition(), node.getValue()); | |
1209 final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), node.getReceiver(), "=~", argsNode, null); | |
1210 return callNode.accept(this); | |
1211 } | |
1212 | |
1213 @Override | |
1214 public Object visitMatchNode(org.jrubyparser.ast.MatchNode node) { | |
1215 return unimplemented(node); | |
1216 } | |
1217 | |
1218 @Override | |
1219 public Object visitModuleNode(org.jrubyparser.ast.ModuleNode node) { | |
1220 // See visitClassNode | |
1221 | |
1222 final SourceSection sourceSection = translate(node.getPosition()); | |
1223 | |
1224 final String name = node.getCPath().getName(); | |
1225 | |
1226 final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, | |
1227 new UniqueMethodIdentifier()); | |
1228 final ModuleTranslator classTranslator = new ModuleTranslator(context, this, newEnvironment, source); | |
1229 | |
1230 final MethodDefinitionNode definitionMethod = classTranslator.compileClassNode(node.getPosition(), node.getCPath().getName(), node.getBody()); | |
1231 | |
1232 final DefineOrGetModuleNode defineModuleNode = new DefineOrGetModuleNode(context, sourceSection, name, getModuleToDefineModulesIn(sourceSection)); | |
1233 | |
1234 return new OpenModuleNode(context, sourceSection, defineModuleNode, definitionMethod); | |
1235 } | |
1236 | |
1237 @Override | |
1238 public Object visitMultipleAsgnNode(org.jrubyparser.ast.MultipleAsgnNode node) { | |
1239 final SourceSection sourceSection = translate(node.getPosition()); | |
1240 | |
1241 final org.jrubyparser.ast.ArrayNode preArray = (org.jrubyparser.ast.ArrayNode) node.getPre(); | |
1242 final org.jrubyparser.ast.Node rhs = node.getValue(); | |
1243 | |
1244 RubyNode rhsTranslated; | |
1245 | |
1246 if (rhs == null) { | |
1247 context.implementationMessage("warning: no RHS for multiple assignment - using noop"); | |
1248 rhsTranslated = new NilNode(context, sourceSection); | |
1249 } else { | |
1250 rhsTranslated = (RubyNode) rhs.accept(this); | |
1251 } | |
1252 | |
1253 /* | |
1254 * One very common case is to do | |
1255 * | |
1256 * a, b = c, d | |
1257 */ | |
1258 | |
1259 if (preArray != null && node.getPost() == null && node.getRest() == null && rhsTranslated instanceof UninitialisedArrayLiteralNode && | |
1260 ((UninitialisedArrayLiteralNode) rhsTranslated).getValues().length == preArray.size()) { | |
1261 /* | |
1262 * We can deal with this common case be rewriting as | |
1263 * | |
1264 * temp1 = c; temp2 = d; a = temp1; b = temp2 | |
1265 * | |
1266 * We can't just do | |
1267 * | |
1268 * a = c; b = d | |
1269 * | |
1270 * As we don't know if d depends on the original value of a. | |
1271 * | |
1272 * We also need to return an array [c, d], but we make that result elidable so it isn't | |
1273 * executed if it isn't actually demanded. | |
1274 */ | |
1275 | |
1276 final RubyNode[] rhsValues = ((UninitialisedArrayLiteralNode) rhsTranslated).getValues(); | |
1277 final int assignedValuesCount = preArray.size(); | |
1278 | |
1279 final RubyNode[] sequence = new RubyNode[assignedValuesCount * 2]; | |
1280 | |
1281 final RubyNode[] tempValues = new RubyNode[assignedValuesCount]; | |
1282 | |
1283 for (int n = 0; n < assignedValuesCount; n++) { | |
1284 final String tempName = environment.allocateLocalTemp(); | |
1285 final RubyNode readTemp = environment.findLocalVarNode(tempName, sourceSection); | |
1286 final RubyNode assignTemp = ((ReadNode) readTemp).makeWriteNode(rhsValues[n]); | |
1287 final RubyNode assignFinalValue = translateDummyAssignment(preArray.get(n), readTemp); | |
1288 | |
1289 sequence[n] = assignTemp; | |
1290 sequence[assignedValuesCount + n] = assignFinalValue; | |
1291 | |
1292 tempValues[n] = readTemp; | |
1293 } | |
1294 | |
1295 final RubyNode blockNode = new SequenceNode(context, sourceSection, sequence); | |
1296 | |
1297 final UninitialisedArrayLiteralNode arrayNode = new UninitialisedArrayLiteralNode(context, sourceSection, tempValues); | |
1298 | |
1299 final ElidableResultNode elidableResult = new ElidableResultNode(context, sourceSection, blockNode, arrayNode); | |
1300 | |
1301 return elidableResult; | |
1302 } else if (preArray != null) { | |
1303 /* | |
1304 * The other simple case is | |
1305 * | |
1306 * a, b, c = x | |
1307 * | |
1308 * If x is an array, then it's | |
1309 * | |
1310 * a[0] = x[0] etc | |
1311 * | |
1312 * If x isn't an array then it's | |
1313 * | |
1314 * a, b, c = [x, nil, nil] | |
1315 * | |
1316 * Which I believe is the same effect as | |
1317 * | |
1318 * a, b, c, = *x | |
1319 * | |
1320 * So we insert the splat cast node, even though it isn't there. | |
1321 */ | |
1322 | |
1323 /* | |
1324 * Create a temp for the array. | |
1325 */ | |
1326 | |
1327 final String tempName = environment.allocateLocalTemp(); | |
1328 | |
1329 /* | |
1330 * Create a sequence of instructions, with the first being the literal array assigned to | |
1331 * the temp. | |
1332 */ | |
1333 | |
1334 final List<RubyNode> sequence = new ArrayList<>(); | |
1335 | |
1336 final RubyNode splatCastNode = SplatCastNodeFactory.create(context, sourceSection, rhsTranslated); | |
1337 | |
1338 final RubyNode writeTemp = ((ReadNode) environment.findLocalVarNode(tempName, sourceSection)).makeWriteNode(splatCastNode); | |
1339 | |
1340 sequence.add(writeTemp); | |
1341 | |
1342 /* | |
1343 * Then index the temp array for each assignment on the LHS. | |
1344 */ | |
1345 | |
1346 for (int n = 0; n < preArray.size(); n++) { | |
1347 final ArrayIndexNode assignedValue = ArrayIndexNodeFactory.create(context, sourceSection, n, environment.findLocalVarNode(tempName, sourceSection)); | |
1348 | |
1349 sequence.add(translateDummyAssignment(preArray.get(n), assignedValue)); | |
1350 } | |
1351 | |
1352 if (node.getRest() != null) { | |
1353 final ArrayRestNode assignedValue = new ArrayRestNode(context, sourceSection, preArray.size(), environment.findLocalVarNode(tempName, sourceSection)); | |
1354 | |
1355 sequence.add(translateDummyAssignment(node.getRest(), assignedValue)); | |
1356 } | |
1357 | |
1358 return new SequenceNode(context, sourceSection, sequence.toArray(new RubyNode[sequence.size()])); | |
1359 } else if (node.getPre() == null && node.getPost() == null && node.getRest() instanceof org.jrubyparser.ast.StarNode) { | |
1360 return rhsTranslated; | |
1361 } else if (node.getPre() == null && node.getPost() == null && node.getRest() != null && rhs != null && !(rhs instanceof org.jrubyparser.ast.ArrayNode)) { | |
1362 /* | |
1363 * *a = b | |
1364 * | |
1365 * >= 1.8, this seems to be the same as: | |
1366 * | |
1367 * a = *b | |
1368 */ | |
1369 | |
1370 final RubyNode restTranslated = ((RubyNode) node.getRest().accept(this)).getNonProxyNode(); | |
1371 | |
1372 /* | |
1373 * Sometimes rest is a corrupt write with no RHS, like in other multiple assignments, | |
1374 * and sometimes it is already a read. | |
1375 */ | |
1376 | |
1377 ReadNode restRead; | |
1378 | |
1379 if (restTranslated instanceof ReadNode) { | |
1380 restRead = (ReadNode) restTranslated; | |
1381 } else if (restTranslated instanceof WriteNode) { | |
1382 restRead = (ReadNode) ((WriteNode) restTranslated).makeReadNode(); | |
1383 } else { | |
1384 throw new RuntimeException("Unknown form of multiple assignment " + node + " at " + node.getPosition()); | |
1385 } | |
1386 | |
1387 final SplatCastNode rhsSplatCast = SplatCastNodeFactory.create(context, sourceSection, rhsTranslated); | |
1388 | |
1389 return restRead.makeWriteNode(rhsSplatCast); | |
1390 } else if (node.getPre() == null && node.getPost() == null && node.getRest() != null && rhs != null && rhs instanceof org.jrubyparser.ast.ArrayNode) { | |
1391 /* | |
1392 * *a = [b, c] | |
1393 * | |
1394 * This seems to be the same as: | |
1395 * | |
1396 * a = [b, c] | |
1397 */ | |
1398 | |
1399 final RubyNode restTranslated = ((RubyNode) node.getRest().accept(this)).getNonProxyNode(); | |
1400 | |
1401 /* | |
1402 * Sometimes rest is a corrupt write with no RHS, like in other multiple assignments, | |
1403 * and sometimes it is already a read. | |
1404 */ | |
1405 | |
1406 ReadNode restRead; | |
1407 | |
1408 if (restTranslated instanceof ReadNode) { | |
1409 restRead = (ReadNode) restTranslated; | |
1410 } else if (restTranslated instanceof WriteNode) { | |
1411 restRead = (ReadNode) ((WriteNode) restTranslated).makeReadNode(); | |
1412 } else { | |
1413 throw new RuntimeException("Unknown form of multiple assignment " + node + " at " + node.getPosition()); | |
1414 } | |
1415 | |
1416 return restRead.makeWriteNode(rhsTranslated); | |
1417 } else { | |
1418 throw new RuntimeException("Unknown form of multiple assignment " + node + " at " + node.getPosition()); | |
1419 } | |
1420 } | |
1421 | |
1422 private RubyNode translateDummyAssignment(org.jrubyparser.ast.Node dummyAssignment, RubyNode rhs) { | |
1423 final SourceSection sourceSection = translate(dummyAssignment.getPosition()); | |
1424 | |
1425 /* | |
1426 * This is tricky. To represent the RHS of a multiple assignment they use corrupt assignment | |
1427 * values, in some cases with no value to be assigned, and in other cases with a dummy | |
1428 * value. We can't visit them normally, as they're corrupt. We can't just modify them to | |
1429 * have our RHS, as that's a node in our AST, not theirs. We can't use a dummy value in | |
1430 * their AST because I can't add new visitors to this interface. | |
1431 */ | |
1432 | |
1433 RubyNode translated; | |
1434 | |
1435 if (dummyAssignment instanceof org.jrubyparser.ast.LocalAsgnNode) { | |
1436 /* | |
1437 * They have a dummy NilImplicitNode as the RHS. Translate, convert to read, convert to | |
1438 * write which allows us to set the RHS. | |
1439 */ | |
1440 | |
1441 final WriteNode dummyTranslated = (WriteNode) ((RubyNode) dummyAssignment.accept(this)).getNonProxyNode(); | |
1442 translated = ((ReadNode) dummyTranslated.makeReadNode()).makeWriteNode(rhs); | |
1443 } else if (dummyAssignment instanceof org.jrubyparser.ast.InstAsgnNode) { | |
1444 /* | |
1445 * Same as before, just a different type of assignment. | |
1446 */ | |
1447 | |
1448 final WriteInstanceVariableNode dummyTranslated = (WriteInstanceVariableNode) dummyAssignment.accept(this); | |
1449 translated = dummyTranslated.makeReadNode().makeWriteNode(rhs); | |
1450 } else if (dummyAssignment instanceof org.jrubyparser.ast.AttrAssignNode) { | |
1451 /* | |
1452 * They've given us an AttrAssignNode with the final argument, the assigned value, | |
1453 * missing. If we translate that we'll get foo.[]=(index), so missing the value. To | |
1454 * solve we have a special version of the visitCallNode that allows us to pass another | |
1455 * already translated argument, visitCallNodeExtraArgument. However, we initially have | |
1456 * an AttrAssignNode, so we also need a special version of that. | |
1457 */ | |
1458 | |
1459 final org.jrubyparser.ast.AttrAssignNode dummyAttrAssignment = (org.jrubyparser.ast.AttrAssignNode) dummyAssignment; | |
1460 translated = visitAttrAssignNodeExtraArgument(dummyAttrAssignment, rhs); | |
1461 } else if (dummyAssignment instanceof org.jrubyparser.ast.DAsgnNode) { | |
1462 final RubyNode dummyTranslated = (RubyNode) dummyAssignment.accept(this); | |
1463 | |
1464 if (dummyTranslated.getNonProxyNode() instanceof WriteLevelVariableNode) { | |
1465 translated = ((ReadNode) ((WriteLevelVariableNode) dummyTranslated.getNonProxyNode()).makeReadNode()).makeWriteNode(rhs); | |
1466 } else { | |
1467 translated = ((ReadNode) ((WriteLocalVariableNode) dummyTranslated.getNonProxyNode()).makeReadNode()).makeWriteNode(rhs); | |
1468 } | |
1469 } else { | |
1470 translated = ((ReadNode) environment.findLocalVarNode(environment.allocateLocalTemp(), sourceSection)).makeWriteNode(rhs); | |
1471 } | |
1472 | |
1473 return translated; | |
1474 } | |
1475 | |
1476 @Override | |
1477 public Object visitNewlineNode(org.jrubyparser.ast.NewlineNode node) { | |
1478 RubyNode translated = (RubyNode) node.getNextNode().accept(this); | |
1479 | |
1480 if (context.getConfiguration().getDebug()) { | |
1481 | |
1482 RubyProxyNode proxy; | |
1483 if (translated instanceof RubyProxyNode) { | |
1484 proxy = (RubyProxyNode) translated; | |
13569
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
1485 if (proxy.getChild() instanceof CallNode) { |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
1486 // Special case; replace proxy with one registered by line, merge in information |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
1487 final CallNode callNode = (CallNode) proxy.getChild(); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
1488 final ProbeChain probeChain = proxy.getProbeChain(); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
1489 |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
1490 proxy = new RubyProxyNode(context, callNode, probeChain); |
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
1491 } |
13514 | 1492 } else { |
1493 proxy = new RubyProxyNode(context, translated); | |
1494 } | |
13569
1894412de0ed
Ruby: major upgrade in debugging support, mainly for navigation: step, next (passing over calls), return (from enclosing function), etc. Also a few bug fixes.
Michael Van De Vanter <michael.van.de.vanter@oracle.com>
parents:
13514
diff
changeset
|
1495 proxy.markAs(NodePhylum.STATEMENT); |
13514 | 1496 translated = proxy; |
1497 } | |
1498 | |
1499 if (context.getConfiguration().getTrace()) { | |
1500 RubyProxyNode proxy; | |
1501 if (translated instanceof RubyProxyNode) { | |
1502 proxy = (RubyProxyNode) translated; | |
1503 } else { | |
1504 proxy = new RubyProxyNode(context, translated); | |
1505 } | |
1506 proxy.getProbeChain().appendProbe(new RubyTraceProbe(context)); | |
1507 | |
1508 translated = proxy; | |
1509 } | |
1510 | |
1511 return translated; | |
1512 } | |
1513 | |
1514 @Override | |
1515 public Object visitNextNode(org.jrubyparser.ast.NextNode node) { | |
1516 return new NextNode(context, translate(node.getPosition())); | |
1517 } | |
1518 | |
1519 @Override | |
1520 public Object visitNilNode(org.jrubyparser.ast.NilNode node) { | |
1521 return new NilNode(context, translate(node.getPosition())); | |
1522 } | |
1523 | |
1524 @Override | |
1525 public Object visitNotNode(org.jrubyparser.ast.NotNode node) { | |
1526 final SourceSection sourceSection = translate(node.getPosition()); | |
1527 | |
1528 final BooleanCastNode booleanCastNode = BooleanCastNodeFactory.create(context, sourceSection, (RubyNode) node.getCondition().accept(this)); | |
1529 | |
1530 return new NotNode(context, sourceSection, booleanCastNode); | |
1531 } | |
1532 | |
1533 @Override | |
1534 public Object visitNthRefNode(org.jrubyparser.ast.NthRefNode node) { | |
1535 final SourceSection sourceSection = translate(node.getPosition()); | |
1536 | |
1537 final String name = "$" + node.getMatchNumber(); | |
1538 | |
1539 RubyNode readLocal = environment.findLocalVarNode(name, sourceSection); | |
1540 | |
1541 if (readLocal == null) { | |
1542 environment.declareVar(name); | |
1543 readLocal = environment.findLocalVarNode(name, sourceSection); | |
1544 } | |
1545 | |
1546 return readLocal; | |
1547 } | |
1548 | |
1549 @Override | |
1550 public Object visitOpAsgnAndNode(org.jrubyparser.ast.OpAsgnAndNode node) { | |
1551 final org.jrubyparser.ast.Node lhs = node.getFirst(); | |
1552 final org.jrubyparser.ast.Node rhs = node.getSecond(); | |
1553 | |
1554 return AndNodeFactory.create(context, translate(node.getPosition()), (RubyNode) lhs.accept(this), (RubyNode) rhs.accept(this)); | |
1555 } | |
1556 | |
1557 @Override | |
1558 public Object visitOpAsgnNode(org.jrubyparser.ast.OpAsgnNode node) { | |
1559 /* | |
1560 * We're going to de-sugar a.foo += c into a.foo = a.foo + c. Note that we can't evaluate a | |
1561 * more than once, so we put it into a temporary, and we're doing something more like: | |
1562 * | |
1563 * temp = a; temp.foo = temp.foo + c | |
1564 */ | |
1565 | |
1566 final String temp = environment.allocateLocalTemp(); | |
1567 final org.jrubyparser.ast.Node writeReceiverToTemp = new org.jrubyparser.ast.LocalAsgnNode(node.getPosition(), temp, 0, node.getReceiver()); | |
1568 final org.jrubyparser.ast.Node readReceiverFromTemp = new org.jrubyparser.ast.LocalVarNode(node.getPosition(), 0, temp); | |
1569 | |
1570 final org.jrubyparser.ast.Node readMethod = new org.jrubyparser.ast.CallNode(node.getPosition(), readReceiverFromTemp, node.getVariableName(), null); | |
1571 final org.jrubyparser.ast.Node operation = new org.jrubyparser.ast.CallNode(node.getPosition(), readMethod, node.getOperatorName(), buildArrayNode(node.getPosition(), node.getValue())); | |
1572 final org.jrubyparser.ast.Node writeMethod = new org.jrubyparser.ast.CallNode(node.getPosition(), readReceiverFromTemp, node.getVariableName() + "=", buildArrayNode(node.getPosition(), | |
1573 operation)); | |
1574 | |
1575 final org.jrubyparser.ast.BlockNode block = new org.jrubyparser.ast.BlockNode(node.getPosition()); | |
1576 block.add(writeReceiverToTemp); | |
1577 block.add(writeMethod); | |
1578 | |
1579 return block.accept(this); | |
1580 } | |
1581 | |
1582 @Override | |
1583 public Object visitOpAsgnOrNode(org.jrubyparser.ast.OpAsgnOrNode node) { | |
1584 /* | |
1585 * De-sugar x ||= y into x || x = y. No repeated evaluations there so it's easy. It's also | |
1586 * basically how jruby-parser represents it already. We'll do it directly, rather than via | |
1587 * another JRuby AST node. | |
1588 */ | |
1589 | |
1590 final org.jrubyparser.ast.Node lhs = node.getFirst(); | |
1591 final org.jrubyparser.ast.Node rhs = node.getSecond(); | |
1592 | |
1593 return OrNodeFactory.create(context, translate(node.getPosition()), (RubyNode) lhs.accept(this), (RubyNode) rhs.accept(this)); | |
1594 } | |
1595 | |
1596 @Override | |
1597 public Object visitOpElementAsgnNode(org.jrubyparser.ast.OpElementAsgnNode node) { | |
1598 /* | |
1599 * We're going to de-sugar a[b] += c into a[b] = a[b] + c. See discussion in | |
1600 * visitOpAsgnNode. | |
1601 */ | |
1602 | |
1603 org.jrubyparser.ast.Node index; | |
1604 | |
1605 if (node.getArgs() == null) { | |
1606 index = null; | |
1607 } else { | |
1608 index = node.getArgs().childNodes().get(0); | |
1609 } | |
1610 | |
1611 final org.jrubyparser.ast.Node operand = node.getValue(); | |
1612 | |
1613 final String temp = environment.allocateLocalTemp(); | |
1614 final org.jrubyparser.ast.Node writeArrayToTemp = new org.jrubyparser.ast.LocalAsgnNode(node.getPosition(), temp, 0, node.getReceiver()); | |
1615 final org.jrubyparser.ast.Node readArrayFromTemp = new org.jrubyparser.ast.LocalVarNode(node.getPosition(), 0, temp); | |
1616 | |
1617 final org.jrubyparser.ast.Node arrayRead = new org.jrubyparser.ast.CallNode(node.getPosition(), readArrayFromTemp, "[]", buildArrayNode(node.getPosition(), index)); | |
1618 | |
1619 final String op = node.getOperatorName(); | |
1620 | |
1621 org.jrubyparser.ast.Node operation = null; | |
1622 | |
1623 if (op.equals("||")) { | |
1624 operation = new org.jrubyparser.ast.OrNode(node.getPosition(), arrayRead, operand); | |
1625 } else if (op.equals("&&")) { | |
1626 operation = new org.jrubyparser.ast.AndNode(node.getPosition(), arrayRead, operand); | |
1627 } else { | |
1628 operation = new org.jrubyparser.ast.CallNode(node.getPosition(), arrayRead, node.getOperatorName(), buildArrayNode(node.getPosition(), operand)); | |
1629 } | |
1630 | |
1631 final org.jrubyparser.ast.Node arrayWrite = new org.jrubyparser.ast.CallNode(node.getPosition(), readArrayFromTemp, "[]=", buildArrayNode(node.getPosition(), index, operation)); | |
1632 | |
1633 final org.jrubyparser.ast.BlockNode block = new org.jrubyparser.ast.BlockNode(node.getPosition()); | |
1634 block.add(writeArrayToTemp); | |
1635 block.add(arrayWrite); | |
1636 | |
1637 return block.accept(this); | |
1638 } | |
1639 | |
1640 private static org.jrubyparser.ast.ArrayNode buildArrayNode(org.jrubyparser.SourcePosition sourcePosition, org.jrubyparser.ast.Node first, org.jrubyparser.ast.Node... rest) { | |
1641 if (first == null) { | |
1642 return new org.jrubyparser.ast.ArrayNode(sourcePosition); | |
1643 } | |
1644 | |
1645 final org.jrubyparser.ast.ArrayNode array = new org.jrubyparser.ast.ArrayNode(sourcePosition, first); | |
1646 | |
1647 for (org.jrubyparser.ast.Node node : rest) { | |
1648 array.add(node); | |
1649 } | |
1650 | |
1651 return array; | |
1652 } | |
1653 | |
1654 @Override | |
1655 public Object visitOrNode(org.jrubyparser.ast.OrNode node) { | |
1656 final SourceSection sourceSection = translate(node.getPosition()); | |
1657 | |
1658 RubyNode x; | |
1659 | |
1660 if (node.getFirst() == null) { | |
1661 x = new NilNode(context, sourceSection); | |
1662 } else { | |
1663 x = (RubyNode) node.getFirst().accept(this); | |
1664 } | |
1665 | |
1666 RubyNode y; | |
1667 | |
1668 if (node.getSecond() == null) { | |
1669 y = new NilNode(context, sourceSection); | |
1670 } else { | |
1671 y = (RubyNode) node.getSecond().accept(this); | |
1672 } | |
1673 | |
1674 return OrNodeFactory.create(context, sourceSection, x, y); | |
1675 } | |
1676 | |
1677 @Override | |
1678 public Object visitPostExeNode(org.jrubyparser.ast.PostExeNode node) { | |
1679 return unimplemented(node); | |
1680 } | |
1681 | |
1682 @Override | |
1683 public Object visitPreExeNode(org.jrubyparser.ast.PreExeNode node) { | |
1684 return unimplemented(node); | |
1685 } | |
1686 | |
1687 @Override | |
1688 public Object visitRedoNode(org.jrubyparser.ast.RedoNode node) { | |
1689 return new RedoNode(context, translate(node.getPosition())); | |
1690 } | |
1691 | |
1692 @Override | |
1693 public Object visitRegexpNode(org.jrubyparser.ast.RegexpNode node) { | |
1694 RubyRegexp regexp; | |
1695 | |
1696 try { | |
1697 final String patternText = node.getValue(); | |
1698 | |
1699 int flags = Pattern.MULTILINE | Pattern.UNIX_LINES; | |
1700 | |
1701 final org.jrubyparser.RegexpOptions options = node.getOptions(); | |
1702 | |
1703 if (options.isIgnorecase()) { | |
1704 flags |= Pattern.CASE_INSENSITIVE; | |
1705 } | |
1706 | |
1707 if (options.isMultiline()) { | |
1708 // TODO(cs): isn't this the default? | |
1709 flags |= Pattern.MULTILINE; | |
1710 } | |
1711 | |
1712 final Pattern pattern = Pattern.compile(patternText, flags); | |
1713 | |
1714 regexp = new RubyRegexp(context.getCoreLibrary().getRegexpClass(), pattern); | |
1715 } catch (PatternSyntaxException e) { | |
1716 context.implementationMessage("failed to parse Ruby regexp " + node.getValue() + " as Java regexp - replacing with ."); | |
1717 regexp = new RubyRegexp(context.getCoreLibrary().getRegexpClass(), "."); | |
1718 } | |
1719 | |
1720 final ObjectLiteralNode literalNode = new ObjectLiteralNode(context, translate(node.getPosition()), regexp); | |
1721 return literalNode; | |
1722 } | |
1723 | |
1724 @Override | |
1725 public Object visitRescueBodyNode(org.jrubyparser.ast.RescueBodyNode node) { | |
1726 return unimplemented(node); | |
1727 } | |
1728 | |
1729 @Override | |
1730 public Object visitRescueNode(org.jrubyparser.ast.RescueNode node) { | |
1731 final SourceSection sourceSection = translate(node.getPosition()); | |
1732 | |
1733 RubyNode tryPart; | |
1734 | |
1735 if (node.getBody() != null) { | |
1736 tryPart = (RubyNode) node.getBody().accept(this); | |
1737 } else { | |
1738 tryPart = new NilNode(context, sourceSection); | |
1739 } | |
1740 | |
1741 final List<RescueNode> rescueNodes = new ArrayList<>(); | |
1742 | |
1743 org.jrubyparser.ast.RescueBodyNode rescueBody = node.getRescue(); | |
1744 | |
1745 while (rescueBody != null) { | |
1746 if (rescueBody.getExceptions() != null) { | |
1747 if (rescueBody.getExceptions() instanceof org.jrubyparser.ast.ArrayNode) { | |
1748 final List<org.jrubyparser.ast.Node> exceptionNodes = ((org.jrubyparser.ast.ArrayNode) rescueBody.getExceptions()).childNodes(); | |
1749 | |
1750 final RubyNode[] handlingClasses = new RubyNode[exceptionNodes.size()]; | |
1751 | |
1752 for (int n = 0; n < handlingClasses.length; n++) { | |
1753 handlingClasses[n] = (RubyNode) exceptionNodes.get(n).accept(this); | |
1754 } | |
1755 | |
1756 RubyNode translatedBody; | |
1757 | |
1758 if (rescueBody.getBody() == null) { | |
1759 translatedBody = new NilNode(context, sourceSection); | |
1760 } else { | |
1761 translatedBody = (RubyNode) rescueBody.getBody().accept(this); | |
1762 } | |
1763 | |
1764 final RescueClassesNode rescueNode = new RescueClassesNode(context, sourceSection, handlingClasses, translatedBody); | |
1765 rescueNodes.add(rescueNode); | |
1766 } else if (rescueBody.getExceptions() instanceof org.jrubyparser.ast.SplatNode) { | |
1767 final org.jrubyparser.ast.SplatNode splat = (org.jrubyparser.ast.SplatNode) rescueBody.getExceptions(); | |
1768 | |
1769 RubyNode splatTranslated; | |
1770 | |
1771 if (splat.getValue() == null) { | |
1772 splatTranslated = new NilNode(context, sourceSection); | |
1773 } else { | |
1774 splatTranslated = (RubyNode) splat.getValue().accept(this); | |
1775 } | |
1776 | |
1777 RubyNode bodyTranslated; | |
1778 | |
1779 if (rescueBody.getBody() == null) { | |
1780 bodyTranslated = new NilNode(context, sourceSection); | |
1781 } else { | |
1782 bodyTranslated = (RubyNode) rescueBody.getBody().accept(this); | |
1783 } | |
1784 | |
1785 final RescueSplatNode rescueNode = new RescueSplatNode(context, sourceSection, splatTranslated, bodyTranslated); | |
1786 rescueNodes.add(rescueNode); | |
1787 } else { | |
1788 unimplemented(node); | |
1789 } | |
1790 } else { | |
1791 RubyNode bodyNode; | |
1792 | |
1793 if (rescueBody.getBody() == null) { | |
1794 bodyNode = new NilNode(context, sourceSection); | |
1795 } else { | |
1796 bodyNode = (RubyNode) rescueBody.getBody().accept(this); | |
1797 } | |
1798 | |
1799 final RescueAnyNode rescueNode = new RescueAnyNode(context, sourceSection, bodyNode); | |
1800 rescueNodes.add(rescueNode); | |
1801 } | |
1802 | |
1803 rescueBody = rescueBody.getOptRescue(); | |
1804 } | |
1805 | |
1806 RubyNode elsePart; | |
1807 | |
1808 if (node.getElse() != null) { | |
1809 elsePart = (RubyNode) node.getElse().accept(this); | |
1810 } else { | |
1811 elsePart = new NilNode(context, sourceSection); | |
1812 } | |
1813 | |
1814 return new TryNode(context, sourceSection, tryPart, rescueNodes.toArray(new RescueNode[rescueNodes.size()]), elsePart); | |
1815 } | |
1816 | |
1817 @Override | |
1818 public Object visitRestArgNode(org.jrubyparser.ast.RestArgNode node) { | |
1819 return unimplemented(node); | |
1820 } | |
1821 | |
1822 @Override | |
1823 public Object visitRetryNode(org.jrubyparser.ast.RetryNode node) { | |
1824 return new RetryNode(context, translate(node.getPosition())); | |
1825 } | |
1826 | |
1827 @Override | |
1828 public Object visitReturnNode(org.jrubyparser.ast.ReturnNode node) { | |
1829 final SourceSection sourceSection = translate(node.getPosition()); | |
1830 | |
1831 RubyNode translatedChild; | |
1832 | |
1833 if (node.getValue() == null) { | |
1834 translatedChild = new NilNode(context, sourceSection); | |
1835 } else { | |
1836 translatedChild = (RubyNode) node.getValue().accept(this); | |
1837 } | |
1838 | |
1839 return new ReturnNode(context, sourceSection, environment.getReturnID(), translatedChild); | |
1840 } | |
1841 | |
1842 @Override | |
1843 public Object visitRootNode(org.jrubyparser.ast.RootNode node) { | |
1844 return unimplemented(node); | |
1845 } | |
1846 | |
1847 @Override | |
1848 public Object visitSClassNode(org.jrubyparser.ast.SClassNode node) { | |
1849 final SourceSection sourceSection = translate(node.getPosition()); | |
1850 | |
1851 final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getParser().allocateReturnID(), true, true, | |
1852 new UniqueMethodIdentifier()); | |
1853 final ModuleTranslator classTranslator = new ModuleTranslator(context, this, newEnvironment, source); | |
1854 | |
1855 final MethodDefinitionNode definitionMethod = classTranslator.compileClassNode(node.getPosition(), "singleton", node.getBody()); | |
1856 | |
1857 final RubyNode receiverNode = (RubyNode) node.getReceiver().accept(this); | |
1858 | |
1859 final SingletonClassNode singletonClassNode = new SingletonClassNode(context, sourceSection, receiverNode); | |
1860 | |
1861 return new OpenModuleNode(context, sourceSection, singletonClassNode, definitionMethod); | |
1862 } | |
1863 | |
1864 @Override | |
1865 public Object visitSValueNode(org.jrubyparser.ast.SValueNode node) { | |
1866 return node.getValue().accept(this); | |
1867 } | |
1868 | |
1869 @Override | |
1870 public Object visitSelfNode(org.jrubyparser.ast.SelfNode node) { | |
1871 return new SelfNode(context, translate(node.getPosition())); | |
1872 } | |
1873 | |
1874 @Override | |
1875 public Object visitSplatNode(org.jrubyparser.ast.SplatNode node) { | |
1876 final SourceSection sourceSection = translate(node.getPosition()); | |
1877 | |
1878 RubyNode value; | |
1879 | |
1880 if (node.getValue() == null) { | |
1881 value = new NilNode(context, sourceSection); | |
1882 } else { | |
1883 value = (RubyNode) node.getValue().accept(this); | |
1884 } | |
1885 | |
1886 return SplatCastNodeFactory.create(context, sourceSection, value); | |
1887 } | |
1888 | |
1889 @Override | |
1890 public Object visitStrNode(org.jrubyparser.ast.StrNode node) { | |
1891 return new StringLiteralNode(context, translate(node.getPosition()), node.getValue()); | |
1892 } | |
1893 | |
1894 @Override | |
1895 public Object visitSuperNode(org.jrubyparser.ast.SuperNode node) { | |
1896 return unimplemented(node); | |
1897 } | |
1898 | |
1899 @Override | |
1900 public Object visitSymbolNode(org.jrubyparser.ast.SymbolNode node) { | |
1901 return new ObjectLiteralNode(context, translate(node.getPosition()), new RubySymbol(context.getCoreLibrary().getSymbolClass(), node.getName())); | |
1902 } | |
1903 | |
1904 @Override | |
1905 public Object visitToAryNode(org.jrubyparser.ast.ToAryNode node) { | |
1906 return unimplemented(node); | |
1907 } | |
1908 | |
1909 @Override | |
1910 public Object visitTrueNode(org.jrubyparser.ast.TrueNode node) { | |
1911 return new BooleanLiteralNode(context, translate(node.getPosition()), true); | |
1912 } | |
1913 | |
1914 @Override | |
1915 public Object visitUndefNode(org.jrubyparser.ast.UndefNode node) { | |
1916 return unimplemented(node); | |
1917 } | |
1918 | |
1919 @Override | |
1920 public Object visitUntilNode(org.jrubyparser.ast.UntilNode node) { | |
1921 final SourceSection sourceSection = translate(node.getPosition()); | |
1922 | |
1923 RubyNode condition; | |
1924 | |
1925 if (node.getCondition() == null) { | |
1926 condition = new NilNode(context, sourceSection); | |
1927 } else { | |
1928 condition = (RubyNode) node.getCondition().accept(this); | |
1929 } | |
1930 | |
1931 final BooleanCastNode conditionCast = BooleanCastNodeFactory.create(context, sourceSection, condition); | |
1932 final NotNode conditionCastNot = new NotNode(context, sourceSection, conditionCast); | |
1933 final BooleanCastNode conditionCastNotCast = BooleanCastNodeFactory.create(context, sourceSection, conditionCastNot); | |
1934 | |
1935 final RubyNode body = (RubyNode) node.getBody().accept(this); | |
1936 | |
1937 return new WhileNode(context, sourceSection, conditionCastNotCast, body); | |
1938 } | |
1939 | |
1940 @Override | |
1941 public Object visitVAliasNode(org.jrubyparser.ast.VAliasNode node) { | |
1942 return unimplemented(node); | |
1943 } | |
1944 | |
1945 @Override | |
1946 public Object visitVCallNode(org.jrubyparser.ast.VCallNode node) { | |
1947 final org.jrubyparser.ast.Node receiver = new org.jrubyparser.ast.SelfNode(node.getPosition()); | |
1948 final org.jrubyparser.ast.Node args = null; | |
1949 final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), receiver, node.getName(), args); | |
1950 | |
1951 return callNode.accept(this); | |
1952 } | |
1953 | |
1954 @Override | |
1955 public Object visitWhenNode(org.jrubyparser.ast.WhenNode node) { | |
1956 return unimplemented(node); | |
1957 } | |
1958 | |
1959 @Override | |
1960 public Object visitWhileNode(org.jrubyparser.ast.WhileNode node) { | |
1961 final SourceSection sourceSection = translate(node.getPosition()); | |
1962 | |
1963 RubyNode condition; | |
1964 | |
1965 if (node.getCondition() == null) { | |
1966 condition = new NilNode(context, sourceSection); | |
1967 } else { | |
1968 condition = (RubyNode) node.getCondition().accept(this); | |
1969 } | |
1970 | |
1971 final BooleanCastNode conditionCast = BooleanCastNodeFactory.create(context, sourceSection, condition); | |
1972 | |
1973 final RubyNode body = (RubyNode) node.getBody().accept(this); | |
1974 | |
1975 return new WhileNode(context, sourceSection, conditionCast, body); | |
1976 } | |
1977 | |
1978 @Override | |
1979 public Object visitXStrNode(org.jrubyparser.ast.XStrNode node) { | |
1980 SourceSection sourceSection = translate(node.getPosition()); | |
1981 | |
1982 final StringLiteralNode literal = new StringLiteralNode(context, sourceSection, node.getValue()); | |
1983 | |
1984 return new SystemNode(context, sourceSection, literal); | |
1985 } | |
1986 | |
1987 @Override | |
1988 public Object visitYieldNode(org.jrubyparser.ast.YieldNode node) { | |
1989 final List<org.jrubyparser.ast.Node> arguments = new ArrayList<>(); | |
1990 | |
1991 final org.jrubyparser.ast.Node argsNode = node.getArgs(); | |
1992 | |
1993 if (argsNode != null) { | |
1994 if (argsNode instanceof org.jrubyparser.ast.ListNode) { | |
1995 arguments.addAll(((org.jrubyparser.ast.ListNode) node.getArgs()).childNodes()); | |
1996 } else { | |
1997 arguments.add(node.getArgs()); | |
1998 } | |
1999 } | |
2000 | |
2001 final List<RubyNode> argumentsTranslated = new ArrayList<>(); | |
2002 | |
2003 for (org.jrubyparser.ast.Node argument : arguments) { | |
2004 argumentsTranslated.add((RubyNode) argument.accept(this)); | |
2005 } | |
2006 | |
2007 final RubyNode[] argumentsTranslatedArray = argumentsTranslated.toArray(new RubyNode[argumentsTranslated.size()]); | |
2008 | |
2009 return new YieldNode(context, translate(node.getPosition()), argumentsTranslatedArray); | |
2010 } | |
2011 | |
2012 @Override | |
2013 public Object visitZArrayNode(org.jrubyparser.ast.ZArrayNode node) { | |
2014 final RubyNode[] values = new RubyNode[0]; | |
2015 | |
2016 return new UninitialisedArrayLiteralNode(context, translate(node.getPosition()), values); | |
2017 } | |
2018 | |
2019 @Override | |
2020 public Object visitZSuperNode(org.jrubyparser.ast.ZSuperNode node) { | |
2021 return unimplemented(node); | |
2022 } | |
2023 | |
2024 public Object visitArgumentNode(org.jrubyparser.ast.ArgumentNode node) { | |
2025 return unimplemented(node); | |
2026 } | |
2027 | |
2028 public Object visitCommentNode(org.jrubyparser.ast.CommentNode node) { | |
2029 return unimplemented(node); | |
2030 } | |
2031 | |
2032 public Object visitKeywordArgNode(org.jrubyparser.ast.KeywordArgNode node) { | |
2033 return unimplemented(node); | |
2034 } | |
2035 | |
2036 public Object visitKeywordRestArgNode(org.jrubyparser.ast.KeywordRestArgNode node) { | |
2037 return unimplemented(node); | |
2038 } | |
2039 | |
2040 public Object visitListNode(org.jrubyparser.ast.ListNode node) { | |
2041 return unimplemented(node); | |
2042 } | |
2043 | |
2044 public Object visitMethodNameNode(org.jrubyparser.ast.MethodNameNode node) { | |
2045 return unimplemented(node); | |
2046 } | |
2047 | |
2048 public Object visitOptArgNode(org.jrubyparser.ast.OptArgNode node) { | |
2049 return unimplemented(node); | |
2050 } | |
2051 | |
2052 public Object visitSyntaxNode(org.jrubyparser.ast.SyntaxNode node) { | |
2053 return unimplemented(node); | |
2054 } | |
2055 | |
2056 public Object visitImplicitNilNode(org.jrubyparser.ast.ImplicitNilNode node) { | |
2057 return new NilNode(context, translate(node.getPosition())); | |
2058 } | |
2059 | |
2060 public Object visitLambdaNode(org.jrubyparser.ast.LambdaNode node) { | |
2061 // TODO(cs): code copied and modified from visitIterNode - extract common | |
2062 | |
2063 final TranslatorEnvironment newEnvironment = new TranslatorEnvironment(context, environment, environment.getParser(), environment.getReturnID(), false, false, new UniqueMethodIdentifier()); | |
2064 final MethodTranslator methodCompiler = new MethodTranslator(context, this, newEnvironment, false, source); | |
2065 | |
2066 org.jrubyparser.ast.ArgsNode argsNode; | |
2067 | |
2068 if (node.getVar() instanceof org.jrubyparser.ast.ArgsNode) { | |
2069 argsNode = (org.jrubyparser.ast.ArgsNode) node.getVar(); | |
2070 } else if (node.getVar() instanceof org.jrubyparser.ast.DAsgnNode) { | |
2071 final org.jrubyparser.ast.ArgumentNode arg = new org.jrubyparser.ast.ArgumentNode(node.getPosition(), ((org.jrubyparser.ast.DAsgnNode) node.getVar()).getName()); | |
2072 final org.jrubyparser.ast.ListNode preArgs = new org.jrubyparser.ast.ArrayNode(node.getPosition(), arg); | |
2073 argsNode = new org.jrubyparser.ast.ArgsNode(node.getPosition(), preArgs, null, null, null, null, null, null); | |
2074 } else if (node.getVar() == null) { | |
2075 argsNode = null; | |
2076 } else { | |
2077 throw new UnsupportedOperationException(); | |
2078 } | |
2079 | |
2080 final MethodDefinitionNode definitionNode = methodCompiler.compileFunctionNode(translate(node.getPosition()), "(lambda)", argsNode, node.getBody()); | |
2081 | |
2082 return new LambdaNode(context, translate(node.getPosition()), definitionNode); | |
2083 } | |
2084 | |
2085 public Object visitUnaryCallNode(org.jrubyparser.ast.UnaryCallNode node) { | |
2086 final org.jrubyparser.ast.Node callNode = new org.jrubyparser.ast.CallNode(node.getPosition(), node.getReceiver(), node.getName(), null, null); | |
2087 return callNode.accept(this); | |
2088 } | |
2089 | |
2090 protected Object unimplemented(org.jrubyparser.ast.Node node) { | |
2091 context.implementationMessage("warning: %s at %s does nothing", node, node.getPosition()); | |
2092 return new NilNode(context, translate(node.getPosition())); | |
2093 } | |
2094 | |
2095 protected SourceSection translate(final org.jrubyparser.SourcePosition sourcePosition) { | |
2096 try { | |
2097 // TODO(cs): get an identifier | |
2098 final String identifier = "(identifier)"; | |
2099 | |
2100 // TODO(cs): work out the start column | |
2101 final int startColumn = -1; | |
2102 | |
2103 final int charLength = sourcePosition.getEndOffset() - sourcePosition.getStartOffset(); | |
2104 | |
2105 return new DefaultSourceSection(source, identifier, sourcePosition.getStartLine() + 1, startColumn, sourcePosition.getStartOffset(), charLength); | |
2106 } catch (UnsupportedOperationException e) { | |
2107 // In some circumstances JRuby can't tell you what the position is | |
2108 return translate(new org.jrubyparser.SourcePosition("(unknown)", 0, 0)); | |
2109 } | |
2110 } | |
2111 | |
2112 protected SequenceNode initFlipFlopStates(SourceSection sourceSection) { | |
2113 final RubyNode[] initNodes = new RubyNode[environment.getFlipFlopStates().size()]; | |
2114 | |
2115 for (int n = 0; n < initNodes.length; n++) { | |
2116 initNodes[n] = new InitFlipFlopSlotNode(context, sourceSection, environment.getFlipFlopStates().get(n)); | |
2117 } | |
2118 | |
2119 return new SequenceNode(context, sourceSection, initNodes); | |
2120 } | |
2121 | |
2122 } |