#vulnerable code 
                                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;
        boolean 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();

                int scan = cursor;

                while (expr[scan] == '\n') {
                    scan++;
                    line++;
                }

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

                return createToken(expr, start, cursor, fields);
            }
            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 (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);
                            }

                            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;
                                    }
                                    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 ((fields & ASTNode.FOLD) != 0) {
                            if (cursor < length && expr[cursor] == '.') {
                                cursor += 1;
                                continue;
                            }

                            return createToken(expr, trimRight(start), cursor, ASTNode.FOLD);
                        }

                        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 (cursor < (length - 1) && expr[cursor + 1] == '.') {
                            fields |= ASTNode.INLINE_COLLECTION;
                            cursor++;
                            continue;
                        }

                        return new InlineCollectionNode(expr, start, ++cursor, fields);

                    default:
                        cursor++;
                }
        }

        return createPropertyToken(start, cursor);
    } 
                            #location 508 
                            #vulnerability type THREAD_SAFETY_VIOLATION
#fixed code
                                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;
        boolean 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();

                int scan = cursor;

                while (expr[scan] == '\n') {
                    scan++;
                    line++;
                }

                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) {
                    char[] _subset = subset(expr, start, cursor - start);
                    int offset;

                    Class cls;
                    if ((offset = ArrayTools.findFirst('.', _subset)) != -1) {
                        String iStr;
                        if (getParserContext().hasImport(iStr = new String(_subset, 0, offset))) {
                            lastWasIdentifier = true;
                            return lastNode = new LiteralDeepPropertyNode(subset(_subset, offset + 1, _subset.length - offset - 1), fields, getParserContext().getImport(iStr)); 
// /                            return lastNode = new Union(_subset, offset + 1, _subset.length, fields, new LiteralNode(getParserContext().getImport(iStr)));
                        }
//                        else if ((cls = createClassSafe(iStr = new String(_subset, offset = ArrayTools.findLast('.', _subset), _subset.length - offset))) != null) {
//
//                        }

                    }

                    else {
                        ASTNode node = new ASTNode(_subset, 0, _subset.length, fields);
                        lastWasIdentifier = node.isIdentifier();
                        return lastNode = node;
                    }
                }

                return createToken(expr, start, cursor, fields);
            }
            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 (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);
                            }

                            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;
                                    }
                                    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 ((fields & ASTNode.FOLD) != 0) {
                            if (cursor < length && expr[cursor] == '.') {
                                cursor += 1;
                                continue;
                            }

                            return createToken(expr, trimRight(start), cursor, ASTNode.FOLD);
                        }

                        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 (cursor < (length - 1) && expr[cursor + 1] == '.') {
                            fields |= ASTNode.INLINE_COLLECTION;
                            cursor++;
                            continue;
                        }

                        return new InlineCollectionNode(expr, start, ++cursor, fields);

                    default:
                        cursor++;
                }
        }

        return createPropertyToken(start, cursor);
    }