/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
Large files files are truncated, but you can click here to view the full file
- /**
- * 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)…
Large files files are truncated, but you can click here to view the full file