/src/main/java/org/mvel2/compiler/AbstractParser.java
Java | 2827 lines | 2265 code | 320 blank | 242 comment | 474 complexity | c69b8ca60eba176cf247ce471d7aae93 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
- /**
- * MVEL 2.0
- * Copyright (C) 2007 The Codehaus
- * Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.mvel2.compiler;
- import org.mvel2.CompileException;
- import org.mvel2.ErrorDetail;
- import org.mvel2.Operator;
- import org.mvel2.ParserContext;
- import org.mvel2.ast.*;
- import org.mvel2.integration.VariableResolverFactory;
- import org.mvel2.util.ErrorUtil;
- import org.mvel2.util.ExecutionStack;
- import org.mvel2.util.FunctionParser;
- import org.mvel2.util.ProtoParser;
- import java.io.Serializable;
- import java.util.HashMap;
- import java.util.WeakHashMap;
- import static java.lang.Boolean.FALSE;
- import static java.lang.Boolean.TRUE;
- import static java.lang.Double.parseDouble;
- import static java.lang.Runtime.getRuntime;
- import static java.lang.System.getProperty;
- import static java.lang.Thread.currentThread;
- import static org.mvel2.Operator.*;
- import static org.mvel2.ast.TypeDescriptor.getClassReference;
- import static org.mvel2.util.ArrayTools.findFirst;
- import static org.mvel2.util.ParseTools.*;
- import static org.mvel2.util.PropertyTools.isEmpty;
- import static org.mvel2.util.Soundex.soundex;
- /**
- * This is the core parser that the subparsers extend.
- *
- * @author Christopher Brock
- */
- public class AbstractParser implements Parser, Serializable {
- protected char[] expr;
- protected int cursor;
- protected int start;
- protected int length;
- protected int end;
- protected int st;
- protected int fields;
- protected static final int OP_OVERFLOW = -2;
- protected static final int OP_TERMINATE = -1;
- protected static final int OP_RESET_FRAME = 0;
- protected static final int OP_CONTINUE = 1;
- protected boolean greedy = true;
- protected boolean lastWasIdentifier = false;
- protected boolean lastWasLineLabel = false;
- protected boolean lastWasComment = false;
- protected boolean compileMode = false;
- protected int literalOnly = -1;
- protected int lastLineStart = 0;
- protected int line = 0;
- protected ASTNode lastNode;
- private static final WeakHashMap<String, char[]> EX_PRECACHE = new WeakHashMap<String, char[]>(15);
- public static HashMap<String, Object> LITERALS;
- public static HashMap<String, Object> CLASS_LITERALS;
- public static HashMap<String, Integer> OPERATORS;
- protected ExecutionStack stk;
- protected ExecutionStack splitAccumulator = new ExecutionStack();
- protected static ThreadLocal<ParserContext> parserContext;
- protected ParserContext pCtx;
- protected ExecutionStack dStack;
- protected Object ctx;
- protected VariableResolverFactory variableFactory;
- protected boolean debugSymbols = false;
- static {
- setupParser();
- }
- /**
- * This method is internally called by the static initializer for AbstractParser in order to setup the parser.
- * The static initialization populates the operator and literal tables for the parser. In some situations, like
- * OSGi, it may be necessary to utilize this manually.
- */
- public static void setupParser() {
- if (LITERALS == null || LITERALS.isEmpty()) {
- LITERALS = new HashMap<String, Object>();
- CLASS_LITERALS = new HashMap<String, Object>();
- OPERATORS = new HashMap<String, Integer>();
- /**
- * Add System and all the class wrappers from the JCL.
- */
- CLASS_LITERALS.put("System", System.class);
- CLASS_LITERALS.put("String", String.class);
- CLASS_LITERALS.put("CharSequence", CharSequence.class);
- CLASS_LITERALS.put("Integer", Integer.class);
- CLASS_LITERALS.put("int", int.class);
- CLASS_LITERALS.put("Long", Long.class);
- CLASS_LITERALS.put("long", long.class);
- CLASS_LITERALS.put("Boolean", Boolean.class);
- CLASS_LITERALS.put("boolean", boolean.class);
- CLASS_LITERALS.put("Short", Short.class);
- CLASS_LITERALS.put("short", short.class);
- CLASS_LITERALS.put("Character", Character.class);
- CLASS_LITERALS.put("char", char.class);
- CLASS_LITERALS.put("Double", Double.class);
- CLASS_LITERALS.put("double", double.class);
- CLASS_LITERALS.put("Float", Float.class);
- CLASS_LITERALS.put("float", float.class);
- CLASS_LITERALS.put("Byte", Byte.class);
- CLASS_LITERALS.put("byte", byte.class);
- CLASS_LITERALS.put("Math", Math.class);
- CLASS_LITERALS.put("Void", Void.class);
- CLASS_LITERALS.put("Object", Object.class);
- CLASS_LITERALS.put("Number", Number.class);
- CLASS_LITERALS.put("Class", Class.class);
- CLASS_LITERALS.put("ClassLoader", ClassLoader.class);
- CLASS_LITERALS.put("Runtime", Runtime.class);
- CLASS_LITERALS.put("Thread", Thread.class);
- CLASS_LITERALS.put("Compiler", Compiler.class);
- CLASS_LITERALS.put("StringBuffer", StringBuffer.class);
- CLASS_LITERALS.put("ThreadLocal", ThreadLocal.class);
- CLASS_LITERALS.put("SecurityManager", SecurityManager.class);
- CLASS_LITERALS.put("StrictMath", StrictMath.class);
- CLASS_LITERALS.put("Exception", Exception.class);
- CLASS_LITERALS.put("Array", java.lang.reflect.Array.class);
- if (parseDouble(getProperty("java.version").substring(0, 3)) >= 1.5) {
- try {
- CLASS_LITERALS.put("StringBuilder", currentThread().getContextClassLoader().loadClass("java.lang.StringBuilder"));
- }
- catch (Exception e) {
- throw new RuntimeException("cannot resolve a built-in literal", e);
- }
- }
- // Setup LITERALS
- LITERALS.putAll(CLASS_LITERALS);
- LITERALS.put("true", TRUE);
- LITERALS.put("false", FALSE);
- LITERALS.put("null", null);
- LITERALS.put("nil", null);
- LITERALS.put("empty", BlankLiteral.INSTANCE);
- setLanguageLevel(Boolean.getBoolean("mvel.future.lang.support") ? 6 : 5);
- }
- }
- protected ASTNode nextTokenSkipSymbols() {
- ASTNode n = nextToken();
- if (n != null && n.getFields() == -1) n = nextToken();
- return n;
- }
- /**
- * Retrieve the next token in the expression.
- *
- * @return -
- */
- protected ASTNode nextToken() {
- try {
- /**
- * If the cursor is at the end of the expression, we have nothing more to do:
- * return null.
- */
- if (!splitAccumulator.isEmpty()) {
- lastNode = (ASTNode) splitAccumulator.pop();
- if (cursor >= end && lastNode instanceof EndOfStatement) {
- return nextToken();
- }
- else {
- return lastNode;
- }
- }
- else if (cursor >= end) {
- return null;
- }
- int brace, idx;
- int tmpStart;
- String name;
- /**
- * Because of parser recursion for sub-expression parsing, we sometimes need to remain
- * certain field states. We do not reset for assignments, boolean mode, list creation or
- * a capture only mode.
- */
- boolean capture = false, union = false;
- if ((fields & ASTNode.COMPILE_IMMEDIATE) != 0 && pCtx == null) {
- debugSymbols = (pCtx = getParserContext()).isDebugSymbols();
- }
- if (debugSymbols) {
- if (!lastWasLineLabel) {
- if (pCtx.getSourceFile() == null) {
- throw new CompileException("unable to produce debugging symbols: source name must be provided.", expr, st);
- }
- if (!pCtx.isLineMapped(pCtx.getSourceFile())) {
- pCtx.initLineMapping(pCtx.getSourceFile(), expr);
- }
- skipWhitespace();
- if (cursor >= end) {
- return null;
- }
- int line = pCtx.getLineFor(pCtx.getSourceFile(), cursor);
- if (!pCtx.isVisitedLine(pCtx.getSourceFile(), pCtx.setLineCount(line)) && !pCtx.isBlockSymbols()) {
- lastWasLineLabel = true;
- pCtx.visitLine(pCtx.getSourceFile(), line);
- return lastNode = pCtx.setLastLineLabel(new LineLabel(pCtx.getSourceFile(), line, pCtx));
- }
- }
- else {
- lastWasComment = lastWasLineLabel = false;
- }
- }
- /**
- * Skip any whitespace currently under the starting point.
- */
- skipWhitespace();
- /**
- * From here to the end of the method is the core MVEL parsing code. Fiddling around here is asking for
- * trouble unless you really know what you're doing.
- */
- st = cursor;
- Mainloop:
- while (cursor != end) {
- if (isIdentifierPart(expr[cursor])) {
- capture = true;
- cursor++;
- while (cursor != end && isIdentifierPart(expr[cursor])) cursor++;
- }
- /**
- * If the current character under the cursor is a valid
- * part of an identifier, we keep capturing.
- */
- if (capture) {
- String t;
- if (OPERATORS.containsKey(t = new String(expr, st, cursor - st))) {
- switch (OPERATORS.get(t)) {
- case NEW:
- if (!isIdentifierPart(expr[st = cursor = trimRight(cursor)])) {
- throw new CompileException("unexpected character (expected identifier): "
- + expr[cursor], expr, st);
- }
- /**
- * Capture the beginning part of the token.
- */
- do {
- captureToNextTokenJunction();
- skipWhitespace();
- }
- while (cursor < end && expr[cursor] == '[');
- /**
- * If it's not a dimentioned array, continue capturing if necessary.
- */
- if (cursor < end && !lastNonWhite(']')) captureToEOT();
- TypeDescriptor descr = new TypeDescriptor(expr, st, trimLeft(cursor) - st, fields);
- if (pCtx == null) pCtx = getParserContext();
- if (pCtx.hasProtoImport(descr.getClassName())) {
- return lastNode = new NewPrototypeNode(descr, pCtx);
- }
- lastNode = new NewObjectNode(descr, fields, pCtx);
- skipWhitespace();
- if (cursor != end && expr[cursor] == '{') {
- if (!((NewObjectNode) lastNode).getTypeDescr().isUndimensionedArray()) {
- throw new CompileException(
- "conflicting syntax: dimensioned array with initializer block",
- expr, st);
- }
- st = cursor;
- Class egressType = lastNode.getEgressType();
- if (egressType == null) {
- try {
- egressType = getClassReference(pCtx, descr);
- }
- catch (ClassNotFoundException e) {
- throw new CompileException("could not instantiate class", expr, st, e);
- }
- }
- cursor = balancedCaptureWithLineAccounting(expr, st, end, expr[cursor], pCtx) + 1;
- if (tokenContinues()) {
- lastNode = new InlineCollectionNode(expr, st, cursor - st, fields,
- egressType, pCtx);
- st = cursor;
- captureToEOT();
- return lastNode = new Union(expr, st + 1, cursor, fields, lastNode, pCtx);
- }
- else {
- return lastNode = new InlineCollectionNode(expr, st, cursor - st, fields,
- egressType, pCtx);
- }
- }
- else if (((NewObjectNode) lastNode).getTypeDescr().isUndimensionedArray()) {
- throw new CompileException("array initializer expected", expr, st);
- }
- st = cursor;
- return lastNode;
- case ASSERT:
- st = cursor = trimRight(cursor);
- captureToEOS();
- return lastNode = new AssertNode(expr, st, cursor-- - st, fields, pCtx);
- case RETURN:
- st = cursor = trimRight(cursor);
- captureToEOS();
- return lastNode = new ReturnNode(expr, st, cursor - st, fields, pCtx);
- case IF:
- return captureCodeBlock(ASTNode.BLOCK_IF);
- case ELSE:
- throw new CompileException("else without if", expr, st);
- case FOREACH:
- return captureCodeBlock(ASTNode.BLOCK_FOREACH);
- case WHILE:
- return captureCodeBlock(ASTNode.BLOCK_WHILE);
- case UNTIL:
- return captureCodeBlock(ASTNode.BLOCK_UNTIL);
- case FOR:
- return captureCodeBlock(ASTNode.BLOCK_FOR);
- case WITH:
- return captureCodeBlock(ASTNode.BLOCK_WITH);
- case DO:
- return captureCodeBlock(ASTNode.BLOCK_DO);
- case STACKLANG:
- return captureCodeBlock(STACKLANG);
- case PROTO:
- return captureCodeBlock(PROTO);
- case ISDEF:
- st = cursor = trimRight(cursor);
- captureToNextTokenJunction();
- return lastNode = new IsDef(expr, st, cursor - st, pCtx);
- case IMPORT:
- st = cursor = trimRight(cursor);
- captureToEOS();
- ImportNode importNode = new ImportNode(expr, st, cursor - st, pCtx);
- if (pCtx == null) pCtx = getParserContext();
- if (importNode.isPackageImport()) {
- pCtx.addPackageImport(importNode.getPackageImport());
- }
- else {
- pCtx.addImport(importNode.getImportClass().getSimpleName(), importNode.getImportClass());
- }
- return lastNode = importNode;
- case IMPORT_STATIC:
- st = cursor = trimRight(cursor);
- captureToEOS();
- StaticImportNode staticImportNode = new StaticImportNode(expr, st, trimLeft(cursor) - st, pCtx);
- if (pCtx == null) pCtx = getParserContext();
- pCtx.addImport(staticImportNode.getMethod().getName(), staticImportNode.getMethod());
- return lastNode = staticImportNode;
- case FUNCTION:
- lastNode = captureCodeBlock(FUNCTION);
- st = cursor + 1;
- return lastNode;
- case UNTYPED_VAR:
- int end;
- st = cursor + 1;
- while (true) {
- captureToEOT();
- end = cursor;
- skipWhitespace();
- if (cursor != end && expr[cursor] == '=') {
- if (end == (cursor = st))
- throw new CompileException("illegal use of reserved word: var", expr, st);
- continue Mainloop;
- }
- else {
- name = new String(expr, st, end - st);
- if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- splitAccumulator.add(lastNode = new IndexedDeclTypedVarNode(idx, st, end - st, Object.class, pCtx));
- }
- else {
- splitAccumulator.add(lastNode = new DeclTypedVarNode(name, expr, st, end - st, Object.class,
- fields, pCtx));
- }
- }
- if (cursor == this.end || expr[cursor] != ',') break;
- else {
- cursor++;
- skipWhitespace();
- st = cursor;
- }
- }
- return (ASTNode) splitAccumulator.pop();
- }
- }
- skipWhitespace();
- /**
- * If we *were* capturing a token, and we just hit a non-identifier
- * character, we stop and figure out what to do.
- */
- if (cursor != end && expr[cursor] == '(') {
- cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '(', pCtx) + 1;
- }
- /**
- * If we encounter any of the following cases, we are still dealing with
- * a contiguous token.
- */
- CaptureLoop:
- while (cursor != end) {
- switch (expr[cursor]) {
- case '.':
- union = true;
- cursor++;
- skipWhitespace();
- continue;
- case '?':
- if (lookToLast() == '.' || cursor == start) {
- union = true;
- cursor++;
- continue;
- }
- else {
- break CaptureLoop;
- }
- case '+':
- switch (lookAhead()) {
- case '+':
- name = new String(subArray(st, trimLeft(cursor)));
- if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- lastNode = new IndexedPostFixIncNode(idx, pCtx);
- }
- else {
- lastNode = new PostFixIncNode(name, pCtx);
- }
- cursor += 2;
- expectEOS();
- return lastNode;
- case '=':
- name = createStringTrimmed(expr, st, cursor - st);
- st = cursor += 2;
- captureToEOS();
- if (union) {
- return lastNode = new DeepAssignmentNode(expr, st = trimRight(st), trimLeft(cursor) - st, fields,
- ADD, name, pCtx);
- }
- else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedAssignmentNode(expr, st, cursor - st, fields,
- ADD, name, idx, pCtx);
- }
- else {
- return lastNode = new OperativeAssign(name, expr, st = trimRight(st), trimLeft(cursor) - st,
- ADD, fields, pCtx);
- }
- }
- if (isDigit(lookAhead()) &&
- cursor > 1 && (expr[cursor - 1] == 'E' || expr[cursor - 1] == 'e')
- && isDigit(expr[cursor - 2])) {
- cursor++;
- // capture = true;
- continue Mainloop;
- }
- break CaptureLoop;
- case '-':
- switch (lookAhead()) {
- case '-':
- name = new String(subArray(st, trimLeft(cursor)));
- if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- lastNode = new IndexedPostFixDecNode(idx, pCtx);
- }
- else {
- lastNode = new PostFixDecNode(name, pCtx);
- }
- cursor += 2;
- expectEOS();
- return lastNode;
- case '=':
- name = new String(expr, st, trimLeft(cursor) - st);
- st = cursor += 2;
- captureToEOS();
- if (union) {
- return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
- SUB, t, pCtx);
- }
- else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
- SUB, idx, fields, pCtx);
- }
- else {
- return lastNode = new OperativeAssign(name, expr, st, cursor - st,
- SUB, fields, pCtx);
- }
- }
- if (isDigit(lookAhead()) &&
- cursor > 1 && (expr[cursor - 1] == 'E' || expr[cursor - 1] == 'e')
- && isDigit(expr[cursor - 2])) {
- cursor++;
- capture = true;
- continue Mainloop;
- }
- break CaptureLoop;
- /**
- * Exit immediately for any of these cases.
- */
- case '!':
- case ',':
- case '"':
- case '\'':
- case ';':
- case ':':
- break CaptureLoop;
- case '\u00AB': // special compact code for recursive parses
- case '\u00BB':
- case '\u00AC':
- case '&':
- case '^':
- case '|':
- case '*':
- case '/':
- case '%':
- char op = expr[cursor];
- if (lookAhead() == '=') {
- name = new String(expr, st, trimLeft(cursor) - st);
- st = cursor += 2;
- captureToEOS();
- if (union) {
- return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
- opLookup(op), t, pCtx);
- }
- else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
- opLookup(op), idx, fields, pCtx);
- }
- else {
- return lastNode = new OperativeAssign(name, expr, st, cursor - st,
- opLookup(op), fields, pCtx);
- }
- }
- break CaptureLoop;
- case '<':
- if ((lookAhead() == '<' && lookAhead(2) == '=')) {
- name = new String(expr, st, trimLeft(cursor) - st);
- st = cursor += 3;
- captureToEOS();
- if (union) {
- return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
- BW_SHIFT_LEFT, t, pCtx);
- }
- else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
- BW_SHIFT_LEFT, idx, fields, pCtx);
- }
- else {
- return lastNode = new OperativeAssign(name, expr, st, cursor - st,
- BW_SHIFT_LEFT, fields, pCtx);
- }
- }
- break CaptureLoop;
- case '>':
- if (lookAhead() == '>') {
- if (lookAhead(2) == '=') {
- name = new String(expr, st, trimLeft(cursor) - st);
- st = cursor += 3;
- captureToEOS();
- if (union) {
- return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
- BW_SHIFT_RIGHT, t, pCtx);
- }
- else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
- BW_SHIFT_RIGHT, idx, fields, pCtx);
- }
- else {
- return lastNode = new OperativeAssign(name, expr, st, cursor - st,
- BW_SHIFT_RIGHT, fields, pCtx);
- }
- }
- else if ((lookAhead(2) == '>' && lookAhead(3) == '=')) {
- name = new String(expr, st, trimLeft(cursor) - st);
- st = cursor += 4;
- captureToEOS();
- if (union) {
- return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
- BW_USHIFT_RIGHT, t, pCtx);
- }
- else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
- BW_USHIFT_RIGHT, idx, fields, pCtx);
- }
- else {
- return lastNode = new OperativeAssign(name, expr, st, cursor - st,
- BW_USHIFT_RIGHT, fields, pCtx);
- }
- }
- }
- break CaptureLoop;
- case '(':
- cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '(', pCtx) + 1;
- continue;
- case '[':
- cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '[', pCtx) + 1;
- continue;
- case '{':
- if (!union) break CaptureLoop;
- cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '{', pCtx) + 1;
- continue;
- case '~':
- if (lookAhead() == '=') {
- // tmp = subArray(start, trimLeft(cursor));
- tmpStart = st;
- int tmpOffset = cursor - st;
- st = cursor += 2;
- captureToEOT();
- return lastNode = new RegExMatch(expr, tmpStart, tmpOffset, fields, st, cursor - st, pCtx);
- }
- break CaptureLoop;
- case '=':
- if (lookAhead() == '+') {
- name = new String(expr, st, trimLeft(cursor) - st);
- st = cursor += 2;
- if (!isNextIdentifierOrLiteral()) {
- throw new CompileException("unexpected symbol '" + expr[cursor] + "'", expr, st);
- }
- captureToEOS();
- if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
- ADD, idx, fields, pCtx);
- }
- else {
- return lastNode = new OperativeAssign(name, expr, st, cursor - st,
- ADD, fields, pCtx);
- }
- }
- else if (lookAhead() == '-') {
- name = new String(expr, st, trimLeft(cursor) - st);
- st = cursor += 2;
- if (!isNextIdentifierOrLiteral()) {
- throw new CompileException("unexpected symbol '" + expr[cursor] + "'", expr, st);
- }
- captureToEOS();
- if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
- SUB, idx, fields, pCtx);
- }
- else {
- return lastNode = new OperativeAssign(name, expr, st, cursor - st,
- SUB, fields, pCtx);
- }
- }
- if (greedy && lookAhead() != '=') {
- cursor++;
- if (union) {
- captureToEOS();
- return lastNode = new DeepAssignmentNode(expr, st, cursor - st,
- fields | ASTNode.ASSIGN, pCtx);
- }
- else if (lastWasIdentifier) {
- return procTypedNode(false);
- }
- else if (pCtx != null && ((idx = pCtx.variableIndexOf(t)) != -1
- && (pCtx.isIndexAllocation()))) {
- captureToEOS();
- IndexedAssignmentNode ian = new IndexedAssignmentNode(expr, st = trimRight(st),
- trimLeft(cursor) - st,
- ASTNode.ASSIGN, idx, pCtx);
- if (idx == -1) {
- pCtx.addIndexedInput(t = ian.getAssignmentVar());
- ian.setRegister(pCtx.variableIndexOf(t));
- }
- return lastNode = ian;
- }
- else {
- captureToEOS();
- return lastNode = new AssignmentNode(expr, st, cursor - st,
- fields | ASTNode.ASSIGN, pCtx);
- }
- }
- break CaptureLoop;
- default:
- if (cursor != end) {
- if (isIdentifierPart(expr[cursor])) {
- if (!union) {
- break CaptureLoop;
- }
- cursor++;
- while (cursor != end && isIdentifierPart(expr[cursor])) cursor++;
- }
- else if ((cursor + 1) != end && isIdentifierPart(expr[cursor + 1])) {
- break CaptureLoop;
- }
- else {
- cursor++;
- }
- }
- else {
- break CaptureLoop;
- }
- }
- }
- /**
- * Produce the token.
- */
- trimWhitespace();
- return createPropertyToken(st, cursor);
- }
- else {
- switch (expr[cursor]) {
- case '.': {
- cursor++;
- if (isDigit(expr[cursor])) {
- capture = true;
- continue;
- }
- expectNextChar_IW('{');
- return lastNode = new ThisWithNode(expr, st, cursor - st - 1
- , cursor + 1,
- (cursor = balancedCaptureWithLineAccounting(expr,
- cursor, end, '{', pCtx) + 1) - 3, fields, pCtx);
- }
- case '@': {
- st++;
- captureToEOT();
- if (pCtx == null || (pCtx.getInterceptors() == null || !pCtx.getInterceptors().
- containsKey(name = new String(expr, st, cursor - st)))) {
- throw new CompileException("reference to undefined interceptor: "
- + new String(expr, st, cursor - st), expr, st);
- }
- return lastNode = new InterceptorWrapper(pCtx.getInterceptors().get(name), nextToken(), pCtx);
- }
- case '=':
- return createOperator(expr, st, (cursor += 2));
- case '-':
- if (lookAhead() == '-') {
- cursor += 2;
- skipWhitespace();
- st = cursor;
- captureIdentifier();
- name = new String(subArray(st, cursor));
- if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedPreFixDecNode(idx, pCtx);
- }
- else {
- return lastNode = new PreFixDecNode(name, pCtx);
- }
- }
- else if ((cursor == start || (lastNode != null &&
- (lastNode instanceof BooleanNode || lastNode.isOperator())))
- && !isDigit(lookAhead())) {
- captureToEOT();
- return new Sign(expr, st, cursor - st, fields, pCtx);
- }
- else if ((cursor != start &&
- (lastNode != null && !(lastNode instanceof BooleanNode || lastNode.isOperator())))
- || !isDigit(lookAhead())) {
- return createOperator(expr, st, cursor++ + 1);
- }
- else if ((cursor - 1) != start || (!isDigit(expr[cursor - 1])) && isDigit(lookAhead())) {
- cursor++;
- break;
- }
- else {
- throw new CompileException("not a statement", expr, st);
- }
- case '+':
- if (lookAhead() == '+') {
- cursor += 2;
- skipWhitespace();
- st = cursor;
- captureIdentifier();
- name = new String(subArray(st, cursor));
- if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
- return lastNode = new IndexedPreFixIncNode(idx, pCtx);
- }
- else {
- return lastNode = new PreFixIncNode(name, pCtx);
- }
- }
- return createOperator(expr, st, cursor++ + 1);
- case '*':
- if (lookAhead() == '*') {
- cursor++;
- }
- return createOperator(expr, st, cursor++ + 1);
- case ';':
- cursor++;
- lastWasIdentifier = false;
- return lastNode = new EndOfStatement(pCtx);
- case '?':
- if (cursor == start) {
- cursor++;
- continue;
- }
- case '#':
- case '/':
- case ':':
- case '^':
- case '%': {
- return createOperator(expr, st, cursor++ + 1);
- }
- case '(': {
- cursor++;
- boolean singleToken = true;
- skipWhitespace();
- for (brace = 1; cursor != end && brace != 0; cursor++) {
- switch (expr[cursor]) {
- case '(':
- brace++;
- break;
- case ')':
- brace--;
- break;
- case '\'':
- cursor = captureStringLiteral('\'', expr, cursor, end);
- break;
- case '"':
- cursor = captureStringLiteral('"', expr, cursor, end);
- break;
- case 'i':
- if (brace == 1 && isWhitespace(lookBehind()) && lookAhead() == 'n' && isWhitespace(lookAhead(2))) {
- for (int level = brace; cursor != end; cursor++) {
- switch (expr[cursor]) {
- case '(':
- brace++;
- break;
- case ')':
- if (--brace < level) {
- cursor++;
- if (tokenContinues()) {
- lastNode = new Fold(expr, trimRight(st + 1),
- cursor - st - 2, fields, pCtx);
- if (expr[st = cursor] == '.') st++;
- captureToEOT();
- return lastNode = new Union(expr, st = trimRight(st),
- cursor - st, fields, lastNode, pCtx);
- }
- else {
- return lastNode = new Fold(expr, trimRight(st + 1),
- cursor - st - 2, fields, pCtx);
- }
- }
- break;
- case '\'':
- cursor = captureStringLiteral('\'', expr, cursor, end);
- break;
- case '"':
- cursor = captureStringLiteral('\"', expr, cursor, end);
- break;
- }
- }
- throw new CompileException("unterminated projection; closing parathesis required",
- expr, st);
- }
- break;
- default:
- /**
- * Check to see if we should disqualify this current token as a potential
- * type-cast candidate.
- */
- if (expr[cursor] != '.') {
- switch (expr[cursor]) {
- case '[':
- case ']':
- break;
- default:
- if (!(isIdentifierPart(expr[cursor]) || expr[cursor] == '.')) {
- singleToken = false;
- }
- }
- }
- }
- }
- if (brace != 0) {
- throw new CompileException("unbalanced braces in expression: (" + brace + "):",
- expr, st);
- }
- tmpStart = -1;
- if (singleToken) {
- int _st;
- TypeDescriptor tDescr = new TypeDescriptor(expr, _st = trimRight(st + 1),
- trimLeft(cursor - 1) - _st, fields);
- Class cls;
- try {
- if (tDescr.isClass() && (cls = getClassReference(pCtx, tDescr)) != null) {
- // lookahead to check if it could be a real cast
- boolean isCast = false;
- for (int i = cursor; i < expr.length; i++) {
- if (expr[i] == ' ' || expr[i] == '\t') continue;
- isCast = isIdentifierPart(expr[i]) || expr[i] == '\'' || expr[i] == '"' || expr[i] == '(';
- break;
- }
- if (isCast) {
- st = cursor;
- captureToEOT();
- // captureToEOS();
- return lastNode = new TypeCast(expr, st, cursor - st,
- cls, fields, pCtx);
- }
- }
- }
- catch (ClassNotFoundException e) {
- // fallthrough
- }
- }
- if (tmpStart != -1) {
- return handleUnion(handleSubstatement(new Substatement(expr, tmpStart, cursor - tmpStart, fields, pCtx)));
- }
- else {
- return handleUnion(
- handleSubstatement(
- new Substatement(expr, st = trimRight(st + 1),
- trimLeft(cursor - 1) - st, fields, pCtx)));
- }
- }
- case '}':
- case ']':
- case ')': {
- throw new CompileException("unbalanced braces", expr, st);
- }
- case '>': {
- switch (expr[cursor + 1]) {
- case '>':
- if (expr[cursor += 2] == '>') cursor++;
- return createOperator(expr, st, cursor);
- case '=':
- return createOperator(expr, st, cursor += 2);
- default:
- return createOperator(expr, st, ++cursor);
- }
- }
- case '<': {
- if (expr[++cursor] == '<') {
- if (expr[++cursor] == '<') cursor++;
- return createOperator(expr, st, cursor);
- }
- else if (expr[cursor] == '=') {
- return createOperator(expr, st, ++cursor);
- }
- else {
- return createOperator(expr, st, cursor);
- }
- }
- case '\'':
- case '"':
- lastNode = new LiteralNode(handleStringEscapes(subset(expr, st + 1,
- (cursor = captureStringLiteral(expr[cursor], expr, cursor, end)) - st - 1))
- , String.class, pCtx);
- cursor++;
- if (tokenContinues()) {
- return lastNode = handleUnion(lastNode);
- }
- return lastNode;
- case '&': {
- if (expr[cursor++ + 1] == '&') {
- return createOperator(expr, st, ++cursor);
- }
- else {
- return createOperator(expr, st, cursor);
- }
- }
- case '|': {
- if (expr[cursor++ + 1] == '|') {
- return createOperator(expr, st, ++cursor);
- }
- else {
- return createOperator(expr, st, cursor);
- }
- }
- case '~':
- if ((cursor++ - 1 != 0 || !isIdentifierPart(lookBehind()))
- && isDigit(expr[cursor])) {
- st = cursor;
- captureToEOT();
- return lastNode = new Invert(expr, st, cursor - st, fields, pCtx);
- }
- else if (expr[cursor] == '(') {
- st = cursor--;
- captureToEOT();
- return lastNode = new Invert(expr, st, cursor - st, fields, pCtx);
- }
- else {
- if (expr[cursor] == '=') cursor++;
- return createOperator(expr, st, cursor);
- }
- case '!': {
- ++cursor;
- if (isNextIdentifier()) {
- if (lastNode != null && !lastNode.isOperator()) {
- throw new CompileException("unexpected operator '!'", expr, st);
- }
- st = cursor;
- captureToEOT();
- if ("new".equals(name = new String(expr, st, cursor - st))
- || "isdef".equals(name)) {
- captureToEOT();
- return lastNode = new Negation(expr, st, cursor - st, fields, pCtx);
- }
- else {
- return lastNode = new Negation(expr, st, cursor - st, fields, pCtx);
- }
- }
- else if (expr[cursor] == '(') {
- st = cursor--;
- captureToEOT();
- return lastNode = new Negation(expr, st, cursor - st, fields, pCtx);
- }
- else if (expr[cursor] == '!') {
- // just ignore a double negation
- ++cursor;
- return nextToken();
- }
- else if (expr[cursor] != '=')
- throw new CompileException("unexpected operator '!'", expr, st, null);
- else {
- return createOperator(expr, st, ++cursor);
- }
- }
- case '[':
- case '{':
- cursor = balancedCaptureWithLineAccounting(expr, cursor, end, expr[cursor], pCtx) + 1;
- if (tokenContinues()) {
- lastNode = new InlineCollectionNode(expr, st, cursor - st, fields, pCtx);
- st = cursor;
- captureToEOT();
- if (expr[st] == '.') st++;
- return lastNode = new Union(expr, st, cursor - st, fields, lastNode, pCtx);
- }
- else {
- return lastNode = new InlineCollectionNode(expr, st, cursor - st, fields, pCtx);
- }
- default:
- cursor++;
- }
- }
- }
- if (st == cursor)
- return null;
- else
- return createPropertyToken(st, cursor);
- }
- catch (RedundantCodeException e) {
- return nextToken();
- }
- catch (NumberFormatException e) {
- throw new CompileException("badly formatted number: " + e.getMessage(), expr, st, e);
- }
- catch (StringIndexOutOfBoundsException e) {
- throw new CompileException("unexpected end of statement", expr, cursor, e);
- }
- catch (ArrayIndexOutOfBoundsException e) {
- throw new CompileException("unexpected end of statement", expr, cursor, e);
- }
- catch (CompileException e) {
- throw ErrorUtil.rewriteIfNeeded(e, expr, cursor);
- }
- }
- public ASTNode handleSubstatement(Substatement stmt) {
- if (stmt.getStatement() != null && stmt.getStatement().isLiteralOnly()) {
- return new LiteralNode(stmt.getStatement().getValue(null, null, null), getParserContext());
- }
- else {
- return stmt;
- }
- }
- /**
- * Handle a union between a closed statement and a residual property chain.
- *
- * @param node an ast node
- * @return ASTNode
- */
- protected ASTNode handleUnion(ASTNode node) {
- if (cursor != end) {
- skipWhitespace();
- int union = -1;
- if (cursor < end) {
- switch (expr[cursor]) {
- case '.':
- union = cursor + 1;
- break;
- case '[':
- union = cursor;
- }
- }
- if (union != -1) {
- captureToEOT();
- return lastNode = new Union(expr, union, cursor - union, fields, node, getParserContext());
- }
- }
- return lastNode = node;
- }
- /**
- * Create an operator node.
- *
- * @param expr an char[] containing the expression
- * @param start the start offet for the token
- * @param end the end offset for the token
- * @return ASTNode
- */
- private ASTNode createOperator(final char[] expr, final int start, final int end) {
- lastWasIdentifier = false;
- return lastNode = new OperatorNode(OPERATORS.get(new String(expr, start, end - start)), expr, start, getParserContext());
- }
- /**
- * Create a copy of an array based on a sub-range. Works faster than System.arrayCopy() for arrays shorter than
- * 1000 elements in most cases, so the parser uses this internally.
- *
- * @param start the start offset
- * @param end the end offset
- * @return an array
- */
- private char[] subArray(final int start, final int end) {
- if (start >= end) return new char[0];
- char[] newA = new char[end - start];
- for (int i = 0; i != newA.length; i++) {
- newA[i] = expr[i + start];
- }
- return newA;
- }
- /**
- * Generate a property token
- *
- * @param st the start offset
- * @param end the end offset
- * @return an ast node
- */
- private ASTNode createPropertyToken(int st, int end) {
- String tmp;
- if (isPropertyOnly(expr, st, end)) {
- if (pCtx != null && pCtx.hasImports()) {
- int find;
- if ((find = findFirst('.', st, end - st, expr)) != -1) {
- String iStr = new String(expr, st, find - st);
- if (pCtx.hasImport(iStr)) {
- lastWasIdentifier = true;
- return lastNode = new LiteralDeepPropertyNode(expr, find + 1, end - find - 1, fields,
- pCtx.getImport(iStr), pCtx);
- }
- }
- else {
- if (pCtx.hasImport(tmp = new String(expr, st, cursor - st))) {
- lastWasIdentifier = true;
- return lastNode = new LiteralNode(pCtx.getStaticOrClassImport(tmp), pCtx);
- }
- }
- }
- if (LITERALS.containsKey(tmp = new String(expr, st, end - st))) {
- lastWasIdentifier = true;
- return lastNode = new LiteralNode(LITERALS.get(tmp), pCtx);
- }
- else if (OPERATORS.containsKey(tmp)) {
- lastWasIdentifier = false;
- return lastNode = new OperatorNode(OPERATORS.get(tmp), expr, st, pCtx);
- }
- else if (lastWasIdentifier) {
- return procTypedNode(true);
- }
- }
- if (pCtx != null && pCtx.hasImports() && isArrayType(expr, st, end)) {
- if (pCtx.hasImport(new String(expr, st, cursor - st - 2))) {
- lastWasIdentifier = true;
- TypeDescriptor typeDescriptor = new TypeDescriptor(expr, st, cursor - st, fields);
- try {
- return lastNode = new LiteralNode(typeDescriptor.getClassReference(pCtx), pCtx);
- }
- catch (ClassNotFoundException e) {
- throw new CompileException("could not resolve class: " + typeDescriptor.getClassName(), expr, st);
- }
- }
- }
- lastWasIdentifier = true;
- return lastNode = new ASTNode(expr, trimRight(st), trimLeft(end) - st, fields, pCtx);
- }
- /**
- * Process the current typed node
- *
- * @param decl node is a declaration or not
- * @return and ast node
- */
- private ASTNode procTypedNode(boolean decl) {
- while (true) {
- if (lastNode.getLiteralValue() instanceof String) {
- char[] tmp = ((String) lastNode.getLiteralValue()).toCharArray();
- TypeDescriptor tDescr = new TypeDescriptor(tmp, 0, tmp.length, 0);
- try {
- lastNode.setLiteralValue(getClassReference(pCtx, tDescr));
- lastNode.discard();
- }
- catch (Exception e) {
- // fall through;
- }
- }
- if (lastNode.isLiteral() && lastNode.getLiteralValue() instanceof Class) {
- lastNode.discard();
- captureToEOS();
- if (decl) {
- splitAccumulator.add(new DeclTypedVarNode(new String(expr, st, cursor - st), expr, st, cursor - st,
- (Class) lastNode.getLiteralValue(), fields | ASTNode.ASSIGN, pCtx));
- }
- else {
- captureToEOS();
- splitAccumulator.add(new TypedVarNode(expr, st, cursor - st - 1, fields | ASTNode.ASSIGN, (Class)
- lastNode.getLiteralValue(), pCtx));
- }
- }
- else if (lastNode instanceof Proto) {
- captureToEOS();
- if (decl) {
- splitAccumulator.add(new DeclProtoVarNode(new String(expr, st, cursor - st),
- (Proto) lastNode, fields | ASTNode.ASSIGN, pCtx));
- }
- else {
- splitAccumulator.add(new ProtoVarNode(expr, st, cursor - st, fields | ASTNode.ASSIGN, (Proto)
- lastNode, pCtx));
- }
- }
- // this redundant looking code is needed to work with the interpreter and MVELSH properly.
- else if ((fields & ASTNode.COMPILE_IMMEDIATE) == 0) {
- if (stk.peek() instanceof Class) {
- captureToEOS();
- if (decl) {
- splitAccumulator.add(new DeclTypedVarNode(new String(expr, st, cursor - st), expr, st, cursor - st,
- (Class) stk.pop(), fields | ASTNode.ASSIGN, pCtx));
- }
- else {
- splitAccumulator.add(new TypedVarNode(expr, st, cursor - st,
- fields | ASTNode.ASSIGN, (Class) stk.pop(), pCtx));
- }
- }
- else if (stk.peek() instanceof Proto) {
- captureToEOS();
- if (decl) {
- splitAccumulator.add(new DeclProtoVarNode(new String(expr, st, cursor - st),
- (Proto) stk.pop(), fields | ASTNode.ASSIGN, pCtx));
- }
- else {
- splitAccumulator.add(new ProtoVarNode(expr, st, cursor - st, fields | ASTNode.ASSIGN, (Proto)
- stk.pop(), pCtx));
- }
- }
- else {
- throw new CompileException("unknown class or illegal statement: " + lastNode.getLiteralValue(), expr, cursor);
- }
- }
- else {
- throw new CompileException("unknown class or illegal statement: " + lastNode.getLiteralValue(), expr, cursor);
- }
- skipWhitespace();
- if (cursor < end && expr[cursor] == ',') {
- st = ++cursor;
- splitAccumulator.add(new EndOfStatement(pCtx));
- }
- else {
- return (ASTNode) splitAccumulator.pop();
- }
- }
- }
- /**
- * Generate a code block token.
- *
- * @param condStart the start offset for the condition
- * @param condEnd the end offset for the condition
- * @param blockStart the start offset for the block
- * @param blockEnd the end offset for the block
- * @param type the type of block
- * @return and ast node
- */
- private ASTNode createBlockToken(final int condStart,
- final int condEnd, final int blockStart, final int blockEnd, int type) {
- lastWasIdentifier = false;
- cursor++;
- if (isStatementNotManuallyTerminated()) {
- splitAccumulator.add(new EndOfStatement(pCtx));
- }
- int condOffset = condEnd - condStart;
- int blockOffset = blockEnd - blockStart;
- if (blockOffset < 0) blockOffset = 0;
- switch (type) {
- case ASTNode.BLOCK_IF:
- return new IfNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
- case ASTNode.BLOCK_FOR:
- for (int i = condStart; i < condEnd; i++) {
- if (expr[i] == ';')
- return new ForNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
- else if (expr[i] == ':')
- break;
- }
- case ASTNode.BLOCK_FOREACH:
- return new ForEachNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
- case ASTNode.BLOCK_WHILE:
- return new WhileNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
- case ASTNode.BLOCK_UNTIL:
- return new UntilNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
- case ASTNode.BLOCK_DO:
- return new DoNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
- case ASTNode.BLOCK_DO_UNTIL:
- return new DoUntilNode(expr, condStart, condOffset, blockStart, blockOffset, pCtx);
- default:
- return new WithNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
- }
- }
- /**
- * Capture a code block by type.
- *
- * @param type the block type
- * @return an ast node
- */
- private ASTNode captureCodeBlock(int type) {
- boolean cond = true;
- ASTNode first = null;
- ASTNode tk = null;
- switch (type) {
- case ASTNode.BLOCK_IF: {
- do {
- if (tk != null) {
- captureToNextTokenJunction();
- skipWhitespace();
- cond = expr[cursor] != '{' && expr[cursor] == 'i' && expr[++cursor] == 'f'
- && expr[cursor = incNextNonBlank()] == '(';
- }
- if (((IfNode) (tk = _captureBlock(tk, expr, cond, type))).getElseBlock() != null) {
- cursor++;
- return first;
- }
- if (first == null) first = tk;
- if (cursor != end && expr[cursor] != ';') {
- cursor++;
- }
- }
- while (ifThenElseBlockContinues());
- return first;
- }
- case ASTNode.BLOCK_DO:
- skipWhitespace();
- return _captureBlock(null, expr, false, type);
- default: // either BLOCK_WITH or BLOCK_FOREACH
- captureToNextTokenJunction();
- skipWhitespace();
- return _captureBlock(null, expr, true, type);
- }
- }
- private ASTNode _captureBlock(ASTNode node, final char[] expr, boolean cond, int type) {
- skipWhitespace();
- int startCond = 0;
- int endCond = 0;
- int blockStart;
- int blockEnd;
- String name;
- /**
- * Functions are a special case we handle differently from the rest of block parsing
- */
- switch (type) {
- case FUNCTION: {
- int st = cursor;
- captureToNextTokenJunction();
- if (cursor == end) {
- throw new CompileException("unexpected end of statement", expr, st);
- }
- /**
- * Check to see if the name is legal.
- */
- if (isReservedWord(name = createStringTrimmed(expr, st, cursor - st))
- || isNotValidNameorLabel(name))
- throw new CompileException("illegal function name or use of reserved word", expr, cursor);
- if (pCtx == null) pCtx = getParserContext();
- FunctionParser parser = new FunctionParser(name, cursor, end - cursor, expr, fields, pCtx, splitAccumulator);
- Function function = parser.parse();
- cursor = parser.getCursor();
- return lastNode = function;
- }
- case PROTO: {
- if (ProtoParser.isUnresolvedWaiting()) {
- if (pCtx == null) pCtx = getParserContext();
- ProtoParser.checkForPossibleUnresolvedViolations(expr, cursor, pCtx);
- }
- int st = cursor;
- captureToNextTokenJunction();
- if (isReservedWord(name = createStringTrimmed(expr, st, cursor - st))
- || isNotValidNameorLabel(name))
- throw new CompileException("illegal prototype name or use of reserved word", expr, cursor);
- if (expr[cursor = nextNonBlank()] != '{') {
- throw new CompileException("expected '{' but found: " + expr[cursor], expr, cursor);
- }
- cursor = balancedCaptureWithLineAccounting(expr, st = cursor + 1, end, '{', pCtx);
- if (pCtx == null) pCtx = getParserContext();
- ProtoParser parser = new ProtoParser(expr, st, cursor, name, pCtx, fields, splitAccumulator);
- Proto proto = parser.parse();
- if (pCtx == null) pCtx = getParserContext();
- pCtx.addImport(proto);
- proto.setCursorPosition(st, cursor);
- cursor = parser.getCursor();
- ProtoParser.notifyForLateResolution(proto);
- return lastNode = proto;
- }
- case STACKLANG: {
- if (expr[cursor = nextNonBlank()] != '{') {
- throw new CompileException("expected '{' but found: " + expr[cursor], expr, cursor);
- }
- int st;
- cursor = balancedCaptureWithLineAccounting(expr, st = cursor + 1, end, '{', pCtx);
- if (pCtx == null) pCtx = getParserContext();
- Stacklang stacklang = new Stacklang(expr, st, cursor - st, fields, pCtx);
- cursor++;
- return lastNode = stacklang;
- }
- default:
- if (cond) {
- if (expr[cursor] != '(') {
- throw new CompileException("expected '(' but encountered: " + expr[cursor], expr, cursor);
- }
- /**
- * This block is an: IF, FOREACH or WHILE node.
- */
- endCond = cursor = balancedCaptureWithLineAccounting(expr, startCond = cursor, end, '(', pCtx);
- startCond++;
- cursor++;
- }
- }
- skipWhitespace();
- if (cursor >= end) {
- throw new CompileException("unexpected end of statement", expr, end);
- }
- else if (expr[cursor] == '{') {
- blockEnd = cursor = balancedCaptureWithLineAccounting(expr, blockStart = cursor, end, '{', pCtx);
- }
- else {
- blockStart = cursor - 1;
- captureToEOSorEOL();
- blockEnd = cursor + 1;
- }
- if (type == ASTNode.BLOCK_IF) {
- IfNode ifNode = (IfNode) node;
- if (node != null) {
- if (!cond) {
- return ifNode.setElseBlock(expr, st = trimRight(blockStart + 1), trimLeft(blockEnd) - st, pCtx);
- }
- else {
- return ifNode.setElseIf((IfNode) createBlockToken(startCond, endCond, trimRight(blockStart + 1),
- trimLeft(blockEnd), type));
- }
- }
- else {
- return createBlockToken(startCond, endCond, blockStart + 1, blockEnd, type);
- }
- }
- else if (type == ASTNode.BLOCK_DO) {
- cursor++;
- skipWhitespace();
- st = cursor;
- captureToNextTokenJunction();
- if ("while".equals(name = new String(expr, st, cursor - st))) {
- skipWhitespace();
- startCond = cursor + 1;
- endCond = cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '(', pCtx);
- return createBlockToken(startCond, endCond, trimRight(blockStart + 1), trimLeft(blockEnd), type);
- }
- else if ("until".equals(name)) {
- skipWhitespace();
- startCond = cursor + 1;
- endCond = cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '(', pCtx);
- return createBlockToken(startCond, endCond, trimRight(blockStart + 1), trimLeft(blockEnd),
- ASTNode.BLOCK_DO_UNTIL);
- }
- else {
- throw new CompileException("expected 'while' or 'until' but encountered: " + name, expr, cursor);
- }
- }
- // DON"T REMOVE THIS COMMENT!
- // else if (isFlag(ASTNode.BLOCK_FOREACH) || isFlag(ASTNode.BLOCK_WITH)) {
- else {
- return createBlockToken(startCond, endCond, trimRight(blockStart + 1), trimLeft(blockEnd), type);
- }
- }
- /**
- * Checking from the current cursor position, check to see if the if-then-else block continues.
- *
- * @return boolean value
- */
- protected boolean ifThenElseBlockContinues() {
- if ((cursor + 4) < end) {
- if (expr[cursor] != ';') cursor--;
- skipWhitespace();
- return expr[cursor] == 'e' && expr[cursor + 1] == 'l' && expr[cursor + 2] == 's' && expr[cursor + 3] == 'e'
- && (isWhitespace(expr[cursor + 4]) || expr[cursor + 4] == '{');
- }
- return false;
- }
- /**
- * Checking from the current cursor position, check to see if we're inside a contiguous identifier.
- *
- * @return -
- */
- protected boolean tokenContinues() {
- if (cursor == end) return false;
- else if (expr[cursor] == '.' || expr[cursor] == '[') return true;
- else if (isWhitespace(expr[cursor])) {
- int markCurrent = cursor;
- skipWhitespace();
- if (cursor != end && (expr[cursor] == '.' || expr[cursor] == '[')) return true;
- cursor = markCurrent;
- }
- return false;
- }
- /**
- * The parser should find a statement ending condition when this is called, otherwise everything should blow up.
- */
- protected void expectEOS() {
- skipWhitespace();
- if (cursor != end && expr[cursor] != ';') {
- switch (expr[cursor]) {
- case '&':
- if (lookAhead() == '&') return;
- else break;
- case '|':
- if (lookAhead() == '|') return;
- else break;
- case '!':
- if (lookAhead() == '=') return;
- else break;
- case '<':
- case '>':
- return;
- case '=': {
- switch (lookAhead()) {
- case '=':
- case '+':
- case '-':
- case '*':
- return;
- }
- break;
- }
- case '+':
- case '-':
- case '/':
- case '*':
- if (lookAhead() == '=') return;
- else break;
- }
- throw new CompileException("expected end of statement but encountered: "
- + (cursor == end ? "<end of stream>" : expr[cursor]), expr, cursor);
- }
- }
- /**
- * Checks to see if the next part of the statement is an identifier part.
- *
- * @return boolean true if next part is identifier part.
- */
- protected boolean isNextIdentifier() {
- while (cursor != end && isWhitespace(expr[cursor])) cursor++;
- return cursor != end && isIdentifierPart(expr[cursor]);
- }
- /**
- * Capture from the current cursor position, to the end of the statement.
- */
- protected void captureToEOS() {
- while (cursor != end) {
- switch (expr[cursor]) {
- case '(':
- case '[':
- case '{':
- if ((cursor = balancedCaptureWithLineAccounting(expr, cursor, end, expr[cursor], pCtx)) >= end)
- return;
- break;
- case '"':
- case '\'':
- cursor = captureStringLiteral(expr[cursor], expr, cursor, end);
- break;
- case ',':
- case ';':
- case '}':
- return;
- }
- cursor++;
- }
- }
- /**
- * From the current cursor position, capture to the end of statement, or the end of line, whichever comes first.
- */
- protected void captureToEOSorEOL() {
- while (cursor != end && (expr[cursor] != '\n' && expr[cursor] != '\r' && expr[cursor] != ';')) {
- cursor++;
- }
- }
- /**
- * From the current cursor position, capture to the end of the line.
- */
- // protected void captureToEOL() {
- // while (cursor != end && (expr[cursor] != '\n')) cursor++;
- // }
- /**
- * Capture to the end of the current identifier under the cursor.
- */
- protected void captureIdentifier() {
- boolean captured = false;
- if (cursor == end) throw new CompileException("unexpected end of statement: EOF", expr, cursor);
- while (cursor != end) {
- switch (expr[cursor]) {
- case ';':
- return;
- default: {
- if (!isIdentifierPart(expr[cursor])) {
- if (captured) return;
- throw new CompileException("unexpected symbol (was expecting an identifier): " + expr[cursor],
- expr, cursor);
- }
- else {
- captured = true;
- }
- }
- }
- cursor++;
- }
- }
- /**
- * From the current cursor position, capture to the end of the current token.
- */
- protected void captureToEOT() {
- skipWhitespace();
- do {
- switch (expr[cursor]) {
- case '(':
- case '[':
- case '{':
- if ((cursor = balancedCaptureWithLineAccounting(expr, cursor, end, expr[cursor], pCtx)) == -1) {
- throw new CompileException("unbalanced braces", expr, cursor);
- }
- break;
- case '*':
- case '/':
- case '+':
- case '%':
- case ',':
- case '=':
- case '&':
- case '|':
- case ';':
- return;
- case '.':
- skipWhitespace();
- break;
- case '\'':
- cursor = captureStringLiteral('\'', expr, cursor, end);
- break;
- case '"':
- cursor = captureStringLiteral('"', expr, cursor, end);
- break;
- default:
- if (isWhitespace(expr[cursor])) {
- skipWhitespace();
- if (cursor < end && expr[cursor] == '.') {
- if (cursor != end) cursor++;
- skipWhitespace();
- break;
- }
- else {
- trimWhitespace();
- return;
- }
- }
- }
- }
- while (++cursor < end);
- }
- protected boolean lastNonWhite(char c) {
- int i = cursor - 1;
- while (isWhitespace(expr[i])) i--;
- return c == expr[i];
- }
- /**
- * From the specified cursor position, trim out any whitespace between the current position and the end of the
- * last non-whitespace character.
- *
- * @param pos - current position
- * @return new position.
- */
- protected int trimLeft(int pos) {
- if (pos > end) pos = end;
- while (pos > 0 && pos >= st && (isWhitespace(expr[pos - 1]) || expr[pos - 1] == ';')) pos--;
- return pos;
- }
- /**
- * From the specified cursor position, trim out any whitespace between the current position and beginning of the
- * first non-whitespace character.
- *
- * @param pos -
- * @return -
- */
- protected int trimRight(int pos) {
- while (pos != end && isWhitespace(expr[pos])) pos++;
- return pos;
- }
- /**
- * If the cursor is currently pointing to whitespace, move the cursor forward to the first non-whitespace
- * character, but account for carraige returns in the script (updates parser field: line).
- */
- protected void skipWhitespace() {
- Skip:
- while (cursor != end) {
- switch (expr[cursor]) {
- case '\n':
- line++;
- lastLineStart = cursor;
- case '\r':
- cursor++;
- continue;
- case '/':
- if (cursor + 1 != end) {
- switch (expr[cursor + 1]) {
- case '/':
- cursor++;
- while (cursor != end && expr[cursor] != '\n') {
- cursor++;
- }
- if (cursor != end) {
- cursor++;
- }
- line++;
- lastLineStart = cursor;
- continue;
- case '*':
- int len = end - 1;
- int st = cursor;
- cursor++;
- while (cursor != len && !(expr[cursor] == '*' && expr[cursor + 1] == '/')) {
- cursor++;
- }
- if (cursor != len) {
- cursor += 2;
- }
- for (int i = st; i < cursor; i++) {
- expr[i] = ' ';
- }
- continue;
- default:
- break Skip;
- }
- }
- default:
- if (!isWhitespace(expr[cursor])) break Skip;
- }
- cursor++;
- }
- }
- /**
- * From the current cursor position, capture to the end of the next token junction.
- */
- protected void captureToNextTokenJunction() {
- while (cursor != end) {
- switch (expr[cursor]) {
- case '{':
- case '(':
- return;
- case '/':
- if (expr[cursor + 1] == '*') return;
- case '[':
- cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '[', pCtx) + 1;
- continue;
- default:
- if (isWhitespace(expr[cursor])) {
- return;
- }
- cursor++;
- }
- }
- }
- /**
- * From the current cursor position, trim backward over any whitespace to the first non-whitespace character.
- */
- protected void trimWhitespace() {
- while (cursor != 0 && isWhitespace(expr[cursor - 1])) cursor--;
- }
- /**
- * Set and finesse the expression, trimming an leading or proceeding whitespace.
- *
- * @param expression the expression
- */
- protected void setExpression(String expression) {
- if (expression != null && expression.length() != 0) {
- synchronized (EX_PRECACHE) {
- if ((this.expr = EX_PRECACHE.get(expression)) == null) {
- end = length = (this.expr = expression.toCharArray()).length;
- // trim any whitespace.
- while (start < length && isWhitespace(expr[start])) start++;
-
- while (length != 0 && isWhitespace(this.expr[length - 1])) length--;
- char[] e = new char[length];
- for (int i = 0; i != e.length; i++)
- e[i] = expr[i];
- EX_PRECACHE.put(expression, e);
- }
- else {
- end = length = this.expr.length;
- }
- }
- }
- }
- /**
- * Set and finesse the expression, trimming an leading or proceeding whitespace.
- *
- * @param expression the expression
- */
- protected void setExpression(char[] expression) {
- end = length = (this.expr = expression).length;
- while (start < length && isWhitespace(expr[start])) start++;
- while (length != 0 && isWhitespace(this.expr[length - 1])) length--;
- }
- /**
- * Return the previous non-whitespace character.
- *
- * @return -
- */
- protected char lookToLast() {
- if (cursor == start) return 0;
- int temp = cursor;
- for (; ; ) {
- if (temp == start || !isWhitespace(expr[--temp])) break;
- }
- return expr[temp];
- }
- /**
- * Return the last character (delta -1 of cursor position).
- *
- * @return -
- */
- protected char lookBehind() {
- if (cursor == start) return 0;
- else return expr[cursor - 1];
- }
- /**
- * Return the next character (delta 1 of cursor position).
- *
- * @return -
- */
- protected char lookAhead() {
- if (cursor + 1 != end) {
- return expr[cursor + 1];
- }
- else {
- return 0;
- }
- }
- /**
- * Return the character, forward of the currrent cursor position based on the specified range delta.
- *
- * @param range -
- * @return -
- */
- protected char lookAhead(int range) {
- if ((cursor + range) >= end) return 0;
- else {
- return expr[cursor + range];
- }
- }
- /**
- * Returns true if the next is an identifier or literal.
- *
- * @return true of false
- */
- protected boolean isNextIdentifierOrLiteral() {
- int tmp = cursor;
- if (tmp == end) return false;
- else {
- while (tmp != end && isWhitespace(expr[tmp])) tmp++;
- if (tmp == end) return false;
- char n = expr[tmp];
- return isIdentifierPart(n) || isDigit(n) || n == '\'' || n == '"';
- }
- }
- /**
- * Increment one cursor position, and move cursor to next non-blank part.
- *
- * @return cursor position
- */
- public int incNextNonBlank() {
- cursor++;
- return nextNonBlank();
- }
- /**
- * Move to next cursor position from current cursor position.
- *
- * @return cursor position
- */
- public int nextNonBlank() {
- if ((cursor + 1) >= end) {
- throw new CompileException("unexpected end of statement", expr, st);
- }
- int i = cursor;
- while (i != end && isWhitespace(expr[i])) i++;
- return i;
- }
- /**
- * Expect the next specified character or fail
- *
- * @param c character
- */
- public void expectNextChar_IW(char c) {
- nextNonBlank();
- if (cursor == end) throw new CompileException("unexpected end of statement", expr, st);
- if (expr[cursor] != c)
- throw new CompileException("unexpected character ('" + expr[cursor] + "'); was expecting: " + c, expr, st);
- }
- /**
- * NOTE: This method assumes that the current position of the cursor is at the end of a logical statement, to
- * begin with.
- * <p/>
- * Determines whether or not the logical statement is manually terminated with a statement separator (';').
- *
- * @return -
- */
- protected boolean isStatementNotManuallyTerminated() {
- if (cursor >= end) return false;
- int c = cursor;
- while (c != end && isWhitespace(expr[c])) c++;
- return !(c != end && expr[c] == ';');
- }
- protected ParserContext getParserContext() {
- if (parserContext == null || parserContext.get() == null) {
- newContext();
- }
- return parserContext.get();
- }
- public static ParserContext getCurrentThreadParserContext() {
- return contextControl(GET_OR_CREATE, null, null);
- }
- public static void setCurrentThreadParserContext(ParserContext pCtx) {
- contextControl(SET, pCtx, null);
- }
- /**
- * Create a new ParserContext in the current thread.
- */
- public void newContext() {
- contextControl(SET, new ParserContext(), this);
- }
- /**
- * Create a new ParserContext in the current thread, using the one specified.
- *
- * @param pCtx -
- */
- public void newContext(ParserContext pCtx) {
- contextControl(SET, this.pCtx = pCtx, this);
- }
- /**
- * Remove the current ParserContext from the thread.
- */
- public void removeContext() {
- contextControl(REMOVE, null, this);
- }
- public static ParserContext contextControl(int operation, ParserContext pCtx, AbstractParser parser) {
- synchronized (getRuntime()) {
- if (parserContext == null) parserContext = new ThreadLocal<ParserContext>();
- switch (operation) {
- case SET:
- pCtx.setRootParser(parser);
- parserContext.set(pCtx);
- return pCtx;
- case REMOVE:
- parserContext.set(null);
- return null;
- case GET_OR_CREATE:
- if (parserContext.get() == null) {
- parserContext.set(new ParserContext(parser));
- }
- case GET:
- return parserContext.get();
- }
- }
- return null;
- }
- protected static final int SET = 0;
- protected static final int REMOVE = 1;
- protected static final int GET = 2;
- protected static final int GET_OR_CREATE = 3;
- protected void addFatalError(String message) {
- pCtx.addError(new ErrorDetail(expr, st, true, message));
- }
- protected void addFatalError(String message, int start) {
- pCtx.addError(new ErrorDetail(expr, start, true, message));
- }
- public static final int LEVEL_5_CONTROL_FLOW = 5;
- public static final int LEVEL_4_ASSIGNMENT = 4;
- public static final int LEVEL_3_ITERATION = 3;
- public static final int LEVEL_2_MULTI_STATEMENT = 2;
- public static final int LEVEL_1_BASIC_LANG = 1;
- public static final int LEVEL_0_PROPERTY_ONLY = 0;
- public static void setLanguageLevel(int level) {
- OPERATORS.clear();
- OPERATORS.putAll(loadLanguageFeaturesByLevel(level));
- }
- public static HashMap<String, Integer> loadLanguageFeaturesByLevel(int languageLevel) {
- HashMap<String, Integer> operatorsTable = new HashMap<String, Integer>();
- switch (languageLevel) {
- case 6: // prototype definition
- operatorsTable.put("proto", PROTO);
- case 5: // control flow operations
- operatorsTable.put("if", IF);
- operatorsTable.put("else", ELSE);
- operatorsTable.put("?", TERNARY);
- operatorsTable.put("switch", SWITCH);
- operatorsTable.put("function", FUNCTION);
- operatorsTable.put("def", FUNCTION);
- operatorsTable.put("stacklang", STACKLANG);
- case 4: // assignment
- operatorsTable.put("=", ASSIGN);
- operatorsTable.put("var", UNTYPED_VAR);
- operatorsTable.put("+=", ASSIGN_ADD);
- operatorsTable.put("-=", ASSIGN_SUB);
- operatorsTable.put("/=", ASSIGN_DIV);
- operatorsTable.put("%=", ASSIGN_MOD);
- case 3: // iteration
- operatorsTable.put("foreach", FOREACH);
- operatorsTable.put("while", WHILE);
- operatorsTable.put("until", UNTIL);
- operatorsTable.put("for", FOR);
- operatorsTable.put("do", DO);
- case 2: // multi-statement
- operatorsTable.put("return", RETURN);
- operatorsTable.put(";", END_OF_STMT);
- case 1: // boolean, math ops, projection, assertion, objection creation, block setters, imports
- operatorsTable.put("+", ADD);
- operatorsTable.put("-", SUB);
- operatorsTable.put("*", MULT);
- operatorsTable.put("**", POWER);
- operatorsTable.put("/", DIV);
- operatorsTable.put("%", MOD);
- operatorsTable.put("==", EQUAL);
- operatorsTable.put("!=", NEQUAL);
- operatorsTable.put(">", GTHAN);
- operatorsTable.put(">=", GETHAN);
- operatorsTable.put("<", LTHAN);
- operatorsTable.put("<=", LETHAN);
- operatorsTable.put("&&", AND);
- operatorsTable.put("and", AND);
- operatorsTable.put("||", OR);
- operatorsTable.put("or", CHOR);
- operatorsTable.put("~=", REGEX);
- operatorsTable.put("instanceof", INSTANCEOF);
- operatorsTable.put("is", INSTANCEOF);
- operatorsTable.put("contains", CONTAINS);
- operatorsTable.put("soundslike", SOUNDEX);
- operatorsTable.put("strsim", SIMILARITY);
- operatorsTable.put("convertable_to", CONVERTABLE_TO);
- operatorsTable.put("isdef", ISDEF);
- operatorsTable.put("#", STR_APPEND);
- operatorsTable.put("&", BW_AND);
- operatorsTable.put("|", BW_OR);
- operatorsTable.put("^", BW_XOR);
- operatorsTable.put("<<", BW_SHIFT_LEFT);
- operatorsTable.put("<<<", BW_USHIFT_LEFT);
- operatorsTable.put(">>", BW_SHIFT_RIGHT);
- operatorsTable.put(">>>", BW_USHIFT_RIGHT);
- operatorsTable.put("new", Operator.NEW);
- operatorsTable.put("in", PROJECTION);
- operatorsTable.put("with", WITH);
- operatorsTable.put("assert", ASSERT);
- operatorsTable.put("import", IMPORT);
- operatorsTable.put("import_static", IMPORT_STATIC);
- operatorsTable.put("++", INC);
- operatorsTable.put("--", DEC);
- case 0: // Property access and inline collections
- operatorsTable.put(":", TERNARY_ELSE);
- }
- return operatorsTable;
- }
- /**
- * Remove the current parser context from the thread.
- */
- public static void resetParserContext() {
- contextControl(REMOVE, null, null);
- }
- protected static boolean isArithmeticOperator(int operator) {
- return operator != -1 && operator < 6;
- }
- /**
- * Reduce the current operations on the stack.
- *
- * @param operator the operator
- * @return a stack control code
- */
- protected int arithmeticFunctionReduction(int operator) {
- ASTNode tk;
- int operator2;
- /**
- * If the next token is an operator, we check to see if it has a higher
- * precdence.
- */
- if ((tk = nextToken()) != null) {
- if (isArithmeticOperator(operator2 = tk.getOperator()) && PTABLE[operator2] > PTABLE[operator]) {
- stk.xswap();
- /**
- * The current arith. operator is of higher precedence the last.
- */
- tk = nextToken();
- /**
- * Check to see if we're compiling or executing interpretively. If we're compiling, we really
- * need to stop if this is not a literal.
- */
- if (compileMode && !tk.isLiteral()) {
- splitAccumulator.push(tk, new OperatorNode(operator2, expr, st, pCtx));
- return OP_OVERFLOW;
- }
- dStack.push(operator = operator2, tk.getReducedValue(ctx, ctx, variableFactory));
- while (true) {
- // look ahead again
- if ((tk = nextToken()) != null && (operator2 = tk.getOperator()) != -1
- && operator2 != END_OF_STMT && PTABLE[operator2] > PTABLE[operator]) {
- // if we have back to back operations on the stack, we don't xswap
- if (dStack.isReduceable()) {
- stk.copyx2(dStack);
- }
- /**
- * This operator is of higher precedence, or the same level precedence. push to the RHS.
- */
- dStack.push(operator = operator2, nextToken().getReducedValue(ctx, ctx, variableFactory));
- continue;
- }
- else if (tk != null && operator2 != -1 && operator2 != END_OF_STMT) {
- if (PTABLE[operator2] == PTABLE[operator]) {
- if (!dStack.isEmpty()) dreduce();
- else {
- while (stk.isReduceable()) {
- stk.xswap_op();
- }
- }
- /**
- * This operator is of the same level precedence. push to the RHS.
- */
- dStack.push(operator = operator2, nextToken().getReducedValue(ctx, ctx, variableFactory));
- continue;
- }
- else {
- /**
- * The operator doesn't have higher precedence. Therfore reduce the LHS.
- */
- while (dStack.size() > 1) {
- dreduce();
- }
- operator = tk.getOperator();
- // Reduce the lesser or equal precedence operations.
- while (stk.size() != 1 && stk.peek2() instanceof Integer &&
- ((operator2 = (Integer) stk.peek2()) < PTABLE.length) &&
- PTABLE[operator2] >= PTABLE[operator]) {
- stk.xswap_op();
- }
- }
- }
- else {
- /**
- * There are no more tokens.
- */
- if (dStack.size() > 1) {
- dreduce();
- }
- if (stk.isReduceable()) stk.xswap();
- break;
- }
- if ((tk = nextToken()) != null) {
- switch (operator) {
- case AND: {
- if (!(stk.peekBoolean())) return OP_TERMINATE;
- else {
- splitAccumulator.add(tk);
- return AND;
- }
- }
- case OR: {
- if ((stk.peekBoolean())) return OP_TERMINATE;
- else {
- splitAccumulator.add(tk);
- return OR;
- }
- }
- default:
- stk.push(operator, tk.getReducedValue(ctx, ctx, variableFactory));
- }
- }
- }
- }
- else if (!tk.isOperator()) {
- throw new CompileException("unexpected token: " + tk.getName(), expr, st);
- }
- else {
- reduce();
- splitAccumulator.push(tk);
- }
- }
- // while any values remain on the stack
- // keep XSWAPing and reducing, until there is nothing left.
- if (stk.isReduceable()) {
- while (true) {
- reduce();
- if (stk.isReduceable()) {
- stk.xswap();
- }
- else {
- break;
- }
- }
- }
- return OP_RESET_FRAME;
- }
- private void dreduce() {
- stk.copy2(dStack);
- stk.op();
- }
- /**
- * This method is called when we reach the point where we must subEval a trinary operation in the expression.
- * (ie. val1 op val2). This is not the same as a binary operation, although binary operations would appear
- * to have 3 structures as well. A binary structure (or also a junction in the expression) compares the
- * current state against 2 downrange structures (usually an op and a val).
- */
- protected void reduce() {
- Object v1, v2;
- int operator;
- try {
- switch (operator = (Integer) stk.pop()) {
- case ADD:
- case SUB:
- case DIV:
- case MULT:
- case MOD:
- case EQUAL:
- case NEQUAL:
- case GTHAN:
- case LTHAN:
- case GETHAN:
- case LETHAN:
- case POWER:
- stk.op(operator);
- break;
- case AND:
- v1 = stk.pop();
- stk.push(((Boolean) stk.pop()) && ((Boolean) v1));
- break;
- case OR:
- v1 = stk.pop();
- stk.push(((Boolean) stk.pop()) || ((Boolean) v1));
- break;
- case CHOR:
- v1 = stk.pop();
- if (!isEmpty(v2 = stk.pop()) || !isEmpty(v1)) {
- stk.clear();
- stk.push(!isEmpty(v2) ? v2 : v1);
- return;
- }
- else stk.push(null);
- break;
- case REGEX:
- stk.push(java.util.regex.Pattern.compile(java.lang.String.valueOf(stk.pop()))
- .matcher(java.lang.String.valueOf(stk.pop())).matches());
- break;
- case INSTANCEOF:
- stk.push(((Class) stk.pop()).isInstance(stk.pop()));
- break;
- case CONVERTABLE_TO:
- stk.push(org.mvel2.DataConversion.canConvert(stk.peek2().getClass(), (Class) stk.pop2()));
- break;
- case CONTAINS:
- stk.push(containsCheck(stk.peek2(), stk.pop2()));
- break;
- case SOUNDEX:
- stk.push(soundex(java.lang.String.valueOf(stk.pop()))
- .equals(soundex(java.lang.String.valueOf(stk.pop()))));
- break;
- case SIMILARITY:
- stk.push(similarity(java.lang.String.valueOf(stk.pop()), java.lang.String.valueOf(stk.pop())));
- break;
- default:
- reduceNumeric(operator);
- }
- }
- catch (ClassCastException e) {
- throw new CompileException("syntax error or incompatable types", expr, st, e);
- }
- catch (ArithmeticException e) {
- throw new CompileException("arithmetic error: " + e.getMessage(), expr, st, e);
- }
- catch (Exception e) {
- throw new CompileException("failed to subEval expression", expr, st, e);
- }
- }
- private void reduceNumeric(int operator) {
- Object op1 = stk.peek2();
- Object op2 = stk.pop2();
- if (op1 instanceof Integer) {
- if (op2 instanceof Integer) {
- reduce((Integer) op1, operator, (Integer) op2);
- }
- else {
- reduce((Integer) op1, operator, (Long) op2);
- }
- }
- else {
- if (op2 instanceof Integer) {
- reduce((Long) op1, operator, (Integer) op2);
- }
- else {
- reduce((Long) op1, operator, (Long) op2);
- }
- }
- }
- private void reduce(int op1, int operator, int op2) {
- switch (operator) {
- case BW_AND:
- stk.push(op1 & op2);
- break;
- case BW_OR:
- stk.push(op1 | op2);
- break;
- case BW_XOR:
- stk.push(op1 ^ op2);
- break;
- case BW_SHIFT_LEFT:
- stk.push(op1 << op2);
- break;
- case BW_USHIFT_LEFT:
- int iv2 = op1;
- if (iv2 < 0) iv2 *= -1;
- stk.push(iv2 << op2);
- break;
- case BW_SHIFT_RIGHT:
- stk.push(op1 >> op2);
- break;
- case BW_USHIFT_RIGHT:
- stk.push(op1 >>> op2);
- break;
- }
- }
- private void reduce(int op1, int operator, long op2) {
- switch (operator) {
- case BW_AND:
- stk.push(op1 & op2);
- break;
- case BW_OR:
- stk.push(op1 | op2);
- break;
- case BW_XOR:
- stk.push(op1 ^ op2);
- break;
- case BW_SHIFT_LEFT:
- stk.push(op1 << op2);
- break;
- case BW_USHIFT_LEFT:
- int iv2 = op1;
- if (iv2 < 0) iv2 *= -1;
- stk.push(iv2 << op2);
- break;
- case BW_SHIFT_RIGHT:
- stk.push(op1 >> op2);
- break;
- case BW_USHIFT_RIGHT:
- stk.push(op1 >>> op2);
- break;
- }
- }
- private void reduce(long op1, int operator, int op2) {
- switch (operator) {
- case BW_AND:
- stk.push(op1 & op2);
- break;
- case BW_OR:
- stk.push(op1 | op2);
- break;
- case BW_XOR:
- stk.push(op1 ^ op2);
- break;
- case BW_SHIFT_LEFT:
- stk.push(op1 << op2);
- break;
- case BW_USHIFT_LEFT:
- long iv2 = op1;
- if (iv2 < 0) iv2 *= -1;
- stk.push(iv2 << op2);
- break;
- case BW_SHIFT_RIGHT:
- stk.push(op1 >> op2);
- break;
- case BW_USHIFT_RIGHT:
- stk.push(op1 >>> op2);
- break;
- }
- }
- private void reduce(long op1, int operator, long op2) {
- switch (operator) {
- case BW_AND:
- stk.push(op1 & op2);
- break;
- case BW_OR:
- stk.push(op1 | op2);
- break;
- case BW_XOR:
- stk.push(op1 ^ op2);
- break;
- case BW_SHIFT_LEFT:
- stk.push(op1 << op2);
- break;
- case BW_USHIFT_LEFT:
- long iv2 = op1;
- if (iv2 < 0) iv2 *= -1;
- stk.push(iv2 << op2);
- break;
- case BW_SHIFT_RIGHT:
- stk.push(op1 >> op2);
- break;
- case BW_USHIFT_RIGHT:
- stk.push(op1 >>> op2);
- break;
- }
- }
- public int getCursor() {
- return cursor;
- }
- public char[] getExpression() {
- return expr;
- }
- private static int asInt(final Object o) {
- return (Integer) o;
- }
- public void setPCtx(ParserContext pCtx) {
- this.debugSymbols = (this.pCtx = pCtx).isDebugSymbols();
- }
- }