de.walware.statet.r.core.rsource.ast.RAst.java Source code

Java tutorial

Introduction

Here is the source code for de.walware.statet.r.core.rsource.ast.RAst.java

Source

/*=============================================================================#
 # Copyright (c) 2007-2015 Stephan Wahlbrink (WalWare.de) 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
 # 
 # Contributors:
 #     Stephan Wahlbrink - initial API and implementation
 #=============================================================================*/

package de.walware.statet.r.core.rsource.ast;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.collections.primitives.ArrayIntList;
import org.apache.commons.collections.primitives.IntList;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.Region;

import de.walware.ecommons.ltk.ast.IAstNode;
import de.walware.ecommons.ltk.ast.ICommonAstVisitor;

import de.walware.statet.r.core.model.ArgsDefinition;
import de.walware.statet.r.core.model.ArgsDefinition.Arg;
import de.walware.statet.r.core.model.RCoreFunctions;
import de.walware.statet.r.core.rlang.RTerminal;
import de.walware.statet.r.core.rsource.IRSourceConstants;

/**
 * 
 */
public class RAst {

    private static class LowestFDefAssignmentSearchVisitor extends GenericVisitor implements ICommonAstVisitor {

        private final int fStartOffset;
        private final int fStopOffset;
        private boolean fInAssignment;
        private RAstNode fAssignment;

        public LowestFDefAssignmentSearchVisitor(final int offset) {
            fStartOffset = offset;
            fStopOffset = offset;
        }

        @Override
        public void visit(final IAstNode node) throws InvocationTargetException {
            if (node instanceof RAstNode) {
                ((RAstNode) node).acceptInR(this);
                return;
            }
            if (node.getStopOffset() >= fStartOffset && fStopOffset >= node.getOffset()) {
                node.acceptInChildren(this);
                return;
            }
        }

        @Override
        public void visitNode(final RAstNode node) throws InvocationTargetException {
            if (node.getStopOffset() >= fStartOffset && fStopOffset >= node.getOffset()) {
                node.acceptInRChildren(this);
                return;
            }
        }

        @Override
        public void visit(final Assignment node) throws InvocationTargetException {
            if (fInAssignment) {
                node.getSourceChild().acceptInR(this);
                return;
            }
            if (node.getStopOffset() >= fStartOffset && fStopOffset >= node.getOffset()) {
                fInAssignment = true;
                node.getSourceChild().acceptInR(this);
                fInAssignment = false;
                return;
            }
        }

        @Override
        public void visit(final FDef node) throws InvocationTargetException {
            if (fInAssignment || (node.getStopOffset() >= fStartOffset && fStopOffset >= node.getOffset())) {
                RAstNode take = node;
                RAstNode candidate = node.getRParent();
                // TODO: use analyzed ElementAccess if possible
                AssignExpr assign = null;
                while ((assign = checkAssign(candidate)) != null && assign.valueNode == take) {
                    take = assign.assignNode;
                    candidate = take.getRParent();
                }
                fAssignment = take;
                throw new OperationCanceledException();
            }
        }

    }

    public static RAstNode findLowestFDefAssignment(final IAstNode root, final int offset) {
        final LowestFDefAssignmentSearchVisitor visitor = new LowestFDefAssignmentSearchVisitor(offset);
        try {
            root.accept(visitor);
        } catch (final OperationCanceledException e) {
        } catch (final InvocationTargetException e) {
        }
        return visitor.fAssignment;
    }

    private static class DeepestCommandsSearchVisitor extends GenericVisitor implements ICommonAstVisitor {

        private final int fStartOffset;
        private final int fStopOffset;
        private RAstNode fContainer;
        private final List<RAstNode> fCommands = new ArrayList<RAstNode>();

        public DeepestCommandsSearchVisitor(final int startOffset, final int stopOffset) {
            fStartOffset = startOffset;
            fStopOffset = stopOffset;
        }

