AbstractParser.java :  » Scripting » mvel » org » mvel » Java Open Source

Java Open Source » Scripting » mvel 
mvel » org » mvel » AbstractParser.java
package org.mvel;

import static org.mvel.Operator.*;
import org.mvel.ast.*;
import static org.mvel.util.ArrayTools.findFirst;
import org.mvel.util.ExecutionStack;
import org.mvel.util.ParseTools;
import static org.mvel.util.ParseTools.*;
import static org.mvel.util.PropertyTools.isDigit;
import static org.mvel.util.PropertyTools.isIdentifierPart;
import org.mvel.util.ThisLiteral;

import java.io.Serializable;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.lang.Character.isWhitespace;
import static java.lang.Float.parseFloat;
import static java.lang.System.arraycopy;
import static java.lang.System.getProperty;
import static java.util.Collections.synchronizedMap;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * @author Christopher Brock
 */
public class AbstractParser implements Serializable {
    protected char[] expr;
    protected int cursor;
    protected int length;
    protected int fields;

    protected boolean greedy = true;
    protected boolean lastWasIdentifier = false;
    protected boolean lastWasLineLabel = false;
    protected boolean lastWasComment = false;

    protected boolean debugSymbols = false;

    private int line = 1;

    protected ASTNode lastNode;

    private static Map<String, char[]> EX_PRECACHE;

    public static final Map<String, Object> LITERALS =
            new HashMap<String, Object>(35, 0.4f);

    public static final Map<String, Integer> OPERATORS =
            new HashMap<String, Integer>(25 * 2, 0.4f);

    protected ExecutionStack splitAccumulator = new ExecutionStack();

    protected static ThreadLocal<ParserContext> parserContext;

    static {
        configureFactory();

        /**
         * Setup the basic literals
         */
        AbstractParser.LITERALS.put("true", TRUE);
        AbstractParser.LITERALS.put("false", FALSE);

        AbstractParser.LITERALS.put("null", null);
        AbstractParser.LITERALS.put("nil", null);

        AbstractParser.LITERALS.put("empty", BlankLiteral.INSTANCE);

        AbstractParser.LITERALS.put("this", ThisLiteral.class);

        /**
         * Add System and all the class wrappers from the JCL.
         */
        LITERALS.put("System", System.class);

        LITERALS.put("String", String.class);

        LITERALS.put("Integer", Integer.class);
        LITERALS.put("int", Integer.class);

        LITERALS.put("Long", Long.class);
        LITERALS.put("long", Long.class);

        LITERALS.put("Boolean", Boolean.class);
        LITERALS.put("boolean", Boolean.class);

        LITERALS.put("Short", Short.class);
        LITERALS.put("short", Short.class);

        LITERALS.put("Character", Character.class);
        LITERALS.put("char", Character.class);

        LITERALS.put("Double", Double.class);
        LITERALS.put("double", double.class);

        LITERALS.put("Float", Float.class);
        LITERALS.put("float", float.class);

        LITERALS.put("Math", Math.class);
        LITERALS.put("Void", Void.class);
        LITERALS.put("Object", Object.class);

        LITERALS.put("Class", Class.class);
        LITERALS.put("ClassLoader", ClassLoader.class);
        LITERALS.put("Runtime", Runtime.class);
        LITERALS.put("Thread", Thread.class);
        LITERALS.put("Compiler", Compiler.class);
        LITERALS.put("StringBuffer", StringBuffer.class);
        LITERALS.put("ThreadLocal", ThreadLocal.class);
        LITERALS.put("SecurityManager", SecurityManager.class);
        LITERALS.put("StrictMath", StrictMath.class);

        LITERALS.put("Array", java.lang.reflect.Array.class);

        if (parseFloat(getProperty("java.version").substring(0, 2)) >= 1.5) {
            try {
                LITERALS.put("StringBuilder", Class.forName("java.lang.StringBuilder"));
            }
            catch (Exception e) {
                throw new RuntimeException("cannot resolve a built-in literal", e);
            }
        }

        _loadLanguageFeaturesByLevel(5);
    }


    static void configureFactory() {
        if (MVEL.THREAD_SAFE) {
            EX_PRECACHE = synchronizedMap(new WeakHashMap<String, char[]>(10));
        }
        else {
            EX_PRECACHE = new WeakHashMap<String, char[]>(10);
        }
    }

