Package r.nodes.tools

Source Code of r.nodes.tools.BuildExecutableTree$SplitArgumentList

package r.nodes.tools;

import r.Utils;
import r.builtins.Primitives;
import r.data.*;
import r.data.internal.*;
import r.errors.RError;
import r.nodes.exec.*;
import r.nodes.ast.*;
import r.nodes.ast.Constant;
import r.nodes.ast.Function;
import r.nodes.ast.FunctionCall;
import r.nodes.ast.If;
import r.nodes.ast.Not;
import r.nodes.ast.Sequence;
import r.nodes.ast.UnaryMinus;
import r.nodes.ast.UpdateVector;
import r.runtime.*;

public class BuildExecutableTree implements Visitor {

    public static final boolean OPTIMIZE_RETURN = true;
    public static final boolean DEBUG_SPECIAL_NODES = false;
    public static final boolean DEBUG_RETURN = false;

    RFunction rootEnclosingFunction;
    RNode result;

    public static RSymbol RETURN_SYMBOL = RSymbol.getSymbol("return");

    public RNode createLazyRootTree(final ASTNode ast) {
        return new BaseR(ast) {

            @Child RNode node = adoptChild(createLazyTree(ast));

            @Override public final Object execute(Frame frame) {
                try {
                    return node.execute(frame);
                } catch (r.nodes.exec.Loop.ContinueException ce) {
                    throw RError.getNoLoopForBreakNext(ast);
                } catch (r.nodes.exec.Loop.BreakException be) {
                    throw RError.getNoLoopForBreakNext(ast);
                }
            }

            @Override protected <N extends RNode> N replaceChild(RNode oldNode, N newNode) {
                assert oldNode != null;
                if (node == oldNode) {
                    node = newNode;
                    return adoptInternal(newNode);
                }
                return super.replaceChild(oldNode, newNode);
            }

        };
    }

    public RNode createTree(ASTNode ast) {
        ast.accept(this);
        return result;
    }

    public RNode createTree(ASTNode ast, RFunction enclosingFunction) {
        try {
            this.rootEnclosingFunction = enclosingFunction;
            ast.accept(this);
            return result;
        } finally {
            this.rootEnclosingFunction = null;
        }
    }

    @SuppressWarnings("static-method") private RNode createLazyTree(ASTNode ast) {
        return new LazyBuild(ast);
    }

    @Override public void visit(If ast) {

        ASTNode falseBranch = ast.getFalseCase();
        ASTNode cond = ast.getCond();
        RNode rcond = createLazyTree(cond);
        RNode rtrueBranch = createLazyTree(ast.getTrueCase());
        RNode rfalseBranch = (falseBranch == null) ? r.nodes.exec.Constant.getNull() : createLazyTree(falseBranch);

        if (cond instanceof EQ) {
            EQ e = (EQ) cond;
            ASTNode rhs = e.getRHS();
            ASTNode lhs = e.getLHS();
            if (rhs instanceof Constant) {
                result = new r.nodes.exec.If.IfConst(ast, rcond, createTree(lhs), rtrueBranch, rfalseBranch, ((Constant) rhs).getValue());
                return;
            }
            if (lhs instanceof Constant) {
                result = new r.nodes.exec.If.IfConst(ast, rcond, createTree(rhs), rtrueBranch, rfalseBranch, ((Constant) lhs).getValue());
                return;
            }
        }

        if (falseBranch == null) {
            result = new r.nodes.exec.If.IfNoElse(ast, rcond, rtrueBranch);
        } else {
            result = new r.nodes.exec.If.IfElse(ast, rcond, rtrueBranch, rfalseBranch);
        }
    }

    @Override public void visit(Repeat repeat) {
        result = new r.nodes.exec.Loop.Repeat(repeat, createLazyTree(repeat.getBody()));
    }

    @Override public void visit(While n) {
        ASTNode cond = n.getCond();
        if (cond instanceof Constant) {
            RAny value = ((Constant) cond).getValue();
            int l = value.asLogical().getLogical(0);
            if (l == RLogical.TRUE) {
                result = new r.nodes.exec.Loop.Repeat(n, createLazyTree(n.getBody()));
                return;
            }
        }
        result = new r.nodes.exec.Loop.While(n, createTree(cond), createLazyTree(n.getBody()));
    }

    @Override public void visit(For n) {
        // an experiment: this actually didn't give any speedups, but it doesn't make any sense...
        //        ASTNode body = n.getBody();
        //
        //        ASTNode sbody = skipTrivialSequences(body);
        //        if (sbody instanceof For) {
        //            // inner loops
        //            For innerFor = (For) sbody;
        //            FrameSlot cvarSlot = getFrameSlot(n, n.getCVar());
        //            FrameSlot innerCvarSlot = getFrameSlot(innerFor, innerFor.getCVar());
        //            if (cvarSlot != null && innerCvarSlot != null) {
        //                result = new r.nodes.exec.Loop.For.NestedLocalIntSequenceRange(n, cvarSlot, createTree(n.getRange()), innerCvarSlot, createTree(innerFor.getRange()), createLazyTree(innerFor.getBody()));
        //                return;
        //            }
        //        }
        result = new r.nodes.exec.Loop.For.IntSimpleRangeFor(n, n.getCVar(), createTree(n.getRange()), createLazyTree(n.getBody()));
    }