        @Override
        public void visit(final IAstNode node) throws InvocationTargetException {
            if (node instanceof RAstNode) {
                ((RAstNode) node).acceptInR(this);
                return;
            }
            if (node.getStopOffset() >= fStartOffset && fStopOffset >= node.getOffset()) {
                node.acceptInChildren(this);
                return;
            }
        }

        @Override
        public void visitNode(final RAstNode node) throws InvocationTargetException {
            if (node.fStopOffset >= fStartOffset
                    && ((fStartOffset == fStopOffset) ? fStopOffset >= node.fStartOffset
                            : fStopOffset > node.fStartOffset)) {
                if (fContainer != null && fContainer == node.fRParent) {
                    fCommands.add(node);
                }
            }

            if (node.fStartOffset <= fStartOffset && fStopOffset <= node.fStopOffset) {
                node.acceptInRChildren(this);
                return;
            }
        }

        @Override
        public void visit(final Block node) throws InvocationTargetException {
            if (node.fStartOffset <= fStartOffset && fStopOffset <= node.fStopOffset) {
                fCommands.clear();
                if (node.fStartOffset == fStartOffset && fStopOffset == node.fStopOffset) {
                    fCommands.add(node);
                    fContainer = null;
                    return;
                }
                fContainer = node;

                node.acceptInRChildren(this);

                if (fCommands.isEmpty() && node.fStopOffset > fStartOffset) {
                    fCommands.add(node);
                }
                return;
            }
        }

        @Override
        public void visit(final SourceComponent node) throws InvocationTargetException {
            if (node.fStopOffset >= fStartOffset && fStopOffset >= node.fStartOffset) {
                fCommands.clear();
                fContainer = node;

                node.acceptInRChildren(this);
                return;
            }
        }

    }

    private static class NextCommandsSearchVisitor extends GenericVisitor implements ICommonAstVisitor {

        private final int fOffset;
        private RAstNode fContainer;
        private RAstNode fNext;

        public NextCommandsSearchVisitor(final int offset) {
            fOffset = offset;
        }

        @Override
        public void visit(final IAstNode node) throws InvocationTargetException {
            if (node instanceof RAstNode) {
                ((RAstNode) node).acceptInR(this);
                return;
            }
            if (node.getStopOffset() >= fOffset && fOffset >= node.getOffset()) {
                node.acceptInChildren(this);
                return;
            }
        }

        @Override
        public void visitNode(final RAstNode node) throws InvocationTargetException {
            if (fNext == null) {
                if (node.fStartOffset >= fOffset) {
                    if (fContainer != null && fContainer == node.fRParent) {
                        fNext = node;
                        return;
                    } else {
                        node.acceptInRChildren(this);
                        return;
                    }
                }
            }
        }

        @Override
        public void visit(final Block node) throws InvocationTargetException {
            if (fNext == null) {
                if (node.fStartOffset >= fOffset) {
                    if (fContainer != null && fContainer == node.fRParent) {
                        fNext = node;
                        return;
                    } else {
                        node.acceptInRChildren(this);
                        return;
                    }
                }
                if (node.fStopOffset >= fOffset) {
                    fContainer = node;
                    node.acceptInRChildren(this);
                    return;
                }
            }
        }

        @Override
        public void visit(final SourceComponent node) throws InvocationTargetException {
            if (fNext == null) {
                final IAstNode parent = node.getParent();
                if (node.fStopOffset >= fOffset &&
                // R script file or inside R chunk
                        (parent == null || (parent.getOffset() <= fOffset && fOffset <= parent.getStopOffset()))) {
                    fContainer = node;
                    node.acceptInRChildren(this);
                    return;
                }
            }
        }

    }

    public static RAstNode[] findDeepestCommands(final IAstNode root, final int startOffset, final int stopOffset) {
        final DeepestCommandsSearchVisitor visitor = new DeepestCommandsSearchVisitor(startOffset, stopOffset);
        try {
            root.accept(visitor);
        } catch (final InvocationTargetException e) {
        }
        return visitor.fCommands.toArray(new RAstNode[visitor.fCommands.size()]);
    }

