| /******************************************************************************* |
| * Copyright (c) 2005, 2016 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| *******************************************************************************/ |
| package org.eclipse.dltk.ruby.internal.parsers.jruby; |
| |
| import java.math.BigInteger; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.ListIterator; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.dltk.ast.ASTNode; |
| import org.eclipse.dltk.ast.Modifiers; |
| import org.eclipse.dltk.ast.declarations.Argument; |
| import org.eclipse.dltk.ast.declarations.MethodDeclaration; |
| import org.eclipse.dltk.ast.declarations.ModuleDeclaration; |
| import org.eclipse.dltk.ast.declarations.TypeDeclaration; |
| import org.eclipse.dltk.ast.expressions.BigNumericLiteral; |
| import org.eclipse.dltk.ast.expressions.BooleanLiteral; |
| import org.eclipse.dltk.ast.expressions.CallArgumentsList; |
| import org.eclipse.dltk.ast.expressions.CallExpression; |
| import org.eclipse.dltk.ast.expressions.ExpressionConstants; |
| import org.eclipse.dltk.ast.expressions.FloatNumericLiteral; |
| import org.eclipse.dltk.ast.expressions.NilLiteral; |
| import org.eclipse.dltk.ast.expressions.NumericLiteral; |
| import org.eclipse.dltk.ast.expressions.StringLiteral; |
| import org.eclipse.dltk.ast.references.ConstantReference; |
| import org.eclipse.dltk.ast.references.SimpleReference; |
| import org.eclipse.dltk.ast.references.VariableReference; |
| import org.eclipse.dltk.ast.statements.Block; |
| import org.eclipse.dltk.compiler.util.Util; |
| import org.eclipse.dltk.ruby.ast.RubyAliasExpression; |
| import org.eclipse.dltk.ruby.ast.RubyArrayExpression; |
| import org.eclipse.dltk.ruby.ast.RubyAssignment; |
| import org.eclipse.dltk.ruby.ast.RubyBacktickStringLiteral; |
| import org.eclipse.dltk.ruby.ast.RubyBeginExpression; |
| import org.eclipse.dltk.ruby.ast.RubyBinaryExpression; |
| import org.eclipse.dltk.ruby.ast.RubyBlock; |
| import org.eclipse.dltk.ruby.ast.RubyBreakExpression; |
| import org.eclipse.dltk.ruby.ast.RubyCallArgumentsList; |
| import org.eclipse.dltk.ruby.ast.RubyCaseStatement; |
| import org.eclipse.dltk.ruby.ast.RubyClassDeclaration; |
| import org.eclipse.dltk.ruby.ast.RubyColonExpression; |
| import org.eclipse.dltk.ruby.ast.RubyConstantDeclaration; |
| import org.eclipse.dltk.ruby.ast.RubyDAssgnExpression; |
| import org.eclipse.dltk.ruby.ast.RubyDRegexpExpression; |
| import org.eclipse.dltk.ruby.ast.RubyDSymbolExpression; |
| import org.eclipse.dltk.ruby.ast.RubyDVarExpression; |
| import org.eclipse.dltk.ruby.ast.RubyDefinedExpression; |
| import org.eclipse.dltk.ruby.ast.RubyDotExpression; |
| import org.eclipse.dltk.ruby.ast.RubyDynamicBackquoteStringExpression; |
| import org.eclipse.dltk.ruby.ast.RubyDynamicStringExpression; |
| import org.eclipse.dltk.ruby.ast.RubyEnsureExpression; |
| import org.eclipse.dltk.ruby.ast.RubyEvaluatableStringExpression; |
| import org.eclipse.dltk.ruby.ast.RubyForStatement2; |
| import org.eclipse.dltk.ruby.ast.RubyHashExpression; |
| import org.eclipse.dltk.ruby.ast.RubyHashPairExpression; |
| import org.eclipse.dltk.ruby.ast.RubyIfStatement; |
| import org.eclipse.dltk.ruby.ast.RubyMatch2Expression; |
| import org.eclipse.dltk.ruby.ast.RubyMatch3Expression; |
| import org.eclipse.dltk.ruby.ast.RubyMatchExpression; |
| import org.eclipse.dltk.ruby.ast.RubyMethodArgument; |
| import org.eclipse.dltk.ruby.ast.RubyModuleDeclaration; |
| import org.eclipse.dltk.ruby.ast.RubyMultipleAssignmentStatement; |
| import org.eclipse.dltk.ruby.ast.RubyNextExpression; |
| import org.eclipse.dltk.ruby.ast.RubyNotExpression; |
| import org.eclipse.dltk.ruby.ast.RubyRedoExpression; |
| import org.eclipse.dltk.ruby.ast.RubyRegexpExpression; |
| import org.eclipse.dltk.ruby.ast.RubyRescueBodyStatement; |
| import org.eclipse.dltk.ruby.ast.RubyRescueStatement; |
| import org.eclipse.dltk.ruby.ast.RubyRetryExpression; |
| import org.eclipse.dltk.ruby.ast.RubyReturnStatement; |
| import org.eclipse.dltk.ruby.ast.RubySelfReference; |
| import org.eclipse.dltk.ruby.ast.RubySingletonClassDeclaration; |
| import org.eclipse.dltk.ruby.ast.RubySingletonMethodDeclaration; |
| import org.eclipse.dltk.ruby.ast.RubySuperExpression; |
| import org.eclipse.dltk.ruby.ast.RubySymbolReference; |
| import org.eclipse.dltk.ruby.ast.RubyUndefStatement; |
| import org.eclipse.dltk.ruby.ast.RubyUntilStatement; |
| import org.eclipse.dltk.ruby.ast.RubyVariableKind; |
| import org.eclipse.dltk.ruby.ast.RubyWhenStatement; |
| import org.eclipse.dltk.ruby.ast.RubyWhileStatement; |
| import org.eclipse.dltk.ruby.ast.RubyYieldExpression; |
| import org.eclipse.dltk.ruby.core.RubyPlugin; |
| import org.eclipse.dltk.ruby.core.utils.RubySyntaxUtils; |
| import org.eclipse.dltk.ruby.internal.parser.JRubySourceParser; |
| import org.eclipse.osgi.util.NLS; |
| import org.jruby.ast.AliasNode; |
| import org.jruby.ast.AndNode; |
| import org.jruby.ast.ArgsCatNode; |
| import org.jruby.ast.ArgsNode; |
| import org.jruby.ast.ArgsPushNode; |
| import org.jruby.ast.ArgumentNode; |
| import org.jruby.ast.ArrayNode; |
| import org.jruby.ast.AttrAssignNode; |
| import org.jruby.ast.BackRefNode; |
| import org.jruby.ast.BeginNode; |
| import org.jruby.ast.BignumNode; |
| import org.jruby.ast.BlockArgNode; |
| import org.jruby.ast.BlockNode; |
| import org.jruby.ast.BlockPassNode; |
| import org.jruby.ast.BreakNode; |
| import org.jruby.ast.CallNode; |
| import org.jruby.ast.CaseNode; |
| import org.jruby.ast.ClassNode; |
| import org.jruby.ast.ClassVarAsgnNode; |
| import org.jruby.ast.ClassVarDeclNode; |
| import org.jruby.ast.ClassVarNode; |
| import org.jruby.ast.Colon2Node; |
| import org.jruby.ast.Colon3Node; |
| import org.jruby.ast.ConstDeclNode; |
| import org.jruby.ast.ConstNode; |
| import org.jruby.ast.DAsgnNode; |
| import org.jruby.ast.DRegexpNode; |
| import org.jruby.ast.DStrNode; |
| import org.jruby.ast.DSymbolNode; |
| import org.jruby.ast.DVarNode; |
| import org.jruby.ast.DXStrNode; |
| import org.jruby.ast.DefinedNode; |
| import org.jruby.ast.DefnNode; |
| import org.jruby.ast.DefsNode; |
| import org.jruby.ast.DotNode; |
| import org.jruby.ast.EnsureNode; |
| import org.jruby.ast.EvStrNode; |
| import org.jruby.ast.FCallNode; |
| import org.jruby.ast.FalseNode; |
| import org.jruby.ast.FixnumNode; |
| import org.jruby.ast.FlipNode; |
| import org.jruby.ast.FloatNode; |
| import org.jruby.ast.ForNode; |
| import org.jruby.ast.GlobalAsgnNode; |
| import org.jruby.ast.GlobalVarNode; |
| import org.jruby.ast.HashNode; |
| import org.jruby.ast.IfNode; |
| import org.jruby.ast.InstAsgnNode; |
| import org.jruby.ast.InstVarNode; |
| import org.jruby.ast.IterNode; |
| import org.jruby.ast.ListNode; |
| import org.jruby.ast.LocalAsgnNode; |
| import org.jruby.ast.LocalVarNode; |
| import org.jruby.ast.Match2Node; |
| import org.jruby.ast.Match3Node; |
| import org.jruby.ast.MatchNode; |
| import org.jruby.ast.ModuleNode; |
| import org.jruby.ast.MultipleAsgnNode; |
| import org.jruby.ast.NewlineNode; |
| import org.jruby.ast.NextNode; |
| import org.jruby.ast.NilNode; |
| import org.jruby.ast.Node; |
| import org.jruby.ast.NotNode; |
| import org.jruby.ast.NthRefNode; |
| import org.jruby.ast.OpAsgnAndNode; |
| import org.jruby.ast.OpAsgnNode; |
| import org.jruby.ast.OpAsgnOrNode; |
| import org.jruby.ast.OpElementAsgnNode; |
| import org.jruby.ast.OptNNode; |
| import org.jruby.ast.OrNode; |
| import org.jruby.ast.PostExeNode; |
| import org.jruby.ast.RedoNode; |
| import org.jruby.ast.RegexpNode; |
| import org.jruby.ast.RescueBodyNode; |
| import org.jruby.ast.RescueNode; |
| import org.jruby.ast.RetryNode; |
| import org.jruby.ast.ReturnNode; |
| import org.jruby.ast.RootNode; |
| import org.jruby.ast.SClassNode; |
| import org.jruby.ast.SValueNode; |
| import org.jruby.ast.SelfNode; |
| import org.jruby.ast.SplatNode; |
| import org.jruby.ast.StrNode; |
| import org.jruby.ast.SuperNode; |
| import org.jruby.ast.SymbolNode; |
| import org.jruby.ast.ToAryNode; |
| import org.jruby.ast.TrueNode; |
| import org.jruby.ast.UndefNode; |
| import org.jruby.ast.UntilNode; |
| import org.jruby.ast.VAliasNode; |
| import org.jruby.ast.VCallNode; |
| import org.jruby.ast.WhenNode; |
| import org.jruby.ast.WhileNode; |
| import org.jruby.ast.XStrNode; |
| import org.jruby.ast.YieldNode; |
| import org.jruby.ast.ZArrayNode; |
| import org.jruby.ast.ZSuperNode; |
| import org.jruby.ast.visitor.NodeVisitor; |
| import org.jruby.evaluator.Instruction; |
| import org.jruby.lexer.yacc.ISourcePosition; |
| import org.jruby.lexer.yacc.SourcePosition; |
| import org.jruby.runtime.Arity; |
| import org.jruby.runtime.Visibility; |
| |
| /** |
| * RubyASTBuildVisitor performs transformation from JRuby's AST to DLTK AST. |
| */ |
| public class RubyASTBuildVisitor implements NodeVisitor { |
| |
| protected static final boolean TRACE_RECOVERING = Boolean |
| .valueOf( |
| Platform |
| .getDebugOption("org.eclipse.dltk.ruby.core/parsing/traceRecoveryWhenInterpretingAST")) //$NON-NLS-1$ |
| .booleanValue(); |
| |
| private ModuleDeclaration module; |
| |
| private final char[] content; |
| |
| protected static interface IState { |
| public void add(ASTNode node); |
| } |
| |
| protected static String getShortClassName(Object instance) { |
| final String className = instance.getClass().getName(); |
| int dotIndex = className.lastIndexOf('.'); |
| int dollarIndex = className.lastIndexOf('$'); |
| if (dotIndex >= 0 || dollarIndex >= 0) { |
| return className.substring(Math.max(dotIndex, dollarIndex) + 1); |
| } else { |
| return className; |
| } |
| } |
| |
| protected static class CollectingState implements IState { |
| private final ArrayList<ASTNode> list; |
| |
| public CollectingState() { |
| list = new ArrayList<ASTNode>(); |
| } |
| |
| @Override |
| public void add(ASTNode s) { |
| list.add(s); |
| } |
| |
| public ArrayList<ASTNode> getList() { |
| return list; |
| } |
| |
| public void reset() { |
| list.clear(); |
| } |
| |
| @Override |
| public String toString() { |
| return getShortClassName(this) + '[' + list + ']'; |
| } |
| |
| } |
| |
| protected static class ArgumentsState implements IState { |
| private final RubyCallArgumentsList list; |
| |
| public ArgumentsState(RubyCallArgumentsList list) { |
| this.list = list; |
| } |
| |
| @Override |
| public void add(ASTNode s) { |
| list.addArgument(s, 0); |
| } |
| |
| @Override |
| public String toString() { |
| return getShortClassName(this) + '[' + list + ']'; |
| } |
| } |
| |
| protected static class TopLevelState implements IState { |
| private final ModuleDeclaration module; |
| |
| public TopLevelState(ModuleDeclaration module) { |
| this.module = module; |
| } |
| |
| @Override |
| public void add(ASTNode statement) { |
| module.getStatements().add(statement); |
| } |
| |
| @Override |
| public String toString() { |
| return getShortClassName(this); |
| } |
| } |
| |
| protected static abstract class ClassLikeState implements IState { |
| public int visibility; |
| public final TypeDeclaration type; |
| public final String fullName; |
| |
| public ClassLikeState(TypeDeclaration type, String parentName) { |
| this.type = type; |
| this.fullName = parentName.length() == 0 ? type.getName() |
| : parentName + "::" + type.getName(); //$NON-NLS-1$ |
| visibility = Modifiers.AccPublic; |
| } |
| |
| @Override |
| public void add(ASTNode statement) { |
| type.getStatements().add(statement); |
| } |
| |
| @Override |
| public String toString() { |
| return getShortClassName(this) + '[' + fullName + ']'; |
| } |
| } |
| |
| protected static class ClassState extends ClassLikeState { |
| |
| public ClassState(TypeDeclaration type, String parentName) { |
| super(type, parentName); |
| } |
| |
| } |
| |
| protected static class ModuleState extends ClassLikeState { |
| |
| public ModuleState(TypeDeclaration type, String parentName) { |
| super(type, parentName); |
| } |
| |
| } |
| |
| protected static class MethodState implements IState { |
| |
| private final MethodDeclaration method; |
| |
| public MethodState(MethodDeclaration method) { |
| this.method = method; |
| } |
| |
| @Override |
| public void add(ASTNode statement) { |
| method.getStatements().add(statement); |
| } |
| |
| @Override |
| public String toString() { |
| return getShortClassName(this) + '[' + method.getName() + ']'; |
| } |
| } |
| |
| protected static class BlockState implements IState { |
| |
| public final Block block; |
| |
| public BlockState(Block block) { |
| this.block = block; |
| } |
| |
| @Override |
| public void add(ASTNode statement) { |
| block.getStatements().add(statement); |
| } |
| |
| @Override |
| public String toString() { |
| return getShortClassName(this); |
| } |
| } |
| |
| private static class StateManager { |
| private LinkedList<IState> states = new LinkedList<IState>(); |
| |
| public IState peek() { |
| return states.getLast(); |
| } |
| |
| public void pop() { |
| states.removeLast(); |
| } |
| |
| public void push(IState state) { |
| states.add(state); |
| } |
| |
| public boolean isClassLikeState() { |
| IState state = peek(); |
| if (state instanceof ClassLikeState) { |
| return true; |
| } else if (states.size() > 1) { |
| ListIterator<IState> i = states.listIterator(states.size()); |
| while (i.hasPrevious()) { |
| IState s = i.previous(); |
| if (s instanceof ClassLikeState) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| public ClassLikeState getClassLikeState() { |
| IState state = peek(); |
| if (state instanceof ClassLikeState) { |
| return (ClassLikeState) state; |
| } else if (states.size() > 1) { |
| final ListIterator<IState> i = states.listIterator(states.size()); |
| while (i.hasPrevious()) { |
| final IState s = i.previous(); |
| if (s instanceof ClassLikeState) { |
| return (ClassLikeState) s; |
| } |
| } |
| } |
| return null; |
| } |
| |
| public String getFullClassName() { |
| final ClassLikeState classState = getClassLikeState(); |
| if (classState != null) { |
| return classState.fullName; |
| } else { |
| return Util.EMPTY_STRING; |
| } |
| } |
| |
| } |
| |
| private StateManager states = new StateManager(); |
| |
| protected ASTNode collectSingleNodeSafe(Node pathNode) { |
| return collectSingleNodeSafe(pathNode, false); |
| } |
| |
| /** |
| * Safe wrapper for <code>collectSingleStatement0</code>. It checks |
| * SILENT_MODE flag, and it is true, just performs |
| * <code>node.accept(this);</code> without throwing an exception. |
| * |
| * @param node |
| * @param allowZero |
| * @return |
| */ |
| protected ASTNode collectSingleNodeSafe(Node node, boolean allowZero) { |
| ASTNode res = null; |
| try { |
| res = collectSingleNode0(node, allowZero); |
| } catch (Throwable t) { |
| if (JRubySourceParser.isSilentState()) { |
| node.accept(this); |
| } else { |
| throw new RuntimeException(t); |
| } |
| } |
| return res; |
| } |
| |
| /** |
| * Tries to convert single JRuby's node to single DLTK AST node. If |
| * conversion fails, and for ex., more than one or zero nodes were fetched |
| * as result, then RuntimeException will be thrown. Option |
| * <code>allowZero</code> allows to fetch no DLTK nodes and just return null |
| * without throwing an exception. |
| * |
| * @param node |
| * @param allowZero |
| * @return |
| */ |
| protected ASTNode collectSingleNode0(Node node, boolean allowZero) { |
| if (node == null) |
| return null; |
| CollectingState state = new CollectingState(); |
| states.push(state); |
| node.accept(this); |
| states.pop(); |
| |
| ArrayList<ASTNode> list = state.getList(); |
| if (list.size() == 1) |
| return list.iterator().next(); |
| |
| if (node instanceof NewlineNode) { |
| NewlineNode newlineNode = (NewlineNode) node; |
| node = newlineNode.getNextNode(); |
| } |
| if (list.size() > 1) { |
| throw new RuntimeException( |
| NLS.bind( |
| Messages.RubyASTBuildVisitor_jrubyNodeHasntBeenConvertedIntoAnyDltkAstNode, |
| node.getClass().getName())); |
| } |
| if (allowZero) |
| return null; |
| throw new RuntimeException( |
| NLS.bind( |
| Messages.RubyASTBuildVisitor_jrubyNodeHasntBeenConvertedIntoAnyDltkAstNode, |
| node.getClass().getName())); |
| } |
| |
| protected char[] getContent() { |
| return content; |
| } |
| |
| public RubyASTBuildVisitor(ModuleDeclaration module, char[] content) { |
| this.module = module; |
| this.content = content; |
| states.push(new TopLevelState(this.module)); |
| } |
| |
| @Override |
| public Instruction visitAliasNode(AliasNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| final int start = pos.getStartOffset(); |
| int end = pos.getEndOffset(); |
| while (end > start && Character.isWhitespace(content[end - 1])) { |
| --end; |
| } |
| RubyAliasExpression expr = new RubyAliasExpression(start, end, iVisited |
| .getOldName(), iVisited.getNewName()); |
| states.peek().add(expr); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitAndNode(AndNode iVisited) { // done |
| ASTNode left = collectSingleNodeSafe(iVisited.getFirstNode()); |
| ASTNode right = collectSingleNodeSafe(iVisited.getSecondNode()); |
| RubyBinaryExpression b = new RubyBinaryExpression(left, |
| ExpressionConstants.E_BAND, right); |
| states.peek().add(b); |
| return null; |
| } |
| |
| // should never get here |
| @Override |
| public Instruction visitArgsNode(ArgsNode iVisited) { |
| if (iVisited.getOptArgs() != null) { |
| iVisited.getOptArgs().accept(this); |
| } |
| return null; |
| } |
| |
| // should never get here |
| @Override |
| public Instruction visitArgsCatNode(ArgsCatNode iVisited) { |
| if (iVisited.getFirstNode() != null) { |
| iVisited.getFirstNode().accept(this); |
| } |
| if (iVisited.getSecondNode() != null) { |
| iVisited.getSecondNode().accept(this); |
| } |
| return null; |
| } |
| |
| private List<ASTNode> processListNode(ListNode node) { // done |
| CollectingState coll = new CollectingState(); |
| |
| states.push(coll); |
| Iterator<Node> iterator = node.childNodes().iterator(); |
| while (iterator.hasNext()) { |
| iterator.next().accept(this); |
| } |
| states.pop(); |
| |
| return coll.getList(); |
| } |
| |
| @Override |
| public Instruction visitArrayNode(ArrayNode iVisited) { // done |
| List<ASTNode> exprs = processListNode(iVisited); |
| |
| ISourcePosition position = iVisited.getPosition(); |
| RubyArrayExpression arr = new RubyArrayExpression(); |
| arr.setEnd(position.getEndOffset()); |
| arr.setStart(position.getStartOffset()); |
| arr.setChilds(exprs); |
| states.peek().add(arr); |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitBackRefNode(BackRefNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| VariableReference ref = new VariableReference(pos.getStartOffset(), pos |
| .getEndOffset(), "$" + iVisited.getType()); //$NON-NLS-1$ |
| states.peek().add(ref); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitBeginNode(BeginNode iVisited) { // done |
| ASTNode body = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyBeginExpression e = new RubyBeginExpression(pos.getStartOffset(), |
| pos.getEndOffset(), body); |
| states.peek().add(e); |
| return null; |
| } |
| |
| // should never get here |
| @Override |
| public Instruction visitBlockArgNode(BlockArgNode iVisited) { |
| return null; |
| } |
| |
| @Override |
| public Instruction visitBlockNode(BlockNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| Block block = new Block(pos.getStartOffset(), pos.getEndOffset()); |
| states.push(new BlockState(block)); |
| Iterator<Node> iterator = iVisited.childNodes().iterator(); |
| while (iterator.hasNext()) { |
| iterator.next().accept(this); |
| } |
| states.pop(); |
| states.peek().add(block); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitBlockPassNode(BlockPassNode iVisited) { |
| // ASTNode args = collectSingleNodeSafe(iVisited.getArgsNode()); |
| ASTNode body = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyBlock e = new RubyBlock(pos.getStartOffset(), pos.getEndOffset(), |
| body); |
| // TODO: handle vars |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitBreakNode(BreakNode iVisited) { // done |
| ASTNode value = collectSingleNodeSafe(iVisited.getValueNode()); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyBreakExpression e = new RubyBreakExpression(pos.getStartOffset(), |
| pos.getEndOffset(), value); |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitConstDeclNode(ConstDeclNode iVisited) { |
| Node pathNode = iVisited.getConstNode(); |
| ASTNode pathResult = null; |
| if (pathNode != null) |
| pathResult = collectSingleNodeSafe(pathNode); |
| ASTNode value = collectSingleNodeSafe(iVisited.getValueNode()); |
| ISourcePosition position = iVisited.getPosition(); |
| int start = position.getStartOffset(); |
| int end = start; |
| while (RubySyntaxUtils.isWhitespace(content[end])) { |
| end++; |
| } |
| while (RubySyntaxUtils.isNameChar(content[end])) { |
| end++; |
| } |
| SimpleReference name = new SimpleReference(start, end, iVisited |
| .getName()); |
| RubyConstantDeclaration node = new RubyConstantDeclaration(position |
| .getStartOffset(), position.getEndOffset(), pathResult, name, |
| value); |
| states.peek().add(node); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitClassVarAsgnNode(ClassVarAsgnNode iVisited) { |
| processVariableAssignment(iVisited, iVisited.getName(), |
| RubyVariableKind.CLASS, iVisited.getValueNode()); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitClassVarDeclNode(ClassVarDeclNode iVisited) { |
| processVariableAssignment(iVisited, iVisited.getName(), |
| RubyVariableKind.CLASS, iVisited.getValueNode()); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitClassVarNode(ClassVarNode iVisited) { |
| processVariableReference(iVisited, iVisited.getName(), |
| RubyVariableKind.CLASS); |
| return null; |
| } |
| |
| private void fixCallOffsets(CallExpression callNode, String nameNode, |
| int possibleDotPosition, int firstArgStart, int lastArgEnd) { |
| int dotPosition = RubySyntaxUtils.skipWhitespaceForward(content, |
| possibleDotPosition); |
| if (dotPosition >= 0 && dotPosition < content.length |
| && content[dotPosition] == ']') |
| dotPosition++; |
| if (dotPosition >= 0 && dotPosition < content.length |
| && (content[dotPosition] == '.' || content[dotPosition] == ':')) { |
| if (content[dotPosition] == ':') |
| dotPosition++; |
| fixFunctionCallOffsets(callNode, nameNode, dotPosition + 1, |
| firstArgStart, lastArgEnd); |
| return; |
| } |
| String methodName = nameNode; |
| if (methodName == RubySyntaxUtils.ARRAY_GET_METHOD) { |
| // TODO |
| } else if (methodName == RubySyntaxUtils.ARRAY_PUT_METHOD) { |
| // TODO |
| } else { |
| // WTF? |
| if (TRACE_RECOVERING) |
| RubyPlugin |
| .log("Ruby AST: non-dot-call not recognized, non-dot found at " //$NON-NLS-1$ |
| + dotPosition + ", function name " + methodName); //$NON-NLS-1$ |
| } |
| |
| // trim end whitespaces |
| int sourceEnd = callNode.sourceEnd() - 1; |
| while (sourceEnd >= 0 && Character.isWhitespace(content[sourceEnd])) |
| sourceEnd--; |
| if (sourceEnd >= 0) |
| callNode.setEnd(sourceEnd + 1); |
| |
| } |
| |
| private void fixFunctionCallOffsets(CallExpression callNode, |
| String methodName, int possibleNameStart, int firstArgStart, |
| int lastArgEnd) { |
| int nameStart = RubySyntaxUtils.skipWhitespaceForward(content, |
| possibleNameStart); |
| int nameEnd = nameStart + methodName.length(); |
| |
| // Assert.isLegal(nameSequence.toString().equals(methodName)); //XXX |
| callNode.getCallName().setStart(nameStart); |
| callNode.getCallName().setEnd(nameEnd); |
| |
| if (firstArgStart < 0) { |
| int lParenOffset = RubySyntaxUtils.skipWhitespaceForward(content, |
| nameEnd); |
| if (lParenOffset >= 0 && content[lParenOffset] == '(') { |
| int rParenOffset = RubySyntaxUtils.skipWhitespaceForward( |
| content, lParenOffset + 1); |
| if (rParenOffset >= 0 && content[rParenOffset] == ')') |
| callNode.setEnd(rParenOffset + 1); |
| else { |
| if (TRACE_RECOVERING) |
| RubyPlugin |
| .log("Ruby AST: function call, empty args, no closing paren; " //$NON-NLS-1$ |
| + "opening paren at " //$NON-NLS-1$ |
| + lParenOffset |
| + ", function name " + methodName); //$NON-NLS-1$ |
| callNode.setEnd(lParenOffset - 1); // don't include these |
| // parens |
| } |
| } |
| } else { |
| if (nameEnd > firstArgStart) { |
| if (callNode.getArgs().getChilds().isEmpty()) { |
| callNode.setEnd(nameEnd); |
| } |
| if (TRACE_RECOVERING) |
| RubyPlugin |
| .log("DLTKASTBuildVisitor.fixFunctionCallOffsets(" //$NON-NLS-1$ |
| + methodName + "): nameEnd > firstArgStart"); //$NON-NLS-1$ |
| return; |
| } |
| int lParenOffset = RubySyntaxUtils.skipWhitespaceForward(content, |
| nameEnd, firstArgStart); |
| if (lParenOffset >= 0 && content[lParenOffset] == '(') { |
| if (lastArgEnd <= lParenOffset) { |
| if (TRACE_RECOVERING) |
| RubyPlugin |
| .log("DLTKASTBuildVisitor.fixFunctionCallOffsets(" //$NON-NLS-1$ |
| + methodName |
| + "): lastArgEnd <= lParenOffset"); //$NON-NLS-1$ |
| return; |
| } |
| int rParenOffset = RubySyntaxUtils.skipWhitespaceForward( |
| content, lastArgEnd); |
| if (rParenOffset >= 0 && content[rParenOffset] == ')') |
| callNode.setEnd(rParenOffset + 1); |
| else { |
| if (TRACE_RECOVERING) |
| RubyPlugin |
| .log("Ruby AST: function call, non-empty args, no closing paren; " //$NON-NLS-1$ |
| + "opening paren at " //$NON-NLS-1$ |
| + lParenOffset + ", " //$NON-NLS-1$ |
| + "last argument ending at " //$NON-NLS-1$ |
| + lastArgEnd + ", function name " //$NON-NLS-1$ |
| + methodName); |
| callNode.setEnd(lastArgEnd); // probably no closing paren |
| } |
| } |
| } |
| |
| if (lastArgEnd >= 0 && callNode.sourceEnd() < lastArgEnd) |
| callNode.setEnd(lastArgEnd); |
| } |
| |
| /** |
| * @fixme iteration not correctly defined |
| */ |
| @Override |
| public Instruction visitCallNode(CallNode iVisited) { |
| String methodName = iVisited.getName(); |
| CollectingState collector = new CollectingState(); |
| |
| Assert.isTrue(iVisited.getReceiverNode() != null); |
| states.push(collector); |
| iVisited.getReceiverNode().accept(this); |
| states.pop(); |
| // TODO: uncomment when visitor is done |
| if (collector.getList().size() > 1) { |
| if (TRACE_RECOVERING) |
| RubyPlugin.log("DLTKASTBuildVisitor.visitCallNode(" //$NON-NLS-1$ |
| + methodName |
| + "): receiver " //$NON-NLS-1$ |
| + iVisited.getReceiverNode().getClass().getName() |
| + " turned into multiple nodes"); //$NON-NLS-1$ |
| } |
| ASTNode recv; |
| if (collector.getList().size() < 1) { |
| recv = new NumericLiteral(-1, -1, 0); |
| recv.setStart(iVisited.getPosition().getStartOffset()); |
| recv.setEnd(iVisited.getPosition().getEndOffset() + 1); |
| } else |
| recv = collector.getList().get(0); |
| |
| collector.reset(); |
| |
| int argsStart = -1, argsEnd = -1; |
| RubyCallArgumentsList argList = new RubyCallArgumentsList(); |
| Node argsNode = iVisited.getArgsNode(); |
| if (argsNode != null) { |
| argList.setStart(argsNode.getPosition().getStartOffset()); |
| argList.setEnd(argsNode.getPosition().getEndOffset()); |
| |
| states.push(new ArgumentsState(argList)); |
| if (argsNode instanceof ListNode) { |
| ListNode arrayNode = (ListNode) argsNode; |
| List<Node> list = arrayNode.childNodes(); |
| for (Iterator<Node> iter = list.iterator(); iter.hasNext();) { |
| Node node = iter.next(); |
| node.accept(this); |
| } |
| } else { |
| if (TRACE_RECOVERING) |
| RubyPlugin.log("DLTKASTBuildVisitor.visitCallNode(" //$NON-NLS-1$ |
| + methodName + ") - unknown args node type: " //$NON-NLS-1$ |
| + argsNode.getClass().getName()); |
| argsNode.accept(this); |
| } |
| states.pop(); |
| List<Node> children = argsNode.childNodes(); |
| if (children.size() > 0) { |
| argsStart = children.get(0).getPosition() |
| .getStartOffset(); |
| argsEnd = children.get(children.size() - 1) |
| .getPosition().getEndOffset(); |
| // correction for nodes with incorrect positions |
| List<ASTNode> argListExprs = argList.getChilds(); |
| if (!argListExprs.isEmpty()) |
| argsEnd = Math.max(argsEnd, argListExprs |
| .get(argListExprs.size() - 1).sourceEnd()); |
| } |
| } |
| if (iVisited.getIterNode() != null) { |
| ASTNode s = collectSingleNodeSafe(iVisited.getIterNode()); |
| argList.addNode(s); |
| |
| } |
| |
| argList.autosetOffsets(); |
| CallExpression c = new CallExpression(recv, methodName, argList); |
| int receiverEnd = recv.sourceEnd(); |
| |
| c.setStart(iVisited.getPosition().getStartOffset()); |
| c |
| .setEnd(argsEnd >= 0 ? argsEnd : iVisited.getPosition() |
| .getEndOffset()); // just in case, should |
| // be overriden |
| fixCallOffsets(c, methodName, receiverEnd, argsStart, argsEnd); |
| |
| this.states.peek().add(c); |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitCaseNode(CaseNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyCaseStatement statement = new RubyCaseStatement(pos |
| .getStartOffset(), pos.getEndOffset()); |
| ASTNode caseTarget = collectSingleNodeSafe(iVisited.getCaseNode()); |
| statement.setTarget(caseTarget); |
| Node caseBody = iVisited.getFirstWhenNode(); |
| ASTNode caseSt = collectSingleNodeSafe(caseBody); |
| List whens = new ArrayList(1); |
| while (caseBody instanceof WhenNode) { |
| WhenNode whenNode = (WhenNode) caseBody; |
| whens.add(caseSt); |
| caseBody = whenNode.getNextCase(); |
| caseSt = collectSingleNodeSafe(caseBody); |
| } |
| statement.setWhens(whens); |
| if (caseSt != null) { |
| statement.setElseWhen(caseSt); |
| } |
| states.peek().add(statement); |
| return null; |
| } |
| |
| private static String colons2Name(Node cpathNode) { |
| String name = ""; //$NON-NLS-1$ |
| while (cpathNode instanceof Colon2Node) { |
| Colon2Node colon2Node = (Colon2Node) cpathNode; |
| if (name.length() > 0) |
| name = "::" + name; //$NON-NLS-1$ |
| name = colon2Node.getName() + name; |
| cpathNode = colon2Node.getLeftNode(); |
| } |
| if (cpathNode instanceof Colon3Node) { |
| Colon3Node colon3Node = (Colon3Node) cpathNode; |
| if (name.length() > 0) |
| name = "::" + name; //$NON-NLS-1$ |
| name = "::" + colon3Node.getName() + name; //$NON-NLS-1$ |
| } else if (cpathNode instanceof ConstNode) { |
| ConstNode constNode = (ConstNode) cpathNode; |
| if (name.length() > 0) |
| name = "::" + name; //$NON-NLS-1$ |
| name = constNode.getName() + name; |
| } |
| return name; |
| } |
| |
| private ISourcePosition fixNamePosition(ISourcePosition pos) { |
| int start = pos.getStartOffset(); |
| int end = pos.getEndOffset(); |
| |
| while (end - 1 >= 0 && (end - 1) > start |
| && !RubySyntaxUtils.isNameChar(content[end - 1])) { |
| end--; |
| } |
| if (end >= 0) { |
| while (end < content.length |
| && RubySyntaxUtils.isNameChar(content[end])) |
| end++; |
| } |
| return new SourcePosition(pos.getFile(), pos.getStartLine(), pos |
| .getEndLine(), start, end); |
| } |
| |
| private ISourcePosition fixBorders(ISourcePosition pos) { |
| int start = pos.getStartOffset(); |
| int end = pos.getEndOffset(); |
| while (end - 1 >= 0 && !RubySyntaxUtils.isNameChar(content[end - 1])) { |
| end--; |
| } |
| if (end >= 0) { |
| while (end < content.length |
| && RubySyntaxUtils.isNameChar(content[end])) |
| end++; |
| } |
| return new SourcePosition(pos.getFile(), pos.getStartLine(), pos |
| .getEndLine(), start, end); |
| } |
| |
| @Override |
| public Instruction visitClassNode(ClassNode iVisited) { |
| Node cpathNode = iVisited.getCPath(); |
| Node superClassNode = iVisited.getSuperNode(); |
| ASTNode cpath = collectSingleNodeSafe(cpathNode); |
| ASTNode supernode = collectSingleNodeSafe(superClassNode); |
| ISourcePosition pos = iVisited.getCPath().getPosition(); |
| ISourcePosition cPos = iVisited.getPosition(); |
| cPos = fixNamePosition(cPos); |
| pos = fixNamePosition(pos); |
| |
| RubyClassDeclaration type = new RubyClassDeclaration(supernode, cpath, |
| null, cPos.getStartOffset(), cPos.getEndOffset()); |
| |
| String name = colons2Name(cpathNode); |
| type.setName(name); |
| |
| states.peek().add(type); |
| final String enclosingTypeName = states.getFullClassName(); |
| type.setEnclosingTypeName(enclosingTypeName); |
| states.push(new ClassState(type, enclosingTypeName)); |
| // body |
| Node bodyNode = iVisited.getBodyNode(); |
| if (bodyNode != null) { |
| pos = bodyNode.getPosition(); |
| int end = -1; |
| while (bodyNode instanceof NewlineNode) |
| bodyNode = ((NewlineNode) bodyNode).getNextNode(); |
| if (bodyNode instanceof BlockNode) { |
| BlockNode blockNode = (BlockNode) bodyNode; |
| // XXX !!!! |
| end = blockNode.getLast().getPosition().getEndOffset() + 1; |
| } |
| pos = fixBorders(pos); |
| Block bl = new Block(pos.getStartOffset(), (end == -1) ? pos |
| .getEndOffset() + 1 : end); |
| type.setBody(bl); |
| |
| if (bodyNode instanceof BlockNode) { |
| for (Iterator<Node> iterator = bodyNode.childNodes().iterator(); iterator |
| .hasNext();) { |
| Node n = iterator.next(); |
| n.accept(this); |
| } |
| } else |
| bodyNode.accept(this); |
| } |
| |
| states.pop(); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitColon2Node(Colon2Node iVisited) { |
| |
| CollectingState collector = new CollectingState(); |
| states.push(collector); |
| if (iVisited.getLeftNode() != null) { |
| iVisited.getLeftNode().accept(this); |
| } |
| states.pop(); |
| |
| int start = iVisited.getPosition().getStartOffset(); |
| int end = iVisited.getPosition().getEndOffset(); |
| ASTNode left = null; |
| if (collector.list.size() == 1) { |
| |
| left = collector.list.get(0); |
| |
| } |
| |
| String right = iVisited.getName(); |
| |
| if (left != null) { |
| RubyColonExpression colon = new RubyColonExpression(right, left); |
| colon.setStart(start); |
| colon.setEnd(end); |
| states.peek().add(colon); |
| } else { |
| ConstantReference ref = new ConstantReference(start, end, right); |
| states.peek().add(ref); |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitColon3Node(Colon3Node iVisited) { |
| ISourcePosition position = iVisited.getPosition(); |
| RubyColonExpression colon = new RubyColonExpression(iVisited.getName(), |
| null); |
| colon.setStart(position.getStartOffset()); |
| colon.setEnd(position.getEndOffset()); |
| states.peek().add(colon); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitConstNode(ConstNode iVisited) { |
| String name = iVisited.getName(); |
| ISourcePosition pos = iVisited.getPosition(); |
| pos = fixBorders(pos); |
| this.states.peek().add( |
| new ConstantReference(pos.getStartOffset(), pos.getEndOffset(), |
| name)); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitDAsgnNode(DAsgnNode iVisited) { |
| ISourcePosition pos = iVisited.getPosition(); |
| ASTNode valueNode = this.collectSingleNodeSafe(iVisited.getValueNode(), |
| true); |
| RubyDAssgnExpression e = new RubyDAssgnExpression(pos.getStartOffset(), |
| pos.getEndOffset(), iVisited.getName(), valueNode); |
| |
| states.peek().add(e); |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitDRegxNode(DRegexpNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| List<ASTNode> list = processListNode(iVisited); |
| RubyDRegexpExpression ex = new RubyDRegexpExpression(pos |
| .getStartOffset(), pos.getEndOffset()); |
| ex.setChilds(list); |
| states.peek().add(ex); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitDStrNode(DStrNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| List<ASTNode> list = processListNode(iVisited); |
| RubyDynamicStringExpression ex = new RubyDynamicStringExpression(pos |
| .getStartOffset(), pos.getEndOffset()); |
| ex.setChilds(list); |
| states.peek().add(ex); |
| return null; |
| } |
| |
| /** |
| * @see NodeVisitor#visitDSymbolNode(DSymbolNode) |
| */ |
| @Override |
| public Instruction visitDSymbolNode(DSymbolNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| List<ASTNode> list = processListNode(iVisited); |
| RubyDSymbolExpression ex = new RubyDSymbolExpression(pos |
| .getStartOffset(), pos.getEndOffset()); |
| ex.setChilds(list); |
| states.peek().add(ex); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitDVarNode(DVarNode iVisited) { // done (?) |
| String name = iVisited.getName(); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyDVarExpression e = new RubyDVarExpression(pos.getStartOffset(), pos |
| .getEndOffset(), name); |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitDXStrNode(DXStrNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| List<ASTNode> list = processListNode(iVisited); |
| RubyDynamicBackquoteStringExpression ex = new RubyDynamicBackquoteStringExpression( |
| pos.getStartOffset(), pos.getEndOffset()); |
| ex.setChilds(list); |
| states.peek().add(ex); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitDefinedNode(DefinedNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| ASTNode value = collectSingleNodeSafe(iVisited.getExpressionNode()); |
| RubyDefinedExpression e = new RubyDefinedExpression(pos |
| .getStartOffset(), pos.getEndOffset(), value); |
| states.peek().add(e); |
| return null; |
| } |
| |
| private List<Argument> processMethodArguments(ArgsNode args) { |
| List<Argument> arguments = new ArrayList<Argument>(); |
| Arity arity = args.getArity(); |
| int endPos = args.getPosition().getStartOffset() - 1; |
| if (arity.getValue() != 0) { // BIG XXX, PLEASE CHECK IT |
| ListNode argsList = args.getArgs(); |
| if (argsList != null) { |
| Iterator<Node> i = argsList.childNodes().iterator(); |
| while (i.hasNext()) { |
| Node nde = i.next(); |
| if (nde instanceof ArgumentNode) { |
| ArgumentNode a = (ArgumentNode) nde; |
| Argument aa = new RubyMethodArgument(); |
| ISourcePosition argPos = fixNamePosition(a |
| .getPosition()); |
| |
| if (argPos.getEndOffset() > endPos) |
| endPos = argPos.getEndOffset(); |
| |
| aa.set(new SimpleReference(argPos.getStartOffset(), |
| argPos.getEndOffset(), a.getName()), null); |
| aa.setModifier(RubyMethodArgument.SIMPLE); |
| arguments.add(aa); |
| } |
| } |
| } |
| ListNode optArgs = args.getOptArgs(); |
| if (optArgs != null) { |
| Iterator<?> iterator = optArgs.childNodes().iterator(); |
| while (iterator.hasNext()) { |
| Object obj = iterator.next(); |
| if (obj instanceof LocalAsgnNode) { |
| LocalAsgnNode a = (LocalAsgnNode) obj; |
| Argument aa = new RubyMethodArgument(); |
| ISourcePosition argPos = a.getPosition(); |
| |
| if (argPos.getEndOffset() > endPos) |
| endPos = argPos.getEndOffset(); |
| |
| CollectingState coll = new CollectingState(); |
| states.push(coll); |
| a.getValueNode().accept(this); |
| states.pop(); |
| ASTNode defaultVal = null; |
| int nameStart = argPos.getStartOffset(); |
| int nameEnd = argPos.getEndOffset(); |
| |
| if (coll.list.size() == 1) { |
| Object object = coll.list.get(0); |
| defaultVal = (ASTNode) object; |
| if (defaultVal.sourceStart() > nameStart |
| && defaultVal.sourceStart() < nameEnd) { |
| nameEnd = defaultVal.sourceStart(); |
| } |
| } |
| |
| aa.set(new SimpleReference(nameStart, nameEnd, a |
| .getName()), defaultVal); |
| aa.setModifier(RubyMethodArgument.SIMPLE); |
| arguments.add(aa); |
| } else { |
| System.err |
| .println(Messages.RubyASTBuildVisitor_unknownArgumentType); |
| } |
| } |
| } |
| } |
| if (args.getRestArg() >= 0) { |
| |
| // restore vararg name and position |
| int vaStart = 0, vaEnd = 0; |
| IState s = states.peek(); |
| if (s instanceof MethodState) { |
| int bodyStart = args.getPosition().getEndOffset(); |
| if (endPos >= 0 && endPos < bodyStart) { // this assumes that |
| // sourceStart is always set and always less than |
| // contents.length() |
| while (endPos < bodyStart && content[endPos] != '*') |
| endPos++; |
| endPos++; |
| if (endPos < content.length) { |
| vaStart = endPos - 1; |
| while (RubySyntaxUtils |
| .isIdentifierCharacter(content[endPos])) |
| endPos++; |
| vaEnd = endPos; |
| } |
| |
| } |
| } |
| |
| Argument aa = new RubyMethodArgument(); |
| aa.set(new SimpleReference(vaStart + 1, vaEnd, String.copyValueOf( |
| content, vaStart, vaEnd - vaStart)), null); |
| aa.setModifier(RubyMethodArgument.VARARG); |
| arguments.add(aa); |
| } |
| BlockArgNode blockArgNode = args.getBlockArgNode(); |
| if (blockArgNode != null) { |
| ISourcePosition position = fixNamePosition(blockArgNode |
| .getPosition()); |
| String baName = String.copyValueOf(content, position |
| .getStartOffset() - 1, position.getEndOffset() |
| - (position.getStartOffset() - 1)); |
| Argument aa = new RubyMethodArgument(); |
| aa.set(new SimpleReference(position.getStartOffset(), position |
| .getEndOffset(), baName), null); // XXX: |
| aa.setModifier(RubyMethodArgument.BLOCK); |
| arguments.add(aa); |
| } |
| return arguments; |
| } |
| |
| private void setMethodVisibility(MethodDeclaration method, |
| Visibility visibility) { |
| if (visibility.isPrivate()) |
| ASTUtils.setVisibility(method, Modifiers.AccPrivate); |
| |
| if (visibility.isPublic()) |
| ASTUtils.setVisibility(method, Modifiers.AccPublic); |
| |
| if (visibility.isProtected()) |
| ASTUtils.setVisibility(method, Modifiers.AccProtected); |
| } |
| |
| // method |
| @Override |
| public Instruction visitDefnNode(DefnNode iVisited) { |
| // Collection comments = iVisited.getComments(); |
| // System.out.println(comments); |
| ArgumentNode nameNode = iVisited.getNameNode(); |
| |
| ISourcePosition pos = fixNamePosition(nameNode.getPosition()); |
| ISourcePosition cPos = fixNamePosition(iVisited.getPosition()); |
| MethodDeclaration method = new MethodDeclaration(iVisited.getName(), |
| pos.getStartOffset(), pos.getEndOffset(), |
| cPos.getStartOffset(), cPos.getEndOffset()); |
| |
| setMethodVisibility(method, iVisited.getVisibility()); |
| if (states.isClassLikeState()) { |
| ClassLikeState classState = states.getClassLikeState(); |
| ASTUtils.setVisibility(method, classState.visibility); |
| } |
| |
| final ClassLikeState classState = states.getClassLikeState(); |
| if (classState != null) { |
| method.setDeclaringTypeName(classState.fullName); |
| } |
| states.peek().add(method); |
| states.push(new MethodState(method)); |
| Node bodyNode = iVisited.getBodyNode(); |
| if (bodyNode != null) { |
| ISourcePosition bodyPos = bodyNode.getPosition(); |
| method.getBody().setStart(bodyPos.getStartOffset()); |
| method.getBody().setEnd(bodyPos.getEndOffset()); |
| if (bodyNode instanceof BlockNode) { |
| for (Iterator<Node> iterator = bodyNode.childNodes().iterator(); iterator |
| .hasNext();) { |
| Node n = iterator.next(); |
| n.accept(this); |
| } |
| } else |
| bodyNode.accept(this); |
| } |
| ArgsNode args = iVisited.getArgsNode(); |
| if (args != null) { |
| List<Argument> arguments = processMethodArguments(args); |
| method.acceptArguments(arguments); |
| } |
| states.pop(); |
| return null; |
| } |
| |
| private ISourcePosition restoreMethodNamePosition(DefsNode node, int recvEnd) { |
| ISourcePosition recvPos = node.getReceiverNode().getPosition(); |
| int pos = recvEnd; |
| if (pos >= 0) { |
| while (pos < content.length && content[pos] != '.' |
| && content[pos] != ':') |
| pos++; |
| if (content[pos] == ':') |
| pos++; |
| } |
| if (pos >= content.length || pos < 0) |
| return recvPos; |
| int nameStart = RubySyntaxUtils.skipWhitespaceForward(content, pos + 1); |
| int nameEnd = nameStart; |
| while (RubySyntaxUtils.isIdentifierCharacter(content[nameEnd])) |
| nameEnd++; |
| return new SourcePosition(recvPos.getFile(), recvPos.getStartLine(), |
| recvPos.getEndLine(), nameStart, nameEnd); |
| } |
| |
| // singleton method |
| @Override |
| public Instruction visitDefsNode(DefsNode iVisited) { |
| ASTNode receiverExpression = null; |
| Node receiverNode = iVisited.getReceiverNode(); |
| CollectingState collectingState = new CollectingState(); |
| |
| states.push(collectingState); |
| receiverNode.accept(this); |
| states.pop(); |
| if (collectingState.list.size() == 1) { |
| Object obj = collectingState.list.get(0); |
| receiverExpression = (ASTNode) obj; |
| } |
| |
| ISourcePosition cPos = iVisited.getPosition(); |
| // if (receiverExpression == null) |
| // System.out.println(); |
| ISourcePosition namePos = restoreMethodNamePosition(iVisited, |
| receiverExpression.sourceEnd()); |
| String name = iVisited.getName(); |
| // if (receiverNode instanceof SelfNode) { |
| // name = "self." + name; |
| // } else if (receiverNode instanceof ConstNode) { |
| // name = ((ConstNode) receiverNode).getName() + "." + name; |
| // } |
| RubySingletonMethodDeclaration method = new RubySingletonMethodDeclaration( |
| name, namePos.getStartOffset(), namePos.getEndOffset(), cPos |
| .getStartOffset(), cPos.getEndOffset(), |
| receiverExpression); |
| method.setModifier(Modifiers.AccStatic); |
| ASTUtils.setVisibility(method, Modifiers.AccPublic); |
| // if (states.peek() instanceof ClassLikeState) { |
| if (states.isClassLikeState()) { |
| ClassLikeState classState = states.getClassLikeState(); |
| ASTUtils.setVisibility(method, classState.visibility); |
| } |
| states.peek().add(method); |
| states.push(new MethodState(method)); |
| ArgsNode args = iVisited.getArgsNode(); |
| if (args != null) { |
| List list = processMethodArguments(args); |
| method.acceptArguments(list); |
| } |
| Node bodyNode = iVisited.getBodyNode(); |
| if (bodyNode != null) { |
| if (bodyNode instanceof BlockNode) { |
| for (Iterator iterator = bodyNode.childNodes().iterator(); iterator |
| .hasNext();) { |
| Node n = (Node) iterator.next(); |
| n.accept(this); |
| } |
| } else |
| bodyNode.accept(this); |
| } |
| states.pop(); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitDotNode(DotNode iVisited) { // done |
| ASTNode begin = collectSingleNodeSafe(iVisited.getBeginNode()); |
| ASTNode end = collectSingleNodeSafe(iVisited.getEndNode()); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyDotExpression e = new RubyDotExpression(pos.getStartOffset(), pos |
| .getEndOffset(), begin, end); |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitEnsureNode(EnsureNode iVisited) { // done |
| ASTNode body = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ASTNode ensure = collectSingleNodeSafe(iVisited.getEnsureNode()); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyEnsureExpression e = new RubyEnsureExpression(pos.getStartOffset(), |
| pos.getEndOffset(), ensure, body); |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitEvStrNode(EvStrNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| ASTNode body = collectSingleNodeSafe(iVisited.getBody()); |
| RubyEvaluatableStringExpression e = new RubyEvaluatableStringExpression( |
| pos.getStartOffset(), pos.getEndOffset(), body); |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitFCallNode(FCallNode iVisited) { |
| |
| String methodName = iVisited.getName(); |
| |
| if (states.isClassLikeState()) { |
| if (methodName.equals("private")) //$NON-NLS-1$ |
| handleVisibilitySetter(iVisited, Modifiers.AccPrivate); |
| else if (methodName.equals("protected")) //$NON-NLS-1$ |
| handleVisibilitySetter(iVisited, Modifiers.AccProtected); |
| else if (methodName.equals("public")) //$NON-NLS-1$ |
| handleVisibilitySetter(iVisited, Modifiers.AccPublic); |
| } |
| |
| RubyCallArgumentsList argList = new RubyCallArgumentsList(); |
| Node argsNode = iVisited.getArgsNode(); |
| if (argsNode != null) { |
| argList.setStart(argsNode.getPosition().getStartOffset()); |
| argList.setEnd(argsNode.getPosition().getEndOffset()); |
| |
| states.push(new ArgumentsState(argList)); |
| if (argsNode instanceof ListNode) { |
| ListNode arrayNode = (ListNode) argsNode; |
| List list = arrayNode.childNodes(); |
| for (Iterator iter = list.iterator(); iter.hasNext();) { |
| Node node = (Node) iter.next(); |
| node.accept(this); |
| } |
| } else { |
| if (TRACE_RECOVERING) |
| RubyPlugin.log("DLTKASTBuildVisitor.visitFCallNode(" //$NON-NLS-1$ |
| + methodName + ") - unknown args node type: " //$NON-NLS-1$ |
| + argsNode.getClass().getName()); |
| argsNode.accept(this); |
| } |
| states.pop(); |
| } |
| |
| if (iVisited.getIterNode() != null) { |
| ASTNode s = collectSingleNodeSafe(iVisited.getIterNode()); |
| argList.addNode(s); |
| } |
| |
| argList.autosetOffsets(); |
| CallExpression c = new CallExpression(null, methodName, argList); |
| |
| int funcNameStart = iVisited.getPosition().getStartOffset(); |
| c.setStart(funcNameStart); |
| c.setEnd(funcNameStart + methodName.length()); |
| fixFunctionCallOffsets(c, methodName, funcNameStart, argList |
| .sourceStart(), argList.sourceEnd()); |
| |
| states.peek().add(c); |
| |
| return null; |
| } |
| |
| private void handleVisibilitySetter(FCallNode node, int newVisibility) { |
| if (states.isClassLikeState()) { |
| ClassLikeState classState = states.getClassLikeState(); |
| Node argsNode = node.getArgsNode(); |
| if (argsNode instanceof ArrayNode) { |
| ArrayNode argsArrayNode = (ArrayNode) argsNode; |
| List args = argsArrayNode.childNodes(); |
| for (Iterator iter = args.iterator(); iter.hasNext();) { |
| Node arg = (Node) iter.next(); |
| if (arg instanceof SymbolNode) { |
| SymbolNode symbolNode = (SymbolNode) arg; |
| String xmethodName = symbolNode.getName(); |
| List statements = classState.type.getStatements(); |
| for (Iterator statIter = statements.iterator(); statIter |
| .hasNext();) { |
| ASTNode statement = (ASTNode) statIter.next(); |
| if (statement instanceof MethodDeclaration) { |
| MethodDeclaration methodDeclaration = (MethodDeclaration) statement; |
| if (methodDeclaration.getName().equals( |
| xmethodName)) |
| ASTUtils.setVisibility(methodDeclaration, |
| newVisibility); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public Instruction visitFalseNode(FalseNode iVisited) { // done |
| ISourcePosition position = iVisited.getPosition(); |
| states.peek().add( |
| new BooleanLiteral(position.getStartOffset(), position |
| .getEndOffset(), false)); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitFlipNode(FlipNode iVisited) { |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitForNode(ForNode iVisited) { // done |
| ASTNode varNode = collectSingleNodeSafe(iVisited.getVarNode()); |
| ASTNode listSt = collectSingleNodeSafe(iVisited.getIterNode()); |
| ASTNode listNode; |
| if (!(listSt instanceof org.eclipse.dltk.ast.ASTListNode)) { |
| org.eclipse.dltk.ast.ASTListNode list = new org.eclipse.dltk.ast.ASTListNode(); |
| list.addNode(listSt); |
| listNode = list; |
| } else |
| listNode = listSt; |
| ASTNode bodyNode = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyForStatement2 statement = new RubyForStatement2(pos |
| .getStartOffset(), pos.getEndOffset(), varNode, |
| (org.eclipse.dltk.ast.ASTListNode) listNode, bodyNode); |
| states.peek().add(statement); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitGlobalAsgnNode(GlobalAsgnNode iVisited) { |
| processVariableAssignment(iVisited, iVisited.getName(), |
| RubyVariableKind.GLOBAL, iVisited.getValueNode()); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitGlobalVarNode(GlobalVarNode iVisited) { |
| processVariableReference(iVisited, iVisited.getName(), |
| RubyVariableKind.GLOBAL); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitHashNode(HashNode iVisited) { // done |
| ListNode listNode = iVisited.getListNode(); |
| List<ASTNode> exprs = processListNode(listNode); |
| |
| ISourcePosition position = iVisited.getPosition(); |
| RubyHashExpression arr = new RubyHashExpression(); |
| arr.setStart(position.getStartOffset()); |
| arr.setEnd(position.getEndOffset()); |
| |
| if (arr.sourceEnd() == arr.sourceStart() && listNode != null) { |
| arr.setStart(listNode.getPosition().getStartOffset()); |
| arr.setEnd(listNode.getPosition().getEndOffset()); |
| } |
| |
| List<ASTNode> hashPairs = new ArrayList<ASTNode>(); |
| |
| if (exprs.size() % 2 == 0) { |
| Iterator<ASTNode> i = exprs.iterator(); |
| while (i.hasNext()) { |
| ASTNode key = i.next(); |
| ASTNode value = i.next(); |
| RubyHashPairExpression e = new RubyHashPairExpression(key |
| .sourceStart(), value.sourceEnd(), key, value); |
| hashPairs.add(e); |
| } |
| } else { |
| if (!JRubySourceParser.isSilentState()) { |
| throw new RuntimeException( |
| Messages.RubyASTBuildVisitor_unpairedHash); |
| } |
| } |
| |
| arr.setChilds(hashPairs); |
| states.peek().add(arr); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitInstAsgnNode(InstAsgnNode iVisited) { |
| processVariableAssignment(iVisited, iVisited.getName(), |
| RubyVariableKind.INSTANCE, iVisited.getValueNode()); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitInstVarNode(InstVarNode iVisited) { |
| processVariableReference(iVisited, iVisited.getName(), |
| RubyVariableKind.INSTANCE); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitIfNode(IfNode iVisited) { // done |
| ASTNode condition = collectSingleNodeSafe(iVisited.getCondition()); |
| ASTNode thenPart = collectSingleNodeSafe(iVisited.getThenBody()); |
| ASTNode elsePart = collectSingleNodeSafe(iVisited.getElseBody()); |
| RubyIfStatement res = new RubyIfStatement(condition, thenPart, elsePart); |
| res.setStart(iVisited.getPosition().getStartOffset()); |
| res.setEnd(iVisited.getPosition().getEndOffset() + 1); |
| states.peek().add(res); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitIterNode(IterNode iVisited) { // done |
| ASTNode bodyNode = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ASTNode varNode = collectSingleNodeSafe(iVisited.getVarNode()); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyBlock block = new RubyBlock(pos.getStartOffset(), pos |
| .getEndOffset(), bodyNode); |
| block.addVar(varNode); |
| states.peek().add(block); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitLocalAsgnNode(LocalAsgnNode iVisited) { |
| processVariableAssignment(iVisited, iVisited.getName(), |
| RubyVariableKind.LOCAL, iVisited.getValueNode()); |
| return null; |
| } |
| |
| private void processVariableAssignment(Node iVisited, String name, |
| RubyVariableKind varKind, Node valueNode) { |
| ISourcePosition pos = iVisited.getPosition(); |
| ASTNode left = new VariableReference(pos.getStartOffset(), pos |
| .getStartOffset() |
| + name.length(), name, varKind); |
| ASTNode right = collectSingleNodeSafe(valueNode); |
| RubyAssignment assgn = new RubyAssignment(left, right); |
| copyOffsets(assgn, iVisited); |
| states.peek().add(assgn); |
| } |
| |
| @Override |
| public Instruction visitLocalVarNode(LocalVarNode iVisited) { |
| processVariableReference(iVisited, iVisited.getName(), |
| RubyVariableKind.LOCAL); |
| return null; |
| } |
| |
| private void processVariableReference(Node iVisited, String varName, |
| RubyVariableKind varKind) { |
| final ISourcePosition pos = iVisited.getPosition(); |
| final int start = pos.getStartOffset(); |
| int end = pos.getEndOffset(); |
| if (end - start > varName.length()) { |
| end = start + varName.length(); |
| } |
| VariableReference node = new VariableReference(start, end, varName, |
| varKind); |
| states.peek().add(node); |
| } |
| |
| private void copyOffsets(ASTNode target, Node source) { |
| ISourcePosition pos = source.getPosition(); |
| target.setStart(pos.getStartOffset()); |
| target.setEnd(pos.getEndOffset()); |
| } |
| |
| @Override |
| public Instruction visitMultipleAsgnNode(MultipleAsgnNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyMultipleAssignmentStatement s = new RubyMultipleAssignmentStatement( |
| pos.getStartOffset(), pos.getEndOffset()); |
| ListNode headNode = iVisited.getHeadNode(); |
| if (headNode != null) { |
| for (Iterator<Node> iterator = headNode.childNodes().iterator(); iterator |
| .hasNext();) { |
| Node n = iterator.next(); |
| if (n instanceof LocalAsgnNode |
| && ((LocalAsgnNode) n).getValueNode() == null) { |
| String name = ((LocalAsgnNode) n).getName(); |
| ISourcePosition nPos = n.getPosition(); |
| s.addLhs(new VariableReference(nPos.getStartOffset(), nPos |
| .getEndOffset(), name, RubyVariableKind.LOCAL)); |
| } else { |
| ASTNode ss = collectSingleNodeSafe(n); |
| s.addLhs(ss); |
| } |
| } |
| } |
| Node argsNode = iVisited.getArgsNode(); |
| if (argsNode != null) { |
| s.setLeftAsterix(collectSingleNodeSafe(argsNode), argsNode |
| .getPosition().getStartOffset()); |
| } |
| Node valueNode = iVisited.getValueNode(); |
| if (valueNode instanceof ArgsCatNode) { |
| ArgsCatNode argsCatNode = (ArgsCatNode) valueNode; |
| Node firstNode = argsCatNode.getFirstNode(); |
| if (firstNode instanceof ListNode) { |
| ListNode list = (ListNode) firstNode; |
| for (Iterator<Node> iterator = list.childNodes().iterator(); iterator |
| .hasNext();) { |
| Node nd = iterator.next(); |
| s.addRhs(collectSingleNodeSafe(nd)); |
| } |
| } else if (firstNode != null) |
| s.addRhs(collectSingleNodeSafe(firstNode)); |
| Node secondNode = argsCatNode.getSecondNode(); |
| if (secondNode != null) |
| s.setRightAsterix(collectSingleNodeSafe(secondNode)); |
| } else if (valueNode instanceof ListNode) { |
| ListNode list = (ListNode) valueNode; |
| for (Iterator<Node> iterator = list.childNodes().iterator(); iterator |
| .hasNext();) { |
| Node nd = iterator.next(); |
| s.addRhs(collectSingleNodeSafe(nd)); |
| } |
| } else if (valueNode != null) { |
| s.addRhs(collectSingleNodeSafe(valueNode)); |
| } |
| states.peek().add(s); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitMatch2Node(Match2Node iVisited) {// done |
| ISourcePosition pos = iVisited.getPosition(); |
| ASTNode receiverNode = collectSingleNodeSafe(iVisited.getReceiverNode()); |
| ASTNode valueNode = collectSingleNodeSafe(iVisited.getValueNode()); |
| RubyMatch2Expression e = new RubyMatch2Expression(pos.getStartOffset(), |
| pos.getEndOffset(), receiverNode, valueNode); |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitMatch3Node(Match3Node iVisited) {// done |
| ISourcePosition pos = iVisited.getPosition(); |
| ASTNode receiverNode = collectSingleNodeSafe(iVisited.getReceiverNode()); |
| ASTNode valueNode = collectSingleNodeSafe(iVisited.getValueNode()); |
| RubyMatch3Expression e = new RubyMatch3Expression(pos.getStartOffset(), |
| pos.getEndOffset(), receiverNode, valueNode); |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitMatchNode(MatchNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| ASTNode regexp = collectSingleNodeSafe(iVisited.getRegexpNode()); |
| RubyMatchExpression e = new RubyMatchExpression(pos.getStartOffset(), |
| pos.getEndOffset(), regexp); |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitModuleNode(ModuleNode iVisited) { |
| String name = ""; //$NON-NLS-1$ |
| Node cpathNode = iVisited.getCPath(); |
| if (cpathNode instanceof Colon2Node || cpathNode instanceof ConstNode) { |
| name = colons2Name(cpathNode); |
| } |
| ASTNode cpath = collectSingleNodeSafe(cpathNode); |
| ISourcePosition pos = iVisited.getCPath().getPosition(); |
| ISourcePosition cPos = iVisited.getPosition(); |
| cPos = fixNamePosition(cPos); |
| pos = fixNamePosition(pos); |
| RubyModuleDeclaration type = new RubyModuleDeclaration(cpath, null, |
| cPos.getStartOffset(), cPos.getEndOffset()); |
| type.setModifier(Modifiers.AccModule); |
| states.peek().add(type); |
| final String enclosingTypeName = states.getFullClassName(); |
| type.setEnclosingTypeName(enclosingTypeName); |
| states.push(new ModuleState(type, enclosingTypeName)); |
| // body |
| Node bodyNode = iVisited.getBodyNode(); |
| if (bodyNode != null) { |
| pos = bodyNode.getPosition(); |
| int end = -1; |
| while (bodyNode instanceof NewlineNode) |
| bodyNode = ((NewlineNode) bodyNode).getNextNode(); |
| if (bodyNode instanceof BlockNode) { |
| BlockNode blockNode = (BlockNode) bodyNode; |
| // XXX!!!! |
| end = blockNode.getLast().getPosition().getEndOffset(); |
| } else { |
| if (TRACE_RECOVERING) |
| RubyPlugin.log("DLTKASTBuildVisitor.visitModuleNode(" //$NON-NLS-1$ |
| + name + "): unknown body type " //$NON-NLS-1$ |
| + bodyNode.getClass().getName()); |
| } |
| pos = fixBorders(pos); |
| Block bl = new Block(pos.getStartOffset(), (end == -1) ? pos |
| .getEndOffset() : end); |
| type.setBody(bl); |
| bodyNode.accept(this); |
| } |
| states.pop(); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitNewlineNode(NewlineNode iVisited) { // done |
| iVisited.getNextNode().accept(this); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitNextNode(NextNode iVisited) { // done |
| ISourcePosition position = iVisited.getPosition(); |
| RubyCallArgumentsList args = new RubyCallArgumentsList(); |
| Node valueNode = iVisited.getValueNode(); |
| if (valueNode != null) { |
| ASTNode s = collectSingleNodeSafe(valueNode); |
| args.addArgument(s, 0); |
| } |
| states.peek().add( |
| new RubyNextExpression(position.getStartOffset(), position |
| .getEndOffset(), args)); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitNilNode(NilNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| states.peek().add( |
| new NilLiteral(pos.getStartOffset(), pos.getEndOffset())); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitNotNode(NotNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| ASTNode expr = collectSingleNodeSafe(iVisited.getConditionNode()); |
| RubyNotExpression e = new RubyNotExpression(pos.getStartOffset(), pos |
| .getEndOffset(), expr); |
| states.peek().add(e); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitNthRefNode(NthRefNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| states.peek().add( |
| new VariableReference(pos.getStartOffset(), pos.getEndOffset(), |
| "$" + iVisited.getMatchNumber(), //$NON-NLS-1$ |
| RubyVariableKind.GLOBAL)); |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitOpElementAsgnNode(OpElementAsgnNode iVisited) { |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitOpAsgnNode(OpAsgnNode iVisited) { |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitOpAsgnAndNode(OpAsgnAndNode iVisited) { |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitOpAsgnOrNode(OpAsgnOrNode iVisited) { |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitOptNNode(OptNNode iVisited) { |
| // System.out.println("DLTKASTBuildVisitor.visitOptNNode()"); |
| iVisited.getBodyNode().accept(this); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitOrNode(OrNode iVisited) { // done |
| ASTNode leftSt = collectSingleNodeSafe(iVisited.getFirstNode()); |
| ASTNode rightSt = collectSingleNodeSafe(iVisited.getSecondNode()); |
| |
| RubyBinaryExpression b = new RubyBinaryExpression(leftSt, |
| ExpressionConstants.E_BOR, rightSt); |
| states.peek().add(b); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitPostExeNode(PostExeNode iVisited) { |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitRedoNode(RedoNode iVisited) { |
| ISourcePosition position = iVisited.getPosition(); |
| states.peek().add( |
| new RubyRedoExpression(position.getStartOffset(), position |
| .getEndOffset())); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitRescueBodyNode(RescueBodyNode iVisited) { // done |
| ASTNode bodyNode = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ASTNode exceptionNodes = collectSingleNodeSafe(iVisited |
| .getExceptionNodes()); // in fact it would be an expression |
| // list |
| // TODO: exception nodes contains only names of exceptions, not their |
| // names itself |
| // so we need to parse them by hands |
| RubyRescueBodyStatement optRescueNode = (RubyRescueBodyStatement) collectSingleNodeSafe(iVisited |
| .getOptRescueNode()); |
| |
| ISourcePosition pos = iVisited.getPosition(); |
| |
| RubyRescueBodyStatement rescueStatement = new RubyRescueBodyStatement( |
| pos.getStartOffset(), pos.getEndOffset(), bodyNode, |
| exceptionNodes, optRescueNode); |
| |
| states.peek().add(rescueStatement); |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitRescueNode(RescueNode iVisited) { // done |
| ASTNode bodyNode = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ASTNode elseNode = collectSingleNodeSafe(iVisited.getElseNode()); |
| |
| RubyRescueBodyStatement rescueNode = (RubyRescueBodyStatement) collectSingleNodeSafe(iVisited |
| .getRescueNode()); |
| |
| ISourcePosition pos = iVisited.getPosition(); |
| |
| RubyRescueStatement rescueStatement = new RubyRescueStatement(pos |
| .getStartOffset(), pos.getEndOffset(), bodyNode, elseNode, |
| rescueNode); |
| |
| states.peek().add(rescueStatement); |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitRetryNode(RetryNode iVisited) { // done |
| ISourcePosition position = iVisited.getPosition(); |
| states.peek().add( |
| new RubyRetryExpression(position.getStartOffset(), position |
| .getEndOffset())); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitReturnNode(ReturnNode iVisited) { |
| ISourcePosition position = iVisited.getPosition(); |
| ASTNode value = null; |
| if (iVisited.getValueNode() != null) { |
| value = collectSingleNodeSafe(iVisited.getValueNode()); |
| } |
| RubyCallArgumentsList list = new RubyCallArgumentsList(); |
| if (value != null) |
| list.addArgument(value, 0); |
| states.peek().add( |
| new RubyReturnStatement(list, position.getStartOffset(), |
| position.getEndOffset())); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitSClassNode(SClassNode iVisited) { |
| String name = ""; //$NON-NLS-1$ |
| Node receiver = iVisited.getReceiverNode(); |
| if (receiver instanceof ConstNode) { |
| name = "<< " + ((ConstNode) iVisited.getReceiverNode()).getName(); //$NON-NLS-1$ |
| } else if (receiver instanceof SelfNode) { |
| name = "<< self"; //$NON-NLS-1$ |
| } else { |
| int startOffset = receiver.getPosition().getStartOffset(); |
| int endOffset = receiver.getPosition().getEndOffset(); |
| name = "<< " //$NON-NLS-1$ |
| + String.copyValueOf(content, startOffset, |
| endOffset - startOffset).trim(); |
| } |
| ISourcePosition pos = iVisited.getReceiverNode().getPosition(); |
| ISourcePosition cPos = iVisited.getPosition(); |
| RubySingletonClassDeclaration type = new RubySingletonClassDeclaration( |
| name, pos.getStartOffset(), pos.getEndOffset(), cPos |
| .getStartOffset(), cPos.getEndOffset()); |
| states.peek().add(type); |
| |
| CollectingState coll = new CollectingState(); |
| states.push(coll); |
| receiver.accept(this); |
| states.pop(); |
| if (coll.list.size() == 1 && coll.list.get(0) instanceof ASTNode) { |
| Object obj = coll.list.get(0); |
| type.setReceiver((ASTNode) obj); |
| if (obj instanceof SimpleReference) { |
| SimpleReference reference = (SimpleReference) obj; |
| type.setName("<< " + reference.getName()); //$NON-NLS-1$ |
| } |
| } |
| states.push(new ClassState(type, states.getFullClassName())); |
| Node bodyNode = iVisited.getBodyNode(); |
| if (bodyNode != null) { |
| pos = bodyNode.getPosition(); |
| Block bl = new Block(pos.getStartOffset(), pos.getEndOffset() + 1); |
| type.setBody(bl); |
| bodyNode.accept(this); |
| } |
| states.pop(); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitSelfNode(SelfNode iVisited) { |
| ISourcePosition position = fixNamePosition(iVisited.getPosition()); |
| states.peek().add( |
| new RubySelfReference(position.getStartOffset(), position |
| .getEndOffset())); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitSplatNode(SplatNode iVisited) { |
| Iterator<Node> iterator = iVisited.childNodes().iterator(); |
| while (iterator.hasNext()) { |
| iterator.next().accept(this); |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitStrNode(StrNode iVisited) { |
| String value = iVisited.getValue().toString(); |
| ISourcePosition position = iVisited.getPosition(); |
| int start = position.getStartOffset(); |
| int end = position.getEndOffset(); |
| if (value.length() == 0 && !isEmptyString(start, end)) { |
| // FIXME why do we need this code? only for the __FILE__? |
| value = String.copyValueOf(content, start, end - start); |
| } else { |
| value = '"' + value + '"'; |
| } |
| states.peek().add(new StringLiteral(start, end, value)); |
| return null; |
| } |
| |
| private boolean isEmptyString(int start, int end) { |
| if (end - start == 2) { |
| if (content[start] == '\'' && content[start + 1] == '\'' |
| || content[start] == '"' && content[start + 1] == '"') { |
| return true; |
| } |
| } else if (end - start == 3) { |
| if (content[start] == '%') { |
| final char starter = content[start + 1]; |
| if (!RubySyntaxUtils.isValidPercentStringStarter(starter)) { |
| final char terminator = RubySyntaxUtils |
| .getPercentStringTerminator(starter); |
| if (terminator != 0 && content[start + 2] == terminator) { |
| return true; |
| } |
| } |
| } |
| } else if (end - start == 4) { |
| if (content[start] == '%') { |
| if (RubySyntaxUtils |
| .isValidPercentStringStarter(content[start + 1])) { |
| final char terminator = RubySyntaxUtils |
| .getPercentStringTerminator(content[start + 2]); |
| if (terminator != 0 && content[start + 3] == terminator) { |
| return true; |
| } |
| } |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public Instruction visitSValueNode(SValueNode iVisited) { |
| Iterator<Node> iterator = iVisited.childNodes().iterator(); |
| while (iterator.hasNext()) { |
| iterator.next().accept(this); |
| } |
| return null; |
| } |
| |
| private CallArgumentsList processCallArguments(Node argsNode) { |
| RubyCallArgumentsList argList = new RubyCallArgumentsList(); |
| states.push(new ArgumentsState(argList)); |
| |
| if (argsNode instanceof ListNode) { |
| ListNode arrayNode = (ListNode) argsNode; |
| List<Node> list = arrayNode.childNodes(); |
| for (Iterator<Node> iter = list.iterator(); iter.hasNext();) { |
| Node node = iter.next(); |
| node.accept(this); |
| } |
| } else if (argsNode instanceof ArgsCatNode) { |
| ArgsCatNode argsCatNode = (ArgsCatNode) argsNode; |
| CallArgumentsList first = processCallArguments(argsCatNode |
| .getFirstNode()); |
| CallArgumentsList second = processCallArguments(argsCatNode |
| .getSecondNode()); |
| for (Iterator<ASTNode> iterator = first.getChilds().iterator(); iterator |
| .hasNext();) { |
| ASTNode e = iterator.next(); |
| argList.addNode(e); |
| } |
| for (Iterator<ASTNode> iterator = second.getChilds().iterator(); iterator |
| .hasNext();) { |
| ASTNode e = iterator.next(); |
| argList.addNode(e); |
| } |
| } else if (argsNode != null) { |
| argsNode.accept(this); |
| } |
| |
| states.pop(); |
| |
| return argList; |
| } |
| |
| @Override |
| public Instruction visitSuperNode(SuperNode iVisited) { // done |
| Node argsNode = iVisited.getArgsNode(); |
| CallArgumentsList callArguments = processCallArguments(argsNode); |
| |
| Node iterNode = iVisited.getIterNode(); |
| ASTNode block = collectSingleNodeSafe(iterNode); |
| |
| ISourcePosition pos = iVisited.getPosition(); |
| |
| RubySuperExpression expr = new RubySuperExpression( |
| pos.getStartOffset(), pos.getEndOffset(), callArguments, block); |
| |
| states.peek().add(expr); |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitToAryNode(ToAryNode iVisited) { |
| Iterator<Node> iterator = iVisited.childNodes().iterator(); |
| while (iterator.hasNext()) { |
| iterator.next().accept(this); |
| } |
| return null; |
| } |
| |
| @Override |
| public Instruction visitTrueNode(TrueNode iVisited) { // done |
| ISourcePosition position = iVisited.getPosition(); |
| states.peek().add( |
| new BooleanLiteral(position.getStartOffset(), position |
| .getEndOffset(), true)); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitUndefNode(UndefNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyUndefStatement s = new RubyUndefStatement(pos.getStartOffset(), pos |
| .getEndOffset()); |
| states.peek().add(s); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitUntilNode(UntilNode iVisited) { // done |
| ASTNode condition = collectSingleNodeSafe(iVisited.getConditionNode()); |
| ASTNode body = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyUntilStatement st = new RubyUntilStatement(condition, body); |
| st.setStart(pos.getStartOffset()); |
| st.setEnd(pos.getEndOffset()); |
| states.peek().add(st); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitVAliasNode(VAliasNode iVisited) { |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitVCallNode(VCallNode iVisited) { |
| String methodName = iVisited.getName(); |
| |
| if (states.isClassLikeState()) { |
| ClassLikeState classState = states.getClassLikeState(); |
| if (methodName.equals("private")) //$NON-NLS-1$ |
| classState.visibility = Modifiers.AccPrivate; |
| else if (methodName.equals("protected")) //$NON-NLS-1$ |
| classState.visibility = Modifiers.AccProtected; |
| else if (methodName.equals("public")) //$NON-NLS-1$ |
| classState.visibility = Modifiers.AccPublic; |
| } |
| |
| ISourcePosition pos = iVisited.getPosition(); |
| int funcNameStart = pos.getStartOffset(); |
| // Assert.isTrue(funcNameStart + methodName.length() == funcNameEnd); |
| CallExpression c = new CallExpression(null, methodName, |
| CallArgumentsList.EMPTY); |
| c.setStart(funcNameStart); |
| c.setEnd(funcNameStart + methodName.length()); |
| c.getCallName().setStart(funcNameStart); |
| c.getCallName().setEnd(funcNameStart + methodName.length()); |
| this.states.peek().add(c); |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitWhenNode(WhenNode iVisited) { // done |
| ISourcePosition position = iVisited.getPosition(); |
| RubyWhenStatement statement = new RubyWhenStatement(position |
| .getStartOffset(), position.getEndOffset()); |
| ASTNode bodyStatement = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ASTNode expressionsStatement = collectSingleNodeSafe(iVisited |
| .getExpressionNodes()); |
| |
| statement.setBody(bodyStatement); |
| if (expressionsStatement instanceof org.eclipse.dltk.ast.ASTListNode) { |
| org.eclipse.dltk.ast.ASTListNode list = (org.eclipse.dltk.ast.ASTListNode) expressionsStatement; |
| statement.setExpressions(list.getChilds()); |
| } else { |
| List<ASTNode> list = new ArrayList<ASTNode>(1); |
| list.add(expressionsStatement); |
| statement.setExpressions(list); |
| } |
| |
| states.peek().add(statement); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitWhileNode(WhileNode iVisited) { // done |
| ASTNode condition = collectSingleNodeSafe(iVisited.getConditionNode()); |
| ASTNode body = collectSingleNodeSafe(iVisited.getBodyNode()); |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyWhileStatement st = new RubyWhileStatement(condition, body); |
| st.setStart(pos.getStartOffset()); |
| st.setEnd(pos.getEndOffset()); |
| states.peek().add(st); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitXStrNode(XStrNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| String value = iVisited.getValue().toString(); |
| RubyBacktickStringLiteral s = new RubyBacktickStringLiteral(pos |
| .getStartOffset(), pos.getEndOffset(), value); |
| states.peek().add(s); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitYieldNode(YieldNode iVisited) { |
| ISourcePosition position = iVisited.getPosition(); |
| RubyCallArgumentsList args = new RubyCallArgumentsList(); |
| Node valueNode = iVisited.getArgsNode(); |
| if (valueNode != null) { |
| ASTNode s = collectSingleNodeSafe(valueNode); |
| args.addArgument(s, 0); |
| } |
| states.peek().add( |
| new RubyYieldExpression(position.getStartOffset(), position |
| .getEndOffset(), args)); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitZArrayNode(ZArrayNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| RubyArrayExpression arr = new RubyArrayExpression(); |
| arr.setStart(pos.getStartOffset()); |
| arr.setEnd(pos.getEndOffset()); |
| states.peek().add(arr); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitZSuperNode(ZSuperNode iVisited) { // done |
| |
| CallArgumentsList callArguments = new CallArgumentsList(); // no |
| // arguments |
| |
| Node iterNode = iVisited.getIterNode(); |
| ASTNode block = collectSingleNodeSafe(iterNode); |
| |
| ISourcePosition pos = iVisited.getPosition(); |
| |
| RubySuperExpression expr = new RubySuperExpression( |
| pos.getStartOffset(), pos.getEndOffset(), callArguments, block); |
| |
| states.peek().add(expr); |
| |
| return null; |
| } |
| |
| /** |
| * @see NodeVisitor#visitBignumNode(BignumNode) |
| */ |
| @Override |
| public Instruction visitBignumNode(BignumNode iVisited) {// done |
| ISourcePosition pos = iVisited.getPosition(); |
| BigInteger value = iVisited.getValue(); |
| BigNumericLiteral literal = new BigNumericLiteral(pos.getStartOffset(), |
| pos.getEndOffset(), value); |
| states.peek().add(literal); |
| return null; |
| } |
| |
| /** |
| * @see NodeVisitor#visitFixnumNode(FixnumNode) |
| */ |
| @Override |
| public Instruction visitFixnumNode(FixnumNode iVisited) { |
| ISourcePosition pos = iVisited.getPosition(); |
| NumericLiteral node = new NumericLiteral(pos.getStartOffset(), pos |
| .getEndOffset(), iVisited.getValue()); |
| states.peek().add(node); |
| return null; |
| } |
| |
| /** |
| * @see NodeVisitor#visitFloatNode(FloatNode) |
| */ |
| @Override |
| public Instruction visitFloatNode(FloatNode iVisited) { // done |
| ISourcePosition pos = iVisited.getPosition(); |
| double value = iVisited.getValue(); |
| FloatNumericLiteral num = new FloatNumericLiteral(pos.getStartOffset(), |
| pos.getEndOffset(), value); |
| states.peek().add(num); |
| return null; |
| } |
| |
| /** |
| * @see NodeVisitor#visitRegexpNode(RegexpNode) |
| */ |
| @Override |
| public Instruction visitRegexpNode(RegexpNode iVisited) { // done |
| Pattern pattern = iVisited.getPattern(); |
| ISourcePosition position = iVisited.getPosition(); |
| String value = iVisited.getValue().toString(); |
| RubyRegexpExpression e = new RubyRegexpExpression(position |
| .getStartOffset(), position.getEndOffset(), value); |
| e.setPattern(pattern); |
| states.peek().add(e); |
| return null; |
| } |
| |
| /** |
| * @see NodeVisitor#visitSymbolNode(SymbolNode) |
| */ |
| @Override |
| public Instruction visitSymbolNode(SymbolNode iVisited) { // done |
| final String symName = iVisited.getName(); |
| final ISourcePosition position = iVisited.getPosition(); |
| final int start = position.getStartOffset(); |
| int nameLen = symName.length(); |
| if (content[start] == ':') { |
| ++nameLen; |
| } |
| int end = position.getEndOffset(); |
| if (end - start > nameLen) { |
| end = start + nameLen; |
| } |
| RubySymbolReference sr = new RubySymbolReference(start, end, symName); |
| states.peek().add(sr); |
| |
| return null; |
| } |
| |
| @Override |
| public Instruction visitArgsPushNode(ArgsPushNode arg0) { |
| // TODO Auto-generated method stub |
| return null; |
| } |
| |
| @Override |
| public Instruction visitAttrAssignNode(AttrAssignNode arg0) { // done |
| ASTNode receiver = collectSingleNodeSafe(arg0.getReceiverNode()); |
| CallArgumentsList list = processCallArguments(arg0.getArgsNode()); |
| CallExpression expr = new CallExpression(receiver, arg0.getName(), list); |
| copyOffsets(expr, arg0); |
| int possNameStart = arg0.getPosition().getStartOffset(); |
| if (receiver != null) |
| possNameStart = receiver.sourceEnd() + 1; |
| fixFunctionCallOffsets(expr, arg0.getName(), possNameStart, list |
| .sourceStart(), list.sourceEnd()); |
| states.peek().add(expr); |
| return null; |
| } |
| |
| @Override |
| public Instruction visitRootNode(RootNode arg0) { // done |
| Node bodyNode = arg0.getBodyNode(); |
| if (bodyNode instanceof BlockNode) { |
| BlockNode blockNode = (BlockNode) bodyNode; |
| Iterator<Node> iterator = blockNode.childNodes().iterator(); |
| while (iterator.hasNext()) { |
| iterator.next().accept(this); |
| } |
| } else if (bodyNode != null) |
| bodyNode.accept(this); |
| return null; |
| } |
| } |