0001: /**
0002: * MVEL (The MVFLEX Expression Language)
0003: *
0004: * Copyright (C) 2007 Christopher Brock, MVFLEX/Valhalla Project and the Codehaus
0005: *
0006: * Licensed under the Apache License, Version 2.0 (the "License");
0007: * you may not use this file except in compliance with the License.
0008: * You may obtain a copy of the License at
0009: *
0010: * http://www.apache.org/licenses/LICENSE-2.0
0011: *
0012: * Unless required by applicable law or agreed to in writing, software
0013: * distributed under the License is distributed on an "AS IS" BASIS,
0014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0015: * See the License for the specific language governing permissions and
0016: * limitations under the License.
0017: *
0018: */package org.mvel.compiler;
0019:
0020: import org.mvel.*;
0021: import static org.mvel.Operator.*;
0022: import org.mvel.ast.*;
0023: import static org.mvel.util.ArrayTools.findFirst;
0024: import org.mvel.util.ExecutionStack;
0025: import static org.mvel.util.ParseTools.*;
0026: import static org.mvel.util.PropertyTools.isDigit;
0027: import static org.mvel.util.PropertyTools.isIdentifierPart;
0028: import org.mvel.util.Stack;
0029:
0030: import java.io.Serializable;
0031: import static java.lang.Boolean.FALSE;
0032: import static java.lang.Boolean.TRUE;
0033: import static java.lang.Character.isWhitespace;
0034: import static java.lang.Float.parseFloat;
0035: import static java.lang.Runtime.getRuntime;
0036: import static java.lang.System.getProperty;
0037: import static java.lang.Thread.currentThread;
0038: import static java.util.Collections.synchronizedMap;
0039: import java.util.HashMap;
0040: import java.util.Map;
0041: import java.util.WeakHashMap;
0042:
0043: /**
0044: * @author Christopher Brock
0045: */
0046: public class AbstractParser implements Serializable {
0047: protected char[] expr;
0048: protected int cursor;
0049: protected int length;
0050: protected int fields;
0051:
0052: protected boolean greedy = true;
0053: protected boolean lastWasIdentifier = false;
0054: protected boolean lastWasLineLabel = false;
0055: protected boolean lastWasComment = false;
0056: protected boolean literalOnly = true;
0057:
0058: protected boolean debugSymbols = false;
0059:
0060: private int line = 1;
0061:
0062: protected ASTNode lastNode;
0063:
0064: private static Map<String, char[]> EX_PRECACHE;
0065:
0066: public static final Map<String, Object> LITERALS = new HashMap<String, Object>(
0067: 35 * 2, 0.4f);
0068:
0069: public static final Map<String, Integer> OPERATORS = new HashMap<String, Integer>(
0070: 25 * 2, 0.4f);
0071:
0072: protected Stack stk;
0073: protected ExecutionStack splitAccumulator = new ExecutionStack();
0074:
0075: protected static ThreadLocal<ParserContext> parserContext;
0076:
0077: static {
0078: configureFactory();
0079:
0080: /**
0081: * Setup the basic literals
0082: */
0083: AbstractParser.LITERALS.put("true", TRUE);
0084: AbstractParser.LITERALS.put("false", FALSE);
0085:
0086: AbstractParser.LITERALS.put("null", null);
0087: AbstractParser.LITERALS.put("nil", null);
0088:
0089: AbstractParser.LITERALS.put("empty", BlankLiteral.INSTANCE);
0090:
0091: // AbstractParser.LITERALS.put("this", ThisLiteral.class);
0092:
0093: /**
0094: * Add System and all the class wrappers from the JCL.
0095: */
0096: LITERALS.put("System", System.class);
0097:
0098: LITERALS.put("String", String.class);
0099:
0100: LITERALS.put("Integer", Integer.class);
0101: LITERALS.put("int", Integer.class);
0102:
0103: LITERALS.put("Long", Long.class);
0104: LITERALS.put("long", Long.class);
0105:
0106: LITERALS.put("Boolean", Boolean.class);
0107: LITERALS.put("boolean", Boolean.class);
0108:
0109: LITERALS.put("Short", Short.class);
0110: LITERALS.put("short", Short.class);
0111:
0112: LITERALS.put("Character", Character.class);
0113: LITERALS.put("char", Character.class);
0114:
0115: LITERALS.put("Double", Double.class);
0116: LITERALS.put("double", double.class);
0117:
0118: LITERALS.put("Float", Float.class);
0119: LITERALS.put("float", float.class);
0120:
0121: LITERALS.put("Math", Math.class);
0122: LITERALS.put("Void", Void.class);
0123: LITERALS.put("Object", Object.class);
0124:
0125: LITERALS.put("Class", Class.class);
0126: LITERALS.put("ClassLoader", ClassLoader.class);
0127: LITERALS.put("Runtime", Runtime.class);
0128: LITERALS.put("Thread", Thread.class);
0129: LITERALS.put("Compiler", Compiler.class);
0130: LITERALS.put("StringBuffer", StringBuffer.class);
0131: LITERALS.put("ThreadLocal", ThreadLocal.class);
0132: LITERALS.put("SecurityManager", SecurityManager.class);
0133: LITERALS.put("StrictMath", StrictMath.class);
0134:
0135: LITERALS.put("Array", java.lang.reflect.Array.class);
0136:
0137: if (parseFloat(getProperty("java.version").substring(0, 2)) >= 1.5) {
0138: try {
0139: LITERALS.put("StringBuilder", currentThread()
0140: .getContextClassLoader().loadClass(
0141: "java.lang.StringBuilder"));
0142: } catch (Exception e) {
0143: throw new RuntimeException(
0144: "cannot resolve a built-in literal", e);
0145: }
0146: }
0147:
0148: //LITERALS.putAll(Units.MEASUREMENTS_ALL);
0149:
0150: //loadLanguageFeaturesByLevel(5);
0151: setLanguageLevel(5);
0152: }
0153:
0154: public static void configureFactory() {
0155: if (MVEL.THREAD_SAFE) {
0156: EX_PRECACHE = synchronizedMap(new WeakHashMap<String, char[]>(
0157: 10));
0158: } else {
0159: EX_PRECACHE = new WeakHashMap<String, char[]>(10);
0160: }
0161: }
0162:
0163: protected ASTNode nextTokenSkipSymbols() {
0164: ASTNode n = nextToken();
0165: if (n != null && n.getFields() == -1)
0166: n = nextToken();
0167: return n;
0168: }
0169:
0170: /**
0171: * Retrieve the next token in the expression.
0172: *
0173: * @return -
0174: */
0175: protected ASTNode nextToken() {
0176: try {
0177:
0178: /**
0179: * If the cursor is at the end of the expression, we have nothing more to do:
0180: * return null.
0181: */
0182: if (cursor >= length) {
0183: return null;
0184: } else if (!splitAccumulator.isEmpty()) {
0185: return lastNode = (ASTNode) splitAccumulator.pop();
0186: }
0187:
0188: int brace, start = cursor, idx;
0189:
0190: /**
0191: * Because of parser recursion for sub-expression parsing, we sometimes need to remain
0192: * certain field states. We do not reset for assignments, boolean mode, list creation or
0193: * a capture only mode.
0194: */
0195: fields = fields
0196: & (ASTNode.INLINE_COLLECTION | ASTNode.COMPILE_IMMEDIATE);
0197:
0198: boolean capture = false, union = false;
0199:
0200: ParserContext pCtx = getParserContext();
0201:
0202: if (debugSymbols) {
0203: if (!lastWasLineLabel) {
0204: if (pCtx.getSourceFile() == null) {
0205: throw new CompileException(
0206: "unable to produce debugging symbols: source name must be provided.");
0207: }
0208:
0209: line = pCtx.getLineCount();
0210:
0211: skipWhitespaceWithLineAccounting();
0212:
0213: if (!pCtx.isKnownLine(pCtx.getSourceFile(), pCtx
0214: .setLineCount(line))
0215: && !pCtx.isBlockSymbols()) {
0216: lastWasLineLabel = true;
0217: pCtx.setLineAndOffset(line, cursor);
0218: return lastNode = pCtx
0219: .setLastLineLabel(new LineLabel(pCtx
0220: .getSourceFile(), line));
0221: }
0222: } else {
0223: lastWasComment = lastWasLineLabel = false;
0224: }
0225: }
0226:
0227: /**
0228: * Skip any whitespace currently under the starting point.
0229: */
0230: while (start != length && isWhitespace(expr[start]))
0231: start++;
0232:
0233: /**
0234: * From here to the end of the method is the core MVEL parsing code. Fiddling around here is asking for
0235: * trouble unless you really know what you're doing.
0236: */
0237: for (cursor = start; cursor != length;) {
0238: if (isIdentifierPart(expr[cursor])) {
0239: /**
0240: * If the current character under the cursor is a valid
0241: * part of an identifier, we keep capturing.
0242: */
0243: capture = true;
0244: cursor++;
0245: } else if (capture) {
0246: String t;
0247: if (OPERATORS.containsKey(t = new String(expr,
0248: start, cursor - start))) {
0249: switch (OPERATORS.get(t)) {
0250: case NEW:
0251: start = cursor = trimRight(cursor);
0252: captureToEOT();
0253: return lastNode = new NewObjectNode(
0254: subArray(start, cursor), fields);
0255:
0256: case ASSERT:
0257: start = cursor = trimRight(cursor);
0258: captureToEOS();
0259: return lastNode = new AssertNode(subArray(
0260: start, cursor--), fields);
0261:
0262: case RETURN:
0263: start = cursor = trimRight(cursor);
0264: captureToEOS();
0265: return lastNode = new ReturnNode(subArray(
0266: start, cursor), fields);
0267:
0268: case IF:
0269: return captureCodeBlock(ASTNode.BLOCK_IF);
0270:
0271: case FOREACH:
0272: return captureCodeBlock(ASTNode.BLOCK_FOREACH);
0273:
0274: case WHILE:
0275: return captureCodeBlock(ASTNode.BLOCK_WHILE);
0276:
0277: case WITH:
0278: return captureCodeBlock(ASTNode.BLOCK_WITH);
0279:
0280: case IMPORT:
0281: start = cursor = trimRight(cursor);
0282: captureToEOS();
0283: ImportNode importNode = new ImportNode(
0284: subArray(start, cursor--));
0285: if (importNode.isPackageImport()) {
0286: pCtx.addPackageImport(importNode
0287: .getPackageImport());
0288: cursor++;
0289: } else {
0290: pCtx.addImport(
0291: getSimpleClassName(importNode
0292: .getImportClass()),
0293: importNode.getImportClass());
0294: }
0295: return importNode;
0296:
0297: case IMPORT_STATIC:
0298: start = cursor = trimRight(cursor);
0299: captureToEOS();
0300: return lastNode = new StaticImportNode(
0301: subArray(start, cursor--));
0302:
0303: case FUNCTION:
0304: Function function = (Function) captureCodeBlock(FUNCTION);
0305: capture = false;
0306: start = cursor + 1;
0307: return function;
0308:
0309: case UNTYPED_VAR:
0310: start = cursor + 1;
0311: captureToEOT();
0312: int end = cursor;
0313:
0314: skipWhitespace();
0315: if (expr[cursor] == '=') {
0316: cursor = start;
0317: continue;
0318: } else {
0319: String name = new String(subArray(
0320: start, end));
0321: if ((idx = pCtx.variableIndexOf(name)) != -1) {
0322: return lastNode = new IndexedDeclTypedVarNode(
0323: idx, Object.class);
0324: } else {
0325: return lastNode = new DeclTypedVarNode(
0326: name, Object.class, fields);
0327: }
0328: }
0329: }
0330: }
0331:
0332: skipWhitespace();
0333:
0334: /**
0335: * If we *were* capturing a token, and we just hit a non-identifier
0336: * character, we stop and figure out what to do.
0337: */
0338: if (cursor != length && expr[cursor] == '(') {
0339: cursor = balancedCapture(expr, cursor, '(') + 1;
0340: }
0341:
0342: /**
0343: * If we encounter any of the following cases, we are still dealing with
0344: * a contiguous token.
0345: */
0346: String name;
0347: if (cursor != length) {
0348: switch (expr[cursor]) {
0349: case '?':
0350: if (lookToLast() == '.') {
0351: capture = true;
0352: cursor++;
0353: continue;
0354: }
0355: case '+':
0356: switch (lookAhead()) {
0357: case '+':
0358: if ((idx = pCtx
0359: .variableIndexOf(name = new String(
0360: subArray(start, cursor)))) != -1) {
0361: lastNode = new IndexedPostFixIncNode(
0362: idx);
0363: } else {
0364: lastNode = new PostFixIncNode(name);
0365: }
0366:
0367: cursor += 2;
0368:
0369: return lastNode;
0370:
0371: case '=':
0372: name = new String(expr, start,
0373: trimLeft(cursor) - start);
0374: start = cursor += 2;
0375: captureToEOS();
0376:
0377: if (union) {
0378: return lastNode = new DeepAssignmentNode(
0379: subArray(start, cursor),
0380: fields, Operator.ADD, t);
0381: } else if ((idx = pCtx
0382: .variableIndexOf(name)) != -1) {
0383: return lastNode = new IndexedAssignmentNode(
0384: subArray(start, cursor),
0385: fields, Operator.ADD, name,
0386: idx);
0387: } else {
0388: return lastNode = new AssignmentNode(
0389: subArray(start, cursor),
0390: fields, Operator.ADD, name);
0391: }
0392: }
0393:
0394: break;
0395:
0396: case '-':
0397: switch (lookAhead()) {
0398: case '-':
0399: if ((idx = pCtx
0400: .variableIndexOf(name = new String(
0401: subArray(start, cursor)))) != -1) {
0402: lastNode = new IndexedPostFixDecNode(
0403: idx);
0404: } else {
0405: lastNode = new PostFixDecNode(name);
0406: }
0407: cursor += 2;
0408:
0409: return lastNode;
0410:
0411: case '=':
0412: name = new String(expr, start,
0413: trimLeft(cursor) - start);
0414: start = cursor += 2;
0415:
0416: captureToEOS();
0417:
0418: if ((idx = pCtx.variableIndexOf(name)) != -1) {
0419: return lastNode = new IndexedOperativeAssign(
0420: subArray(start, cursor),
0421: Operator.SUB, idx, fields);
0422: } else {
0423: return lastNode = new OperativeAssign(
0424: name, subArray(start,
0425: cursor),
0426: Operator.SUB, fields);
0427: }
0428: }
0429: break;
0430:
0431: case '*':
0432: if (isNext('=')) {
0433: name = new String(expr, start,
0434: trimLeft(cursor) - start);
0435:
0436: start = cursor += 2;
0437: captureToEOS();
0438:
0439: if ((idx = pCtx.variableIndexOf(name)) != -1) {
0440: return lastNode = new IndexedOperativeAssign(
0441: subArray(start, cursor),
0442: Operator.MULT, idx, fields);
0443: } else {
0444: return lastNode = new OperativeAssign(
0445: name, subArray(start,
0446: cursor),
0447: Operator.MULT, fields);
0448: }
0449:
0450: }
0451: break;
0452:
0453: case '/':
0454: if (isNext('=')) {
0455: name = new String(expr, start,
0456: trimLeft(cursor) - start);
0457:
0458: start = cursor += 2;
0459: captureToEOS();
0460:
0461: if ((idx = pCtx.variableIndexOf(name)) != -1) {
0462: return lastNode = new IndexedOperativeAssign(
0463: subArray(start, cursor),
0464: Operator.DIV, idx, fields);
0465: } else {
0466: return lastNode = new OperativeAssign(
0467: name, subArray(start,
0468: cursor),
0469: Operator.DIV, fields);
0470: }
0471: }
0472: break;
0473:
0474: case ']':
0475: case '[':
0476: cursor = balancedCapture(expr, cursor, '[') + 1;
0477: continue;
0478: case '.':
0479: union = true;
0480: cursor++;
0481: continue;
0482:
0483: case '~':
0484: if (isNext('=')) {
0485: char[] stmt = subArray(start,
0486: trimLeft(cursor));
0487:
0488: start = cursor += 2;
0489: skipWhitespace();
0490:
0491: return lastNode = new RegExMatch(
0492: stmt,
0493: fields,
0494: subArray(
0495: start,
0496: (cursor = balancedCapture(
0497: expr, cursor,
0498: expr[cursor]) + 1)));
0499: }
0500: break;
0501:
0502: case '=':
0503: if (isNext('+')) {
0504: name = new String(expr, start,
0505: trimLeft(cursor) - start);
0506:
0507: start = cursor += 2;
0508: captureToEOS();
0509:
0510: if ((idx = pCtx.variableIndexOf(name)) != -1) {
0511: return lastNode = new IndexedOperativeAssign(
0512: subArray(start, cursor),
0513: Operator.ADD, idx, fields);
0514: } else {
0515: return lastNode = new OperativeAssign(
0516: name, subArray(start,
0517: cursor),
0518: Operator.ADD, fields);
0519: }
0520: }
0521:
0522: if (greedy && !isNext('=')) {
0523: cursor++;
0524:
0525: captureToEOS();
0526:
0527: if (union) {
0528: return lastNode = new DeepAssignmentNode(
0529: subArray(start, cursor),
0530: fields | ASTNode.ASSIGN);
0531: } else if (lastWasIdentifier) {
0532:
0533: /**
0534: * Check for typing information.
0535: */
0536: if (lastNode.getLiteralValue() instanceof String) {
0537: if (pCtx
0538: .hasImport((String) lastNode
0539: .getLiteralValue())) {
0540: lastNode
0541: .setLiteralValue(pCtx
0542: .getImport((String) lastNode
0543: .getLiteralValue()));
0544: lastNode.setAsLiteral();
0545: lastNode.discard();
0546: } else if (stk != null
0547: && stk.peek() instanceof Class) {
0548: lastNode
0549: .setLiteralValue(stk
0550: .pop());
0551: lastNode.setAsLiteral();
0552: lastNode.discard();
0553: } else {
0554: try {
0555: /**
0556: * take a stab in the dark and try and load the class
0557: */
0558: lastNode
0559: .setLiteralValue(createClass((String) lastNode
0560: .getLiteralValue()));
0561: lastNode.setAsLiteral();
0562: lastNode.discard();
0563: } catch (ClassNotFoundException e) {
0564: /**
0565: * Just fail through.
0566: */
0567: }
0568: }
0569: }
0570:
0571: if (lastNode.isLiteral()
0572: && lastNode
0573: .getLiteralValue() instanceof Class) {
0574: lastNode.discard();
0575:
0576: captureToEOS();
0577: return new TypedVarNode(
0578: subArray(start, cursor),
0579: fields | ASTNode.ASSIGN,
0580: (Class) lastNode
0581: .getLiteralValue());
0582: }
0583:
0584: throw new CompileException(
0585: "unknown class or illegal statement: "
0586: + lastNode
0587: .getLiteralValue(),
0588: expr, cursor);
0589: } else if (pCtx != null
0590: && ((idx = pCtx
0591: .variableIndexOf(t)) != -1 || (pCtx
0592: .isIndexAllocation()))) {
0593: IndexedAssignmentNode ian = new IndexedAssignmentNode(
0594: subArray(start, cursor),
0595: ASTNode.ASSIGN, idx);
0596:
0597: if (idx == -1) {
0598: pCtx.addIndexedVariable(t = ian
0599: .getAssignmentVar());
0600: ian.setRegister(idx = pCtx
0601: .variableIndexOf(t));
0602: }
0603: return lastNode = ian;
0604: } else {
0605: return lastNode = new AssignmentNode(
0606: subArray(start, cursor),
0607: fields | ASTNode.ASSIGN);
0608: }
0609: }
0610: }
0611: }
0612:
0613: /**
0614: * Produce the token.
0615: */
0616: trimWhitespace();
0617:
0618: return createPropertyToken(start, cursor);
0619: } else {
0620: String name;
0621:
0622: switch (expr[cursor]) {
0623: case '@': {
0624: start++;
0625: captureToEOT();
0626:
0627: name = new String(expr, start, cursor - start);
0628:
0629: if (pCtx.getInterceptors() == null
0630: || !pCtx.getInterceptors().containsKey(
0631: name)) {
0632: throw new CompileException(
0633: "reference to undefined interceptor: "
0634: + name, expr, cursor);
0635: }
0636:
0637: return lastNode = new InterceptorWrapper(pCtx
0638: .getInterceptors().get(name),
0639: nextToken());
0640: }
0641:
0642: case '=':
0643: return createToken(expr, start, (cursor += 2),
0644: fields);
0645:
0646: case '-':
0647: if (isNext('-')) {
0648: start = cursor += 2;
0649: captureToEOT();
0650:
0651: if ((idx = pCtx
0652: .variableIndexOf(name = new String(
0653: subArray(start, cursor)))) != -1) {
0654: return lastNode = new IndexedPreFixDecNode(
0655: idx);
0656: } else {
0657: return lastNode = new PreFixDecNode(
0658: name);
0659: }
0660: } else if ((cursor != 0 && !isWhitespace(lookBehind()))
0661: || !isDigit(lookAhead())) {
0662: return createToken(expr, start,
0663: cursor++ + 1, fields);
0664: } else if ((cursor - 1) != 0
0665: || (!isDigit(lookBehind()))
0666: && isDigit(lookAhead())) {
0667: cursor++;
0668: break;
0669: }
0670:
0671: case '+':
0672: if (isNext('+')) {
0673: start = cursor += 2;
0674: captureToEOT();
0675:
0676: if ((idx = pCtx
0677: .variableIndexOf(name = new String(
0678: subArray(start, cursor)))) != -1) {
0679: return lastNode = new IndexedPreFixIncNode(
0680: idx);
0681: } else {
0682: return lastNode = new PreFixIncNode(
0683: name);
0684: }
0685: }
0686: return createToken(expr, start, cursor++ + 1,
0687: fields);
0688:
0689: case '*':
0690: if (isNext('*')) {
0691: cursor++;
0692: }
0693: return createToken(expr, start, cursor++ + 1,
0694: fields);
0695:
0696: case ';':
0697: cursor++;
0698: lastWasIdentifier = false;
0699: return lastNode = new EndOfStatement();
0700:
0701: case '#':
0702: case '/':
0703: if (isNext(expr[cursor])) {
0704: /**
0705: * Handle single line comments.
0706: */
0707: // while (cursor != length && expr[cursor] != '\n') cursor++;
0708: captureToEOL();
0709:
0710: if (debugSymbols) {
0711: line = pCtx.getLineCount();
0712:
0713: skipWhitespaceWithLineAccounting();
0714:
0715: if (lastNode instanceof LineLabel) {
0716: pCtx.getLastLineLabel()
0717: .setLineNumber(line);
0718: pCtx.addKnownLine(line);
0719: }
0720:
0721: lastWasComment = true;
0722:
0723: pCtx.setLineCount(line);
0724: } else if (cursor != length) {
0725: skipWhitespace();
0726: }
0727:
0728: if ((start = cursor) >= length)
0729: return null;
0730:
0731: continue;
0732: } else if (expr[cursor] == '/' && isNext('*')) {
0733: /**
0734: * Handle multi-line comments.
0735: */
0736: int len = length - 1;
0737:
0738: /**
0739: * This probably seems highly redundant, but sub-compilations within the same
0740: * source will spawn a new compiler, and we need to sync this with the
0741: * parser context;
0742: */
0743: if (debugSymbols) {
0744: line = pCtx.getLineCount();
0745: }
0746:
0747: while (true) {
0748: cursor++;
0749: /**
0750: * Since multi-line comments may cross lines, we must keep track of any line-break
0751: * we encounter.
0752: */
0753: if (debugSymbols) {
0754: skipWhitespaceWithLineAccounting();
0755: }
0756:
0757: if (cursor == len) {
0758: throw new CompileException(
0759: "unterminated block comment",
0760: expr, cursor);
0761: }
0762: if (expr[cursor] == '*' && isNext('/')) {
0763: if ((cursor += 2) >= length)
0764: return null;
0765: skipWhitespaceWithLineAccounting();
0766: start = cursor;
0767: break;
0768: }
0769: }
0770:
0771: if (debugSymbols) {
0772: pCtx.setLineCount(line);
0773:
0774: if (lastNode instanceof LineLabel) {
0775: pCtx.getLastLineLabel()
0776: .setLineNumber(line);
0777: pCtx.addKnownLine(line);
0778: }
0779:
0780: lastWasComment = true;
0781: }
0782:
0783: continue;
0784: }
0785:
0786: case '?':
0787: case ':':
0788: case '^':
0789: case '%': {
0790: return createToken(expr, start, cursor++ + 1,
0791: fields);
0792: }
0793:
0794: case '(': {
0795: cursor++;
0796:
0797: boolean singleToken = true;
0798: boolean lastWS = false;
0799:
0800: skipWhitespace();
0801: for (brace = 1; cursor != length && brace != 0; cursor++) {
0802: switch (expr[cursor]) {
0803: case '(':
0804: brace++;
0805: break;
0806: case ')':
0807: brace--;
0808: break;
0809: case '\'':
0810: cursor = captureStringLiteral('\'',
0811: expr, cursor, length);
0812: break;
0813: case '"':
0814: cursor = captureStringLiteral('"',
0815: expr, cursor, length);
0816: break;
0817:
0818: case 'i':
0819: if (isNext('n')
0820: && isWhitespace(lookAhead(2))
0821: && !isIdentifierPart(lookBehind())) {
0822: fields |= ASTNode.FOLD;
0823: for (int level = brace; cursor != length; cursor++) {
0824: switch (expr[cursor]) {
0825: case '(':
0826: brace++;
0827: break;
0828: case ')':
0829: if (--brace != level) {
0830: if (lookAhead() == '.') {
0831: lastNode = createToken(
0832: expr,
0833: trimRight(start + 1),
0834: (start = cursor++),
0835: ASTNode.FOLD);
0836: captureToEOT();
0837: return lastNode = new Union(
0838: expr,
0839: trimRight(start + 2),
0840: cursor,
0841: fields,
0842: lastNode);
0843: } else {
0844: return createToken(
0845: expr,
0846: trimRight(start + 1),
0847: cursor++,
0848: ASTNode.FOLD);
0849: }
0850: }
0851: break;
0852: case '\'':
0853: cursor = captureStringLiteral(
0854: '\'', expr, cursor,
0855: length);
0856: break;
0857: case '"':
0858: cursor = captureStringLiteral(
0859: '\'', expr, cursor,
0860: length);
0861: break;
0862: }
0863: }
0864: }
0865: break;
0866:
0867: default:
0868: /**
0869: * Check to see if we should disqualify this current token as a potential
0870: * type-cast candidate.
0871: */
0872: if ((lastWS && expr[cursor] != '.')
0873: || !(isIdentifierPart(expr[cursor]) || expr[cursor] == '.')) {
0874: singleToken = false;
0875: } else if (isWhitespace(expr[cursor])) {
0876: lastWS = true;
0877: skipWhitespace();
0878: cursor--;
0879: }
0880: }
0881: }
0882:
0883: if (brace != 0) {
0884: throw new CompileException(
0885: "unbalanced braces in expression: ("
0886: + brace + "):", expr,
0887: cursor);
0888: }
0889:
0890: char[] _subset = null;
0891: if (singleToken) {
0892: int st;
0893: String tokenStr = new String(
0894: _subset = subset(expr,
0895: st = trimRight(start + 1),
0896: trimLeft(cursor - 1) - st));
0897:
0898: if (pCtx.hasImport(tokenStr)) {
0899: start = cursor;
0900: captureToEOS();
0901: return lastNode = new TypeCast(subset(
0902: expr, start, cursor - start),
0903: pCtx.getImport(tokenStr),
0904: fields);
0905: } else {
0906: try {
0907: /**
0908: *
0909: * take a stab in the dark and try and load the class
0910: */
0911: int _start = cursor;
0912: captureToEOS();
0913: return lastNode = new TypeCast(
0914: subset(expr, _start, cursor
0915: - _start),
0916: createClass(tokenStr),
0917: fields);
0918:
0919: } catch (ClassNotFoundException e) {
0920: /**
0921: * Just fail through.
0922: */
0923: }
0924: }
0925: }
0926:
0927: if (_subset != null) {
0928: return handleUnion(handleSubstatement(new Substatement(
0929: _subset, fields)));
0930: } else {
0931: return handleUnion(handleSubstatement(new Substatement(
0932: subset(
0933: expr,
0934: start = trimRight(start + 1),
0935: trimLeft(cursor - 1)
0936: - start), fields)));
0937: }
0938: }
0939:
0940: case '}':
0941: case ']':
0942: case ')': {
0943: throw new ParseException("unbalanced braces",
0944: expr, cursor);
0945: }
0946:
0947: case '>': {
0948: if (expr[cursor + 1] == '>') {
0949: if (expr[cursor += 2] == '>')
0950: cursor++;
0951: return createToken(expr, start, cursor,
0952: fields);
0953: } else if (expr[cursor + 1] == '=') {
0954: return createToken(expr, start,
0955: cursor += 2, fields);
0956: } else {
0957: return createToken(expr, start, ++cursor,
0958: fields);
0959: }
0960: }
0961:
0962: case '<': {
0963: if (expr[++cursor] == '<') {
0964: if (expr[++cursor] == '<')
0965: cursor++;
0966: return createToken(expr, start, cursor,
0967: fields);
0968: } else if (expr[cursor] == '=') {
0969: return createToken(expr, start, ++cursor,
0970: fields);
0971: } else {
0972: return createToken(expr, start, cursor,
0973: fields);
0974: }
0975: }
0976:
0977: case '\'':
0978: case '"':
0979: lastNode = new LiteralNode(
0980: handleStringEscapes(subset(expr,
0981: start + 1,
0982: (cursor = captureStringLiteral(
0983: expr[cursor], expr,
0984: cursor, length))
0985: - start - 1)),
0986: String.class);
0987:
0988: cursor++;
0989:
0990: if (tokenContinues()) {
0991: return lastNode = handleUnion(lastNode);
0992: }
0993:
0994: return lastNode;
0995:
0996: case '&': {
0997: if (expr[cursor++ + 1] == '&') {
0998: return createToken(expr, start, ++cursor,
0999: fields);
1000: } else {
1001: return createToken(expr, start, cursor,
1002: fields);
1003: }
1004: }
1005:
1006: case '|': {
1007: if (expr[cursor++ + 1] == '|') {
1008: return createToken(expr, start, ++cursor,
1009: fields);
1010: } else {
1011: return createToken(expr, start, cursor,
1012: fields);
1013: }
1014: }
1015:
1016: case '~':
1017: if ((cursor++ - 1 != 0 || !isIdentifierPart(lookBehind()))
1018: && isDigit(expr[cursor])) {
1019: start = cursor;
1020: captureToEOT();
1021: return lastNode = new Invert(subset(expr,
1022: start, cursor - start), fields);
1023: } else if (expr[cursor] == '(') {
1024: start = cursor--;
1025: captureToEOT();
1026: return lastNode = new Invert(subset(expr,
1027: start, cursor - start), fields);
1028: } else {
1029: if (expr[cursor] == '=')
1030: cursor++;
1031: return createToken(expr, start, cursor,
1032: fields);
1033: }
1034:
1035: case '!': {
1036: if (isIdentifierPart(expr[++cursor])) {
1037: start = cursor;
1038: captureToEOT();
1039: return lastNode = new Negation(subset(expr,
1040: start, cursor - start), fields);
1041: } else if (expr[cursor] == '(') {
1042: start = cursor--;
1043: captureToEOT();
1044: return lastNode = new Negation(subset(expr,
1045: start, cursor - start), fields);
1046: } else if (expr[cursor] != '=')
1047: throw new CompileException(
1048: "unexpected operator '!'", expr,
1049: cursor, null);
1050: else {
1051: return createToken(expr, start, ++cursor,
1052: fields);
1053: }
1054: }
1055:
1056: case '[':
1057: case '{':
1058: cursor = balancedCapture(expr, cursor,
1059: expr[cursor]) + 1;
1060: if (tokenContinues()) {
1061: // if (lookAhead(1) == '.') {
1062: lastNode = new InlineCollectionNode(expr,
1063: start, start = cursor, fields);
1064: captureToEOT();
1065: return lastNode = new Union(expr,
1066: start + 1, cursor, fields, lastNode);
1067: } else {
1068: return lastNode = new InlineCollectionNode(
1069: expr, start, cursor, fields);
1070: }
1071:
1072: default:
1073: cursor++;
1074: }
1075: }
1076: }
1077:
1078: if (start == cursor)
1079: return null;
1080: return createPropertyToken(start, cursor);
1081: } catch (CompileException e) {
1082: throw new CompileException(e.getMessage(), expr, cursor, e
1083: .getCursor() == 0, e);
1084: }
1085: }
1086:
1087: public ASTNode handleSubstatement(Substatement stmt) {
1088: if (stmt.getStatement() != null
1089: && stmt.getStatement().isLiteralOnly()) {
1090: return new LiteralNode(stmt.getStatement().getValue(null,
1091: null, null), fields);
1092: } else {
1093: return stmt;
1094: }
1095: }
1096:
1097: protected ASTNode handleUnion(ASTNode node) {
1098: if (cursor != length) {
1099: skipWhitespace();
1100: if (expr[cursor] == '.') {
1101: int union = cursor + 1;
1102: captureToEOT();
1103: return lastNode = new Union(expr, union, cursor,
1104: fields, node);
1105: } else if (expr[cursor] == '[') {
1106: captureToEOT();
1107: return lastNode = new Union(expr, cursor, cursor,
1108: fields, node);
1109: }
1110: }
1111: return lastNode = node;
1112: }
1113:
1114: /**
1115: * Most of this method should be self-explanatory.
1116: *
1117: * @param expr -
1118: * @param start -
1119: * @param end -
1120: * @param fields -
1121: * @return -
1122: */
1123: private ASTNode createToken(final char[] expr, final int start,
1124: final int end, int fields) {
1125: lastWasIdentifier = (lastNode = new ASTNode(expr, start, end,
1126: fields)).isIdentifier();
1127: return lastNode;
1128: }
1129:
1130: private char[] subArray(final int start, final int end) {
1131: if (start >= end)
1132: return new char[0];
1133:
1134: char[] newA = new char[end - start];
1135: for (int i = 0; i != newA.length; i++)
1136: newA[i] = expr[i + start];
1137:
1138: return newA;
1139: }
1140:
1141: private ASTNode createPropertyToken(int start, int end) {
1142: lastWasIdentifier = true;
1143: String tmp;
1144: if (parserContext != null && parserContext.get() != null
1145: && parserContext.get().hasImports()) {
1146: char[] _subset = subset(expr, start, cursor - start);
1147: int offset;
1148:
1149: if ((offset = findFirst('.', _subset)) != -1) {
1150: String iStr = new String(_subset, 0, offset);
1151: if (getParserContext().hasImport(iStr)) {
1152: return lastNode = new LiteralDeepPropertyNode(
1153: subset(_subset, offset + 1, _subset.length
1154: - offset - 1), fields,
1155: getParserContext().getImport(iStr));
1156: }
1157: } else {
1158: if (getParserContext().hasImport(
1159: tmp = new String(_subset))) {
1160: Object i = getParserContext()
1161: .getStaticOrClassImport(tmp);
1162:
1163: if (i instanceof Class) {
1164: return lastNode = new LiteralNode(i,
1165: Class.class);
1166: }
1167: }
1168:
1169: lastWasIdentifier = true;
1170: return lastNode = new ASTNode(_subset, 0,
1171: _subset.length, fields);
1172: }
1173: } else if ((fields & ASTNode.METHOD) != 0) {
1174: return lastNode = new ASTNode(expr, start, end, fields);
1175: } else if (LITERALS.containsKey(tmp = new String(expr, start,
1176: end - start))) {
1177: return lastNode = new LiteralNode(LITERALS.get(tmp));
1178: } else if (OPERATORS.containsKey(tmp)) {
1179: return lastNode = new OperatorNode(OPERATORS.get(tmp));
1180: }
1181:
1182: return lastNode = new ASTNode(expr, start, end, fields);
1183: }
1184:
1185: private ASTNode createBlockToken(final int condStart,
1186: final int condEnd, final int blockStart,
1187: final int blockEnd, int type) {
1188:
1189: lastWasIdentifier = false;
1190:
1191: cursor++;
1192:
1193: if (!isStatementManuallyTerminated()) {
1194: splitAccumulator.add(new EndOfStatement());
1195: }
1196:
1197: switch (type) {
1198: case ASTNode.BLOCK_IF:
1199: return new IfNode(subArray(condStart, condEnd), subArray(
1200: blockStart, blockEnd), fields);
1201: case ASTNode.BLOCK_FOREACH:
1202: return new ForEachNode(subArray(condStart, condEnd),
1203: subArray(blockStart, blockEnd), fields);
1204: case ASTNode.BLOCK_WHILE:
1205: return new WhileNode(subArray(condStart, condEnd),
1206: subArray(blockStart, blockEnd), fields);
1207: default:
1208: return new WithNode(subArray(condStart, condEnd), subArray(
1209: blockStart, blockEnd), fields);
1210: }
1211: }
1212:
1213: private ASTNode captureCodeBlock(int type) {
1214: boolean cond = true;
1215:
1216: ASTNode first = null;
1217: ASTNode tk = null;
1218:
1219: switch (type) {
1220: case ASTNode.BLOCK_IF: {
1221: do {
1222: if (tk != null) {
1223: captureToNextTokenJunction();
1224: skipWhitespace();
1225:
1226: cond = expr[cursor] != '{'
1227: && expr[cursor] == 'i'
1228: && expr[++cursor] == 'f'
1229: && (isWhitespace(expr[++cursor]) || expr[cursor] == '(');
1230: }
1231:
1232: if (((IfNode) (tk = _captureBlock(tk, expr, cond, type)))
1233: .getElseBlock() != null) {
1234: cursor++;
1235: return first;
1236: }
1237:
1238: if (first == null)
1239: first = tk;
1240:
1241: if (cursor != length && expr[cursor] != ';') {
1242: cursor++;
1243: }
1244: } while (ifThenElseblockContinues());
1245: return first;
1246: }
1247:
1248: default: // either BLOCK_WITH or BLOCK_FOREACH
1249: captureToNextTokenJunction();
1250: if (debugSymbols) {
1251: skipWhitespaceWithLineAccounting();
1252: } else {
1253: skipWhitespace();
1254: }
1255: return _captureBlock(null, expr, true, type);
1256: }
1257:
1258: }
1259:
1260: private ASTNode _captureBlock(ASTNode node, final char[] expr,
1261: boolean cond, int type) {
1262: skipWhitespace();
1263: int startCond = 0;
1264: int endCond = 0;
1265:
1266: int blockStart;
1267: int blockEnd;
1268:
1269: /**
1270: * Functions are a special case we handle differently from the rest of block parsing
1271: */
1272: if (type == FUNCTION) {
1273: int start = cursor;
1274:
1275: captureToNextTokenJunction();
1276:
1277: if (cursor == length) {
1278: throw new CompileException(
1279: "unexpected end of statement", expr, start);
1280: }
1281:
1282: /**
1283: * Grabe the function name.
1284: */
1285: String functionName = new String(expr, start,
1286: (startCond = cursor) - start).trim();
1287:
1288: /**
1289: * Check to see if the name is legal.
1290: */
1291: if (isReservedWord(functionName)
1292: || !isValidNameorLabel(functionName))
1293: throw new CompileException(
1294: "illegal function name or use of reserved word",
1295: expr, cursor);
1296:
1297: if (expr[cursor] == '(') {
1298: /**
1299: * If we discover an opening bracket after the function name, we check to see
1300: * if this function accepts parameters.
1301: */
1302:
1303: endCond = cursor = balancedCapture(expr,
1304: startCond = cursor, '(');
1305: startCond++;
1306: cursor++;
1307:
1308: skipWhitespace();
1309:
1310: if (cursor >= length) {
1311: throw new CompileException("unbalanced braces",
1312: expr, cursor);
1313: } else if (expr[cursor] == '{') {
1314: blockStart = cursor;
1315: blockEnd = cursor = balancedCapture(expr, cursor,
1316: '{');
1317: } else {
1318: blockStart = cursor;
1319: captureToEOS();
1320: blockEnd = cursor;
1321: }
1322: } else {
1323: /**
1324: * This function has not parameters.
1325: */
1326: if (expr[cursor] == '{') {
1327: /**
1328: * This function is bracketed. We capture the entire range in the brackets.
1329: */
1330: blockStart = cursor;
1331: blockEnd = cursor = balancedCapture(expr, cursor,
1332: '{');
1333: } else {
1334: /**
1335: * This is a single statement function declaration. We only capture the statement.
1336: */
1337: blockStart = cursor;
1338: captureToEOS();
1339: blockEnd = cursor;
1340: }
1341: }
1342:
1343: /**
1344: * Trim any whitespace from the captured block range.
1345: */
1346: blockStart = trimRight(blockStart + 1);
1347: blockEnd = trimLeft(blockEnd);
1348:
1349: cursor++;
1350:
1351: /**
1352: * Check if the function is manually terminated.
1353: */
1354: if (!isStatementManuallyTerminated()) {
1355: /**
1356: * Add an EndOfStatement to the split accumulator in the parser.
1357: */
1358: splitAccumulator.add(new EndOfStatement());
1359: }
1360:
1361: /**
1362: * Produce the funciton node.
1363: */
1364: return new Function(functionName, subArray(startCond,
1365: endCond), subArray(blockStart, blockEnd));
1366: } else if (cond) {
1367: /**
1368: * This block is an: IF, FOREACH or WHILE node.
1369: */
1370:
1371: if (debugSymbols) {
1372: int[] cap = balancedCaptureWithLineAccounting(expr,
1373: startCond = cursor, '(');
1374:
1375: endCond = cursor = cap[0];
1376:
1377: startCond++;
1378: cursor++;
1379:
1380: getParserContext().setLineCount(
1381: line = getParserContext().getLineCount()
1382: + cap[1]);
1383: } else {
1384: endCond = cursor = balancedCapture(expr,
1385: startCond = cursor, '(');
1386: startCond++;
1387: cursor++;
1388: }
1389: }
1390:
1391: skipWhitespace();
1392:
1393: if (cursor >= length) {
1394: throw new CompileException("unbalanced braces", expr,
1395: cursor);
1396: } else if (expr[cursor] == '{') {
1397: blockStart = cursor;
1398:
1399: if (debugSymbols) {
1400: int[] cap = balancedCaptureWithLineAccounting(expr,
1401: cursor, '{');
1402: blockEnd = cursor = cap[0];
1403:
1404: getParserContext().setLineCount(
1405: (line = getParserContext().getLineCount()
1406: + cap[1]));
1407: } else {
1408: blockEnd = cursor = balancedCapture(expr, cursor, '{');
1409: }
1410: } else {
1411: blockStart = cursor - 1;
1412: captureToEOSorEOL();
1413: blockEnd = cursor + 1;
1414: }
1415:
1416: if (type == ASTNode.BLOCK_IF) {
1417: IfNode ifNode = (IfNode) node;
1418:
1419: if (node != null) {
1420: if (!cond) {
1421: return ifNode.setElseBlock(subArray(
1422: trimRight(blockStart + 1),
1423: trimLeft(blockEnd - 1)));
1424: } else {
1425: return ifNode.setElseIf((IfNode) createBlockToken(
1426: startCond, endCond,
1427: trimRight(blockStart + 1),
1428: trimLeft(blockEnd), type));
1429: }
1430: } else {
1431: return createBlockToken(startCond, endCond,
1432: blockStart + 1, blockEnd, type);
1433: }
1434: }
1435: // DON"T REMOVE THIS COMMENT!
1436: // else if (isFlag(ASTNode.BLOCK_FOREACH) || isFlag(ASTNode.BLOCK_WITH)) {
1437: else {
1438: return createBlockToken(startCond, endCond,
1439: trimRight(blockStart + 1), trimLeft(blockEnd), type);
1440: }
1441: }
1442:
1443: /**
1444: * Checking from the current cursor position, check to see if the if-then-else block continues.
1445: *
1446: * @return boolean value
1447: */
1448: protected boolean ifThenElseblockContinues() {
1449: if ((cursor + 4) < length) {
1450: if (expr[cursor] != ';')
1451: cursor--;
1452: skipWhitespace();
1453: return expr[cursor] == 'e'
1454: && expr[cursor + 1] == 'l'
1455: && expr[cursor + 2] == 's'
1456: && expr[cursor + 3] == 'e'
1457: && (isWhitespace(expr[cursor + 4]) || expr[cursor + 4] == '{');
1458: }
1459: return false;
1460: }
1461:
1462: /**
1463: * Checking from the current cursor position, check to see if we're inside a contiguous identifier.
1464: *
1465: * @return
1466: */
1467: protected boolean tokenContinues() {
1468: if (cursor >= length)
1469: return false;
1470: else if (expr[cursor] == '.' || expr[cursor] == '[')
1471: return true;
1472: else if (isWhitespace(expr[cursor])) {
1473: int markCurrent = cursor;
1474: skipWhitespace();
1475: if (cursor != length
1476: && (expr[cursor] == '.' || expr[cursor] == '['))
1477: return true;
1478: cursor = markCurrent;
1479: }
1480: return false;
1481: }
1482:
1483: /**
1484: * Capture from the current cursor position, to the end of the statement.
1485: */
1486: protected void captureToEOS() {
1487: while (cursor != length) {
1488: switch (expr[cursor]) {
1489: case '(':
1490: case '[':
1491: case '{':
1492: cursor = balancedCapture(expr, cursor, expr[cursor]);
1493: break;
1494:
1495: case ';':
1496: case '}':
1497: return;
1498:
1499: }
1500: cursor++;
1501: }
1502: }
1503:
1504: /**
1505: * From the current cursor position, capture to the end of statement, or the end of line, whichever comes first.
1506: */
1507: protected void captureToEOSorEOL() {
1508: while (cursor != length
1509: && (expr[cursor] != '\n' && expr[cursor] != '\r' && expr[cursor] != ';')) {
1510: cursor++;
1511: }
1512: }
1513:
1514: /**
1515: * From the current cursor position, capture to the end of the line.
1516: */
1517: protected void captureToEOL() {
1518: while (cursor != length && (expr[cursor] != '\n'))
1519: cursor++;
1520: }
1521:
1522: /**
1523: * From the current cursor position, capture to the end of the current token.
1524: */
1525: protected void captureToEOT() {
1526: skipWhitespace();
1527: while (++cursor != length) {
1528: switch (expr[cursor]) {
1529: case '(':
1530: case '[':
1531: case '{':
1532: cursor = balancedCapture(expr, cursor, expr[cursor]);
1533: break;
1534:
1535: case '=':
1536: case '&':
1537: case '|':
1538: case ';':
1539: return;
1540:
1541: case '.':
1542: skipWhitespace();
1543: break;
1544:
1545: default:
1546: if (isWhitespace(expr[cursor])) {
1547: skipWhitespace();
1548:
1549: if (expr[cursor] == '.') {
1550: if (cursor != length)
1551: cursor++;
1552: skipWhitespace();
1553: break;
1554: } else {
1555: trimWhitespace();
1556: return;
1557: }
1558: }
1559: }
1560: }
1561: }
1562:
1563: /**
1564: * From the specified cursor position, trim out any whitespace between the current position and the end of the
1565: * last non-whitespace character.
1566: *
1567: * @param pos - current position
1568: * @return new position.
1569: */
1570: protected int trimLeft(int pos) {
1571: while (pos != 0 && isWhitespace(expr[pos - 1]))
1572: pos--;
1573: return pos;
1574: }
1575:
1576: /**
1577: * From the specified cursor position, trim out any whitespace between the current position and beginning of the
1578: * first non-whitespace character.
1579: *
1580: * @param pos
1581: * @return
1582: */
1583: protected int trimRight(int pos) {
1584: while (pos != length && isWhitespace(expr[pos]))
1585: pos++;
1586: return pos;
1587: }
1588:
1589: /**
1590: * If the cursor is currently pointing to whitespace, move the cursor forward to the first non-whitespace
1591: * character.
1592: */
1593: protected void skipWhitespace() {
1594: while (cursor != length && isWhitespace(expr[cursor]))
1595: cursor++;
1596: }
1597:
1598: /**
1599: * If the cursor is currently pointing to whitespace, move the cursor forward to the first non-whitespace
1600: * character, but account for carraige returns in the script (updates parser field: line).
1601: */
1602: protected void skipWhitespaceWithLineAccounting() {
1603: while (cursor != length && isWhitespace(expr[cursor])) {
1604: switch (expr[cursor]) {
1605: case '\n':
1606: line++;
1607: case '\r':
1608: cursor++;
1609: continue;
1610: }
1611: cursor++;
1612: }
1613: }
1614:
1615: /**
1616: * From the current cursor position, capture to the end of the next token junction.
1617: */
1618: protected void captureToNextTokenJunction() {
1619: while (cursor != length) {
1620: switch (expr[cursor]) {
1621: case '{':
1622: case '(':
1623: return;
1624: default:
1625: if (isWhitespace(expr[cursor]))
1626: return;
1627: cursor++;
1628: }
1629: }
1630: }
1631:
1632: /**
1633: * From the current cursor position, trim backward over any whitespace to the first non-whitespace character.
1634: */
1635: protected void trimWhitespace() {
1636: while (cursor != 0 && isWhitespace(expr[cursor - 1]))
1637: cursor--;
1638: }
1639:
1640: /**
1641: * Check if the specified string is a reserved word in the parser.
1642: *
1643: * @param name
1644: * @return
1645: */
1646: public static boolean isReservedWord(String name) {
1647: return LITERALS.containsKey(name)
1648: || OPERATORS.containsKey(name);
1649: }
1650:
1651: /**
1652: * Check if the specfied string represents a valid name of label.
1653: *
1654: * @param name
1655: * @return
1656: */
1657: public static boolean isValidNameorLabel(String name) {
1658: for (char c : name.toCharArray()) {
1659: if (c == '.')
1660: return false;
1661: else if (!isIdentifierPart(c))
1662: return false;
1663: }
1664: return true;
1665: }
1666:
1667: protected void setExpression(String expression) {
1668: if (expression != null && !"".equals(expression)) {
1669: this .expr = EX_PRECACHE.get(expression);
1670: if (this .expr == null) {
1671: length = (this .expr = expression.toCharArray()).length;
1672:
1673: // trim any whitespace.
1674: while (length != 0
1675: && isWhitespace(this .expr[length - 1]))
1676: length--;
1677:
1678: char[] e = new char[length];
1679: //arraycopy(this.expr, 0, e, 0, length);
1680: for (int i = 0; i != e.length; i++)
1681: e[i] = expr[i];
1682:
1683: EX_PRECACHE.put(expression, e);
1684: } else {
1685: length = this .expr.length;
1686: }
1687: }
1688: }
1689:
1690: protected void setExpression(char[] expression) {
1691: length = (this .expr = expression).length;
1692: while (length != 0 && isWhitespace(this .expr[length - 1]))
1693: length--;
1694: }
1695:
1696: /**
1697: * Return the previous non-whitespace character.
1698: *
1699: * @return
1700: */
1701: protected char lookToLast() {
1702: if (cursor == 0)
1703: return 0;
1704: int temp = cursor;
1705: while (temp != 0 && isWhitespace(expr[--temp]))
1706: ;
1707: return expr[temp];
1708: }
1709:
1710: /**
1711: * Return the last character (delta -1 of cursor position).
1712: *
1713: * @return
1714: */
1715: protected char lookBehind() {
1716: if (cursor == 0)
1717: return 0;
1718: return expr[cursor - 1];
1719: }
1720:
1721: /**
1722: * Return the next character (delta 1 of cursor position).
1723: *
1724: * @return
1725: */
1726: protected char lookAhead() {
1727: int tmp = cursor + 1;
1728: if (tmp != length)
1729: return expr[tmp];
1730: return 0;
1731: }
1732:
1733: /**
1734: * Return the character, forward of the currrent cursor position based on the specified range delta.
1735: *
1736: * @param range
1737: * @return
1738: */
1739: protected char lookAhead(int range) {
1740: if ((cursor + range) >= length)
1741: return 0;
1742: else {
1743: return expr[cursor + range];
1744: }
1745: }
1746:
1747: /**
1748: * NOTE: This method assumes that the current position of the cursor is at the end of a logical statement, to
1749: * begin with.
1750: * <p/>
1751: * Determines whether or not the logical statement is manually terminated with a statement separator (';').
1752: *
1753: * @return
1754: */
1755: protected boolean isStatementManuallyTerminated() {
1756: if (cursor >= length)
1757: return true;
1758: int c = cursor;
1759: while (c != length && isWhitespace(expr[c]))
1760: c++;
1761: return (c != length && expr[c] == ';');
1762: }
1763:
1764: /**
1765: * Returns true of if the detal 1 of the cursor matches the specified character.
1766: *
1767: * @param c
1768: * @return
1769: */
1770: protected boolean isNext(char c) {
1771: return lookAhead() == c;
1772: }
1773:
1774: protected ParserContext getParserContext() {
1775: if (parserContext == null || parserContext.get() == null) {
1776: newContext();
1777: }
1778: return parserContext.get();
1779: }
1780:
1781: public static ParserContext getCurrentThreadParserContext() {
1782: return contextControl(GET_OR_CREATE, null, null);
1783: }
1784:
1785: public static void setCurrentThreadParserContext(ParserContext pCtx) {
1786: contextControl(SET, pCtx, null);
1787: }
1788:
1789: /**
1790: * Create a new ParserContext in the current thread.
1791: */
1792: protected void newContext() {
1793: contextControl(SET, new ParserContext(), this );
1794: }
1795:
1796: /**
1797: * Create a new ParserContext in the current thread, using the one specified.
1798: *
1799: * @param pCtx
1800: */
1801: protected void newContext(ParserContext pCtx) {
1802: contextControl(SET, pCtx, this );
1803: }
1804:
1805: /**
1806: * Remove the current ParserContext from the thread.
1807: */
1808: protected void removeContext() {
1809: contextControl(REMOVE, null, this );
1810: }
1811:
1812: protected static ParserContext contextControl(int operation,
1813: ParserContext pCtx, AbstractParser parser) {
1814: synchronized (getRuntime()) {
1815: if (parserContext == null)
1816: parserContext = new ThreadLocal<ParserContext>();
1817:
1818: switch (operation) {
1819: case SET:
1820: pCtx.setRootParser(parser);
1821: parserContext.set(pCtx);
1822: return null;
1823:
1824: case REMOVE:
1825: parserContext.set(null);
1826: return null;
1827:
1828: case GET_OR_CREATE:
1829: if (parserContext.get() == null) {
1830: parserContext.set(new ParserContext(parser));
1831: }
1832:
1833: case GET:
1834: return parserContext.get();
1835: }
1836: }
1837:
1838: return null;
1839: }
1840:
1841: protected static final int SET = 0;
1842: protected static final int REMOVE = 1;
1843: protected static final int GET = 2;
1844: protected static final int GET_OR_CREATE = 3;
1845:
1846: public boolean isDebugSymbols() {
1847: return debugSymbols;
1848: }
1849:
1850: public void setDebugSymbols(boolean debugSymbols) {
1851: this .debugSymbols = debugSymbols;
1852: }
1853:
1854: protected static String getCurrentSourceFileName() {
1855: if (parserContext != null && parserContext.get() != null) {
1856: return parserContext.get().getSourceFile();
1857: }
1858: return null;
1859: }
1860:
1861: protected void addFatalError(String message) {
1862: getParserContext().addError(
1863: new ErrorDetail(getParserContext().getLineCount(),
1864: cursor - getParserContext().getLineOffset(),
1865: true, message));
1866: }
1867:
1868: protected void addFatalError(String message, int row, int cols) {
1869: getParserContext().addError(
1870: new ErrorDetail(row, cols, true, message));
1871: }
1872:
1873: protected void addWarning(String message) {
1874: getParserContext().addError(new ErrorDetail(message, false));
1875: }
1876:
1877: public static final int LEVEL_5_CONTROL_FLOW = 5;
1878: public static final int LEVEL_4_ASSIGNMENT = 4;
1879: public static final int LEVEL_3_ITERATION = 3;
1880: public static final int LEVEL_2_MULTI_STATEMENT = 2;
1881: public static final int LEVEL_1_BASIC_LANG = 1;
1882: public static final int LEVEL_0_PROPERTY_ONLY = 0;
1883:
1884: public static void setLanguageLevel(int level) {
1885: OPERATORS.clear();
1886: OPERATORS.putAll(loadLanguageFeaturesByLevel(level));
1887: }
1888:
1889: public static Map<String, Integer> loadLanguageFeaturesByLevel(
1890: int languageLevel) {
1891:
1892: Map<String, Integer> operatorsTable = new HashMap<String, Integer>();
1893:
1894: switch (languageLevel) {
1895: case 5: // control flow operations
1896: operatorsTable.put("if", IF);
1897: operatorsTable.put("else", ELSE);
1898: operatorsTable.put("?", TERNARY);
1899: operatorsTable.put("switch", SWITCH);
1900: operatorsTable.put("function", FUNCTION);
1901: operatorsTable.put("def", FUNCTION);
1902:
1903: case 4: // assignment
1904: operatorsTable.put("=", ASSIGN);
1905: operatorsTable.put("var", UNTYPED_VAR);
1906: operatorsTable.put("+=", ASSIGN_ADD);
1907: operatorsTable.put("-=", ASSIGN_SUB);
1908:
1909: case 3: // iteration
1910: operatorsTable.put("foreach", FOREACH);
1911: operatorsTable.put("while", WHILE);
1912: operatorsTable.put("for", FOR);
1913: operatorsTable.put("do", DO);
1914:
1915: case 2: // multi-statement
1916: operatorsTable.put("return", RETURN);
1917: operatorsTable.put(";", END_OF_STMT);
1918:
1919: case 1: // boolean, math ops, projection, assertion, objection creation, block setters, imports
1920: operatorsTable.put("+", ADD);
1921: operatorsTable.put("-", SUB);
1922: operatorsTable.put("*", MULT);
1923: operatorsTable.put("**", POWER);
1924: operatorsTable.put("/", DIV);
1925: operatorsTable.put("%", MOD);
1926: operatorsTable.put("==", EQUAL);
1927: operatorsTable.put("!=", NEQUAL);
1928: operatorsTable.put(">", GTHAN);
1929: operatorsTable.put(">=", GETHAN);
1930: operatorsTable.put("<", LTHAN);
1931: operatorsTable.put("<=", LETHAN);
1932: operatorsTable.put("&&", AND);
1933: operatorsTable.put("and", AND);
1934: operatorsTable.put("||", OR);
1935: operatorsTable.put("or", CHOR);
1936: operatorsTable.put("~=", REGEX);
1937: operatorsTable.put("instanceof", INSTANCEOF);
1938: operatorsTable.put("is", INSTANCEOF);
1939: operatorsTable.put("contains", CONTAINS);
1940: operatorsTable.put("soundslike", SOUNDEX);
1941: operatorsTable.put("strsim", SIMILARITY);
1942: operatorsTable.put("convertable_to", CONVERTABLE_TO);
1943:
1944: operatorsTable.put("#", STR_APPEND);
1945:
1946: operatorsTable.put("&", BW_AND);
1947: operatorsTable.put("|", BW_OR);
1948: operatorsTable.put("^", BW_XOR);
1949: operatorsTable.put("<<", BW_SHIFT_LEFT);
1950: operatorsTable.put("<<<", BW_USHIFT_LEFT);
1951: operatorsTable.put(">>", BW_SHIFT_RIGHT);
1952: operatorsTable.put(">>>", BW_USHIFT_RIGHT);
1953:
1954: operatorsTable.put("new", Operator.NEW);
1955: operatorsTable.put("in", PROJECTION);
1956:
1957: operatorsTable.put("with", WITH);
1958:
1959: operatorsTable.put("assert", ASSERT);
1960: operatorsTable.put("import", IMPORT);
1961: operatorsTable.put("import_static", IMPORT_STATIC);
1962:
1963: operatorsTable.put("++", INC);
1964: operatorsTable.put("--", DEC);
1965:
1966: case 0: // Property access and inline collections
1967: operatorsTable.put(":", TERNARY_ELSE);
1968: }
1969:
1970: return operatorsTable;
1971: }
1972:
1973: /**
1974: * Remove the current parser context from the thread.
1975: */
1976: public static void resetParserContext() {
1977: contextControl(REMOVE, null, null);
1978: }
1979: }
|