    public static RAstNode findNextCommands(final IAstNode root, final int offset) {
        final NextCommandsSearchVisitor visitor = new NextCommandsSearchVisitor(offset);
        try {
            root.accept(visitor);
        } catch (final InvocationTargetException e) {
        }
        return visitor.fNext;
    }

    public static class AssignExpr {

        public static final Object GLOBAL = new Object();
        public static final Object LOCAL = new Object();

        public final Object environment;
        public final RAstNode assignNode;
        public final RAstNode targetNode;
        public final RAstNode valueNode;

        public AssignExpr(final RAstNode assign, final Object env, final RAstNode target, final RAstNode source) {
            this.assignNode = assign;
            this.environment = env;
            this.targetNode = target;
            this.valueNode = source;
        }

    }

    public static AssignExpr checkAssign(final RAstNode node) {
        switch (node.getNodeType()) {
        case A_LEFT:
        case A_EQUALS:
        case A_RIGHT:
            final Assignment assignNode = (Assignment) node;
            if (assignNode.isSearchOperator()) {
                return new AssignExpr(node, AssignExpr.GLOBAL, assignNode.getTargetChild(),
                        assignNode.getSourceChild());
            } else {
                return new AssignExpr(node, AssignExpr.LOCAL, assignNode.getTargetChild(),
                        assignNode.getSourceChild());
            }
        case F_CALL:
            final FCall callNode = (FCall) node;
            final RAstNode refChild = callNode.getRefChild();
            if (refChild.getNodeType() == NodeType.SYMBOL) {
                final Symbol symbol = (Symbol) refChild;
                if (symbol.fText != null) {
                    if (symbol.fText.equals(RCoreFunctions.BASE_ASSIGN_NAME)) {
                        final ReadedFCallArgs args = readArgs(callNode.getArgsChild(),
                                RCoreFunctions.DEFAULT.BASE_ASSIGN_args);
                        return new AssignExpr(node, AssignExpr.LOCAL, args.allocatedArgs[0], args.allocatedArgs[1]);
                    }
                }
            }
            break;
        case F_CALL_ARGS:
            return checkAssign(node.getRParent());
        case F_CALL_ARG:
            return checkAssign(node.getRParent().getRParent());
        }
        return null;
    }

    public static final class ReadedFCallArgs {

        public final ArgsDefinition argsDef;
        public final FCall.Args argsNode;
        public final FCall.Arg[] allocatedArgs;
        public final FCall.Arg[] ellipsisArgs;
        public final FCall.Arg[] otherArgs;
        public final int[] argsNode2argsDef;

        private ReadedFCallArgs(final ArgsDefinition argsDef, final FCall.Args argsNode,
                final FCall.Arg[] allocatedArgs, final FCall.Arg[] ellipsisArgs, final FCall.Arg[] otherArgs,
                final int[] argsNode2argsDef) {
            this.argsDef = argsDef;
            this.argsNode = argsNode;
            this.allocatedArgs = allocatedArgs;
            this.ellipsisArgs = ellipsisArgs;
            this.otherArgs = otherArgs;
            this.argsNode2argsDef = argsNode2argsDef;
        }

        public FCall.Arg getArgNode(final String name) {
            final int idx = argsDef.indexOf(name);
            if (idx >= 0) {
                return allocatedArgs[idx];
            } else {
                return null;
            }
        }

        public FCall.Arg getArgNode(final int idx) {
            if (idx >= 0) {
                return allocatedArgs[idx];
            } else {
                return null;
            }
        }

        public RAstNode getArgValueNode(final String name) {
            final int idx = argsDef.indexOf(name);
            if (idx >= 0 && allocatedArgs[idx] != null) {
                return allocatedArgs[idx].getValueChild();
            } else {
                return null;
            }
        }

        public RAstNode getArgValueNode(final int idx) {
            if (idx >= 0 && allocatedArgs[idx] != null) {
                return allocatedArgs[idx].getValueChild();
            } else {
                return null;
            }
        }

        @Override
        public String toString() {
            return Arrays.toString(argsNode2argsDef);
        }

    }

    public static final FCall.Arg[] NO_ARGS = new FCall.Arg[0];