    @Override public void visit(Break n) {
        result = new r.nodes.exec.Loop.Break(n);
    }

    @Override public void visit(Next n) {
        result = new r.nodes.exec.Loop.Next(n);
    }

    private RNode returnCallArgument(ASTNode expr) {
        if (expr instanceof FunctionCall) {
            FunctionCall f = (FunctionCall) expr;
            if (f.getName() == RETURN_SYMBOL) {
                SplitArgumentList a = splitArgumentList(f.getArgs(), false);
                if (a.convertedExpressions.length == 0) {
                    return new r.nodes.exec.Constant(expr, RNull.getNull());
                }
                if (a.convertedExpressions.length == 1) {
                    return a.convertedExpressions[0];
                }
            }
        }
        return null;
    }

    // intended for detecting whether a return statement if executed, will be executed last in a function
    private boolean isTrailingInAFunction(ASTNode n) {
        ASTNode parent = n.getParent();
        if (parent == null) {
            return false; // (?) but this should not have return anyway
        }
        if (parent instanceof Function) {
            return true;
        }
        if (parent instanceof Sequence) {
            Sequence parentSeq = (Sequence) parent;
            ASTNode[] exprs = parentSeq.getExprs();
            return (exprs[exprs.length - 1] == n) && isTrailingInAFunction(parentSeq);
        }
        if (parent instanceof If) {
            If parentIf = (If) parent;
            ASTNode trueBranch = parentIf.getTrueCase();
            ASTNode falseBranch = parentIf.getFalseCase();
            return ((trueBranch == n || falseBranch == n ) && isTrailingInAFunction(parentIf));
        }
        return false;
    }

    // replaces trailing return statements just by the argument value
    // introduces IfReturnRest if enabled
    //
    // FIXME: this will not optimize nested if-return-rest constructs (nested within the true branch)
    private RNode buildSequence(Sequence origSequence, ASTNode[] exprs, int from, int to, boolean introduceIfReturnRest) {
        RNode[] rexprs = new RNode[exprs.length - from];
        int i = from;
        for (; i < to; i++) {
            ASTNode e = exprs[i];

            if (introduceIfReturnRest && e instanceof If && !RETURN_SYMBOL.builtinIsOverridden()) {
                If ifExpr = (If) e;
                if (i < to - 1 && ifExpr.getFalseCase() == null) {
                    // if (cond) { .... } ; remaining   - we don't know yet if there is a return
                    ASTNode trueBranch = ifExpr.getTrueCase();
                    RNode truePart = null;
                    RNode returnCallArg = returnCallArgument(trueBranch);
                    if (returnCallArg != null) {
                        truePart = null;
                    } else if (trueBranch instanceof Sequence) {
                        Sequence trueSequence = (Sequence) trueBranch;
                        ASTNode[] trueExprs = trueSequence.getExprs();
                        if (trueExprs.length > 1) {
                            // FIXME: in theory we could be looking recursively into the tree, but recovery would then become hard
                            ASTNode lastTrueExpr = trueExprs[trueExprs.length - 1];
                            returnCallArg = returnCallArgument(lastTrueExpr);
                            if (returnCallArg != null) {
                                truePart = buildSequence(trueSequence, trueExprs, 0, trueExprs.length - 1, false);
                            }
                        }
                    }

                    if (returnCallArg != null) {
                        // if (cond) { .... ; return(returnCallArg) } ; remaining   (no we know there is a return in the true branch
                        RNode remainingSubsequence = buildSequence(origSequence, exprs, i + 1, to, true);
                        RNode newIf = new r.nodes.exec.If.IfReturnRest.ReturnBuiltin(e, createLazyTree(ifExpr.getCond()), truePart,
                                returnCallArg, remainingSubsequence);
                        rexprs[i - from] = newIf;
                        if (DEBUG_RETURN) System.err.println("Converted if with return, new if is " + PrettyPrinter.prettyPrint(newIf.getAST()));
                        i++;
                        break;
                    }
                }
            }
            if (i == to - 1 && isTrailingInAFunction(origSequence) && !RETURN_SYMBOL.builtinIsOverridden()) {
                // last element of a sequence - could it be a trailing return?
                // this optimization is done only for returns known to be trailing in the original AST tree, before introducing
                // IfReturnRest ; such returns will still be trailing in after the introduction of IfReturnRest

                RNode returnCallArg = returnCallArgument(e);
                if (returnCallArg != null) {
                    rexprs[i - from] = returnCallArg; // TODO: listener for return override
                    final RNode lreturnCallArg = returnCallArg;
                    final ASTNode lreturnCallAST = e;
                    SymbolChangeListener listener = new SymbolChangeListener() {
                        public boolean onChange(RSymbol symbol) {
                            RNode oldNode = lreturnCallArg;
                            while(oldNode.getNewNode() != null) {
                                oldNode = oldNode.getNewNode();
                            }
                            RNode[] newArgExprs = new RNode[] { oldNode };
                            RNode parent = oldNode.getParent();
                            oldNode.clearParentPointer(); // FIXME: this sucks, but function call does not support insertion on argument

                                // we can ignore names because return ignores them
                            RNode fullReturnCall = r.nodes.exec.FunctionCall.FACTORY.create(lreturnCallAST, new RSymbol[]{null}, newArgExprs);

                            parent.changeChildPointer(oldNode, fullReturnCall);
                            return false;
                        }
                    };

                    RETURN_SYMBOL.addChangeListener(listener);
                    if (DEBUG_RETURN) System.err.println("Removed trailing return " + PrettyPrinter.prettyPrint(e));
                    continue;
                }
            }
            rexprs[i - from] = createTree(e);
        }
        int length = i - from;
        RNode[] newRExprs = new RNode[length];
        System.arraycopy(rexprs, 0, newRExprs, 0, length);
        return createSequenceFor(origSequence, newRExprs);
    }

