Search in sources :

Example 6 with Substatement

use of org.mvel2.ast.Substatement in project mvel by mvel.

the class ExpressionCompiler method _compile.

/**
 * Initiate an in-context compileShared.  This method should really only be called by the internal API.
 *
 * @return compiled expression object
 */
public CompiledExpression _compile() {
    ASTNode tk;
    ASTNode tkOp;
    ASTNode tkOp2;
    ASTNode tkLA;
    ASTNode tkLA2;
    int op, lastOp = -1;
    cursor = start;
    ASTLinkedList astBuild = new ASTLinkedList();
    stk = new ExecutionStack();
    dStack = new ExecutionStack();
    compileMode = true;
    boolean firstLA;
    try {
        if (verifying) {
            pCtx.initializeTables();
        }
        fields |= COMPILE_IMMEDIATE;
        while ((tk = nextToken()) != null) {
            /**
             * If this is a debug symbol, just add it and continue.
             */
            if (tk.fields == -1) {
                astBuild.addTokenNode(tk);
                continue;
            }
            /**
             * Record the type of the current node..
             */
            returnType = tk.getEgressType();
            if (tk instanceof Substatement) {
                String key = new String(expr, tk.getStart(), tk.getOffset());
                Map<String, CompiledExpression> cec = pCtx.getCompiledExpressionCache();
                Map<String, Class> rtc = pCtx.getReturnTypeCache();
                CompiledExpression compiled = cec.get(key);
                Class rt = rtc.get(key);
                if (compiled == null) {
                    ExpressionCompiler subCompiler = new ExpressionCompiler(expr, tk.getStart(), tk.getOffset(), pCtx);
                    compiled = subCompiler._compile();
                    rt = subCompiler.getReturnType();
                    cec.put(key, compiled);
                    rtc.put(key, rt);
                }
                tk.setAccessor(compiled);
                returnType = rt;
            }
            /**
             * This kludge of code is to handle compileShared-time literal reduction.  We need to avoid
             * reducing for certain literals like, 'this', ternary and ternary else.
             */
            if (!verifyOnly && tk.isLiteral()) {
                if (literalOnly == -1)
                    literalOnly = 1;
                if ((tkOp = nextTokenSkipSymbols()) != null && tkOp.isOperator() && !tkOp.isOperator(Operator.TERNARY) && !tkOp.isOperator(Operator.TERNARY_ELSE)) {
                    /**
                     * If the next token is ALSO a literal, then we have a candidate for a compileShared-time literal
                     * reduction.
                     */
                    if ((tkLA = nextTokenSkipSymbols()) != null && tkLA.isLiteral() && tkOp.getOperator() < 34 && ((lastOp == -1 || (lastOp < PTABLE.length && PTABLE[lastOp] < PTABLE[tkOp.getOperator()])))) {
                        stk.push(tk.getLiteralValue(), tkLA.getLiteralValue(), op = tkOp.getOperator());
                        /**
                         * Reduce the token now.
                         */
                        if (isArithmeticOperator(op)) {
                            if (!compileReduce(op, astBuild))
                                continue;
                        } else {
                            reduce();
                        }
                        firstLA = true;
                        /**
                         * Now we need to check to see if this is a continuing reduction.
                         */
                        while ((tkOp2 = nextTokenSkipSymbols()) != null) {
                            if (isBooleanOperator(tkOp2.getOperator())) {
                                astBuild.addTokenNode(new LiteralNode(stk.pop(), pCtx), verify(pCtx, tkOp2));
                                break;
                            } else if ((tkLA2 = nextTokenSkipSymbols()) != null) {
                                if (tkLA2.isLiteral()) {
                                    stk.push(tkLA2.getLiteralValue(), op = tkOp2.getOperator());
                                    if (isArithmeticOperator(op)) {
                                        compileReduce(op, astBuild);
                                    } else {
                                        reduce();
                                    }
                                } else {
                                    /**
                                     * A reducable line of literals has ended.  We must now terminate here and
                                     * leave the rest to be determined at runtime.
                                     */
                                    if (!stk.isEmpty()) {
                                        astBuild.addTokenNode(new LiteralNode(getStackValueResult(), pCtx));
                                    }
                                    astBuild.addTokenNode(new OperatorNode(tkOp2.getOperator(), expr, st, pCtx), verify(pCtx, tkLA2));
                                    break;
                                }
                                firstLA = false;
                                literalOnly = 0;
                            } else {
                                if (firstLA) {
                                    /**
                                     * There are more tokens, but we can't reduce anymore.  So
                                     * we create a reduced token for what we've got.
                                     */
                                    astBuild.addTokenNode(new LiteralNode(getStackValueResult(), pCtx));
                                } else {
                                    /**
                                     * We have reduced additional tokens, but we can't reduce
                                     * anymore.
                                     */
                                    astBuild.addTokenNode(new LiteralNode(getStackValueResult(), pCtx), tkOp2);
                                    if (tkLA2 != null)
                                        astBuild.addTokenNode(verify(pCtx, tkLA2));
                                }
                                break;
                            }
                        }
                        /**
                         * If there are no more tokens left to parse, we check to see if
                         * we've been doing any reducing, and if so we create the token
                         * now.
                         */
                        if (!stk.isEmpty())
                            astBuild.addTokenNode(new LiteralNode(getStackValueResult(), pCtx));
                        continue;
                    } else {
                        astBuild.addTokenNode(verify(pCtx, tk), verify(pCtx, tkOp));
                        if (tkLA != null)
                            astBuild.addTokenNode(verify(pCtx, tkLA));
                        continue;
                    }
                } else if (tkOp != null && !tkOp.isOperator() && !(tk.getLiteralValue() instanceof Class)) {
                    throw new CompileException("unexpected token: " + tkOp.getName(), expr, tkOp.getStart());
                } else {
                    literalOnly = 0;
                    astBuild.addTokenNode(verify(pCtx, tk));
                    if (tkOp != null)
                        astBuild.addTokenNode(verify(pCtx, tkOp));
                    continue;
                }
            } else {
                if (tk.isOperator()) {
                    lastOp = tk.getOperator();
                } else {
                    literalOnly = 0;
                }
            }
            astBuild.addTokenNode(verify(pCtx, tk));
        }
        astBuild.finish();
        if (verifying && !verifyOnly) {
            pCtx.processTables();
        }
        if (!stk.isEmpty()) {
            throw new CompileException("COMPILE ERROR: non-empty stack after compileShared.", expr, cursor);
        }
        if (!verifyOnly) {
            try {
                return new CompiledExpression(finalizePayload(astBuild, secondPassOptimization, pCtx), pCtx.getSourceFile(), returnType, pCtx.getParserConfiguration(), literalOnly == 1);
            } catch (RuntimeException e) {
                throw new CompileException(e.getMessage(), expr, st, e);
            }
        } else {
            try {
                returnType = CompilerTools.getReturnType(astBuild, pCtx.isStrongTyping());
            } catch (RuntimeException e) {
                throw new CompileException(e.getMessage(), expr, st, e);
            }
            return null;
        }
    } catch (NullPointerException e) {
        throw new CompileException("not a statement, or badly formed structure", expr, st, e);
    } catch (CompileException e) {
        throw ErrorUtil.rewriteIfNeeded(e, expr, st);
    } catch (Throwable e) {
        if (e instanceof RuntimeException)
            throw (RuntimeException) e;
        else {
            throw new CompileException(e.getMessage(), expr, st, e);
        }
    }
}
Also used : ExecutionStack(org.mvel2.util.ExecutionStack) OperatorNode(org.mvel2.ast.OperatorNode) LiteralNode(org.mvel2.ast.LiteralNode) ASTNode(org.mvel2.ast.ASTNode) ASTLinkedList(org.mvel2.util.ASTLinkedList) CompileException(org.mvel2.CompileException) Substatement(org.mvel2.ast.Substatement)