    private static final int FAIL = -1;
    private static final int ELLIPSIS = -2;
    private static final int TEST_PARTIAL = -3;
    private static final int TEST_POSITION = -4;

    /**
     * Reads the arguments of a function call for the given function definition
     * 
     * If follows mainly the R rules expect if R would fail.
     * 
     * 1) Exact matching on tags. 
     *    a) First supplied named argument matching exactly a formal name
     *       is assigned to its formal arguments (position) in <code>argsNode</code>
     *    b) Additional supplied arguments matching exactly the formal name
     *       with an assignment of a) (error) are added to <code>otherArgs</code>
     * 2) Partial matching on tags
     *    a) If the name of the supplied argument matches partially more than one 
     *       unmatched formal name (error), it is added to <code>otherArgs</code>
     *    b) First supplied argument matching partially one unmatched formal name
     *       is assigned to its formal arguments (position) in <code>argsNode</code>
     *    c) Additional supplied arguments matching partially the unmatched formal name 
     *       with an assignment of b) (error) are treated like arguments without any match
     *       in 1) and 2) continue with 3).
     * 3) Positional matching.
     *    a) Any unnamed supplied argument is assigned to unmatched formal
     *       arguments (empty positions) in <code>argsNode</code>, in order.
     *       Until all If there is a ... argument or there is no more empty position.
     *    b) If there is a ... argument, all remaining supplied arguments
     *       are added to <code>ellipsisArgs</code>
     * 4) Remaining supplied arguments (error) are assigned to <code>otherArgs</code>
     * 
     * (see paragraph 'Argument matching' in 'R Language Definition')
     * 
     * @param argsNode the arguments of the call
     * @param argsDef the arguments definition
     * @return
     */
    public static ReadedFCallArgs readArgs(final FCall.Args argsNode, final ArgsDefinition argsDef) {
        final int nodeArgsCount = argsNode.getChildCount();
        final int defArgsCount = argsDef.size();
        final FCall.Arg[] allocatedArgs = (defArgsCount > 0) ? new FCall.Arg[defArgsCount] : NO_ARGS;
        final int ellipsisDefIdx = argsDef.indexOf("..."); //$NON-NLS-1$

        final int[] match = new int[nodeArgsCount];
        int ellipsisCount = 0;
        int failCount = 0;
        boolean testPartial = false;
        boolean testPosition = false;

        for (int nodeIdx = 0; nodeIdx < nodeArgsCount; nodeIdx++) {
            final FCall.Arg argNode = argsNode.getChild(nodeIdx);
            if (argNode.hasName()) {
                final Arg arg = argsDef.get(argNode.getNameChild().getText());
                if (arg != null && arg.index != ellipsisDefIdx) {
                    if (allocatedArgs[arg.index] == null) {
                        allocatedArgs[arg.index] = argNode;
                        match[nodeIdx] = arg.index;
                    } else {
                        failCount++;
                        match[nodeIdx] = FAIL;
                    }
                } else {
                    testPartial = true;
                    match[nodeIdx] = TEST_PARTIAL;
                }
            } else {
                testPosition = true;
                match[nodeIdx] = TEST_POSITION;
            }
        }

        final int ellipsisType = (ellipsisDefIdx >= 0) ? ELLIPSIS : FAIL;
        final int testStop = (ellipsisDefIdx >= 0) ? ellipsisDefIdx : defArgsCount;
        if (testPartial) {
            FCall.Arg[] partialArgs = null;
            ITER_ARGS: for (int nodeIdx = 0; nodeIdx < nodeArgsCount; nodeIdx++) {
                if (match[nodeIdx] == TEST_PARTIAL) {
                    final FCall.Arg argNode = argsNode.getChild(nodeIdx);
                    final String name = argNode.getNameChild().getText();
                    int matchIdx = -1;
                    for (int defIdx = 0; defIdx < testStop; defIdx++) {
                        if (allocatedArgs[defIdx] == null && argsDef.get(defIdx).name.startsWith(name)) {
                            if (matchIdx < 0) {
                                matchIdx = defIdx;
                            } else {
                                failCount++;
                                match[nodeIdx] = FAIL;
                                continue ITER_ARGS;
                            }
                        }
                    }
                    if (matchIdx >= 0) {
                        if (partialArgs == null) {
                            partialArgs = new FCall.Arg[testStop];
                            partialArgs[matchIdx] = argNode;
                            match[nodeIdx] = matchIdx;
                            continue ITER_ARGS;
                        }
                        if (partialArgs[matchIdx] == null) {
                            partialArgs[matchIdx] = argNode;
                            match[nodeIdx] = matchIdx;
                            continue ITER_ARGS;
                        }
                    }
                    ellipsisCount++;
                    match[nodeIdx] = ellipsisType;
                    continue ITER_ARGS;
                }
            }
            if (partialArgs != null) {
                for (int i = 0; i < testStop; i++) {
                    if (partialArgs[i] != null) {
                        allocatedArgs[i] = partialArgs[i];
                    }
                }
            }
        }

        if (testPosition) {
            int defIdx = 0;
            ITER_ARGS: for (int nodeIdx = 0; nodeIdx < nodeArgsCount; nodeIdx++) {
                if (match[nodeIdx] == TEST_POSITION) {
                    ITER_DEFS: while (defIdx < testStop) {
                        if (allocatedArgs[defIdx] == null) {
                            match[nodeIdx] = defIdx;
                            allocatedArgs[defIdx++] = argsNode.getChild(nodeIdx);
                            continue ITER_ARGS;
                        } else {
                            defIdx++;
                            continue ITER_DEFS;
                        }
                    }
                    match[nodeIdx] = ellipsisType;
                    ellipsisCount++;
                    continue ITER_ARGS;
                }
            }
        }

        if (ellipsisType != ELLIPSIS) {
            failCount += ellipsisCount;
            ellipsisCount = 0;
        }
        final FCall.Arg[] ellipsisArgs = (ellipsisCount > 0) ? new FCall.Arg[ellipsisCount] : NO_ARGS;
        final FCall.Arg[] otherArgs = (failCount > 0) ? new FCall.Arg[failCount] : NO_ARGS;
        if (ellipsisCount > 0 || failCount > 0) {
            int ellipsisIdx = 0;
            int otherIdx = 0;
            ITER_ARGS: for (int nodeIdx = 0; nodeIdx < nodeArgsCount; nodeIdx++) {
                switch (match[nodeIdx]) {
                case ELLIPSIS:
                    ellipsisArgs[ellipsisIdx++] = argsNode.getChild(nodeIdx);
                    match[nodeIdx] = ellipsisDefIdx;
                    continue ITER_ARGS;
                case FAIL:
                    otherArgs[otherIdx++] = argsNode.getChild(nodeIdx);
                    continue ITER_ARGS;
                }
            }
        }
        return new ReadedFCallArgs(argsDef, argsNode, allocatedArgs, ellipsisArgs, otherArgs, match);
    }