    private static RNode createSequenceFor(Sequence sequence, RNode[] rexprs) {
        switch (rexprs.length) {
            case 1:
                return rexprs[0];
            case 2:
                return new r.nodes.exec.Sequence.Sequence2(sequence, rexprs);
            case 3:
                return new r.nodes.exec.Sequence.Sequence3(sequence, rexprs);
            case 4:
                return new r.nodes.exec.Sequence.Sequence4(sequence, rexprs);
            case 5:
                return new r.nodes.exec.Sequence.Sequence5(sequence, rexprs);
            case 6:
                return new r.nodes.exec.Sequence.Sequence6(sequence, rexprs);
            default:
                return new r.nodes.exec.Sequence(sequence, rexprs);
        }
    }

    @Override public void visit(Sequence sequence) {
        ASTNode[] exprs = sequence.getExprs();
        if (!OPTIMIZE_RETURN) {
            RNode[] rexprs = new RNode[exprs.length];
            for (int i = 0; i < exprs.length; i++) {
                rexprs[i] = createTree(exprs[i]);
            }
            result = createSequenceFor(sequence, rexprs);
        } else {
            ASTNode parent = sequence.getParent();
            result = buildSequence(sequence, exprs, 0, exprs.length, parent == null || parent instanceof Function);
        }
    }

    @Override public void visit(Not n) {
        result = new r.nodes.exec.Not.LogicalScalar(n, createTree(n.getLHS()));
    }

    @Override public void visit(UnaryMinus m) {
        result = new r.nodes.exec.UnaryMinus.NumericScalar(m, createTree(m.getLHS()));
    }

    @Override public void visit(Constant constant) {
        result = new r.nodes.exec.Constant(constant, constant.getValue());
    }

    @Override public void visit(SimpleAccessVariable readVariable) {
        RSymbol symbol = readVariable.getSymbol();
        result = r.nodes.exec.ReadVariable.getUninitialized(readVariable, symbol);
    }

    /**
     * FieldAccess closely resembles the subset operator, but uses the ReadList class which closely matches the
     * functionality of String-typed selector from vectors.
     */
    @Override public void visit(FieldAccess fa) {
        ASTNode lhs = fa.lhs();
        RNode n = createTree(lhs);
        result = new ReadVector.FieldSelection.UninitializedSelection(fa, n, RSymbol.getSymbol(fa.fieldName()));
    }