Example 7 with Substatement

use of org.mvel2.ast.Substatement in project mvel by mvel.

the class DebugTools method decompile.

private static String decompile(CompiledExpression cExp, boolean nest, DecompileContext context) {
    ASTIterator iter = new ASTLinkedList(cExp.getFirstNode());
    ASTNode tk;
    StringBuffer sbuf = new StringBuffer();
    if (!nest) {
        sbuf.append("Expression Decompile\n-------------\n");
    }
    while (iter.hasMoreNodes()) {
        sbuf.append("(").append(context.node++).append(") ");
        if ((tk = iter.nextNode()) instanceof NestedStatement && ((NestedStatement) tk).getNestedStatement() instanceof CompiledExpression) {
            // noinspection StringConcatenationInsideStringBufferAppend
            sbuf.append("NEST [" + tk.getClass().getSimpleName() + "]: { " + tk.getName() + " }\n");
            sbuf.append(decompile((CompiledExpression) ((NestedStatement) tk).getNestedStatement(), true, context));
        }
        if (tk instanceof Substatement && ((Substatement) tk).getStatement() instanceof CompiledExpression) {
            // noinspection StringConcatenationInsideStringBufferAppend
            sbuf.append("NEST [" + tk.getClass().getSimpleName() + "]: { " + tk.getName() + " }\n");
            sbuf.append(decompile((CompiledExpression) ((Substatement) tk).getStatement(), true, context));
        } else // }
        if (tk.isDebuggingSymbol()) {
            // noinspection StringConcatenationInsideStringBufferAppend
            sbuf.append("DEBUG_SYMBOL :: " + tk.toString());
        } else if (tk.isLiteral()) {
            sbuf.append("LITERAL :: ").append(tk.getLiteralValue()).append("'");
        } else if (tk.isOperator()) {
            sbuf.append("OPERATOR [").append(getOperatorName(tk.getOperator())).append("]: ").append(tk.getName());
            if (tk.isOperator(Operator.END_OF_STMT))
                sbuf.append("\n");
        } else if (tk.isIdentifier()) {
            sbuf.append("REFERENCE :: ").append(tk.getClass().getSimpleName()).append(":").append(tk.getName());
        } else if (tk instanceof BinaryOperation) {
            BinaryOperation bo = (BinaryOperation) tk;
            sbuf.append("OPERATION [" + getOperatorName(bo.getOperation()) + "] {").append(bo.getLeft().getName()).append("} {").append(bo.getRight().getName()).append("}");
        } else {
            // noinspection StringConcatenationInsideStringBufferAppend
            sbuf.append("NODE [" + tk.getClass().getSimpleName() + "] :: " + tk.getName());
        }
        sbuf.append("\n");
    }
    sbuf.append("==END==");
    return sbuf.toString();
}
Also used : NestedStatement(org.mvel2.ast.NestedStatement) BinaryOperation(org.mvel2.ast.BinaryOperation) ASTLinkedList(org.mvel2.util.ASTLinkedList) ASTNode(org.mvel2.ast.ASTNode) ASTIterator(org.mvel2.util.ASTIterator) Substatement(org.mvel2.ast.Substatement) CompiledExpression(org.mvel2.compiler.CompiledExpression)