    /**
     * Retrieve the next token in the expression.
     *
     * @return -
     */
    protected ASTNode nextToken() {
        /**
         * If the cursor is at the end of the expression, we have nothing more to do:
         * return null.
         */
        if (cursor >= length) {
            return null;
        }
        else if (!splitAccumulator.isEmpty()) {
            return lastNode = (ASTNode) splitAccumulator.pop();
        }

        int brace, start = cursor;
        

        /**
         * 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.
         */
        fields = fields & (ASTNode.INLINE_COLLECTION | ASTNode.COMPILE_IMMEDIATE);

        boolean capture = false, union = false;


        if (debugSymbols) {
            if (!lastWasLineLabel) {
                if (getParserContext().getSourceFile() == null) {
                    throw new CompileException("unable to produce debugging symbols: source name must be provided.");
                }

                ParserContext pCtx = getParserContext();

                line = pCtx.getLineCount();

                if (expr[cursor] == '\n' || expr[cursor] == '\r') {
                    skipWhitespaceWithLineAccounting();
                }

                if (lastWasComment) {
                    line++;
                    lastWasComment = false;
                }

                pCtx.setLineCount(line);

                if (!pCtx.isKnownLine(pCtx.getSourceFile(), line)) {
                    lastWasLineLabel = true;

                    pCtx.setLineAndOffset(line, cursor);
                    pCtx.addKnownLine(pCtx.getSourceFile(), line);

                    LineLabel ll = new LineLabel(pCtx.getSourceFile(), line);
                    if (pCtx.getFirstLineLabel() == null) pCtx.setFirstLineLabel(ll);

                    return lastNode = ll;
                }
            }
            else {
                lastWasComment = lastWasLineLabel = false;
            }
        }

        /**
         * Skip any whitespace currently under the starting point.
         */
        while (start < length && isWhitespace(expr[start])) start++;

        /**
         * 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.
         */
        for (cursor = start; cursor < length;) {
            if (isIdentifierPart(expr[cursor])) {
                /**
                 * If the current character under the cursor is a valid
                 * part of an identifier, we keep capturing.
                 */
                capture = true;
                cursor++;
            }
            else if (capture) {
                String t;
                if (OPERATORS.containsKey(t = new String(expr, start, cursor - start))) {
                    switch (OPERATORS.get(t)) {
                        case NEW:
                            start = cursor + 1;
                            captureToEOT();
                            return new NewObjectNode(subArray(start, cursor), fields);

                        case ASSERT:
                            start = cursor + 1;
                            captureToEOS();
                            return new AssertNode(subArray(start, cursor--), fields);

                        case RETURN:
                            start = cursor + 1;
                            captureToEOS();
                            return new ReturnNode(subArray(start, cursor), fields);

                        case IF:
                            fields |= ASTNode.BLOCK_IF;
                            return captureCodeBlock();

                        case FOREACH:
                            fields |= ASTNode.BLOCK_FOREACH;
                            return captureCodeBlock();

                        case WITH:
                            fields |= ASTNode.BLOCK_WITH;
                            return captureCodeBlock();

                        case IMPORT:
                            start = cursor + 1;
                            captureToEOS();
                            ImportNode importNode = new ImportNode(subArray(start, cursor--), fields);
                            getParserContext().addImport(getSimpleClassName(importNode.getImportClass()), importNode.getImportClass());
                            return importNode;

                        case IMPORT_STATIC:
                            start = cursor + 1;
                            captureToEOS();
                            return new StaticImportNode(subArray(start, cursor--), fields);
                    }
                }

                /**
                 * If we *were* capturing a token, and we just hit a non-identifier
                 * character, we stop and figure out what to do.
                 */
                skipWhitespace();

                if (expr[cursor] == '(') {
                    fields |= ASTNode.METHOD;

                    /**
                     * If the current token is a method call or a constructor, we
                     * simply capture the entire parenthesized range and allow
                     * reduction to be dealt with through sub-parsing the property.
                     */
                    cursor++;
                    for (brace = 1; cursor < length && brace > 0;) {
                        switch (expr[cursor++]) {
                            case'(':
                                brace++;
                                break;
                            case')':
                                brace--;
                                break;
                            case'\'':
                                cursor = captureStringLiteral('\'', expr, cursor, length) + 1;
                                break;

                            case'"':
                                cursor = captureStringLiteral('"', expr, cursor, length) + 1;
                                break;

                        }
                    }

                    /**
                     * If the brace counter is greater than 0, we know we have
                     * unbalanced braces in the expression.  So we throw a
                     * optimize error now.
                     */
                    if (brace > 0)
                        throw new CompileException("unbalanced braces in expression: (" + brace + "):", expr, cursor);
                }

                /**
                 * If we encounter any of the following cases, we are still dealing with
                 * a contiguous token.
                 */
                String name;
                if (cursor < length) {
                    switch (expr[cursor]) {
                        case'+':
                            switch (lookAhead(1)) {
                                case'+':
                                    ASTNode n = new PostFixIncNode(subArray(start, cursor), fields);
                                    cursor += 2;
                                    return n;

                                case'=':
                                    name = new String(expr, start, trimLeft(cursor));
                                    start = cursor += 2;
                                    captureToEOS();

                                    if (union) {
                                        return new DeepAssignmentNode(subArray(start, cursor), fields, Operator.ADD, t);
                                    }
                                    else {
                                        return new AssignmentNode(subArray(start, cursor), fields, Operator.ADD, name);
                                    }
                            }

                            break;

                        case'-':
                            switch (lookAhead(1)) {
                                case'-':
                                    ASTNode n = new PostFixDecNode(subArray(start, cursor), fields);
                                    cursor += 2;
                                    return n;

                                case'=':
                                    name = new String(expr, start, trimLeft(cursor));
                                    start = cursor += 2;
                                    captureToEOS();
                                    return new AssignSub(subArray(start, cursor), fields, name);
                            }
                            break;

                        case'*':
                            if (isAt('=', 1)) {
                                name = new String(expr, start, trimLeft(cursor));
                                start = cursor += 2;
                                captureToEOS();
                                return new AssignMult(subArray(start, cursor), fields, name);
                            }
                            break;

                        case'/':
                            if (isAt('=', 1)) {
                                name = new String(expr, start, trimLeft(cursor));
                                start = cursor += 2;
                                captureToEOS();
                                return new AssignDiv(subArray(start, cursor), fields, name);
                            }
                            break;

                        case']':
                        case'[':
                            balancedCapture('[');
                            cursor++;
                            continue;
                        case'.':
                            union = true;
                            cursor++;
                            continue;

                        case'~':
                            if (isAt('=', 1)) {
                                char[] stmt = subArray(start, trimLeft(cursor));
                                start = cursor += 2;
                                captureToEOT();
                                return new RegExMatch(stmt, fields, subArray(start, cursor));
                            }
                            break;

                        case'=':
                            if (isAt('+', 1)) {
                                name = new String(expr, start, trimLeft(cursor));
                                start = cursor += 2;
                                captureToEOS();
                                return new AssignAdd(subArray(start, cursor), fields, name);
                            }

                            if (greedy && !isAt('=', 1)) {
                                cursor++;

                                fields |= ASTNode.ASSIGN;

                                skipWhitespace();
                                captureToEOS();

                                if (union) {
                                    return new DeepAssignmentNode(subArray(start, cursor), fields);
                                }
                                else if (lastWasIdentifier) {

                                    /**
                                     * Check for typing information.
                                     */
                                    if (lastNode.getLiteralValue() instanceof String) {
                                        if (getParserContext().hasImport((String) lastNode.getLiteralValue())) {
                                            lastNode.setLiteralValue(getParserContext().getImport((String) lastNode.getLiteralValue()));
                                            lastNode.setAsLiteral();
                                        }
                                        else {
                                            try {
                                                /**
                                                 *  take a stab in the dark and try and load the class
                                                 */
                                                lastNode.setLiteralValue(createClass((String) lastNode.getLiteralValue()));
                                                lastNode.setAsLiteral();
                                            }
                                            catch (ClassNotFoundException e) {
                                                /**
                                                 * Just fail through.
                                                 */
                                            }
                                        }
                                    }

                                    if (lastNode.isLiteral() && lastNode.getLiteralValue() instanceof Class) {
                                        lastNode.setDiscard(true);

                                        captureToEOS();
                                        return new TypedVarNode(subArray(start, cursor), fields, (Class)
                                                lastNode.getLiteralValue());
                                    }

                                    throw new ParseException("unknown class: " + lastNode.getLiteralValue());
                                }
                                else {
                                    return new AssignmentNode(subArray(start, cursor), fields);
                                }
                            }
                    }
                }

                /**
                 * Produce the token.
                 */
                trimWhitespace();

                if (parserContext != null && parserContext.get() != null && parserContext.get().hasImports()) {
                    char[] _subset = subset(expr, start, cursor - start);
                    int offset;

                    if ((offset = findFirst('.', _subset)) != -1) {
                        String iStr = new String(_subset, 0, offset);
                        if ("this".equals(iStr)) {
                            lastWasIdentifier = true;
                            return lastNode = new ThisValDeepPropertyNode(subset(_subset, offset + 1, _subset.length - offset - 1), fields);
                        }
                        else if (getParserContext().hasImport(iStr)) {
                            lastWasIdentifier = true;
                            return lastNode = new LiteralDeepPropertyNode(subset(_subset, offset + 1, _subset.length - offset - 1), fields, getParserContext().getImport(iStr));
                        }
                    }
                    else {
                        ASTNode node = new ASTNode(_subset, 0, _subset.length, fields);
                        lastWasIdentifier = node.isIdentifier();
                        return lastNode = node;
                    }
                }

                //   return createPropertyToken(expr, start, cursor, fields);

                lastWasIdentifier = true;
                lastNode = createPropertyToken(start, cursor);
                return lastNode;
            }
            else
                switch (expr[cursor]) {
                    case'@': {
                        start++;
                        captureToEOT();

                        String interceptorName = new String(expr, start, cursor - start);

                        if (getParserContext().getInterceptors() == null || !getParserContext().getInterceptors().
                                containsKey(interceptorName)) {
                            throw new CompileException("reference to undefined interceptor: " + interceptorName, expr, cursor);
                        }

                        return new InterceptorWrapper(getParserContext().getInterceptors().get(interceptorName), nextToken());
                    }

                    case'=':
                        return createToken(expr, start, (cursor += 2), fields);

                    case'-':
                        if (isAt('-', 1)) {
                            start = cursor += 2;
                            captureToEOT();
                            return new PreFixDecNode(subArray(start, cursor), fields);
                        }
                        else if ((cursor > 0 && !isWhitespace(lookBehind(1))) || !isDigit(lookAhead(1))) {
                            return createToken(expr, start, cursor++ + 1, fields);
                        }
                        else if ((cursor - 1) < 0 || (!isDigit(lookBehind(1))) && isDigit(lookAhead(1))) {
                            cursor++;
                            break;
                        }

                    case'+':
                        if (isAt('+', 1)) {
                            start = cursor += 2;
                            captureToEOT();
                            return new PreFixIncNode(subArray(start, cursor), fields);
                        }
                        return createToken(expr, start, cursor++ + 1, fields);

                    case'*':
                        if (isAt('*', 1)) {
                            cursor++;
                        }
                        return createToken(expr, start, cursor++ + 1, fields);

                    case';':
                        cursor++;
                        lastWasIdentifier = false;
                        return lastNode = new EndOfStatement();

                    case'#':
                    case'/':
                        if (isAt(expr[cursor], 1)) {
                            /**
                             * Handle single line comments.
                             */
                            while (cursor < length && expr[cursor] != '\n') cursor++;

                            if (debugSymbols) {
                                line = getParserContext().getLineCount();

                                skipWhitespaceWithLineAccounting();

                                if (lastNode instanceof LineLabel) {
                                    getParserContext().getFirstLineLabel().setLineNumber(line);
                                }

                                lastWasComment = true;

                                getParserContext().setLineCount(line);
                            }
                            else if (cursor < length) {
                                skipWhitespace();
                            }

                            if ((start = cursor) >= length) return null;

                            continue;
                        }
                        else if (expr[cursor] == '/' && isAt('*', 1)) {
                            /**
                             * Handle multi-line comments.
                             */
                            int len = length - 1;

                            /**
                             * This probably seems highly redundant, but sub-compilations within the same
                             * source will spawn a new compiler, and we need to sync this with the
                             * parser context;
                             */
                            if (debugSymbols) {
                                line = getParserContext().getLineCount();
                            }

                            while (true) {
                                cursor++;
                                /**
                                 * Since multi-line comments may cross lines, we must keep track of any line-break
                                 * we encounter.
                                 */
//                                if (debugSymbols && expr[cursor] == '\n') {
//                                    line++;
//                                }

                                if (debugSymbols && (expr[cursor] == '\n' || expr[cursor] == '\r')) {
                                    skipWhitespaceWithLineAccounting();
                                }

                                if (cursor == len) {
                                    throw new CompileException("unterminated block comment", expr, cursor);
                                }
                                if (expr[cursor] == '*' && isAt('/', 1)) {
                                    if ((cursor += 2) >= length) return null;
                                    skipWhitespace();
                                    start = cursor;
                                    break;
                                }
                            }

                            if (debugSymbols) {
                                getParserContext().setLineCount(line);
                                if (lastNode instanceof LineLabel) {
                                    getParserContext().getFirstLineLabel().setLineNumber(line);
                                }
                                lastWasComment = true;
                            }

                            continue;
                        }

                    case'?':
                    case':':
                    case'^':
                    case'%': {
                        return createToken(expr, start, cursor++ + 1, fields);
                    }

                    case'(': {
                        cursor++;

                        boolean singleToken = true;
                        boolean lastWS = false;

                        skipWhitespace();
                        for (brace = 1; cursor < length && brace > 0; cursor++) {
                            switch (expr[cursor]) {
                                case'(':
                                    brace++;
                                    break;
                                case')':
                                    brace--;
                                    break;
                                case'\'':
                                    cursor = captureStringLiteral('\'', expr, cursor, length);
                                    break;
                                case'"':
                                    cursor = captureStringLiteral('\'', expr, cursor, length);
                                    break;

                                case'i':
                                    if (isAt('n', 1) && isWhitespace(lookAhead(2))) {
                                        fields |= ASTNode.FOLD;
                                        for (int level = brace; cursor < length; cursor++) {
                                            switch (expr[cursor]) {
                                                case'(':
                                                    brace++;
                                                    break;
                                                case')':
                                                    if (--brace < level) {
                                                        if (lookAhead(1) == '.') {
                                                            ASTNode node = createToken(expr, trimRight(start), (start = cursor++), ASTNode.FOLD);
                                                            captureToEOT();
                                                            return new Union(expr, trimRight(start + 2), cursor, fields, node);
                                                        }
                                                        else {
                                                            return createToken(expr, trimRight(start), cursor++, ASTNode.FOLD);
                                                        }
                                                    }
                                                    break;
                                                case'\'':
                                                    cursor = captureStringLiteral('\'', expr, cursor, length);
                                                    break;
                                                case'"':
                                                    cursor = captureStringLiteral('\'', expr, cursor, length);
                                                    break;
                                            }
                                        }


                                    }
                                    break;
                                default:
                                    /**
                                     * Check to see if we should disqualify this current token as a potential
                                     * type-cast candidate.
                                     */
                                    if (lastWS || !isIdentifierPart(expr[cursor])) {
                                        singleToken = false;
                                    }
                                    else if (isWhitespace(expr[cursor])) {
                                        lastWS = true;
                                        skipWhitespace();
                                        cursor--;
                                    }
                            }
                        }

                        if (brace > 0) {
                            throw new CompileException("unbalanced braces in expression: (" + brace + "):", expr, cursor);
                        }

                        char[] _subset = null;
                        if (singleToken) {
                            String tokenStr = new String(_subset = subset(expr, trimRight(start + 1), trimLeft(cursor - 1) - (start + 1)));

                            if (getParserContext().hasImport(tokenStr)) {
                                start = cursor;
                                captureToEOS();
                                return new TypeCast(expr, start, cursor, fields, getParserContext().getImport(tokenStr));
                            }
//                            else if (LITERALS.containsKey(tokenStr)) {
//                                start = cursor;
//                                captureToEOS();
//                                return new TypeCast(expr, start, cursor, fields, (Class) LITERALS.get(tokenStr));
//                            }
                            else {
                                try {
                                    /**
                                     *
                                     *  take a stab in the dark and try and load the class
                                     */
                                    int _start = cursor;
                                    captureToEOS();
                                    return new TypeCast(expr, _start, cursor, fields, createClass(tokenStr));

                                }
                                catch (ClassNotFoundException e) {
                                    /**
                                     * Just fail through.
                                     */
                                }
                            }
                        }

                        if (_subset != null) {
                            return handleUnion(new Substatement(_subset, fields));
                        }
                        else {
                            return handleUnion(new Substatement(subset(expr, trimRight(start + 1), trimLeft(cursor - 1) - (start + 1)), fields));
                        }
                    }

                    case'}':
                    case']':
                    case')': {
                        throw new ParseException("unbalanced braces", expr, cursor);
                    }

                    case'>': {
                        if (expr[cursor + 1] == '>') {
                            if (expr[cursor += 2] == '>') cursor++;
                            return createToken(expr, start, cursor, fields);
                        }
                        else if (expr[cursor + 1] == '=') {
                            return createToken(expr, start, cursor += 2, fields);
                        }
                        else {
                            return createToken(expr, start, ++cursor, fields);
                        }
                    }

                    case'<': {
                        if (expr[++cursor] == '<') {
                            if (expr[++cursor] == '<') cursor++;
                            return createToken(expr, start, cursor, fields);
                        }
                        else if (expr[cursor] == '=') {
                            return createToken(expr, start, ++cursor, fields);
                        }
                        else {
                            return createToken(expr, start, cursor, fields);
                        }
                    }

                    case'\'':
                        cursor = captureStringLiteral('\'', expr, cursor, length);
                        return new LiteralNode(handleStringEscapes(subset(expr, start + 1, cursor++ - start - 1)), String.class);


                    case'"':
                        cursor = captureStringLiteral('"', expr, cursor, length);
                        return new LiteralNode(handleStringEscapes(subset(expr, start + 1, cursor++ - start - 1)), String.class);


                    case'&': {
                        if (expr[cursor++ + 1] == '&') {
                            return createToken(expr, start, ++cursor, fields);
                        }
                        else {
                            return createToken(expr, start, cursor, fields);
                        }
                    }

                    case'|': {
                        if (expr[cursor++ + 1] == '|') {
                            return createToken(expr, start, ++cursor, fields);
                        }
                        else {
                            return createToken(expr, start, cursor, fields);
                        }
                    }

                    case'~':
                        if ((cursor - 1 < 0 || !isIdentifierPart(lookBehind(1)))
                                && isDigit(expr[cursor + 1])) {

                            fields |= ASTNode.INVERT;
                            start++;
                            cursor++;
                            break;
                        }
                        else if (expr[cursor + 1] == '(') {
                            fields |= ASTNode.INVERT;
                            start = ++cursor;
                            continue;
                        }
                        else {
                            if (expr[cursor + 1] == '=') cursor++;
                            return createToken(expr, start, ++cursor, fields);
                        }

                    case'!': {
                        if (isIdentifierPart(expr[++cursor]) || expr[cursor] == '(') {
                            start = cursor;
                            fields |= ASTNode.NEGATION;
                            continue;
                        }
                        else if (expr[cursor] != '=')
                            throw new CompileException("unexpected operator '!'", expr, cursor, null);
                        else {
                            return createToken(expr, start, ++cursor, fields);
                        }
                    }

                    case'[':
                    case'{':
                        if (balancedCapture(expr[cursor]) == -1) {
                            if (cursor >= length) cursor--;
                            throw new CompileException("unbalanced brace: in inline map/list/array creation", expr, cursor);
                        }

                        if (lookAhead(1) == '.') {
                            InlineCollectionNode n = new InlineCollectionNode(expr, start, start = ++cursor, fields);
                            captureToEOT();
                            return new Union(expr, start + 1, cursor, fields, n);
                        }
                        else {
                            return new InlineCollectionNode(expr, start, ++cursor, fields);
                        }

                    default:
                        cursor++;
                }
        }

        return createPropertyToken(start, cursor);
    }