    @Override public void visit(SimpleAssignVariable assign) {
        RSymbol symbol = assign.getSymbol();
        ASTNode valueNode = assign.getExpr();

        //        if (false && r.builtins.Primitives.get(symbol) != null) {
        //            // FIXME: pidigits uses a variable "c"
        //            Utils.nyi(symbol.pretty() + ": we don't support variables over-shadowing primitives.");
        //            // NOTE: we could support this as long as the value assigned isn't a function, but checking that would be expensive
        //            // it may become cheaper once/if we type-specialize assignment nodes, at some point when we do boxing optimizations
        //        }

        // optimize expressions like x <- x + 1
        // more precisely x <- binaryOp(x,Constant) or y <- binaryOp(Constant, y)

        r.nodes.exec.Arithmetic.ValueArithmetic arit = getValueArithmetic(valueNode);

        if (arit != null) {
            BinaryOperation bin = (BinaryOperation) valueNode;
            ASTNode binLHS = bin.getLHS();
            ASTNode binRHS = bin.getRHS();
            SimpleAccessVariable binVar = null;
            Constant constNode = null;

            if (binLHS instanceof SimpleAccessVariable) {
                binVar = (SimpleAccessVariable) binLHS;
            } else if (binRHS instanceof SimpleAccessVariable) {
                binVar = (SimpleAccessVariable) binRHS;
            }
            if (binLHS instanceof Constant) {
                constNode = (Constant) binLHS;
            } else if (binRHS instanceof Constant) {
                constNode = (Constant) binRHS;
            }
            if (binVar != null && constNode != null) {
                RSymbol binVarSymbol = binVar.getSymbol();
                int slot = getFrameSlot(assign, binVarSymbol);
                if (binVarSymbol == symbol && !assign.isSuper() && (constNode.getValue() instanceof ScalarIntImpl) && slot != -1) {
                    int cValue = ((ScalarIntImpl) constNode.getValue()).getInt();
                    if (cValue == 1) {
                        if (valueNode instanceof Add) {
                            // integer increment
                            if (DEBUG_SPECIAL_NODES) {
                                Utils.debug("increment node at " + PrettyPrinter.prettyPrint(assign));
                            }
                            result = new ArithmeticUpdateVariable.ScalarIntLocalIncrement(assign, slot);
                            return;
                        }
                        if (binLHS instanceof SimpleAccessVariable && valueNode instanceof Sub) {
                            // integer decrement
                            if (DEBUG_SPECIAL_NODES) {
                                Utils.debug("decrement node at " + PrettyPrinter.prettyPrint(assign));
                            }
                            result = new ArithmeticUpdateVariable.ScalarIntLocalDecrement(assign, slot);
                            return;
                        }
                    }
                }
            }
        }

        if (assign.isSuper()) {
            result = r.nodes.exec.SuperWriteVariable.getUninitialized(assign, symbol, createTree(valueNode));
        } else {
            //result = r.nodes.exec.WriteVariable.getUninitialized(assign, assign.getSymbol(), createLazyTree(assign.getExpr()));
            result = r.nodes.exec.WriteVariable.getUninitialized(assign, symbol, createTree(valueNode));
        }

    }

    private static class SplitArgumentList {
        RSymbol[] convertedNames;
        RNode[] convertedExpressions;

        SplitArgumentList(RSymbol[] convertedNames, RNode[] convertedExpressions) {
            this.convertedNames = convertedNames;
            this.convertedExpressions = convertedExpressions;
        }
    }

    private SplitArgumentList splitArgumentList(ArgumentList alist, boolean root) {
        int args = alist.size();
        RSymbol[] names = new RSymbol[args];
        RNode[] expressions = new RNode[args];
        int i = 0;
        for (ArgumentList.Entry e : alist) {
            names[i] = e.getName();
            ASTNode exp = e.getValue();
            if (exp != null) {
                if (root) {
                    if (exp instanceof Constant) { // hack to make builtins see their constant arguments, constant won't rewrite anyway
                        expressions[i] = createTree(exp);
                    } else {
                        expressions[i] = createLazyRootTree(exp);
                    }
                } else {
                    expressions[i] = createTree(exp);
                }
            }
            i++;
        }
        assert Utils.check(i == args);
        return new SplitArgumentList(names, expressions);
    }