Example 8 with Substatement

use of org.mvel2.ast.Substatement in project mvel by mikebrock.

the class MVELInterpretedRuntime method parseAndExecuteInterpreted.

/**
 * Main interpreter loop.
 *
 * @return value
 */
private Object parseAndExecuteInterpreted() {
    ASTNode tk = null;
    int operator;
    lastWasIdentifier = false;
    try {
        while ((tk = nextToken()) != null) {
            holdOverRegister = null;
            if (lastWasIdentifier && lastNode.isDiscard()) {
                stk.discard();
            }
            /**
             * If we are at the beginning of a statement, then we immediately push the first token
             * onto the stack.
             */
            if (stk.isEmpty()) {
                stk.push(tk.getReducedValue(ctx, ctx, variableFactory));
                /**
                 * If this is a substatement, we need to move the result into the d-stack to preserve
                 * proper execution order.
                 */
                if (tk instanceof Substatement && (tk = nextToken()) != null) {
                    if (isArithmeticOperator(operator = tk.getOperator())) {
                        stk.push(nextToken().getReducedValue(ctx, ctx, variableFactory), operator);
                        if (procBooleanOperator(arithmeticFunctionReduction(operator)) == -1)
                            return stk.peek();
                        else
                            continue;
                    }
                } else {
                    continue;
                }
            }
            if (variableFactory.tiltFlag()) {
                return stk.pop();
            }
            switch(procBooleanOperator(operator = tk.getOperator())) {
                case RETURN:
                    variableFactory.setTiltFlag(true);
                    return stk.pop();
                case OP_TERMINATE:
                    return stk.peek();
                case OP_RESET_FRAME:
                    continue;
                case OP_OVERFLOW:
                    if (!tk.isOperator()) {
                        if (!(stk.peek() instanceof Class)) {
                            throw new CompileException("unexpected token or unknown identifier:" + tk.getName(), expr, st);
                        }
                        variableFactory.createVariable(tk.getName(), null, (Class) stk.peek());
                    }
                    continue;
            }
            stk.push(nextToken().getReducedValue(ctx, ctx, variableFactory), operator);
            switch((operator = arithmeticFunctionReduction(operator))) {
                case OP_TERMINATE:
                    return stk.peek();
                case OP_RESET_FRAME:
                    continue;
            }
            if (procBooleanOperator(operator) == OP_TERMINATE)
                return stk.peek();
        }
        if (holdOverRegister != null) {
            return holdOverRegister;
        }
    } catch (CompileException e) {
        throw ErrorUtil.rewriteIfNeeded(e, expr, start);
    } catch (NullPointerException e) {
        if (tk != null && tk.isOperator()) {
            CompileException ce = new CompileException("incomplete statement: " + tk.getName() + " (possible use of reserved keyword as identifier: " + tk.getName() + ")", expr, st, e);
            ce.setExpr(expr);
            ce.setLineNumber(line);
            ce.setCursor(cursor);
            throw ce;
        } else {
            throw e;
        }
    }
    return stk.peek();
}
Also used : ASTNode(org.mvel2.ast.ASTNode) Substatement(org.mvel2.ast.Substatement)