    protected ASTNode handleUnion(ASTNode node) {
        if (cursor < length) {
            skipWhitespace();
            if (expr[cursor] == '.') {
                int union = cursor + 1;
                captureToEOS();
                return new Union(expr, union, cursor, fields, node);
            }
        }
        return node;
    }


    protected int balancedCapture(char type) {
        int depth = 1;
        char term = type;
        switch (type) {
            case'[':
                term = ']';
                break;
            case'{':
                term = '}';
                break;
            case'(':
                term = ')';
                break;
        }

        for (cursor++; cursor < length; cursor++) {
            if (expr[cursor] == type) {
                depth++;
            }
            else if (expr[cursor] == term && --depth == 0) {
                return cursor;
            }
        }

        return -1;
    }

    /**
     * Most of this method should be self-explanatory.
     *
     * @param expr   -
     * @param start  -
     * @param end    -
     * @param fields -
     * @return -
     */
    private ASTNode createToken(final char[] expr, final int start, final int end, int fields) {
        ASTNode tk = new ASTNode(expr, start, end, fields);
        lastWasIdentifier = tk.isIdentifier();
        return lastNode = tk;
    }

    private char[] subArray(final int start, final int end) {
        char[] newA = new char[end - start];
        arraycopy(expr, start, newA, 0, newA.length);
        return newA;
    }

