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);
}
}
}
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();
}
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();
}
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;
}
}
}
Aggregations