    /**
     * @return position of the element name, if possible (symbol or strings), otherwise null
     */
    public static Position getElementNamePosition(final RAstNode node) {
        switch (node.getNodeType()) {
        case SYMBOL:
            if (node.getOperator(0) == RTerminal.SYMBOL_G) {
                if ((node.getStatusCode()
                        & IRSourceConstants.STATUS_MASK_12) == IRSourceConstants.STATUS2_SYNTAX_TOKEN_NOT_CLOSED) {
                    return new Position(node.getOffset() + 1, node.getLength() - 1);
                }
                return new Position(node.getOffset() + 1, node.getLength() - 2);
            }
            return new Position(node.getOffset(), node.getLength());
        case STRING_CONST:
            if ((node.getStatusCode()
                    & IRSourceConstants.STATUS_MASK_12) == IRSourceConstants.STATUS2_SYNTAX_TOKEN_NOT_CLOSED) {
                return new Position(node.getOffset() + 1, node.getLength() - 1);
            }
            return new Position(node.getOffset() + 1, node.getLength() - 2);
        default:
            return null;
        }
    }

    /**
     * @return position of the element name, if possible (symbol or strings), otherwise the node range
     */
    public static IRegion getElementNameRegion(final RAstNode node) {
        switch (node.getNodeType()) {
        case SYMBOL:
            if (node.getOperator(0) == RTerminal.SYMBOL_G) {
                if ((node.getStatusCode()
                        & IRSourceConstants.STATUS_MASK_12) == IRSourceConstants.STATUS2_SYNTAX_TOKEN_NOT_CLOSED) {
                    return new Region(node.getOffset() + 1, node.getLength() - 1);
                }
                return new Region(node.getOffset() + 1, node.getLength() - 2);
            }
            return node;
        case STRING_CONST:
            if ((node.getStatusCode()
                    & IRSourceConstants.STATUS_MASK_12) == IRSourceConstants.STATUS2_SYNTAX_TOKEN_NOT_CLOSED) {
                return new Region(node.getOffset() + 1, node.getLength() - 1);
            }
            return new Region(node.getOffset() + 1, node.getLength() - 2);
        default:
            return node;
        }
    }