    private ASTNode createPropertyToken(int start, int end) {
        return new PropertyASTNode(expr, start, end, fields);
    }

    private ASTNode createBlockToken(final int condStart,
                                     final int condEnd, final int blockStart, final int blockEnd) {

        lastWasIdentifier = false;

        cursor++;

        if (!isStatementManuallyTerminated()) {
            splitAccumulator.add(new EndOfStatement());
        }

        if (isFlag(ASTNode.BLOCK_IF)) {
            return new IfNode(subArray(condStart, condEnd), subArray(blockStart, blockEnd), fields);
        }
        else if (isFlag(ASTNode.BLOCK_FOREACH)) {
            return new ForEachNode(subArray(condStart, condEnd), subArray(blockStart, blockEnd), fields);
        }
        // DONT'T REMOVE THIS COMMENT!
        //       else if (isFlag(ASTNode.BLOCK_WITH)) {
        else {
            return new WithNode(subArray(condStart, condEnd), subArray(blockStart, blockEnd), fields);
        }
    }

    private ASTNode captureCodeBlock() {
        boolean cond = true;

        ASTNode first = null;
        ASTNode tk = null;

        if (isFlag(ASTNode.BLOCK_IF)) {
            do {
                if (tk != null) {
                    skipToNextTokenJunction();
                    skipWhitespace();

                    cond = expr[cursor] != '{' && expr[cursor] == 'i' && expr[++cursor] == 'f'
                            && (isWhitespace(expr[++cursor]) || expr[cursor] == '(');
                }

                if (((IfNode) (tk = _captureBlock(tk, expr, cond))).getElseBlock() != null) {
                    cursor++;
                    return first;
                }

                if (first == null) first = tk;

                if (cursor < length && expr[cursor] != ';') {
                    cursor++;
                }
            }
            while (blockContinues());
        }
        else if (isFlag(ASTNode.BLOCK_FOREACH) || isFlag(ASTNode.BLOCK_WITH)) {
            skipToNextTokenJunction();
            skipWhitespace();
            return _captureBlock(null, expr, true);
        }

        return first;
    }