    private static void detectRepeatedParameters(RSymbol[] params, ASTNode ast) {
        int n = params.length;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (params[i] == params[j]) { throw RError.getRepeatedFormal(ast, params[i].name()); }
            }
        }
    }

    @Override public void visit(Function function) {
        assert Utils.check(function.getRFunction() == null); // TODO the ast.Function must create the RFunction !

        RFunction encf = getEnclosingFunction(function);

        SplitArgumentList a = splitArgumentList(function.getSignature(), true); // the name is not really accurate since, these are parameters

        // note: body has to be built lazily, otherwise nested functions won't work correctly
        detectRepeatedParameters(a.convertedNames, function);
        RFunction impl = function.createImpl(a.convertedNames, a.convertedExpressions, createLazyRootTree(function.getBody()), encf);
        r.nodes.exec.Function functionNode = new r.nodes.exec.Function(impl);

        result = functionNode;
    }

    private RFunction getEnclosingFunction(ASTNode node) {
        // find lexically enclosing function if exists
        Function enfunc = findParent(node, Function.class);
        if (enfunc == null) { return rootEnclosingFunction; }
        RFunction rfunc = enfunc.getRFunction();
        Utils.check(rfunc != null, "RFunction is not yet ready - note lazy build is necessary for functions");
        return rfunc;
    }

    private int getFrameSlot(ASTNode ast, RSymbol symbol) {
        RFunction encFunction = getEnclosingFunction(ast);
        if (encFunction != null) { return encFunction.localSlot(symbol); }
        return -1;
    }

    private boolean hasLocalOrEnclosingFrameSlot(ASTNode ast, RSymbol symbol) {
        RFunction encFunction = getEnclosingFunction(ast);
        if (encFunction == null) { return false; }
        int slot = encFunction.localSlot(symbol);
        if (slot != -1) { return true; }
        return encFunction.enclosingSlot(symbol) != null;
    }

    public static ASTNode skipTrivialSequences(ASTNode astArg) {
        ASTNode ast = astArg;
        for (;;) {
            if (ast == null || !(ast instanceof Sequence)) { return ast; }
            Sequence s = (Sequence) ast;
            ASTNode[] exprs = s.getExprs();
            if (exprs.length != 1) { return ast; }
            ast = exprs[0];
        }
    }

    @Override public void visit(FunctionCall functionCall) {
        // FIXME: In R, function call needs not have a symbol, it can be a lambda expression

        // TODO: FunctionCall for now are ONLY for variable (see Call.create ...).
        // It's maybe smarter to move this instance of here and replace the type of name by expression
        //        SplitArgumentList a = splitArgumentList(functionCall.getArgs(), r.nodes.exec.FunctionCall.PROMISES);
        SplitArgumentList a = splitArgumentList(functionCall.getArgs(), false);
        // NOTE: the "false" argument, which currently ensures that the arguments are not lazy, which in turn
        // makes it easy for hotspot to optimize the code

        RSymbol sym = functionCall.getName();
        RNode rCall = null;
        if (Primitives.STATIC_LOOKUP) {
            r.builtins.CallFactory factory = r.builtins.Primitives.getCallFactory(sym, getEnclosingFunction(functionCall));
            if (factory == null) {
                factory = r.nodes.exec.FunctionCall.FACTORY;
            }
            rCall = factory.create(functionCall, a.convertedNames, a.convertedExpressions);
        } else {
            if (!hasLocalOrEnclosingFrameSlot(functionCall, sym)) {
                rCall = r.nodes.exec.FunctionCall.createBuiltinCall(functionCall, a.convertedNames, a.convertedExpressions);
            }
            if (rCall == null) {
                rCall = r.nodes.exec.FunctionCall.FACTORY.create(functionCall, a.convertedNames, a.convertedExpressions);
            }
        }

        if (!functionCall.isAssignment()) {
            result = rCall;
            return;
        }

        if (!(rCall instanceof AbstractCall)) {
            Utils.nyi("Currently only builtins which are abstract call children can be used with replacement call");
        }

        // replacement assignment
        RNode valueExpr = a.convertedExpressions[a.convertedExpressions.length - 1];
        new ReplacementCall.RememberLast(valueExpr.getAST(), valueExpr); // inserts itself
        SimpleAccessVariable xAST = (SimpleAccessVariable) a.convertedExpressions[0].getAST();

        result = new ReplacementCall(functionCall, functionCall.isSuper(), xAST.getSymbol(), (AbstractCall) rCall);
    }

    public static boolean isArrayColumnSubset(boolean subset, RNode[] selectors, int dims) {
        if (!subset) { return false; }
        for (int i = 0; i < dims - 1; i++) {
            if (selectors[i] != null) { return false; }
        }
        if (selectors[dims - 1] == null) { return false; }
        return true;
    }

    // TODO This has to be changed for partial matching so that exact is checked also for vectors
    @Override public void visit(AccessVector a) {
        SplitArgumentList sa = splitArgumentList(a.getArgs(), false);

        if (sa.convertedExpressions.length == 1) { // vector

            // TODO: should support "exact" and "drop"
            if (a.isSubset()) {
                // expressions like b[x == c]
                // FIXME: add more variations of this
                ASTNode indexNode = a.getArgs().first().getValue();
                if (indexNode instanceof EQ) {
                    EQ eqNode = (EQ) indexNode;
                    if (eqNode.getRHS() instanceof Constant) {
                        RAny cv = ((Constant) eqNode.getRHS()).getValue();
                        if (cv instanceof RDouble && ((RDouble) cv).size() == 1) {
                            double c = ((RDouble) cv).getDouble(0);
                            if (RDouble.RDoubleUtils.isFinite(c)) {
                                result = new ReadVector.LogicalEqualitySelection(a, createTree(a.getVector()), createTree(eqNode.getLHS()), c);
                                return;
                            }
                        }
                    }
                }
            }

            if (a.getArgs().first().getValue() instanceof Colon && a.isSubset()) {
                result = new ReadVector.SimpleIntSimpleRangeSelection(a, createTree(a.getVector()), sa.convertedExpressions, a.isSubset());
            } else {
                RNode e = sa.convertedExpressions[0];
                if (e instanceof r.nodes.exec.Constant) {
                    RAny v = (RAny) e.execute(null);
                    if (v instanceof RDouble || v instanceof RInt) {
                        RInt iv = v.asInt();
                        if (iv.size() == 1) {
                            int index = v.asInt().getInt(0);
                            if (index > 0) {
                                result = new ReadVector.SimpleConstantScalarIntSelection(a, createTree(a.getVector()), sa.convertedExpressions, index, a.isSubset());
                                return;
                            }
                        }
                    }
                }
                result = new ReadVector.DoubleBaseSimpleSelection.ScalarIntSelection(a, createTree(a.getVector()), sa.convertedExpressions, a.isSubset());
            }
            return;
        }
        // array & matrix read
        if (sa.convertedExpressions.length >= 2) { // array or matrix ?

            RNode drop = null;
            RNode exact = null;
            RNode[] selectors = new RNode[sa.convertedExpressions.length];
            int[] nodeIndexes = new int[selectors.length];

            RNode[] nodes = sa.convertedExpressions;
            RSymbol[] names = sa.convertedNames;
            int dims = 0;

            for (int i = 0; i < nodes.length; i++) {
                if (names[i] == RSymbol.DROP_SYMBOL) {
                    assert Utils.check(drop == null); // GNU-R allows multiple occurrences
                    drop = nodes[i];
                } else if (names[i] == RSymbol.EXACT_SYMBOL) {
                    assert Utils.check(exact == null); // GNU-R allows multiple occurrences
                    exact = nodes[i];
                } else {
                    selectors[dims] = nodes[i];
                    nodeIndexes[dims] = i;
                    ++dims;
                }
            }

            assert Utils.check(dims != 0); // FIXME: GNU-R supports this

            if (dims == 2) { // if matrix read, use the specialized matrix form
                if (a.isSubset() && selectors[0] == null && selectors[1] != null) { // matrix column
                    result = new ReadArray.MatrixColumnSubset(a, createTree(a.getVector()), selectors[1], Selector.createDropOptionNode(a, drop), Selector.createExactOptionNode(a, exact));
                    return;

                }
                if (a.isSubset() && selectors[0] != null && selectors[1] == null) {
                    result = new ReadArray.MatrixRowSubset(a, createTree(a.getVector()), selectors[0], Selector.createDropOptionNode(a, drop), Selector.createExactOptionNode(a, exact));
                    return;
                }

                // special handling of m[a:b, c:d]
                ASTNode node0 = a.getArgs().getNode(nodeIndexes[0]);
                ASTNode node1 = a.getArgs().getNode(nodeIndexes[1]);

                if (a.isSubset() && node0 != null && node1 != null && node0 instanceof Colon && node1 instanceof Colon) {
                    Colon rows = (Colon) node0;
                    Colon cols = (Colon) node1;
                    result = new ReadArray.MatrixSequenceSubset(a, createTree(a.getVector()), createTree(rows.getLHS()), createTree(rows.getRHS()), createTree(cols.getLHS()),
                            createTree(cols.getRHS()), Selector.createDropOptionNode(a, drop), Selector.createExactOptionNode(a, exact));
                    return;

                }

                Selector.SelectorNode selectorIExpr = Selector.createSelectorNode(a, a.isSubset(), selectors[0]);
                Selector.SelectorNode selectorJExpr = Selector.createSelectorNode(a, a.isSubset(), selectors[1]);

                if (!a.isSubset()) {
                    result = new ReadArray.MatrixSubscript(a, createTree(a.getVector()), selectorIExpr, selectorJExpr, Selector.createDropOptionNode(a, drop), Selector.createExactOptionNode(a, exact));
                } else {
                    result = new ReadArray.MatrixRead(a, a.isSubset(), createTree(a.getVector()), selectorIExpr, selectorJExpr, Selector.createDropOptionNode(a, drop), Selector.createExactOptionNode(
                            a, exact));
                }
                return;
            }
            // otherwise use array read
            if (isArrayColumnSubset(a.isSubset(), selectors, dims)) {
                result = new ReadArray.ArrayColumnSubset(a, createTree(a.getVector()), dims, selectors[dims - 1], Selector.createDropOptionNode(a, drop), Selector.createExactOptionNode(a, exact));
                return;
            }

            Selector.SelectorNode[] selNodes = new Selector.SelectorNode[dims];
            for (int i = 0; i < selNodes.length; ++i) {
                selNodes[i] = Selector.createSelectorNode(a, a.isSubset(), selectors[i]);
            }

            result = new ReadArray.GenericRead(a, a.isSubset(), createTree(a.getVector()), selNodes, Selector.createDropOptionNode(a, drop), Selector.createExactOptionNode(a, exact));
            return;
        }
        if (sa.convertedExpressions.length == 0) {
            if (a.isSubset()) {
                result = createTree(a.getVector());
                return;
            } else {
                throw RError.getInvalidSubscriptType(a, "symbol");
            }

        }
        Utils.nyi("unsupported indexing style");
    }

    @Override public void visit(UpdateVector u) {
        AccessVector a = u.getVector();
        SplitArgumentList sa = splitArgumentList(a.getArgs(), false);

        if (sa.convertedExpressions.length == 1) {
            ASTNode varAccess = a.getVector();
            if (!(varAccess instanceof SimpleAccessVariable)) {
                Utils.nyi("expecting vector name for vector update");
            }
            RSymbol var = ((SimpleAccessVariable) varAccess).getSymbol();

            if (a.isSubset()) { //FIXME: this optimization is helping only so little.. why?
                // expressions like b[x == c] <- ...
                // FIXME: add more variations of this
                ASTNode indexNode = a.getArgs().first().getValue();
                if (indexNode instanceof EQ) {
                    EQ eqNode = (EQ) indexNode;
                    if (eqNode.getRHS() instanceof Constant) {
                        RAny cv = ((Constant) eqNode.getRHS()).getValue();
                        if (cv instanceof RDouble && ((RDouble) cv).size() == 1) {
                            double c = ((RDouble) cv).getDouble(0);
                            if (RDouble.RDoubleUtils.isFinite(c)) {
                                result = new r.nodes.exec.UpdateVector.LogicalEqualitySelection(u, u.isSuper(), var, createTree(varAccess), createTree(eqNode.getLHS()), c, createTree(u.getRHS()),
                                        a.isSubset());

                                return;
                            }
                        }
                    }
                }
            }

            if (a.getArgs().first().getValue() instanceof Colon && a.isSubset()) {
                result = new r.nodes.exec.UpdateVector.IntSimpleRangeSelection(u, u.isSuper(), var, createTree(varAccess), sa.convertedExpressions, createTree(u.getRHS()), a.isSubset());
            } else {
                result = new r.nodes.exec.UpdateVector.DoubleBaseSimpleSelection.ScalarIntSelection(u, u.isSuper(), var, createTree(varAccess), sa.convertedExpressions, createTree(u.getRHS()),
                        a.isSubset());
            }
        } else if (sa.convertedExpressions.length >= 2) {

            // TODO is drop meaningful for updates??
            // RNode drop = null;
            RNode exact = null;
            RNode[] selectors = new RNode[sa.convertedExpressions.length];

            RNode[] nodes = sa.convertedExpressions;
            RSymbol[] names = sa.convertedNames;
            int dims = 0;

            for (int i = 0; i < nodes.length; i++) {
                /*
                 * if (names[i] == RSymbol.dropName) { if (drop != null) { throw RError.getIncorrectSubscripts(a); }
                 * drop = nodes[i]; continue; } else
                 */if (names[i] == RSymbol.EXACT_SYMBOL) {
                    if (exact != null) { throw RError.getIncorrectSubscripts(a); }
                    exact = nodes[i];
                    continue;
                } else {
                    selectors[dims] = nodes[i];
                    ++dims;
                    continue;
                }
            }
            if (dims == 0) {
                Utils.nyi("unsupported indexing style");
            }
            Selector.SelectorNode[] selNodes = new Selector.SelectorNode[dims];
            for (int i = 0; i < selNodes.length; ++i) {
                selNodes[i] = Selector.createSelectorNode(a, a.isSubset(), selectors[i]);
            }
            // Create the assignment, or super assignment nodes
            boolean isColumn = isArrayColumnSubset(a.isSubset(), selectors, dims);

            ASTNode varAccess = a.getVector();
            RSymbol varName = ((SimpleAccessVariable) varAccess).getSymbol();
            if (!(varAccess instanceof SimpleAccessVariable)) {
                Utils.nyi("expecting matrix name for matrix update");
            }
            RFunction encFunction = getEnclosingFunction(a);
            int varSlot = getFrameSlot(a, varName);

            if (u.isSuper()) {
                result = UpdateArraySuperAssignment.create(a, varName, createTree(varAccess), createTree(u.getRHS()), UpdateArray.create(a, selNodes, a.isSubset(), isColumn));
            } else {
                result = UpdateArrayAssignment.create(a, varName, encFunction, varSlot, createTree(u.getRHS()), UpdateArray.create(a, selNodes, a.isSubset(), isColumn));
                return;
            }
        } else {
            Utils.nyi("Unsupported indexing style");
        }
    }

    /**
     * Converts the field update ($ selector on topmost lhf of assignment a$x = xyz).
     */
    @Override public void visit(UpdateField u) {
        /// FIXME this uses the old variant of $ selector that works also on vectors. Should be replaced.
        FieldAccess fa = u.getVector();
        ASTNode varAccess = fa.lhs();
        if (!(varAccess instanceof SimpleAccessVariable)) {
            Utils.nyi("expecting vector name for vector update"); // TODO: support expressions like x$a$b <- 10
        }
        RSymbol var = ((SimpleAccessVariable) varAccess).getSymbol();
        result = new r.nodes.exec.UpdateVector.DollarListUpdate(u, u.isSuper(), var, createTree(varAccess), RSymbol.getSymbol(fa.fieldName()), createTree(u.getRHS()));
    }

    @Override public void visit(UpdateExpression u) {
        Utils.nyi("generic update expressions not implemented");
    }

    @SuppressWarnings("unchecked") private static <T extends ASTNode> T findParent(ASTNode node, Class<T> clazz) {
        ASTNode n = node.getParent();
        while (n != null) {
            if (clazz.isInstance(n)) { return (T) n; }
            n = n.getParent();
        }
        return null;
    }

    @Override public void visit(EQ eq) {
        result = new r.nodes.exec.Comparison(eq, createTree(eq.getLHS()), createTree(eq.getRHS()), r.nodes.exec.Comparison.getEQ());
    }

    @Override public void visit(NE ne) {
        result = new r.nodes.exec.Comparison(ne, createTree(ne.getLHS()), createTree(ne.getRHS()), r.nodes.exec.Comparison.getNE());
    }

    @Override public void visit(LE le) {
        result = new r.nodes.exec.Comparison(le, createTree(le.getLHS()), createTree(le.getRHS()), r.nodes.exec.Comparison.getLE());
    }

    @Override public void visit(GE ge) {
        result = new r.nodes.exec.Comparison(ge, createTree(ge.getLHS()), createTree(ge.getRHS()), r.nodes.exec.Comparison.getGE());
    }

    @Override public void visit(LT lt) {
        result = new r.nodes.exec.Comparison(lt, createTree(lt.getLHS()), createTree(lt.getRHS()), r.nodes.exec.Comparison.getLT());
    }

    @Override public void visit(GT gt) {
        result = new r.nodes.exec.Comparison(gt, createTree(gt.getLHS()), createTree(gt.getRHS()), r.nodes.exec.Comparison.getGT());
    }

    public static r.nodes.exec.Arithmetic.ValueArithmetic getValueArithmetic(ASTNode ast) {
        if (ast instanceof Add) { return r.nodes.exec.Arithmetic.ADD; }
        if (ast instanceof Mult) { return r.nodes.exec.Arithmetic.MULT; }
        if (ast instanceof IntegerDiv) { return r.nodes.exec.Arithmetic.INTEGER_DIV; }
        if (ast instanceof Mod) { return r.nodes.exec.Arithmetic.MOD; }
        if (ast instanceof Pow) { return r.nodes.exec.Arithmetic.POW; }
        if (ast instanceof Div) { return r.nodes.exec.Arithmetic.DIV; }
        if (ast instanceof Sub) { return r.nodes.exec.Arithmetic.SUB; }
        return null;
    }

    private void visitArithmetic(BinaryOperation op) {
        r.nodes.exec.Arithmetic.ValueArithmetic arit = getValueArithmetic(op);
        assert Utils.check(arit != null);
        result = new r.nodes.exec.Arithmetic(op, createTree(op.getLHS()), createTree(op.getRHS()), arit);
    }

    @Override public void visit(Add add) {
        visitArithmetic(add);
    }

    @Override public void visit(Mult mult) {
        visitArithmetic(mult);
    }

    @Override public void visit(MatMult mult) {
        result = new r.nodes.exec.MatrixOperation.MatrixProduct(mult, createTree(mult.getLHS()), createTree(mult.getRHS()));
    }

    @Override public void visit(OuterMult mult) {
        result = new r.nodes.exec.MatrixOperation.OuterProduct(mult, createTree(mult.getLHS()), createTree(mult.getRHS()));
    }

    @Override public void visit(IntegerDiv div) {
        visitArithmetic(div);
    }

    @Override public void visit(In in) {
        result = new r.nodes.exec.InOperation(in, createTree(in.getLHS()), createTree(in.getRHS()));
    }

    @Override public void visit(Mod mod) {
        visitArithmetic(mod);
    }

    @Override public void visit(Pow pow) {
        visitArithmetic(pow);
    }

    @Override public void visit(Div div) {
        visitArithmetic(div);
    }

    @Override public void visit(Sub sub) {
        visitArithmetic(sub);
    }

    @Override public void visit(Colon col) {
        // FIXME: allow symbol override?
        ASTNode lhs = col.getLHS();
        ASTNode rhs = col.getRHS();
        result = r.builtins.Primitives.getCallFactory(RSymbol.getSymbol(":"), null).create(col, createTree(lhs), createTree(rhs));
        if (lhs instanceof Constant && rhs instanceof Constant) { // TODO: more general constant folding
            RAny value = (RAny) result.execute(null);
            value.ref();
            value.ref();
            result = new r.nodes.exec.Constant(col, value);
        }
    }

    @Override public void visit(And and) {
        result = new r.nodes.exec.LogicalOperation.And(and, createTree(and.getLHS()), createTree(and.getRHS()));
    }

    @Override public void visit(ElementwiseAnd and) {
        result = r.nodes.exec.ElementwiseLogicalOperation.createUninitialized(and, createTree(and.getLHS()), r.nodes.exec.ElementwiseLogicalOperation.AND, createTree(and.getRHS()));
    }

    @Override public void visit(Or or) {
        result = new r.nodes.exec.LogicalOperation.Or(or, createTree(or.getLHS()), createTree(or.getRHS()));
    }

    @Override public void visit(ElementwiseOr or) {
        result = r.nodes.exec.ElementwiseLogicalOperation.createUninitialized(or, createTree(or.getLHS()), r.nodes.exec.ElementwiseLogicalOperation.OR, createTree(or.getRHS()));
    }

    @Override public void visit(ArgumentList.Default.DefaultEntry entry) {}
}
TOP

Related Classes of r.nodes.tools.BuildExecutableTree$SplitArgumentList

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.