Example 9 with Substatement

use of org.mvel2.ast.Substatement in project mvel by mikebrock.

the class MVELRuntime method execute.

/**
 * Main interpreter.
 *
 * @param debugger        Run in debug mode
 * @param expression      The compiled expression object
 * @param ctx             The root context object
 * @param variableFactory The variable factory to be injected
 * @return The resultant value
 * @see org.mvel2.MVEL
 */
public static Object execute(boolean debugger, final CompiledExpression expression, final Object ctx, VariableResolverFactory variableFactory) {
    Object v1, v2;
    ExecutionStack stk = new ExecutionStack();
    variableFactory.setTiltFlag(false);
    ASTNode tk = expression.getFirstNode();
    Integer operator;
    if (tk == null)
        return null;
    try {
        do {
            if (tk.fields == -1) {
                /**
                 * This may seem silly and redundant, however, when an MVEL script recurses into a block
                 * or substatement, a new runtime loop is entered.   Since the debugger state is not
                 * passed through the AST, it is not possible to forward the state directly.  So when we
                 * encounter a debugging symbol, we check the thread local to see if there is are registered
                 * breakpoints.  If we find them, we assume that we are debugging.
                 *
                 * The consequence of this of course, is that it's not ideal to compileShared expressions with
                 * debugging symbols which you plan to use in a production enviroment.
                 */
                if (debugger || (debugger = hasDebuggerContext())) {
                    try {
                        debuggerContext.get().checkBreak((LineLabel) tk, variableFactory, expression);
                    } catch (NullPointerException e) {
                    // do nothing for now.  this isn't as calus as it seems.
                    }
                }
                continue;
            } else if (stk.isEmpty()) {
                stk.push(tk.getReducedValueAccelerated(ctx, ctx, variableFactory));
            }
            if (variableFactory.tiltFlag()) {
                return stk.pop();
            }
            switch(operator = tk.getOperator()) {
                case RETURN:
                    variableFactory.setTiltFlag(true);
                    return stk.pop();
                case NOOP:
                    continue;
                case TERNARY:
                    if (!stk.popBoolean()) {
                        // noinspection StatementWithEmptyBody
                        while (tk.nextASTNode != null && !(tk = tk.nextASTNode).isOperator(TERNARY_ELSE)) ;
                    }
                    stk.clear();
                    continue;
                case TERNARY_ELSE:
                    return stk.pop();
                case END_OF_STMT:
                    /**
                     * If the program doesn't end here then we wipe anything off the stack that remains.
                     * Althought it may seem like intuitive stack optimizations could be leveraged by
                     * leaving hanging values on the stack,  trust me it's not a good idea.
                     */
                    if (tk.nextASTNode != null) {
                        stk.clear();
                    }
                    continue;
            }
            stk.push(tk.nextASTNode.getReducedValueAccelerated(ctx, ctx, variableFactory), operator);
            try {
                while (stk.isReduceable()) {
                    if ((Integer) stk.peek() == CHOR) {
                        stk.pop();
                        v1 = stk.pop();
                        v2 = stk.pop();
                        if (!isEmpty(v2) || !isEmpty(v1)) {
                            stk.clear();
                            stk.push(!isEmpty(v2) ? v2 : v1);
                        } else
                            stk.push(null);
                    } else {
                        stk.op();
                    }
                }
            } catch (ClassCastException e) {
                throw new CompileException("syntax error or incomptable types", new char[0], 0, e);
            } catch (CompileException e) {
                throw e;
            } catch (Exception e) {
                throw new CompileException("failed to compileShared sub expression", new char[0], 0, e);
            }
        } while ((tk = tk.nextASTNode) != null);
        return stk.peek();
    } catch (NullPointerException e) {
        if (tk != null && tk.isOperator() && tk.nextASTNode != null) {
            throw new CompileException("incomplete statement: " + tk.getName() + " (possible use of reserved keyword as identifier: " + tk.getName() + ")", tk.getExpr(), tk.getStart());
        } else {
            throw e;
        }
    }
}
Also used : ExecutionStack(org.mvel2.util.ExecutionStack) ASTNode(org.mvel2.ast.ASTNode)