    private ASTNode _captureBlock(ASTNode node, final char[] expr, boolean cond) {
        skipWhitespace();
        int startCond = 0;
        int endCond = 0;

        if (cond) {
            startCond = ++cursor;
            endCond = balancedCapture('(');
            cursor++;
        }

        int blockStart;
        int blockEnd;

        skipWhitespace();

        if (expr[cursor] == '{') {
            blockStart = cursor;
            if ((blockEnd = balancedCapture('{')) == -1) {
                throw new CompileException("unbalanced braces { }", expr, cursor);
            }
        }
        else {
            blockStart = cursor - 1;
            captureToEOLorOF();
            blockEnd = cursor + 1;
        }

        if (isFlag(ASTNode.BLOCK_IF)) {
            IfNode ifNode = (IfNode) node;

            if (node != null) {
                if (!cond) {
                    ifNode.setElseBlock(subArray(trimRight(blockStart + 1), trimLeft(blockEnd - 1)));
                    return node;
                }
                else {
                    IfNode tk = (IfNode) createBlockToken(startCond, endCond, trimRight(blockStart + 1),
                            trimLeft(blockEnd));

                    ifNode.setElseIf(tk);

                    return tk;
                }
            }
            else {
                return createBlockToken(startCond, endCond, blockStart + 1,
                        blockEnd);
            }
        }
        // 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));
        }
    }

    protected boolean blockContinues() {
        if ((cursor + 4) < length) {
            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;
    }

    protected void captureToEOS() {
        while (cursor < length && expr[cursor] != ';') {
            cursor++;
        }
    }

    protected void captureToEOLorOF() {
        while (cursor < length && (expr[cursor] != '\n' && expr[cursor] != '\r' && expr[cursor] != ';')) {
            cursor++;
        }
    }


    protected void captureToEOT() {
        skipWhitespace();
        while (++cursor < length) {
            switch (expr[cursor]) {
                case'(':
                case'[':
                case'{':
                    if ((cursor = ParseTools.balancedCapture(expr, cursor, expr[cursor])) == -1) {
                        throw new CompileException("unbalanced braces", expr, cursor);
                    }
                    break;

                case'&':
                case'|':
                case';':
                    return;

                case'.':
                    skipWhitespace();
                    break;

                default:
                    if (isWhitespace(expr[cursor])) {
                        skipWhitespace();

                        if (expr[cursor] == '.') {
                            if (cursor < length) cursor++;
                            skipWhitespace();
                            break;
                        }
                        else {
                            trimWhitespace();
                            return;
                        }
                    }
            }
        }
    }

    protected int trimLeft(int pos) {
        while (pos > 0 && isWhitespace(expr[pos - 1])) pos--;
        return pos;
    }

    protected int trimRight(int pos) {
        while (pos < length && isWhitespace(expr[pos])) pos++;
        return pos;
    }

    protected void skipWhitespace() {
        while (isWhitespace(expr[cursor])) cursor++;
    }

    protected void skipWhitespaceWithLineAccounting() {
        while (cursor < length && isWhitespace(expr[cursor])) {
            //    if (expr[cursor] == '\n') line++;
            //     line += captureLineBreaks();

            switch (expr[cursor]) {
                case'\r':
                    cursor++;
                    continue;
                case'\n':
                    cursor++;
                    line++;
                    continue;
            }

            cursor++;
        }
    }

    protected void skipToNextTokenJunction() {
        while (cursor < length) {
            switch (expr[cursor]) {
                case'{':
                case'(':
                    return;
                default:
                    if (isWhitespace(expr[cursor])) return;
                    cursor++;
            }
        }
    }

    protected void trimWhitespace() {
        while (cursor > 0 && isWhitespace(expr[cursor - 1])) cursor--;
    }

    protected ASTNode captureTokenToEOS() {
        int start = cursor;
        captureToEOS();
        return new ASTNode(expr, start, cursor, 0);
    }

    protected void setExpression(String expression) {
        if (expression != null && !"".equals(expression)) {
            if (!EX_PRECACHE.containsKey(expression)) {
                length = (this.expr = expression.toCharArray()).length;

                // trim any whitespace.
                while (isWhitespace(this.expr[length - 1])) length--;

                char[] e = new char[length];
                arraycopy(this.expr, 0, e, 0, length);

                EX_PRECACHE.put(expression, e);
            }
            else {
                length = (expr = EX_PRECACHE.get(expression)).length;
            }
        }
    }

    protected void setExpression(char[] expression) {
        length = (this.expr = expression).length;
        while (length != 0 && isWhitespace(this.expr[length - 1])) length--;
    }

    private boolean isFlag(int bit) {
        return (fields & bit) != 0;
    }

    public static boolean isReservedWord(String name) {
        return LITERALS.containsKey(name) || OPERATORS.containsKey(name);
    }

    protected char lookBehind(int range) {
        if ((cursor - range) <= 0) return 0;
        else {
            return expr[cursor - range];
        }
    }

    protected char lookAhead(int range) {
        if ((cursor + range) >= length) return 0;
        else {
            return expr[cursor + range];
        }
    }

    protected boolean isStatementManuallyTerminated() {
        int c = cursor;
        while (c < length && isWhitespace(expr[c])) c++;
        return (c < length && expr[c] == ';');
    }

    protected boolean isRemain(int range) {
        return (cursor + range) < length;
    }

    protected boolean isAt(char c, int range) {
        return lookAhead(range) == 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);
    }

    protected void newContext() {
        contextControl(SET, new ParserContext(), this);
    }

    protected void newContext(ParserContext pCtx) {
        contextControl(SET, pCtx, this);
    }

    protected void removeContext() {
        contextControl(REMOVE, null, this);
    }

    protected static ParserContext contextControl(int operation, ParserContext pCtx, AbstractParser parser) {
        synchronized (Runtime.getRuntime()) {
            if (parserContext == null) parserContext = new ThreadLocal<ParserContext>();

            switch (operation) {
                case SET:
                    pCtx.setRootParser(parser);
                    parserContext.set(pCtx);
                    return null;

                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;

    public boolean isDebugSymbols() {
        return debugSymbols;
    }

    public void setDebugSymbols(boolean debugSymbols) {
        this.debugSymbols = debugSymbols;
    }

    protected static String getCurrentSourceFileName() {
        if (parserContext != null && parserContext.get() != null) {
            return parserContext.get().getSourceFile();
        }
        return null;
    }

    protected void addFatalError(String message) {
        getParserContext().addError(new ErrorDetail(getParserContext().getLineCount(), cursor - getParserContext().getLineOffset(), true, message));
    }

    protected void addFatalError(String message, int row, int cols) {
        getParserContext().addError(new ErrorDetail(row, cols, true, message));
    }

    protected void addWarning(String message) {
        getParserContext().addError(new ErrorDetail(message, false));
    }

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

    private static void _loadLanguageFeaturesByLevel(int languageLevel) {
        switch (languageLevel) {
            case 5:  // control flow operations
                OPERATORS.put("if", IF);
                OPERATORS.put("else", ELSE);
                OPERATORS.put("?", Operator.TERNARY);
                OPERATORS.put("switch", SWITCH);

            case 4: // assignment
                OPERATORS.put("=", Operator.ASSIGN);
                OPERATORS.put("var", TYPED_VAR);
                OPERATORS.put("+=", ASSIGN_ADD);
                OPERATORS.put("-=", ASSIGN_SUB);

            case 3: // iteration
                OPERATORS.put("foreach", FOREACH);
                OPERATORS.put("while", WHILE);
                OPERATORS.put("for", FOR);
                OPERATORS.put("do", DO);

            case 2: // multi-statement
                OPERATORS.put("return", RETURN);
                OPERATORS.put(";", END_OF_STMT);

            case 1: // boolean, math ops, projection, assertion, objection creation, block setters, imports
                OPERATORS.put("+", ADD);
                OPERATORS.put("-", SUB);
                OPERATORS.put("*", MULT);
                OPERATORS.put("**", POWER);
                OPERATORS.put("/", DIV);
                OPERATORS.put("%", MOD);
                OPERATORS.put("==", EQUAL);
                OPERATORS.put("!=", NEQUAL);
                OPERATORS.put(">", GTHAN);
                OPERATORS.put(">=", GETHAN);
                OPERATORS.put("<", LTHAN);
                OPERATORS.put("<=", LETHAN);
                OPERATORS.put("&&", AND);
                OPERATORS.put("and", AND);
                OPERATORS.put("||", OR);
                OPERATORS.put("or", CHOR);
                OPERATORS.put("~=", REGEX);
                OPERATORS.put("instanceof", INSTANCEOF);
                OPERATORS.put("is", INSTANCEOF);
                OPERATORS.put("contains", CONTAINS);
                OPERATORS.put("soundslike", SOUNDEX);
                OPERATORS.put("strsim", SIMILARITY);
                OPERATORS.put("convertable_to", CONVERTABLE_TO);

                OPERATORS.put("#", STR_APPEND);

                OPERATORS.put("&", BW_AND);
                OPERATORS.put("|", BW_OR);
                OPERATORS.put("^", BW_XOR);
                OPERATORS.put("<<", BW_SHIFT_LEFT);
                OPERATORS.put("<<<", BW_USHIFT_LEFT);
                OPERATORS.put(">>", BW_SHIFT_RIGHT);
                OPERATORS.put(">>>", BW_USHIFT_RIGHT);

                OPERATORS.put("new", Operator.NEW);
                OPERATORS.put("in", PROJECTION);

                OPERATORS.put("with", WITH);

                OPERATORS.put("assert", ASSERT);
                OPERATORS.put("import", IMPORT);
                OPERATORS.put("import_static", IMPORT_STATIC);

                OPERATORS.put("++", INC);
                OPERATORS.put("--", DEC);

            case 0: // Property access and inline collections
                OPERATORS.put(":", TERNARY_ELSE);
        }
    }

    public static void resetParserContext() {
        contextControl(REMOVE, null, null);
    }


}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.