    public static boolean hasErrors(final RAstNode node) {
        return ((node.getStatusCode()
                & (IRSourceConstants.STATUSFLAG_REAL_ERROR | IRSourceConstants.STATUSFLAG_ERROR_IN_CHILD)) != 0);
    }

    public static int[] computeRExpressionIndex(RAstNode node, final RAstNode baseNode) {
        final IntList topdown = new ArrayIntList();
        while (node != baseNode) {
            final RAstNode parent = node.getRParent();
            switch (parent.getNodeType()) {

            // list
            case SOURCELINES:
            case F_DEF_ARGS:
                // [[1]] = name
            case F_CALL:
                topdown.add(parent.getChildIndex(node) + 1);
                node = parent;
                continue;

            // [[1]] = operator
            case BLOCK:
            case GROUP:
            case SUB_INDEXED_S:
            case SUB_INDEXED_D:
            case NS_GET:
            case NS_GET_INT:
            case SUB_NAMED_PART:
            case SUB_NAMED_SLOT:
            case POWER:
            case SIGN:
            case SEQ:
            case SPECIAL:
            case MULT:
            case ADD:
            case RELATIONAL:
            case NOT:
            case AND:
            case OR:
            case MODEL:
            case A_RIGHT:
            case A_EQUALS:
            case A_LEFT:
            case HELP:
            case C_IF:
            case C_FOR:
            case C_WHILE:
            case C_REPEAT:
            case F_DEF:
                topdown.add(parent.getChildIndex(node) + 2);
                node = parent;
                continue;

            // part of parent element
            case SUB_INDEXED_ARGS:
                if (parent == baseNode) {
                    break;
                }
                topdown.add(parent.getChildIndex(node) + 3);
                node = parent.getRParent();
                break;
            case C_IN:
            case F_CALL_ARGS:
                if (parent == baseNode) {
                    break;
                }
                topdown.add(parent.getChildIndex(node) + 2);
                node = parent.getRParent();
                break;

            case SUB_INDEXED_ARG:
            case F_DEF_ARG:
            case F_CALL_ARG:
                node = parent;
                continue;

            case ERROR:
            case ERROR_TERM:
            case DUMMY:
                return null;

            default:
                throw new IllegalStateException("Unexpected parent");
            }
        }
        final int l = topdown.size();
        final int[] path = new int[l];
        for (int i = 0; i < l;) {
            path[i] = topdown.get(l - ++i);
        }
        return path;
    }

    public static RAstNode getRRootNode(RAstNode node, final IRegion region) {
        if (region == null) {
            return node.getRRoot();
        }
        while (node.getRParent() != null) {
            final RAstNode parent = node.getRParent();
            final int beginDiff;
            final int endDiff;
            if ((beginDiff = region.getOffset() - parent.getOffset()) > 0 || (endDiff = region.getOffset()
                    + region.getLength() - parent.getOffset() - parent.getLength()) < 0) {
                return node;
            } else if (beginDiff == 0 && endDiff == 0) {
                return (parent.getNodeType() == NodeType.SOURCELINES) ? node : parent;
            } else {
                node = parent;
                continue;
            }
        }
        return node;
    }