Aggregations

ASTNode (org.mvel2.ast.ASTNode)7 Substatement (org.mvel2.ast.Substatement)6 CompileException (org.mvel2.CompileException)3 ASTLinkedList (org.mvel2.util.ASTLinkedList)3 ExecutionStack (org.mvel2.util.ExecutionStack)3 BinaryOperation (org.mvel2.ast.BinaryOperation)2 LiteralNode (org.mvel2.ast.LiteralNode)2 NestedStatement (org.mvel2.ast.NestedStatement)2 OperatorNode (org.mvel2.ast.OperatorNode)2 AssertNode (org.mvel2.ast.AssertNode)1 AssignmentNode (org.mvel2.ast.AssignmentNode)1 BooleanNode (org.mvel2.ast.BooleanNode)1 DeclTypedVarNode (org.mvel2.ast.DeclTypedVarNode)1 DeepAssignmentNode (org.mvel2.ast.DeepAssignmentNode)1 EndOfStatement (org.mvel2.ast.EndOfStatement)1 Fold (org.mvel2.ast.Fold)1 ImportNode (org.mvel2.ast.ImportNode)1 IndexedAssignmentNode (org.mvel2.ast.IndexedAssignmentNode)1 IndexedDeclTypedVarNode (org.mvel2.ast.IndexedDeclTypedVarNode)1 IndexedOperativeAssign (org.mvel2.ast.IndexedOperativeAssign)1