    public static boolean isParentChild(final RAstNode parent, RAstNode child) {
        while ((child = child.getRParent()) != null) {
            if (child == parent) {
                return true;
            }
        }
        return false;
    }

    public static Object toJava(RAstNode node) {
        while (node != null) {
            switch (node.getNodeType()) {
            case NUM_CONST:
                switch (node.getOperator(0)) {
                case NUM_NUM:
                    return parseNum(node.getText());
                case NUM_INT:
                    return parseInt(node.getText());
                case TRUE:
                    return Boolean.TRUE;
                case FALSE:
                    return Boolean.FALSE;
                default:
                    break;
                }
                return null;
            case F_CALL_ARG:
                node = ((FCall.Arg) node).getValueChild();
                continue;
            default:
                return null;
            }
        }
        return null;
    }

    public static Integer toJavaInt(RAstNode node) {
        while (node != null) {
            switch (node.getNodeType()) {
            case NUM_CONST:
                switch (node.getOperator(0)) {
                case NUM_NUM: {
                    final Double num = parseNum(node.getText());
                    if (num != null && num.doubleValue() == Math.rint(num.doubleValue())) {
                        return num.intValue();
                    }
                    break;
                }
                case NUM_INT:
                    return parseInt(node.getText());
                case TRUE:
                    return 1;
                case FALSE:
                    return 0;
                default:
                    break;
                }
                return null;
            case F_CALL_ARG:
                node = ((FCall.Arg) node).getValueChild();
                continue;
            default:
                return null;
            }
        }
        return null;
    }

    public static Float toJavaFloat(RAstNode node) {
        while (node != null) {
            switch (node.getNodeType()) {
            case NUM_CONST:
                switch (node.getOperator(0)) {
                case NUM_NUM: {
                    final Double num = parseNum(node.getText());
                    if (num != null && Math.abs(num.doubleValue()) <= Float.MAX_VALUE) {
                        return num.floatValue();
                    }
                    break;
                }
                case NUM_INT: {
                    final Integer num = parseInt(node.getText());
                    if (num != null) {
                        return num.floatValue();
                    }
                    break;
                }
                case TRUE:
                    return 1f;
                case FALSE:
                    return 0f;
                default:
                    break;
                }
                return null;
            case F_CALL_ARG:
                node = ((FCall.Arg) node).getValueChild();
                continue;
            default:
                return null;
            }
        }
        return null;
    }

    public static Double toJavaDouble(RAstNode node) {
        while (node != null) {
            switch (node.getNodeType()) {
            case NUM_CONST:
                switch (node.getOperator(0)) {
                case NUM_NUM: {
                    final Double num = parseNum(node.getText());
                    if (num != null) {
                        return num.doubleValue();
                    }
                    break;
                }
                case NUM_INT: {
                    final Integer num = parseInt(node.getText());
                    if (num != null) {
                        return num.doubleValue();
                    }
                    break;
                }
                case TRUE:
                    return 1.0;
                case FALSE:
                    return 0.0;
                default:
                    break;
                }
                return null;
            case F_CALL_ARG:
                node = ((FCall.Arg) node).getValueChild();
                continue;
            default:
                return null;
            }
        }
        return null;
    }

    private static Double parseNum(String text) {
        if (text != null && !text.isEmpty()) {
            try {
                return Double.valueOf(text);
            } catch (NumberFormatException e) {
            }
        }
        return null;
    }

    private static Integer parseInt(String text) {
        if (text != null && !text.isEmpty()) {
            try {
                if (text.endsWith("L")) { //$NON-NLS-1$
                    text = text.substring(0, text.length() - 1);
                }
                if (text.startsWith("0x")) { //$NON-NLS-1$
                    text = text.substring(2);
                    return Integer.parseInt(text, 16);
                } else {
                    return Integer.parseInt(text);
                }
            } catch (NumberFormatException e) {
            }
        }
        return null;
    }

}