PageRenderTime 131ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/src/main/java/org/mvel2/compiler/AbstractParser.java

https://github.com/sherl0cks/mvel
Java | 2827 lines | 2265 code | 320 blank | 242 comment | 474 complexity | c69b8ca60eba176cf247ce471d7aae93 MD5 | raw file
Possible License(s): BSD-3-Clause, Apache-2.0
  1. /**
  2. * MVEL 2.0
  3. * Copyright (C) 2007 The Codehaus
  4. * Mike Brock, Dhanji Prasanna, John Graham, Mark Proctor
  5. *
  6. * Licensed under the Apache License, Version 2.0 (the "License");
  7. * you may not use this file except in compliance with the License.
  8. * You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.mvel2.compiler;
  19. import org.mvel2.CompileException;
  20. import org.mvel2.ErrorDetail;
  21. import org.mvel2.Operator;
  22. import org.mvel2.ParserContext;
  23. import org.mvel2.ast.*;
  24. import org.mvel2.integration.VariableResolverFactory;
  25. import org.mvel2.util.ErrorUtil;
  26. import org.mvel2.util.ExecutionStack;
  27. import org.mvel2.util.FunctionParser;
  28. import org.mvel2.util.ProtoParser;
  29. import java.io.Serializable;
  30. import java.util.HashMap;
  31. import java.util.WeakHashMap;
  32. import static java.lang.Boolean.FALSE;
  33. import static java.lang.Boolean.TRUE;
  34. import static java.lang.Double.parseDouble;
  35. import static java.lang.Runtime.getRuntime;
  36. import static java.lang.System.getProperty;
  37. import static java.lang.Thread.currentThread;
  38. import static org.mvel2.Operator.*;
  39. import static org.mvel2.ast.TypeDescriptor.getClassReference;
  40. import static org.mvel2.util.ArrayTools.findFirst;
  41. import static org.mvel2.util.ParseTools.*;
  42. import static org.mvel2.util.PropertyTools.isEmpty;
  43. import static org.mvel2.util.Soundex.soundex;
  44. /**
  45. * This is the core parser that the subparsers extend.
  46. *
  47. * @author Christopher Brock
  48. */
  49. public class AbstractParser implements Parser, Serializable {
  50. protected char[] expr;
  51. protected int cursor;
  52. protected int start;
  53. protected int length;
  54. protected int end;
  55. protected int st;
  56. protected int fields;
  57. protected static final int OP_OVERFLOW = -2;
  58. protected static final int OP_TERMINATE = -1;
  59. protected static final int OP_RESET_FRAME = 0;
  60. protected static final int OP_CONTINUE = 1;
  61. protected boolean greedy = true;
  62. protected boolean lastWasIdentifier = false;
  63. protected boolean lastWasLineLabel = false;
  64. protected boolean lastWasComment = false;
  65. protected boolean compileMode = false;
  66. protected int literalOnly = -1;
  67. protected int lastLineStart = 0;
  68. protected int line = 0;
  69. protected ASTNode lastNode;
  70. private static final WeakHashMap<String, char[]> EX_PRECACHE = new WeakHashMap<String, char[]>(15);
  71. public static HashMap<String, Object> LITERALS;
  72. public static HashMap<String, Object> CLASS_LITERALS;
  73. public static HashMap<String, Integer> OPERATORS;
  74. protected ExecutionStack stk;
  75. protected ExecutionStack splitAccumulator = new ExecutionStack();
  76. protected static ThreadLocal<ParserContext> parserContext;
  77. protected ParserContext pCtx;
  78. protected ExecutionStack dStack;
  79. protected Object ctx;
  80. protected VariableResolverFactory variableFactory;
  81. protected boolean debugSymbols = false;
  82. static {
  83. setupParser();
  84. }
  85. /**
  86. * This method is internally called by the static initializer for AbstractParser in order to setup the parser.
  87. * The static initialization populates the operator and literal tables for the parser. In some situations, like
  88. * OSGi, it may be necessary to utilize this manually.
  89. */
  90. public static void setupParser() {
  91. if (LITERALS == null || LITERALS.isEmpty()) {
  92. LITERALS = new HashMap<String, Object>();
  93. CLASS_LITERALS = new HashMap<String, Object>();
  94. OPERATORS = new HashMap<String, Integer>();
  95. /**
  96. * Add System and all the class wrappers from the JCL.
  97. */
  98. CLASS_LITERALS.put("System", System.class);
  99. CLASS_LITERALS.put("String", String.class);
  100. CLASS_LITERALS.put("CharSequence", CharSequence.class);
  101. CLASS_LITERALS.put("Integer", Integer.class);
  102. CLASS_LITERALS.put("int", int.class);
  103. CLASS_LITERALS.put("Long", Long.class);
  104. CLASS_LITERALS.put("long", long.class);
  105. CLASS_LITERALS.put("Boolean", Boolean.class);
  106. CLASS_LITERALS.put("boolean", boolean.class);
  107. CLASS_LITERALS.put("Short", Short.class);
  108. CLASS_LITERALS.put("short", short.class);
  109. CLASS_LITERALS.put("Character", Character.class);
  110. CLASS_LITERALS.put("char", char.class);
  111. CLASS_LITERALS.put("Double", Double.class);
  112. CLASS_LITERALS.put("double", double.class);
  113. CLASS_LITERALS.put("Float", Float.class);
  114. CLASS_LITERALS.put("float", float.class);
  115. CLASS_LITERALS.put("Byte", Byte.class);
  116. CLASS_LITERALS.put("byte", byte.class);
  117. CLASS_LITERALS.put("Math", Math.class);
  118. CLASS_LITERALS.put("Void", Void.class);
  119. CLASS_LITERALS.put("Object", Object.class);
  120. CLASS_LITERALS.put("Number", Number.class);
  121. CLASS_LITERALS.put("Class", Class.class);
  122. CLASS_LITERALS.put("ClassLoader", ClassLoader.class);
  123. CLASS_LITERALS.put("Runtime", Runtime.class);
  124. CLASS_LITERALS.put("Thread", Thread.class);
  125. CLASS_LITERALS.put("Compiler", Compiler.class);
  126. CLASS_LITERALS.put("StringBuffer", StringBuffer.class);
  127. CLASS_LITERALS.put("ThreadLocal", ThreadLocal.class);
  128. CLASS_LITERALS.put("SecurityManager", SecurityManager.class);
  129. CLASS_LITERALS.put("StrictMath", StrictMath.class);
  130. CLASS_LITERALS.put("Exception", Exception.class);
  131. CLASS_LITERALS.put("Array", java.lang.reflect.Array.class);
  132. if (parseDouble(getProperty("java.version").substring(0, 3)) >= 1.5) {
  133. try {
  134. CLASS_LITERALS.put("StringBuilder", currentThread().getContextClassLoader().loadClass("java.lang.StringBuilder"));
  135. }
  136. catch (Exception e) {
  137. throw new RuntimeException("cannot resolve a built-in literal", e);
  138. }
  139. }
  140. // Setup LITERALS
  141. LITERALS.putAll(CLASS_LITERALS);
  142. LITERALS.put("true", TRUE);
  143. LITERALS.put("false", FALSE);
  144. LITERALS.put("null", null);
  145. LITERALS.put("nil", null);
  146. LITERALS.put("empty", BlankLiteral.INSTANCE);
  147. setLanguageLevel(Boolean.getBoolean("mvel.future.lang.support") ? 6 : 5);
  148. }
  149. }
  150. protected ASTNode nextTokenSkipSymbols() {
  151. ASTNode n = nextToken();
  152. if (n != null && n.getFields() == -1) n = nextToken();
  153. return n;
  154. }
  155. /**
  156. * Retrieve the next token in the expression.
  157. *
  158. * @return -
  159. */
  160. protected ASTNode nextToken() {
  161. try {
  162. /**
  163. * If the cursor is at the end of the expression, we have nothing more to do:
  164. * return null.
  165. */
  166. if (!splitAccumulator.isEmpty()) {
  167. lastNode = (ASTNode) splitAccumulator.pop();
  168. if (cursor >= end && lastNode instanceof EndOfStatement) {
  169. return nextToken();
  170. }
  171. else {
  172. return lastNode;
  173. }
  174. }
  175. else if (cursor >= end) {
  176. return null;
  177. }
  178. int brace, idx;
  179. int tmpStart;
  180. String name;
  181. /**
  182. * Because of parser recursion for sub-expression parsing, we sometimes need to remain
  183. * certain field states. We do not reset for assignments, boolean mode, list creation or
  184. * a capture only mode.
  185. */
  186. boolean capture = false, union = false;
  187. if ((fields & ASTNode.COMPILE_IMMEDIATE) != 0 && pCtx == null) {
  188. debugSymbols = (pCtx = getParserContext()).isDebugSymbols();
  189. }
  190. if (debugSymbols) {
  191. if (!lastWasLineLabel) {
  192. if (pCtx.getSourceFile() == null) {
  193. throw new CompileException("unable to produce debugging symbols: source name must be provided.", expr, st);
  194. }
  195. if (!pCtx.isLineMapped(pCtx.getSourceFile())) {
  196. pCtx.initLineMapping(pCtx.getSourceFile(), expr);
  197. }
  198. skipWhitespace();
  199. if (cursor >= end) {
  200. return null;
  201. }
  202. int line = pCtx.getLineFor(pCtx.getSourceFile(), cursor);
  203. if (!pCtx.isVisitedLine(pCtx.getSourceFile(), pCtx.setLineCount(line)) && !pCtx.isBlockSymbols()) {
  204. lastWasLineLabel = true;
  205. pCtx.visitLine(pCtx.getSourceFile(), line);
  206. return lastNode = pCtx.setLastLineLabel(new LineLabel(pCtx.getSourceFile(), line, pCtx));
  207. }
  208. }
  209. else {
  210. lastWasComment = lastWasLineLabel = false;
  211. }
  212. }
  213. /**
  214. * Skip any whitespace currently under the starting point.
  215. */
  216. skipWhitespace();
  217. /**
  218. * From here to the end of the method is the core MVEL parsing code. Fiddling around here is asking for
  219. * trouble unless you really know what you're doing.
  220. */
  221. st = cursor;
  222. Mainloop:
  223. while (cursor != end) {
  224. if (isIdentifierPart(expr[cursor])) {
  225. capture = true;
  226. cursor++;
  227. while (cursor != end && isIdentifierPart(expr[cursor])) cursor++;
  228. }
  229. /**
  230. * If the current character under the cursor is a valid
  231. * part of an identifier, we keep capturing.
  232. */
  233. if (capture) {
  234. String t;
  235. if (OPERATORS.containsKey(t = new String(expr, st, cursor - st))) {
  236. switch (OPERATORS.get(t)) {
  237. case NEW:
  238. if (!isIdentifierPart(expr[st = cursor = trimRight(cursor)])) {
  239. throw new CompileException("unexpected character (expected identifier): "
  240. + expr[cursor], expr, st);
  241. }
  242. /**
  243. * Capture the beginning part of the token.
  244. */
  245. do {
  246. captureToNextTokenJunction();
  247. skipWhitespace();
  248. }
  249. while (cursor < end && expr[cursor] == '[');
  250. /**
  251. * If it's not a dimentioned array, continue capturing if necessary.
  252. */
  253. if (cursor < end && !lastNonWhite(']')) captureToEOT();
  254. TypeDescriptor descr = new TypeDescriptor(expr, st, trimLeft(cursor) - st, fields);
  255. if (pCtx == null) pCtx = getParserContext();
  256. if (pCtx.hasProtoImport(descr.getClassName())) {
  257. return lastNode = new NewPrototypeNode(descr, pCtx);
  258. }
  259. lastNode = new NewObjectNode(descr, fields, pCtx);
  260. skipWhitespace();
  261. if (cursor != end && expr[cursor] == '{') {
  262. if (!((NewObjectNode) lastNode).getTypeDescr().isUndimensionedArray()) {
  263. throw new CompileException(
  264. "conflicting syntax: dimensioned array with initializer block",
  265. expr, st);
  266. }
  267. st = cursor;
  268. Class egressType = lastNode.getEgressType();
  269. if (egressType == null) {
  270. try {
  271. egressType = getClassReference(pCtx, descr);
  272. }
  273. catch (ClassNotFoundException e) {
  274. throw new CompileException("could not instantiate class", expr, st, e);
  275. }
  276. }
  277. cursor = balancedCaptureWithLineAccounting(expr, st, end, expr[cursor], pCtx) + 1;
  278. if (tokenContinues()) {
  279. lastNode = new InlineCollectionNode(expr, st, cursor - st, fields,
  280. egressType, pCtx);
  281. st = cursor;
  282. captureToEOT();
  283. return lastNode = new Union(expr, st + 1, cursor, fields, lastNode, pCtx);
  284. }
  285. else {
  286. return lastNode = new InlineCollectionNode(expr, st, cursor - st, fields,
  287. egressType, pCtx);
  288. }
  289. }
  290. else if (((NewObjectNode) lastNode).getTypeDescr().isUndimensionedArray()) {
  291. throw new CompileException("array initializer expected", expr, st);
  292. }
  293. st = cursor;
  294. return lastNode;
  295. case ASSERT:
  296. st = cursor = trimRight(cursor);
  297. captureToEOS();
  298. return lastNode = new AssertNode(expr, st, cursor-- - st, fields, pCtx);
  299. case RETURN:
  300. st = cursor = trimRight(cursor);
  301. captureToEOS();
  302. return lastNode = new ReturnNode(expr, st, cursor - st, fields, pCtx);
  303. case IF:
  304. return captureCodeBlock(ASTNode.BLOCK_IF);
  305. case ELSE:
  306. throw new CompileException("else without if", expr, st);
  307. case FOREACH:
  308. return captureCodeBlock(ASTNode.BLOCK_FOREACH);
  309. case WHILE:
  310. return captureCodeBlock(ASTNode.BLOCK_WHILE);
  311. case UNTIL:
  312. return captureCodeBlock(ASTNode.BLOCK_UNTIL);
  313. case FOR:
  314. return captureCodeBlock(ASTNode.BLOCK_FOR);
  315. case WITH:
  316. return captureCodeBlock(ASTNode.BLOCK_WITH);
  317. case DO:
  318. return captureCodeBlock(ASTNode.BLOCK_DO);
  319. case STACKLANG:
  320. return captureCodeBlock(STACKLANG);
  321. case PROTO:
  322. return captureCodeBlock(PROTO);
  323. case ISDEF:
  324. st = cursor = trimRight(cursor);
  325. captureToNextTokenJunction();
  326. return lastNode = new IsDef(expr, st, cursor - st, pCtx);
  327. case IMPORT:
  328. st = cursor = trimRight(cursor);
  329. captureToEOS();
  330. ImportNode importNode = new ImportNode(expr, st, cursor - st, pCtx);
  331. if (pCtx == null) pCtx = getParserContext();
  332. if (importNode.isPackageImport()) {
  333. pCtx.addPackageImport(importNode.getPackageImport());
  334. }
  335. else {
  336. pCtx.addImport(importNode.getImportClass().getSimpleName(), importNode.getImportClass());
  337. }
  338. return lastNode = importNode;
  339. case IMPORT_STATIC:
  340. st = cursor = trimRight(cursor);
  341. captureToEOS();
  342. StaticImportNode staticImportNode = new StaticImportNode(expr, st, trimLeft(cursor) - st, pCtx);
  343. if (pCtx == null) pCtx = getParserContext();
  344. pCtx.addImport(staticImportNode.getMethod().getName(), staticImportNode.getMethod());
  345. return lastNode = staticImportNode;
  346. case FUNCTION:
  347. lastNode = captureCodeBlock(FUNCTION);
  348. st = cursor + 1;
  349. return lastNode;
  350. case UNTYPED_VAR:
  351. int end;
  352. st = cursor + 1;
  353. while (true) {
  354. captureToEOT();
  355. end = cursor;
  356. skipWhitespace();
  357. if (cursor != end && expr[cursor] == '=') {
  358. if (end == (cursor = st))
  359. throw new CompileException("illegal use of reserved word: var", expr, st);
  360. continue Mainloop;
  361. }
  362. else {
  363. name = new String(expr, st, end - st);
  364. if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  365. splitAccumulator.add(lastNode = new IndexedDeclTypedVarNode(idx, st, end - st, Object.class, pCtx));
  366. }
  367. else {
  368. splitAccumulator.add(lastNode = new DeclTypedVarNode(name, expr, st, end - st, Object.class,
  369. fields, pCtx));
  370. }
  371. }
  372. if (cursor == this.end || expr[cursor] != ',') break;
  373. else {
  374. cursor++;
  375. skipWhitespace();
  376. st = cursor;
  377. }
  378. }
  379. return (ASTNode) splitAccumulator.pop();
  380. }
  381. }
  382. skipWhitespace();
  383. /**
  384. * If we *were* capturing a token, and we just hit a non-identifier
  385. * character, we stop and figure out what to do.
  386. */
  387. if (cursor != end && expr[cursor] == '(') {
  388. cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '(', pCtx) + 1;
  389. }
  390. /**
  391. * If we encounter any of the following cases, we are still dealing with
  392. * a contiguous token.
  393. */
  394. CaptureLoop:
  395. while (cursor != end) {
  396. switch (expr[cursor]) {
  397. case '.':
  398. union = true;
  399. cursor++;
  400. skipWhitespace();
  401. continue;
  402. case '?':
  403. if (lookToLast() == '.' || cursor == start) {
  404. union = true;
  405. cursor++;
  406. continue;
  407. }
  408. else {
  409. break CaptureLoop;
  410. }
  411. case '+':
  412. switch (lookAhead()) {
  413. case '+':
  414. name = new String(subArray(st, trimLeft(cursor)));
  415. if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  416. lastNode = new IndexedPostFixIncNode(idx, pCtx);
  417. }
  418. else {
  419. lastNode = new PostFixIncNode(name, pCtx);
  420. }
  421. cursor += 2;
  422. expectEOS();
  423. return lastNode;
  424. case '=':
  425. name = createStringTrimmed(expr, st, cursor - st);
  426. st = cursor += 2;
  427. captureToEOS();
  428. if (union) {
  429. return lastNode = new DeepAssignmentNode(expr, st = trimRight(st), trimLeft(cursor) - st, fields,
  430. ADD, name, pCtx);
  431. }
  432. else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  433. return lastNode = new IndexedAssignmentNode(expr, st, cursor - st, fields,
  434. ADD, name, idx, pCtx);
  435. }
  436. else {
  437. return lastNode = new OperativeAssign(name, expr, st = trimRight(st), trimLeft(cursor) - st,
  438. ADD, fields, pCtx);
  439. }
  440. }
  441. if (isDigit(lookAhead()) &&
  442. cursor > 1 && (expr[cursor - 1] == 'E' || expr[cursor - 1] == 'e')
  443. && isDigit(expr[cursor - 2])) {
  444. cursor++;
  445. // capture = true;
  446. continue Mainloop;
  447. }
  448. break CaptureLoop;
  449. case '-':
  450. switch (lookAhead()) {
  451. case '-':
  452. name = new String(subArray(st, trimLeft(cursor)));
  453. if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  454. lastNode = new IndexedPostFixDecNode(idx, pCtx);
  455. }
  456. else {
  457. lastNode = new PostFixDecNode(name, pCtx);
  458. }
  459. cursor += 2;
  460. expectEOS();
  461. return lastNode;
  462. case '=':
  463. name = new String(expr, st, trimLeft(cursor) - st);
  464. st = cursor += 2;
  465. captureToEOS();
  466. if (union) {
  467. return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
  468. SUB, t, pCtx);
  469. }
  470. else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  471. return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
  472. SUB, idx, fields, pCtx);
  473. }
  474. else {
  475. return lastNode = new OperativeAssign(name, expr, st, cursor - st,
  476. SUB, fields, pCtx);
  477. }
  478. }
  479. if (isDigit(lookAhead()) &&
  480. cursor > 1 && (expr[cursor - 1] == 'E' || expr[cursor - 1] == 'e')
  481. && isDigit(expr[cursor - 2])) {
  482. cursor++;
  483. capture = true;
  484. continue Mainloop;
  485. }
  486. break CaptureLoop;
  487. /**
  488. * Exit immediately for any of these cases.
  489. */
  490. case '!':
  491. case ',':
  492. case '"':
  493. case '\'':
  494. case ';':
  495. case ':':
  496. break CaptureLoop;
  497. case '\u00AB': // special compact code for recursive parses
  498. case '\u00BB':
  499. case '\u00AC':
  500. case '&':
  501. case '^':
  502. case '|':
  503. case '*':
  504. case '/':
  505. case '%':
  506. char op = expr[cursor];
  507. if (lookAhead() == '=') {
  508. name = new String(expr, st, trimLeft(cursor) - st);
  509. st = cursor += 2;
  510. captureToEOS();
  511. if (union) {
  512. return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
  513. opLookup(op), t, pCtx);
  514. }
  515. else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  516. return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
  517. opLookup(op), idx, fields, pCtx);
  518. }
  519. else {
  520. return lastNode = new OperativeAssign(name, expr, st, cursor - st,
  521. opLookup(op), fields, pCtx);
  522. }
  523. }
  524. break CaptureLoop;
  525. case '<':
  526. if ((lookAhead() == '<' && lookAhead(2) == '=')) {
  527. name = new String(expr, st, trimLeft(cursor) - st);
  528. st = cursor += 3;
  529. captureToEOS();
  530. if (union) {
  531. return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
  532. BW_SHIFT_LEFT, t, pCtx);
  533. }
  534. else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  535. return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
  536. BW_SHIFT_LEFT, idx, fields, pCtx);
  537. }
  538. else {
  539. return lastNode = new OperativeAssign(name, expr, st, cursor - st,
  540. BW_SHIFT_LEFT, fields, pCtx);
  541. }
  542. }
  543. break CaptureLoop;
  544. case '>':
  545. if (lookAhead() == '>') {
  546. if (lookAhead(2) == '=') {
  547. name = new String(expr, st, trimLeft(cursor) - st);
  548. st = cursor += 3;
  549. captureToEOS();
  550. if (union) {
  551. return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
  552. BW_SHIFT_RIGHT, t, pCtx);
  553. }
  554. else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  555. return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
  556. BW_SHIFT_RIGHT, idx, fields, pCtx);
  557. }
  558. else {
  559. return lastNode = new OperativeAssign(name, expr, st, cursor - st,
  560. BW_SHIFT_RIGHT, fields, pCtx);
  561. }
  562. }
  563. else if ((lookAhead(2) == '>' && lookAhead(3) == '=')) {
  564. name = new String(expr, st, trimLeft(cursor) - st);
  565. st = cursor += 4;
  566. captureToEOS();
  567. if (union) {
  568. return lastNode = new DeepAssignmentNode(expr, st, cursor - st, fields,
  569. BW_USHIFT_RIGHT, t, pCtx);
  570. }
  571. else if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  572. return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
  573. BW_USHIFT_RIGHT, idx, fields, pCtx);
  574. }
  575. else {
  576. return lastNode = new OperativeAssign(name, expr, st, cursor - st,
  577. BW_USHIFT_RIGHT, fields, pCtx);
  578. }
  579. }
  580. }
  581. break CaptureLoop;
  582. case '(':
  583. cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '(', pCtx) + 1;
  584. continue;
  585. case '[':
  586. cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '[', pCtx) + 1;
  587. continue;
  588. case '{':
  589. if (!union) break CaptureLoop;
  590. cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '{', pCtx) + 1;
  591. continue;
  592. case '~':
  593. if (lookAhead() == '=') {
  594. // tmp = subArray(start, trimLeft(cursor));
  595. tmpStart = st;
  596. int tmpOffset = cursor - st;
  597. st = cursor += 2;
  598. captureToEOT();
  599. return lastNode = new RegExMatch(expr, tmpStart, tmpOffset, fields, st, cursor - st, pCtx);
  600. }
  601. break CaptureLoop;
  602. case '=':
  603. if (lookAhead() == '+') {
  604. name = new String(expr, st, trimLeft(cursor) - st);
  605. st = cursor += 2;
  606. if (!isNextIdentifierOrLiteral()) {
  607. throw new CompileException("unexpected symbol '" + expr[cursor] + "'", expr, st);
  608. }
  609. captureToEOS();
  610. if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  611. return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
  612. ADD, idx, fields, pCtx);
  613. }
  614. else {
  615. return lastNode = new OperativeAssign(name, expr, st, cursor - st,
  616. ADD, fields, pCtx);
  617. }
  618. }
  619. else if (lookAhead() == '-') {
  620. name = new String(expr, st, trimLeft(cursor) - st);
  621. st = cursor += 2;
  622. if (!isNextIdentifierOrLiteral()) {
  623. throw new CompileException("unexpected symbol '" + expr[cursor] + "'", expr, st);
  624. }
  625. captureToEOS();
  626. if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  627. return lastNode = new IndexedOperativeAssign(expr, st, cursor - st,
  628. SUB, idx, fields, pCtx);
  629. }
  630. else {
  631. return lastNode = new OperativeAssign(name, expr, st, cursor - st,
  632. SUB, fields, pCtx);
  633. }
  634. }
  635. if (greedy && lookAhead() != '=') {
  636. cursor++;
  637. if (union) {
  638. captureToEOS();
  639. return lastNode = new DeepAssignmentNode(expr, st, cursor - st,
  640. fields | ASTNode.ASSIGN, pCtx);
  641. }
  642. else if (lastWasIdentifier) {
  643. return procTypedNode(false);
  644. }
  645. else if (pCtx != null && ((idx = pCtx.variableIndexOf(t)) != -1
  646. && (pCtx.isIndexAllocation()))) {
  647. captureToEOS();
  648. IndexedAssignmentNode ian = new IndexedAssignmentNode(expr, st = trimRight(st),
  649. trimLeft(cursor) - st,
  650. ASTNode.ASSIGN, idx, pCtx);
  651. if (idx == -1) {
  652. pCtx.addIndexedInput(t = ian.getAssignmentVar());
  653. ian.setRegister(pCtx.variableIndexOf(t));
  654. }
  655. return lastNode = ian;
  656. }
  657. else {
  658. captureToEOS();
  659. return lastNode = new AssignmentNode(expr, st, cursor - st,
  660. fields | ASTNode.ASSIGN, pCtx);
  661. }
  662. }
  663. break CaptureLoop;
  664. default:
  665. if (cursor != end) {
  666. if (isIdentifierPart(expr[cursor])) {
  667. if (!union) {
  668. break CaptureLoop;
  669. }
  670. cursor++;
  671. while (cursor != end && isIdentifierPart(expr[cursor])) cursor++;
  672. }
  673. else if ((cursor + 1) != end && isIdentifierPart(expr[cursor + 1])) {
  674. break CaptureLoop;
  675. }
  676. else {
  677. cursor++;
  678. }
  679. }
  680. else {
  681. break CaptureLoop;
  682. }
  683. }
  684. }
  685. /**
  686. * Produce the token.
  687. */
  688. trimWhitespace();
  689. return createPropertyToken(st, cursor);
  690. }
  691. else {
  692. switch (expr[cursor]) {
  693. case '.': {
  694. cursor++;
  695. if (isDigit(expr[cursor])) {
  696. capture = true;
  697. continue;
  698. }
  699. expectNextChar_IW('{');
  700. return lastNode = new ThisWithNode(expr, st, cursor - st - 1
  701. , cursor + 1,
  702. (cursor = balancedCaptureWithLineAccounting(expr,
  703. cursor, end, '{', pCtx) + 1) - 3, fields, pCtx);
  704. }
  705. case '@': {
  706. st++;
  707. captureToEOT();
  708. if (pCtx == null || (pCtx.getInterceptors() == null || !pCtx.getInterceptors().
  709. containsKey(name = new String(expr, st, cursor - st)))) {
  710. throw new CompileException("reference to undefined interceptor: "
  711. + new String(expr, st, cursor - st), expr, st);
  712. }
  713. return lastNode = new InterceptorWrapper(pCtx.getInterceptors().get(name), nextToken(), pCtx);
  714. }
  715. case '=':
  716. return createOperator(expr, st, (cursor += 2));
  717. case '-':
  718. if (lookAhead() == '-') {
  719. cursor += 2;
  720. skipWhitespace();
  721. st = cursor;
  722. captureIdentifier();
  723. name = new String(subArray(st, cursor));
  724. if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  725. return lastNode = new IndexedPreFixDecNode(idx, pCtx);
  726. }
  727. else {
  728. return lastNode = new PreFixDecNode(name, pCtx);
  729. }
  730. }
  731. else if ((cursor == start || (lastNode != null &&
  732. (lastNode instanceof BooleanNode || lastNode.isOperator())))
  733. && !isDigit(lookAhead())) {
  734. captureToEOT();
  735. return new Sign(expr, st, cursor - st, fields, pCtx);
  736. }
  737. else if ((cursor != start &&
  738. (lastNode != null && !(lastNode instanceof BooleanNode || lastNode.isOperator())))
  739. || !isDigit(lookAhead())) {
  740. return createOperator(expr, st, cursor++ + 1);
  741. }
  742. else if ((cursor - 1) != start || (!isDigit(expr[cursor - 1])) && isDigit(lookAhead())) {
  743. cursor++;
  744. break;
  745. }
  746. else {
  747. throw new CompileException("not a statement", expr, st);
  748. }
  749. case '+':
  750. if (lookAhead() == '+') {
  751. cursor += 2;
  752. skipWhitespace();
  753. st = cursor;
  754. captureIdentifier();
  755. name = new String(subArray(st, cursor));
  756. if (pCtx != null && (idx = pCtx.variableIndexOf(name)) != -1) {
  757. return lastNode = new IndexedPreFixIncNode(idx, pCtx);
  758. }
  759. else {
  760. return lastNode = new PreFixIncNode(name, pCtx);
  761. }
  762. }
  763. return createOperator(expr, st, cursor++ + 1);
  764. case '*':
  765. if (lookAhead() == '*') {
  766. cursor++;
  767. }
  768. return createOperator(expr, st, cursor++ + 1);
  769. case ';':
  770. cursor++;
  771. lastWasIdentifier = false;
  772. return lastNode = new EndOfStatement(pCtx);
  773. case '?':
  774. if (cursor == start) {
  775. cursor++;
  776. continue;
  777. }
  778. case '#':
  779. case '/':
  780. case ':':
  781. case '^':
  782. case '%': {
  783. return createOperator(expr, st, cursor++ + 1);
  784. }
  785. case '(': {
  786. cursor++;
  787. boolean singleToken = true;
  788. skipWhitespace();
  789. for (brace = 1; cursor != end && brace != 0; cursor++) {
  790. switch (expr[cursor]) {
  791. case '(':
  792. brace++;
  793. break;
  794. case ')':
  795. brace--;
  796. break;
  797. case '\'':
  798. cursor = captureStringLiteral('\'', expr, cursor, end);
  799. break;
  800. case '"':
  801. cursor = captureStringLiteral('"', expr, cursor, end);
  802. break;
  803. case 'i':
  804. if (brace == 1 && isWhitespace(lookBehind()) && lookAhead() == 'n' && isWhitespace(lookAhead(2))) {
  805. for (int level = brace; cursor != end; cursor++) {
  806. switch (expr[cursor]) {
  807. case '(':
  808. brace++;
  809. break;
  810. case ')':
  811. if (--brace < level) {
  812. cursor++;
  813. if (tokenContinues()) {
  814. lastNode = new Fold(expr, trimRight(st + 1),
  815. cursor - st - 2, fields, pCtx);
  816. if (expr[st = cursor] == '.') st++;
  817. captureToEOT();
  818. return lastNode = new Union(expr, st = trimRight(st),
  819. cursor - st, fields, lastNode, pCtx);
  820. }
  821. else {
  822. return lastNode = new Fold(expr, trimRight(st + 1),
  823. cursor - st - 2, fields, pCtx);
  824. }
  825. }
  826. break;
  827. case '\'':
  828. cursor = captureStringLiteral('\'', expr, cursor, end);
  829. break;
  830. case '"':
  831. cursor = captureStringLiteral('\"', expr, cursor, end);
  832. break;
  833. }
  834. }
  835. throw new CompileException("unterminated projection; closing parathesis required",
  836. expr, st);
  837. }
  838. break;
  839. default:
  840. /**
  841. * Check to see if we should disqualify this current token as a potential
  842. * type-cast candidate.
  843. */
  844. if (expr[cursor] != '.') {
  845. switch (expr[cursor]) {
  846. case '[':
  847. case ']':
  848. break;
  849. default:
  850. if (!(isIdentifierPart(expr[cursor]) || expr[cursor] == '.')) {
  851. singleToken = false;
  852. }
  853. }
  854. }
  855. }
  856. }
  857. if (brace != 0) {
  858. throw new CompileException("unbalanced braces in expression: (" + brace + "):",
  859. expr, st);
  860. }
  861. tmpStart = -1;
  862. if (singleToken) {
  863. int _st;
  864. TypeDescriptor tDescr = new TypeDescriptor(expr, _st = trimRight(st + 1),
  865. trimLeft(cursor - 1) - _st, fields);
  866. Class cls;
  867. try {
  868. if (tDescr.isClass() && (cls = getClassReference(pCtx, tDescr)) != null) {
  869. // lookahead to check if it could be a real cast
  870. boolean isCast = false;
  871. for (int i = cursor; i < expr.length; i++) {
  872. if (expr[i] == ' ' || expr[i] == '\t') continue;
  873. isCast = isIdentifierPart(expr[i]) || expr[i] == '\'' || expr[i] == '"' || expr[i] == '(';
  874. break;
  875. }
  876. if (isCast) {
  877. st = cursor;
  878. captureToEOT();
  879. // captureToEOS();
  880. return lastNode = new TypeCast(expr, st, cursor - st,
  881. cls, fields, pCtx);
  882. }
  883. }
  884. }
  885. catch (ClassNotFoundException e) {
  886. // fallthrough
  887. }
  888. }
  889. if (tmpStart != -1) {
  890. return handleUnion(handleSubstatement(new Substatement(expr, tmpStart, cursor - tmpStart, fields, pCtx)));
  891. }
  892. else {
  893. return handleUnion(
  894. handleSubstatement(
  895. new Substatement(expr, st = trimRight(st + 1),
  896. trimLeft(cursor - 1) - st, fields, pCtx)));
  897. }
  898. }
  899. case '}':
  900. case ']':
  901. case ')': {
  902. throw new CompileException("unbalanced braces", expr, st);
  903. }
  904. case '>': {
  905. switch (expr[cursor + 1]) {
  906. case '>':
  907. if (expr[cursor += 2] == '>') cursor++;
  908. return createOperator(expr, st, cursor);
  909. case '=':
  910. return createOperator(expr, st, cursor += 2);
  911. default:
  912. return createOperator(expr, st, ++cursor);
  913. }
  914. }
  915. case '<': {
  916. if (expr[++cursor] == '<') {
  917. if (expr[++cursor] == '<') cursor++;
  918. return createOperator(expr, st, cursor);
  919. }
  920. else if (expr[cursor] == '=') {
  921. return createOperator(expr, st, ++cursor);
  922. }
  923. else {
  924. return createOperator(expr, st, cursor);
  925. }
  926. }
  927. case '\'':
  928. case '"':
  929. lastNode = new LiteralNode(handleStringEscapes(subset(expr, st + 1,
  930. (cursor = captureStringLiteral(expr[cursor], expr, cursor, end)) - st - 1))
  931. , String.class, pCtx);
  932. cursor++;
  933. if (tokenContinues()) {
  934. return lastNode = handleUnion(lastNode);
  935. }
  936. return lastNode;
  937. case '&': {
  938. if (expr[cursor++ + 1] == '&') {
  939. return createOperator(expr, st, ++cursor);
  940. }
  941. else {
  942. return createOperator(expr, st, cursor);
  943. }
  944. }
  945. case '|': {
  946. if (expr[cursor++ + 1] == '|') {
  947. return createOperator(expr, st, ++cursor);
  948. }
  949. else {
  950. return createOperator(expr, st, cursor);
  951. }
  952. }
  953. case '~':
  954. if ((cursor++ - 1 != 0 || !isIdentifierPart(lookBehind()))
  955. && isDigit(expr[cursor])) {
  956. st = cursor;
  957. captureToEOT();
  958. return lastNode = new Invert(expr, st, cursor - st, fields, pCtx);
  959. }
  960. else if (expr[cursor] == '(') {
  961. st = cursor--;
  962. captureToEOT();
  963. return lastNode = new Invert(expr, st, cursor - st, fields, pCtx);
  964. }
  965. else {
  966. if (expr[cursor] == '=') cursor++;
  967. return createOperator(expr, st, cursor);
  968. }
  969. case '!': {
  970. ++cursor;
  971. if (isNextIdentifier()) {
  972. if (lastNode != null && !lastNode.isOperator()) {
  973. throw new CompileException("unexpected operator '!'", expr, st);
  974. }
  975. st = cursor;
  976. captureToEOT();
  977. if ("new".equals(name = new String(expr, st, cursor - st))
  978. || "isdef".equals(name)) {
  979. captureToEOT();
  980. return lastNode = new Negation(expr, st, cursor - st, fields, pCtx);
  981. }
  982. else {
  983. return lastNode = new Negation(expr, st, cursor - st, fields, pCtx);
  984. }
  985. }
  986. else if (expr[cursor] == '(') {
  987. st = cursor--;
  988. captureToEOT();
  989. return lastNode = new Negation(expr, st, cursor - st, fields, pCtx);
  990. }
  991. else if (expr[cursor] == '!') {
  992. // just ignore a double negation
  993. ++cursor;
  994. return nextToken();
  995. }
  996. else if (expr[cursor] != '=')
  997. throw new CompileException("unexpected operator '!'", expr, st, null);
  998. else {
  999. return createOperator(expr, st, ++cursor);
  1000. }
  1001. }
  1002. case '[':
  1003. case '{':
  1004. cursor = balancedCaptureWithLineAccounting(expr, cursor, end, expr[cursor], pCtx) + 1;
  1005. if (tokenContinues()) {
  1006. lastNode = new InlineCollectionNode(expr, st, cursor - st, fields, pCtx);
  1007. st = cursor;
  1008. captureToEOT();
  1009. if (expr[st] == '.') st++;
  1010. return lastNode = new Union(expr, st, cursor - st, fields, lastNode, pCtx);
  1011. }
  1012. else {
  1013. return lastNode = new InlineCollectionNode(expr, st, cursor - st, fields, pCtx);
  1014. }
  1015. default:
  1016. cursor++;
  1017. }
  1018. }
  1019. }
  1020. if (st == cursor)
  1021. return null;
  1022. else
  1023. return createPropertyToken(st, cursor);
  1024. }
  1025. catch (RedundantCodeException e) {
  1026. return nextToken();
  1027. }
  1028. catch (NumberFormatException e) {
  1029. throw new CompileException("badly formatted number: " + e.getMessage(), expr, st, e);
  1030. }
  1031. catch (StringIndexOutOfBoundsException e) {
  1032. throw new CompileException("unexpected end of statement", expr, cursor, e);
  1033. }
  1034. catch (ArrayIndexOutOfBoundsException e) {
  1035. throw new CompileException("unexpected end of statement", expr, cursor, e);
  1036. }
  1037. catch (CompileException e) {
  1038. throw ErrorUtil.rewriteIfNeeded(e, expr, cursor);
  1039. }
  1040. }
  1041. public ASTNode handleSubstatement(Substatement stmt) {
  1042. if (stmt.getStatement() != null && stmt.getStatement().isLiteralOnly()) {
  1043. return new LiteralNode(stmt.getStatement().getValue(null, null, null), getParserContext());
  1044. }
  1045. else {
  1046. return stmt;
  1047. }
  1048. }
  1049. /**
  1050. * Handle a union between a closed statement and a residual property chain.
  1051. *
  1052. * @param node an ast node
  1053. * @return ASTNode
  1054. */
  1055. protected ASTNode handleUnion(ASTNode node) {
  1056. if (cursor != end) {
  1057. skipWhitespace();
  1058. int union = -1;
  1059. if (cursor < end) {
  1060. switch (expr[cursor]) {
  1061. case '.':
  1062. union = cursor + 1;
  1063. break;
  1064. case '[':
  1065. union = cursor;
  1066. }
  1067. }
  1068. if (union != -1) {
  1069. captureToEOT();
  1070. return lastNode = new Union(expr, union, cursor - union, fields, node, getParserContext());
  1071. }
  1072. }
  1073. return lastNode = node;
  1074. }
  1075. /**
  1076. * Create an operator node.
  1077. *
  1078. * @param expr an char[] containing the expression
  1079. * @param start the start offet for the token
  1080. * @param end the end offset for the token
  1081. * @return ASTNode
  1082. */
  1083. private ASTNode createOperator(final char[] expr, final int start, final int end) {
  1084. lastWasIdentifier = false;
  1085. return lastNode = new OperatorNode(OPERATORS.get(new String(expr, start, end - start)), expr, start, getParserContext());
  1086. }
  1087. /**
  1088. * Create a copy of an array based on a sub-range. Works faster than System.arrayCopy() for arrays shorter than
  1089. * 1000 elements in most cases, so the parser uses this internally.
  1090. *
  1091. * @param start the start offset
  1092. * @param end the end offset
  1093. * @return an array
  1094. */
  1095. private char[] subArray(final int start, final int end) {
  1096. if (start >= end) return new char[0];
  1097. char[] newA = new char[end - start];
  1098. for (int i = 0; i != newA.length; i++) {
  1099. newA[i] = expr[i + start];
  1100. }
  1101. return newA;
  1102. }
  1103. /**
  1104. * Generate a property token
  1105. *
  1106. * @param st the start offset
  1107. * @param end the end offset
  1108. * @return an ast node
  1109. */
  1110. private ASTNode createPropertyToken(int st, int end) {
  1111. String tmp;
  1112. if (isPropertyOnly(expr, st, end)) {
  1113. if (pCtx != null && pCtx.hasImports()) {
  1114. int find;
  1115. if ((find = findFirst('.', st, end - st, expr)) != -1) {
  1116. String iStr = new String(expr, st, find - st);
  1117. if (pCtx.hasImport(iStr)) {
  1118. lastWasIdentifier = true;
  1119. return lastNode = new LiteralDeepPropertyNode(expr, find + 1, end - find - 1, fields,
  1120. pCtx.getImport(iStr), pCtx);
  1121. }
  1122. }
  1123. else {
  1124. if (pCtx.hasImport(tmp = new String(expr, st, cursor - st))) {
  1125. lastWasIdentifier = true;
  1126. return lastNode = new LiteralNode(pCtx.getStaticOrClassImport(tmp), pCtx);
  1127. }
  1128. }
  1129. }
  1130. if (LITERALS.containsKey(tmp = new String(expr, st, end - st))) {
  1131. lastWasIdentifier = true;
  1132. return lastNode = new LiteralNode(LITERALS.get(tmp), pCtx);
  1133. }
  1134. else if (OPERATORS.containsKey(tmp)) {
  1135. lastWasIdentifier = false;
  1136. return lastNode = new OperatorNode(OPERATORS.get(tmp), expr, st, pCtx);
  1137. }
  1138. else if (lastWasIdentifier) {
  1139. return procTypedNode(true);
  1140. }
  1141. }
  1142. if (pCtx != null && pCtx.hasImports() && isArrayType(expr, st, end)) {
  1143. if (pCtx.hasImport(new String(expr, st, cursor - st - 2))) {
  1144. lastWasIdentifier = true;
  1145. TypeDescriptor typeDescriptor = new TypeDescriptor(expr, st, cursor - st, fields);
  1146. try {
  1147. return lastNode = new LiteralNode(typeDescriptor.getClassReference(pCtx), pCtx);
  1148. }
  1149. catch (ClassNotFoundException e) {
  1150. throw new CompileException("could not resolve class: " + typeDescriptor.getClassName(), expr, st);
  1151. }
  1152. }
  1153. }
  1154. lastWasIdentifier = true;
  1155. return lastNode = new ASTNode(expr, trimRight(st), trimLeft(end) - st, fields, pCtx);
  1156. }
  1157. /**
  1158. * Process the current typed node
  1159. *
  1160. * @param decl node is a declaration or not
  1161. * @return and ast node
  1162. */
  1163. private ASTNode procTypedNode(boolean decl) {
  1164. while (true) {
  1165. if (lastNode.getLiteralValue() instanceof String) {
  1166. char[] tmp = ((String) lastNode.getLiteralValue()).toCharArray();
  1167. TypeDescriptor tDescr = new TypeDescriptor(tmp, 0, tmp.length, 0);
  1168. try {
  1169. lastNode.setLiteralValue(getClassReference(pCtx, tDescr));
  1170. lastNode.discard();
  1171. }
  1172. catch (Exception e) {
  1173. // fall through;
  1174. }
  1175. }
  1176. if (lastNode.isLiteral() && lastNode.getLiteralValue() instanceof Class) {
  1177. lastNode.discard();
  1178. captureToEOS();
  1179. if (decl) {
  1180. splitAccumulator.add(new DeclTypedVarNode(new String(expr, st, cursor - st), expr, st, cursor - st,
  1181. (Class) lastNode.getLiteralValue(), fields | ASTNode.ASSIGN, pCtx));
  1182. }
  1183. else {
  1184. captureToEOS();
  1185. splitAccumulator.add(new TypedVarNode(expr, st, cursor - st - 1, fields | ASTNode.ASSIGN, (Class)
  1186. lastNode.getLiteralValue(), pCtx));
  1187. }
  1188. }
  1189. else if (lastNode instanceof Proto) {
  1190. captureToEOS();
  1191. if (decl) {
  1192. splitAccumulator.add(new DeclProtoVarNode(new String(expr, st, cursor - st),
  1193. (Proto) lastNode, fields | ASTNode.ASSIGN, pCtx));
  1194. }
  1195. else {
  1196. splitAccumulator.add(new ProtoVarNode(expr, st, cursor - st, fields | ASTNode.ASSIGN, (Proto)
  1197. lastNode, pCtx));
  1198. }
  1199. }
  1200. // this redundant looking code is needed to work with the interpreter and MVELSH properly.
  1201. else if ((fields & ASTNode.COMPILE_IMMEDIATE) == 0) {
  1202. if (stk.peek() instanceof Class) {
  1203. captureToEOS();
  1204. if (decl) {
  1205. splitAccumulator.add(new DeclTypedVarNode(new String(expr, st, cursor - st), expr, st, cursor - st,
  1206. (Class) stk.pop(), fields | ASTNode.ASSIGN, pCtx));
  1207. }
  1208. else {
  1209. splitAccumulator.add(new TypedVarNode(expr, st, cursor - st,
  1210. fields | ASTNode.ASSIGN, (Class) stk.pop(), pCtx));
  1211. }
  1212. }
  1213. else if (stk.peek() instanceof Proto) {
  1214. captureToEOS();
  1215. if (decl) {
  1216. splitAccumulator.add(new DeclProtoVarNode(new String(expr, st, cursor - st),
  1217. (Proto) stk.pop(), fields | ASTNode.ASSIGN, pCtx));
  1218. }
  1219. else {
  1220. splitAccumulator.add(new ProtoVarNode(expr, st, cursor - st, fields | ASTNode.ASSIGN, (Proto)
  1221. stk.pop(), pCtx));
  1222. }
  1223. }
  1224. else {
  1225. throw new CompileException("unknown class or illegal statement: " + lastNode.getLiteralValue(), expr, cursor);
  1226. }
  1227. }
  1228. else {
  1229. throw new CompileException("unknown class or illegal statement: " + lastNode.getLiteralValue(), expr, cursor);
  1230. }
  1231. skipWhitespace();
  1232. if (cursor < end && expr[cursor] == ',') {
  1233. st = ++cursor;
  1234. splitAccumulator.add(new EndOfStatement(pCtx));
  1235. }
  1236. else {
  1237. return (ASTNode) splitAccumulator.pop();
  1238. }
  1239. }
  1240. }
  1241. /**
  1242. * Generate a code block token.
  1243. *
  1244. * @param condStart the start offset for the condition
  1245. * @param condEnd the end offset for the condition
  1246. * @param blockStart the start offset for the block
  1247. * @param blockEnd the end offset for the block
  1248. * @param type the type of block
  1249. * @return and ast node
  1250. */
  1251. private ASTNode createBlockToken(final int condStart,
  1252. final int condEnd, final int blockStart, final int blockEnd, int type) {
  1253. lastWasIdentifier = false;
  1254. cursor++;
  1255. if (isStatementNotManuallyTerminated()) {
  1256. splitAccumulator.add(new EndOfStatement(pCtx));
  1257. }
  1258. int condOffset = condEnd - condStart;
  1259. int blockOffset = blockEnd - blockStart;
  1260. if (blockOffset < 0) blockOffset = 0;
  1261. switch (type) {
  1262. case ASTNode.BLOCK_IF:
  1263. return new IfNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
  1264. case ASTNode.BLOCK_FOR:
  1265. for (int i = condStart; i < condEnd; i++) {
  1266. if (expr[i] == ';')
  1267. return new ForNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
  1268. else if (expr[i] == ':')
  1269. break;
  1270. }
  1271. case ASTNode.BLOCK_FOREACH:
  1272. return new ForEachNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
  1273. case ASTNode.BLOCK_WHILE:
  1274. return new WhileNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
  1275. case ASTNode.BLOCK_UNTIL:
  1276. return new UntilNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
  1277. case ASTNode.BLOCK_DO:
  1278. return new DoNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
  1279. case ASTNode.BLOCK_DO_UNTIL:
  1280. return new DoUntilNode(expr, condStart, condOffset, blockStart, blockOffset, pCtx);
  1281. default:
  1282. return new WithNode(expr, condStart, condOffset, blockStart, blockOffset, fields, pCtx);
  1283. }
  1284. }
  1285. /**
  1286. * Capture a code block by type.
  1287. *
  1288. * @param type the block type
  1289. * @return an ast node
  1290. */
  1291. private ASTNode captureCodeBlock(int type) {
  1292. boolean cond = true;
  1293. ASTNode first = null;
  1294. ASTNode tk = null;
  1295. switch (type) {
  1296. case ASTNode.BLOCK_IF: {
  1297. do {
  1298. if (tk != null) {
  1299. captureToNextTokenJunction();
  1300. skipWhitespace();
  1301. cond = expr[cursor] != '{' && expr[cursor] == 'i' && expr[++cursor] == 'f'
  1302. && expr[cursor = incNextNonBlank()] == '(';
  1303. }
  1304. if (((IfNode) (tk = _captureBlock(tk, expr, cond, type))).getElseBlock() != null) {
  1305. cursor++;
  1306. return first;
  1307. }
  1308. if (first == null) first = tk;
  1309. if (cursor != end && expr[cursor] != ';') {
  1310. cursor++;
  1311. }
  1312. }
  1313. while (ifThenElseBlockContinues());
  1314. return first;
  1315. }
  1316. case ASTNode.BLOCK_DO:
  1317. skipWhitespace();
  1318. return _captureBlock(null, expr, false, type);
  1319. default: // either BLOCK_WITH or BLOCK_FOREACH
  1320. captureToNextTokenJunction();
  1321. skipWhitespace();
  1322. return _captureBlock(null, expr, true, type);
  1323. }
  1324. }
  1325. private ASTNode _captureBlock(ASTNode node, final char[] expr, boolean cond, int type) {
  1326. skipWhitespace();
  1327. int startCond = 0;
  1328. int endCond = 0;
  1329. int blockStart;
  1330. int blockEnd;
  1331. String name;
  1332. /**
  1333. * Functions are a special case we handle differently from the rest of block parsing
  1334. */
  1335. switch (type) {
  1336. case FUNCTION: {
  1337. int st = cursor;
  1338. captureToNextTokenJunction();
  1339. if (cursor == end) {
  1340. throw new CompileException("unexpected end of statement", expr, st);
  1341. }
  1342. /**
  1343. * Check to see if the name is legal.
  1344. */
  1345. if (isReservedWord(name = createStringTrimmed(expr, st, cursor - st))
  1346. || isNotValidNameorLabel(name))
  1347. throw new CompileException("illegal function name or use of reserved word", expr, cursor);
  1348. if (pCtx == null) pCtx = getParserContext();
  1349. FunctionParser parser = new FunctionParser(name, cursor, end - cursor, expr, fields, pCtx, splitAccumulator);
  1350. Function function = parser.parse();
  1351. cursor = parser.getCursor();
  1352. return lastNode = function;
  1353. }
  1354. case PROTO: {
  1355. if (ProtoParser.isUnresolvedWaiting()) {
  1356. if (pCtx == null) pCtx = getParserContext();
  1357. ProtoParser.checkForPossibleUnresolvedViolations(expr, cursor, pCtx);
  1358. }
  1359. int st = cursor;
  1360. captureToNextTokenJunction();
  1361. if (isReservedWord(name = createStringTrimmed(expr, st, cursor - st))
  1362. || isNotValidNameorLabel(name))
  1363. throw new CompileException("illegal prototype name or use of reserved word", expr, cursor);
  1364. if (expr[cursor = nextNonBlank()] != '{') {
  1365. throw new CompileException("expected '{' but found: " + expr[cursor], expr, cursor);
  1366. }
  1367. cursor = balancedCaptureWithLineAccounting(expr, st = cursor + 1, end, '{', pCtx);
  1368. if (pCtx == null) pCtx = getParserContext();
  1369. ProtoParser parser = new ProtoParser(expr, st, cursor, name, pCtx, fields, splitAccumulator);
  1370. Proto proto = parser.parse();
  1371. if (pCtx == null) pCtx = getParserContext();
  1372. pCtx.addImport(proto);
  1373. proto.setCursorPosition(st, cursor);
  1374. cursor = parser.getCursor();
  1375. ProtoParser.notifyForLateResolution(proto);
  1376. return lastNode = proto;
  1377. }
  1378. case STACKLANG: {
  1379. if (expr[cursor = nextNonBlank()] != '{') {
  1380. throw new CompileException("expected '{' but found: " + expr[cursor], expr, cursor);
  1381. }
  1382. int st;
  1383. cursor = balancedCaptureWithLineAccounting(expr, st = cursor + 1, end, '{', pCtx);
  1384. if (pCtx == null) pCtx = getParserContext();
  1385. Stacklang stacklang = new Stacklang(expr, st, cursor - st, fields, pCtx);
  1386. cursor++;
  1387. return lastNode = stacklang;
  1388. }
  1389. default:
  1390. if (cond) {
  1391. if (expr[cursor] != '(') {
  1392. throw new CompileException("expected '(' but encountered: " + expr[cursor], expr, cursor);
  1393. }
  1394. /**
  1395. * This block is an: IF, FOREACH or WHILE node.
  1396. */
  1397. endCond = cursor = balancedCaptureWithLineAccounting(expr, startCond = cursor, end, '(', pCtx);
  1398. startCond++;
  1399. cursor++;
  1400. }
  1401. }
  1402. skipWhitespace();
  1403. if (cursor >= end) {
  1404. throw new CompileException("unexpected end of statement", expr, end);
  1405. }
  1406. else if (expr[cursor] == '{') {
  1407. blockEnd = cursor = balancedCaptureWithLineAccounting(expr, blockStart = cursor, end, '{', pCtx);
  1408. }
  1409. else {
  1410. blockStart = cursor - 1;
  1411. captureToEOSorEOL();
  1412. blockEnd = cursor + 1;
  1413. }
  1414. if (type == ASTNode.BLOCK_IF) {
  1415. IfNode ifNode = (IfNode) node;
  1416. if (node != null) {
  1417. if (!cond) {
  1418. return ifNode.setElseBlock(expr, st = trimRight(blockStart + 1), trimLeft(blockEnd) - st, pCtx);
  1419. }
  1420. else {
  1421. return ifNode.setElseIf((IfNode) createBlockToken(startCond, endCond, trimRight(blockStart + 1),
  1422. trimLeft(blockEnd), type));
  1423. }
  1424. }
  1425. else {
  1426. return createBlockToken(startCond, endCond, blockStart + 1, blockEnd, type);
  1427. }
  1428. }
  1429. else if (type == ASTNode.BLOCK_DO) {
  1430. cursor++;
  1431. skipWhitespace();
  1432. st = cursor;
  1433. captureToNextTokenJunction();
  1434. if ("while".equals(name = new String(expr, st, cursor - st))) {
  1435. skipWhitespace();
  1436. startCond = cursor + 1;
  1437. endCond = cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '(', pCtx);
  1438. return createBlockToken(startCond, endCond, trimRight(blockStart + 1), trimLeft(blockEnd), type);
  1439. }
  1440. else if ("until".equals(name)) {
  1441. skipWhitespace();
  1442. startCond = cursor + 1;
  1443. endCond = cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '(', pCtx);
  1444. return createBlockToken(startCond, endCond, trimRight(blockStart + 1), trimLeft(blockEnd),
  1445. ASTNode.BLOCK_DO_UNTIL);
  1446. }
  1447. else {
  1448. throw new CompileException("expected 'while' or 'until' but encountered: " + name, expr, cursor);
  1449. }
  1450. }
  1451. // DON"T REMOVE THIS COMMENT!
  1452. // else if (isFlag(ASTNode.BLOCK_FOREACH) || isFlag(ASTNode.BLOCK_WITH)) {
  1453. else {
  1454. return createBlockToken(startCond, endCond, trimRight(blockStart + 1), trimLeft(blockEnd), type);
  1455. }
  1456. }
  1457. /**
  1458. * Checking from the current cursor position, check to see if the if-then-else block continues.
  1459. *
  1460. * @return boolean value
  1461. */
  1462. protected boolean ifThenElseBlockContinues() {
  1463. if ((cursor + 4) < end) {
  1464. if (expr[cursor] != ';') cursor--;
  1465. skipWhitespace();
  1466. return expr[cursor] == 'e' && expr[cursor + 1] == 'l' && expr[cursor + 2] == 's' && expr[cursor + 3] == 'e'
  1467. && (isWhitespace(expr[cursor + 4]) || expr[cursor + 4] == '{');
  1468. }
  1469. return false;
  1470. }
  1471. /**
  1472. * Checking from the current cursor position, check to see if we're inside a contiguous identifier.
  1473. *
  1474. * @return -
  1475. */
  1476. protected boolean tokenContinues() {
  1477. if (cursor == end) return false;
  1478. else if (expr[cursor] == '.' || expr[cursor] == '[') return true;
  1479. else if (isWhitespace(expr[cursor])) {
  1480. int markCurrent = cursor;
  1481. skipWhitespace();
  1482. if (cursor != end && (expr[cursor] == '.' || expr[cursor] == '[')) return true;
  1483. cursor = markCurrent;
  1484. }
  1485. return false;
  1486. }
  1487. /**
  1488. * The parser should find a statement ending condition when this is called, otherwise everything should blow up.
  1489. */
  1490. protected void expectEOS() {
  1491. skipWhitespace();
  1492. if (cursor != end && expr[cursor] != ';') {
  1493. switch (expr[cursor]) {
  1494. case '&':
  1495. if (lookAhead() == '&') return;
  1496. else break;
  1497. case '|':
  1498. if (lookAhead() == '|') return;
  1499. else break;
  1500. case '!':
  1501. if (lookAhead() == '=') return;
  1502. else break;
  1503. case '<':
  1504. case '>':
  1505. return;
  1506. case '=': {
  1507. switch (lookAhead()) {
  1508. case '=':
  1509. case '+':
  1510. case '-':
  1511. case '*':
  1512. return;
  1513. }
  1514. break;
  1515. }
  1516. case '+':
  1517. case '-':
  1518. case '/':
  1519. case '*':
  1520. if (lookAhead() == '=') return;
  1521. else break;
  1522. }
  1523. throw new CompileException("expected end of statement but encountered: "
  1524. + (cursor == end ? "<end of stream>" : expr[cursor]), expr, cursor);
  1525. }
  1526. }
  1527. /**
  1528. * Checks to see if the next part of the statement is an identifier part.
  1529. *
  1530. * @return boolean true if next part is identifier part.
  1531. */
  1532. protected boolean isNextIdentifier() {
  1533. while (cursor != end && isWhitespace(expr[cursor])) cursor++;
  1534. return cursor != end && isIdentifierPart(expr[cursor]);
  1535. }
  1536. /**
  1537. * Capture from the current cursor position, to the end of the statement.
  1538. */
  1539. protected void captureToEOS() {
  1540. while (cursor != end) {
  1541. switch (expr[cursor]) {
  1542. case '(':
  1543. case '[':
  1544. case '{':
  1545. if ((cursor = balancedCaptureWithLineAccounting(expr, cursor, end, expr[cursor], pCtx)) >= end)
  1546. return;
  1547. break;
  1548. case '"':
  1549. case '\'':
  1550. cursor = captureStringLiteral(expr[cursor], expr, cursor, end);
  1551. break;
  1552. case ',':
  1553. case ';':
  1554. case '}':
  1555. return;
  1556. }
  1557. cursor++;
  1558. }
  1559. }
  1560. /**
  1561. * From the current cursor position, capture to the end of statement, or the end of line, whichever comes first.
  1562. */
  1563. protected void captureToEOSorEOL() {
  1564. while (cursor != end && (expr[cursor] != '\n' && expr[cursor] != '\r' && expr[cursor] != ';')) {
  1565. cursor++;
  1566. }
  1567. }
  1568. /**
  1569. * From the current cursor position, capture to the end of the line.
  1570. */
  1571. // protected void captureToEOL() {
  1572. // while (cursor != end && (expr[cursor] != '\n')) cursor++;
  1573. // }
  1574. /**
  1575. * Capture to the end of the current identifier under the cursor.
  1576. */
  1577. protected void captureIdentifier() {
  1578. boolean captured = false;
  1579. if (cursor == end) throw new CompileException("unexpected end of statement: EOF", expr, cursor);
  1580. while (cursor != end) {
  1581. switch (expr[cursor]) {
  1582. case ';':
  1583. return;
  1584. default: {
  1585. if (!isIdentifierPart(expr[cursor])) {
  1586. if (captured) return;
  1587. throw new CompileException("unexpected symbol (was expecting an identifier): " + expr[cursor],
  1588. expr, cursor);
  1589. }
  1590. else {
  1591. captured = true;
  1592. }
  1593. }
  1594. }
  1595. cursor++;
  1596. }
  1597. }
  1598. /**
  1599. * From the current cursor position, capture to the end of the current token.
  1600. */
  1601. protected void captureToEOT() {
  1602. skipWhitespace();
  1603. do {
  1604. switch (expr[cursor]) {
  1605. case '(':
  1606. case '[':
  1607. case '{':
  1608. if ((cursor = balancedCaptureWithLineAccounting(expr, cursor, end, expr[cursor], pCtx)) == -1) {
  1609. throw new CompileException("unbalanced braces", expr, cursor);
  1610. }
  1611. break;
  1612. case '*':
  1613. case '/':
  1614. case '+':
  1615. case '%':
  1616. case ',':
  1617. case '=':
  1618. case '&':
  1619. case '|':
  1620. case ';':
  1621. return;
  1622. case '.':
  1623. skipWhitespace();
  1624. break;
  1625. case '\'':
  1626. cursor = captureStringLiteral('\'', expr, cursor, end);
  1627. break;
  1628. case '"':
  1629. cursor = captureStringLiteral('"', expr, cursor, end);
  1630. break;
  1631. default:
  1632. if (isWhitespace(expr[cursor])) {
  1633. skipWhitespace();
  1634. if (cursor < end && expr[cursor] == '.') {
  1635. if (cursor != end) cursor++;
  1636. skipWhitespace();
  1637. break;
  1638. }
  1639. else {
  1640. trimWhitespace();
  1641. return;
  1642. }
  1643. }
  1644. }
  1645. }
  1646. while (++cursor < end);
  1647. }
  1648. protected boolean lastNonWhite(char c) {
  1649. int i = cursor - 1;
  1650. while (isWhitespace(expr[i])) i--;
  1651. return c == expr[i];
  1652. }
  1653. /**
  1654. * From the specified cursor position, trim out any whitespace between the current position and the end of the
  1655. * last non-whitespace character.
  1656. *
  1657. * @param pos - current position
  1658. * @return new position.
  1659. */
  1660. protected int trimLeft(int pos) {
  1661. if (pos > end) pos = end;
  1662. while (pos > 0 && pos >= st && (isWhitespace(expr[pos - 1]) || expr[pos - 1] == ';')) pos--;
  1663. return pos;
  1664. }
  1665. /**
  1666. * From the specified cursor position, trim out any whitespace between the current position and beginning of the
  1667. * first non-whitespace character.
  1668. *
  1669. * @param pos -
  1670. * @return -
  1671. */
  1672. protected int trimRight(int pos) {
  1673. while (pos != end && isWhitespace(expr[pos])) pos++;
  1674. return pos;
  1675. }
  1676. /**
  1677. * If the cursor is currently pointing to whitespace, move the cursor forward to the first non-whitespace
  1678. * character, but account for carraige returns in the script (updates parser field: line).
  1679. */
  1680. protected void skipWhitespace() {
  1681. Skip:
  1682. while (cursor != end) {
  1683. switch (expr[cursor]) {
  1684. case '\n':
  1685. line++;
  1686. lastLineStart = cursor;
  1687. case '\r':
  1688. cursor++;
  1689. continue;
  1690. case '/':
  1691. if (cursor + 1 != end) {
  1692. switch (expr[cursor + 1]) {
  1693. case '/':
  1694. cursor++;
  1695. while (cursor != end && expr[cursor] != '\n') {
  1696. cursor++;
  1697. }
  1698. if (cursor != end) {
  1699. cursor++;
  1700. }
  1701. line++;
  1702. lastLineStart = cursor;
  1703. continue;
  1704. case '*':
  1705. int len = end - 1;
  1706. int st = cursor;
  1707. cursor++;
  1708. while (cursor != len && !(expr[cursor] == '*' && expr[cursor + 1] == '/')) {
  1709. cursor++;
  1710. }
  1711. if (cursor != len) {
  1712. cursor += 2;
  1713. }
  1714. for (int i = st; i < cursor; i++) {
  1715. expr[i] = ' ';
  1716. }
  1717. continue;
  1718. default:
  1719. break Skip;
  1720. }
  1721. }
  1722. default:
  1723. if (!isWhitespace(expr[cursor])) break Skip;
  1724. }
  1725. cursor++;
  1726. }
  1727. }
  1728. /**
  1729. * From the current cursor position, capture to the end of the next token junction.
  1730. */
  1731. protected void captureToNextTokenJunction() {
  1732. while (cursor != end) {
  1733. switch (expr[cursor]) {
  1734. case '{':
  1735. case '(':
  1736. return;
  1737. case '/':
  1738. if (expr[cursor + 1] == '*') return;
  1739. case '[':
  1740. cursor = balancedCaptureWithLineAccounting(expr, cursor, end, '[', pCtx) + 1;
  1741. continue;
  1742. default:
  1743. if (isWhitespace(expr[cursor])) {
  1744. return;
  1745. }
  1746. cursor++;
  1747. }
  1748. }
  1749. }
  1750. /**
  1751. * From the current cursor position, trim backward over any whitespace to the first non-whitespace character.
  1752. */
  1753. protected void trimWhitespace() {
  1754. while (cursor != 0 && isWhitespace(expr[cursor - 1])) cursor--;
  1755. }
  1756. /**
  1757. * Set and finesse the expression, trimming an leading or proceeding whitespace.
  1758. *
  1759. * @param expression the expression
  1760. */
  1761. protected void setExpression(String expression) {
  1762. if (expression != null && expression.length() != 0) {
  1763. synchronized (EX_PRECACHE) {
  1764. if ((this.expr = EX_PRECACHE.get(expression)) == null) {
  1765. end = length = (this.expr = expression.toCharArray()).length;
  1766. // trim any whitespace.
  1767. while (start < length && isWhitespace(expr[start])) start++;
  1768. while (length != 0 && isWhitespace(this.expr[length - 1])) length--;
  1769. char[] e = new char[length];
  1770. for (int i = 0; i != e.length; i++)
  1771. e[i] = expr[i];
  1772. EX_PRECACHE.put(expression, e);
  1773. }
  1774. else {
  1775. end = length = this.expr.length;
  1776. }
  1777. }
  1778. }
  1779. }
  1780. /**
  1781. * Set and finesse the expression, trimming an leading or proceeding whitespace.
  1782. *
  1783. * @param expression the expression
  1784. */
  1785. protected void setExpression(char[] expression) {
  1786. end = length = (this.expr = expression).length;
  1787. while (start < length && isWhitespace(expr[start])) start++;
  1788. while (length != 0 && isWhitespace(this.expr[length - 1])) length--;
  1789. }
  1790. /**
  1791. * Return the previous non-whitespace character.
  1792. *
  1793. * @return -
  1794. */
  1795. protected char lookToLast() {
  1796. if (cursor == start) return 0;
  1797. int temp = cursor;
  1798. for (; ; ) {
  1799. if (temp == start || !isWhitespace(expr[--temp])) break;
  1800. }
  1801. return expr[temp];
  1802. }
  1803. /**
  1804. * Return the last character (delta -1 of cursor position).
  1805. *
  1806. * @return -
  1807. */
  1808. protected char lookBehind() {
  1809. if (cursor == start) return 0;
  1810. else return expr[cursor - 1];
  1811. }
  1812. /**
  1813. * Return the next character (delta 1 of cursor position).
  1814. *
  1815. * @return -
  1816. */
  1817. protected char lookAhead() {
  1818. if (cursor + 1 != end) {
  1819. return expr[cursor + 1];
  1820. }
  1821. else {
  1822. return 0;
  1823. }
  1824. }
  1825. /**
  1826. * Return the character, forward of the currrent cursor position based on the specified range delta.
  1827. *
  1828. * @param range -
  1829. * @return -
  1830. */
  1831. protected char lookAhead(int range) {
  1832. if ((cursor + range) >= end) return 0;
  1833. else {
  1834. return expr[cursor + range];
  1835. }
  1836. }
  1837. /**
  1838. * Returns true if the next is an identifier or literal.
  1839. *
  1840. * @return true of false
  1841. */
  1842. protected boolean isNextIdentifierOrLiteral() {
  1843. int tmp = cursor;
  1844. if (tmp == end) return false;
  1845. else {
  1846. while (tmp != end && isWhitespace(expr[tmp])) tmp++;
  1847. if (tmp == end) return false;
  1848. char n = expr[tmp];
  1849. return isIdentifierPart(n) || isDigit(n) || n == '\'' || n == '"';
  1850. }
  1851. }
  1852. /**
  1853. * Increment one cursor position, and move cursor to next non-blank part.
  1854. *
  1855. * @return cursor position
  1856. */
  1857. public int incNextNonBlank() {
  1858. cursor++;
  1859. return nextNonBlank();
  1860. }
  1861. /**
  1862. * Move to next cursor position from current cursor position.
  1863. *
  1864. * @return cursor position
  1865. */
  1866. public int nextNonBlank() {
  1867. if ((cursor + 1) >= end) {
  1868. throw new CompileException("unexpected end of statement", expr, st);
  1869. }
  1870. int i = cursor;
  1871. while (i != end && isWhitespace(expr[i])) i++;
  1872. return i;
  1873. }
  1874. /**
  1875. * Expect the next specified character or fail
  1876. *
  1877. * @param c character
  1878. */
  1879. public void expectNextChar_IW(char c) {
  1880. nextNonBlank();
  1881. if (cursor == end) throw new CompileException("unexpected end of statement", expr, st);
  1882. if (expr[cursor] != c)
  1883. throw new CompileException("unexpected character ('" + expr[cursor] + "'); was expecting: " + c, expr, st);
  1884. }
  1885. /**
  1886. * NOTE: This method assumes that the current position of the cursor is at the end of a logical statement, to
  1887. * begin with.
  1888. * <p/>
  1889. * Determines whether or not the logical statement is manually terminated with a statement separator (';').
  1890. *
  1891. * @return -
  1892. */
  1893. protected boolean isStatementNotManuallyTerminated() {
  1894. if (cursor >= end) return false;
  1895. int c = cursor;
  1896. while (c != end && isWhitespace(expr[c])) c++;
  1897. return !(c != end && expr[c] == ';');
  1898. }
  1899. protected ParserContext getParserContext() {
  1900. if (parserContext == null || parserContext.get() == null) {
  1901. newContext();
  1902. }
  1903. return parserContext.get();
  1904. }
  1905. public static ParserContext getCurrentThreadParserContext() {
  1906. return contextControl(GET_OR_CREATE, null, null);
  1907. }
  1908. public static void setCurrentThreadParserContext(ParserContext pCtx) {
  1909. contextControl(SET, pCtx, null);
  1910. }
  1911. /**
  1912. * Create a new ParserContext in the current thread.
  1913. */
  1914. public void newContext() {
  1915. contextControl(SET, new ParserContext(), this);
  1916. }
  1917. /**
  1918. * Create a new ParserContext in the current thread, using the one specified.
  1919. *
  1920. * @param pCtx -
  1921. */
  1922. public void newContext(ParserContext pCtx) {
  1923. contextControl(SET, this.pCtx = pCtx, this);
  1924. }
  1925. /**
  1926. * Remove the current ParserContext from the thread.
  1927. */
  1928. public void removeContext() {
  1929. contextControl(REMOVE, null, this);
  1930. }
  1931. public static ParserContext contextControl(int operation, ParserContext pCtx, AbstractParser parser) {
  1932. synchronized (getRuntime()) {
  1933. if (parserContext == null) parserContext = new ThreadLocal<ParserContext>();
  1934. switch (operation) {
  1935. case SET:
  1936. pCtx.setRootParser(parser);
  1937. parserContext.set(pCtx);
  1938. return pCtx;
  1939. case REMOVE:
  1940. parserContext.set(null);
  1941. return null;
  1942. case GET_OR_CREATE:
  1943. if (parserContext.get() == null) {
  1944. parserContext.set(new ParserContext(parser));
  1945. }
  1946. case GET:
  1947. return parserContext.get();
  1948. }
  1949. }
  1950. return null;
  1951. }
  1952. protected static final int SET = 0;
  1953. protected static final int REMOVE = 1;
  1954. protected static final int GET = 2;
  1955. protected static final int GET_OR_CREATE = 3;
  1956. protected void addFatalError(String message) {
  1957. pCtx.addError(new ErrorDetail(expr, st, true, message));
  1958. }
  1959. protected void addFatalError(String message, int start) {
  1960. pCtx.addError(new ErrorDetail(expr, start, true, message));
  1961. }
  1962. public static final int LEVEL_5_CONTROL_FLOW = 5;
  1963. public static final int LEVEL_4_ASSIGNMENT = 4;
  1964. public static final int LEVEL_3_ITERATION = 3;
  1965. public static final int LEVEL_2_MULTI_STATEMENT = 2;
  1966. public static final int LEVEL_1_BASIC_LANG = 1;
  1967. public static final int LEVEL_0_PROPERTY_ONLY = 0;
  1968. public static void setLanguageLevel(int level) {
  1969. OPERATORS.clear();
  1970. OPERATORS.putAll(loadLanguageFeaturesByLevel(level));
  1971. }
  1972. public static HashMap<String, Integer> loadLanguageFeaturesByLevel(int languageLevel) {
  1973. HashMap<String, Integer> operatorsTable = new HashMap<String, Integer>();
  1974. switch (languageLevel) {
  1975. case 6: // prototype definition
  1976. operatorsTable.put("proto", PROTO);
  1977. case 5: // control flow operations
  1978. operatorsTable.put("if", IF);
  1979. operatorsTable.put("else", ELSE);
  1980. operatorsTable.put("?", TERNARY);
  1981. operatorsTable.put("switch", SWITCH);
  1982. operatorsTable.put("function", FUNCTION);
  1983. operatorsTable.put("def", FUNCTION);
  1984. operatorsTable.put("stacklang", STACKLANG);
  1985. case 4: // assignment
  1986. operatorsTable.put("=", ASSIGN);
  1987. operatorsTable.put("var", UNTYPED_VAR);
  1988. operatorsTable.put("+=", ASSIGN_ADD);
  1989. operatorsTable.put("-=", ASSIGN_SUB);
  1990. operatorsTable.put("/=", ASSIGN_DIV);
  1991. operatorsTable.put("%=", ASSIGN_MOD);
  1992. case 3: // iteration
  1993. operatorsTable.put("foreach", FOREACH);
  1994. operatorsTable.put("while", WHILE);
  1995. operatorsTable.put("until", UNTIL);
  1996. operatorsTable.put("for", FOR);
  1997. operatorsTable.put("do", DO);
  1998. case 2: // multi-statement
  1999. operatorsTable.put("return", RETURN);
  2000. operatorsTable.put(";", END_OF_STMT);
  2001. case 1: // boolean, math ops, projection, assertion, objection creation, block setters, imports
  2002. operatorsTable.put("+", ADD);
  2003. operatorsTable.put("-", SUB);
  2004. operatorsTable.put("*", MULT);
  2005. operatorsTable.put("**", POWER);
  2006. operatorsTable.put("/", DIV);
  2007. operatorsTable.put("%", MOD);
  2008. operatorsTable.put("==", EQUAL);
  2009. operatorsTable.put("!=", NEQUAL);
  2010. operatorsTable.put(">", GTHAN);
  2011. operatorsTable.put(">=", GETHAN);
  2012. operatorsTable.put("<", LTHAN);
  2013. operatorsTable.put("<=", LETHAN);
  2014. operatorsTable.put("&&", AND);
  2015. operatorsTable.put("and", AND);
  2016. operatorsTable.put("||", OR);
  2017. operatorsTable.put("or", CHOR);
  2018. operatorsTable.put("~=", REGEX);
  2019. operatorsTable.put("instanceof", INSTANCEOF);
  2020. operatorsTable.put("is", INSTANCEOF);
  2021. operatorsTable.put("contains", CONTAINS);
  2022. operatorsTable.put("soundslike", SOUNDEX);
  2023. operatorsTable.put("strsim", SIMILARITY);
  2024. operatorsTable.put("convertable_to", CONVERTABLE_TO);
  2025. operatorsTable.put("isdef", ISDEF);
  2026. operatorsTable.put("#", STR_APPEND);
  2027. operatorsTable.put("&", BW_AND);
  2028. operatorsTable.put("|", BW_OR);
  2029. operatorsTable.put("^", BW_XOR);
  2030. operatorsTable.put("<<", BW_SHIFT_LEFT);
  2031. operatorsTable.put("<<<", BW_USHIFT_LEFT);
  2032. operatorsTable.put(">>", BW_SHIFT_RIGHT);
  2033. operatorsTable.put(">>>", BW_USHIFT_RIGHT);
  2034. operatorsTable.put("new", Operator.NEW);
  2035. operatorsTable.put("in", PROJECTION);
  2036. operatorsTable.put("with", WITH);
  2037. operatorsTable.put("assert", ASSERT);
  2038. operatorsTable.put("import", IMPORT);
  2039. operatorsTable.put("import_static", IMPORT_STATIC);
  2040. operatorsTable.put("++", INC);
  2041. operatorsTable.put("--", DEC);
  2042. case 0: // Property access and inline collections
  2043. operatorsTable.put(":", TERNARY_ELSE);
  2044. }
  2045. return operatorsTable;
  2046. }
  2047. /**
  2048. * Remove the current parser context from the thread.
  2049. */
  2050. public static void resetParserContext() {
  2051. contextControl(REMOVE, null, null);
  2052. }
  2053. protected static boolean isArithmeticOperator(int operator) {
  2054. return operator != -1 && operator < 6;
  2055. }
  2056. /**
  2057. * Reduce the current operations on the stack.
  2058. *
  2059. * @param operator the operator
  2060. * @return a stack control code
  2061. */
  2062. protected int arithmeticFunctionReduction(int operator) {
  2063. ASTNode tk;
  2064. int operator2;
  2065. /**
  2066. * If the next token is an operator, we check to see if it has a higher
  2067. * precdence.
  2068. */
  2069. if ((tk = nextToken()) != null) {
  2070. if (isArithmeticOperator(operator2 = tk.getOperator()) && PTABLE[operator2] > PTABLE[operator]) {
  2071. stk.xswap();
  2072. /**
  2073. * The current arith. operator is of higher precedence the last.
  2074. */
  2075. tk = nextToken();
  2076. /**
  2077. * Check to see if we're compiling or executing interpretively. If we're compiling, we really
  2078. * need to stop if this is not a literal.
  2079. */
  2080. if (compileMode && !tk.isLiteral()) {
  2081. splitAccumulator.push(tk, new OperatorNode(operator2, expr, st, pCtx));
  2082. return OP_OVERFLOW;
  2083. }
  2084. dStack.push(operator = operator2, tk.getReducedValue(ctx, ctx, variableFactory));
  2085. while (true) {
  2086. // look ahead again
  2087. if ((tk = nextToken()) != null && (operator2 = tk.getOperator()) != -1
  2088. && operator2 != END_OF_STMT && PTABLE[operator2] > PTABLE[operator]) {
  2089. // if we have back to back operations on the stack, we don't xswap
  2090. if (dStack.isReduceable()) {
  2091. stk.copyx2(dStack);
  2092. }
  2093. /**
  2094. * This operator is of higher precedence, or the same level precedence. push to the RHS.
  2095. */
  2096. dStack.push(operator = operator2, nextToken().getReducedValue(ctx, ctx, variableFactory));
  2097. continue;
  2098. }
  2099. else if (tk != null && operator2 != -1 && operator2 != END_OF_STMT) {
  2100. if (PTABLE[operator2] == PTABLE[operator]) {
  2101. if (!dStack.isEmpty()) dreduce();
  2102. else {
  2103. while (stk.isReduceable()) {
  2104. stk.xswap_op();
  2105. }
  2106. }
  2107. /**
  2108. * This operator is of the same level precedence. push to the RHS.
  2109. */
  2110. dStack.push(operator = operator2, nextToken().getReducedValue(ctx, ctx, variableFactory));
  2111. continue;
  2112. }
  2113. else {
  2114. /**
  2115. * The operator doesn't have higher precedence. Therfore reduce the LHS.
  2116. */
  2117. while (dStack.size() > 1) {
  2118. dreduce();
  2119. }
  2120. operator = tk.getOperator();
  2121. // Reduce the lesser or equal precedence operations.
  2122. while (stk.size() != 1 && stk.peek2() instanceof Integer &&
  2123. ((operator2 = (Integer) stk.peek2()) < PTABLE.length) &&
  2124. PTABLE[operator2] >= PTABLE[operator]) {
  2125. stk.xswap_op();
  2126. }
  2127. }
  2128. }
  2129. else {
  2130. /**
  2131. * There are no more tokens.
  2132. */
  2133. if (dStack.size() > 1) {
  2134. dreduce();
  2135. }
  2136. if (stk.isReduceable()) stk.xswap();
  2137. break;
  2138. }
  2139. if ((tk = nextToken()) != null) {
  2140. switch (operator) {
  2141. case AND: {
  2142. if (!(stk.peekBoolean())) return OP_TERMINATE;
  2143. else {
  2144. splitAccumulator.add(tk);
  2145. return AND;
  2146. }
  2147. }
  2148. case OR: {
  2149. if ((stk.peekBoolean())) return OP_TERMINATE;
  2150. else {
  2151. splitAccumulator.add(tk);
  2152. return OR;
  2153. }
  2154. }
  2155. default:
  2156. stk.push(operator, tk.getReducedValue(ctx, ctx, variableFactory));
  2157. }
  2158. }
  2159. }
  2160. }
  2161. else if (!tk.isOperator()) {
  2162. throw new CompileException("unexpected token: " + tk.getName(), expr, st);
  2163. }
  2164. else {
  2165. reduce();
  2166. splitAccumulator.push(tk);
  2167. }
  2168. }
  2169. // while any values remain on the stack
  2170. // keep XSWAPing and reducing, until there is nothing left.
  2171. if (stk.isReduceable()) {
  2172. while (true) {
  2173. reduce();
  2174. if (stk.isReduceable()) {
  2175. stk.xswap();
  2176. }
  2177. else {
  2178. break;
  2179. }
  2180. }
  2181. }
  2182. return OP_RESET_FRAME;
  2183. }
  2184. private void dreduce() {
  2185. stk.copy2(dStack);
  2186. stk.op();
  2187. }
  2188. /**
  2189. * This method is called when we reach the point where we must subEval a trinary operation in the expression.
  2190. * (ie. val1 op val2). This is not the same as a binary operation, although binary operations would appear
  2191. * to have 3 structures as well. A binary structure (or also a junction in the expression) compares the
  2192. * current state against 2 downrange structures (usually an op and a val).
  2193. */
  2194. protected void reduce() {
  2195. Object v1, v2;
  2196. int operator;
  2197. try {
  2198. switch (operator = (Integer) stk.pop()) {
  2199. case ADD:
  2200. case SUB:
  2201. case DIV:
  2202. case MULT:
  2203. case MOD:
  2204. case EQUAL:
  2205. case NEQUAL:
  2206. case GTHAN:
  2207. case LTHAN:
  2208. case GETHAN:
  2209. case LETHAN:
  2210. case POWER:
  2211. stk.op(operator);
  2212. break;
  2213. case AND:
  2214. v1 = stk.pop();
  2215. stk.push(((Boolean) stk.pop()) && ((Boolean) v1));
  2216. break;
  2217. case OR:
  2218. v1 = stk.pop();
  2219. stk.push(((Boolean) stk.pop()) || ((Boolean) v1));
  2220. break;
  2221. case CHOR:
  2222. v1 = stk.pop();
  2223. if (!isEmpty(v2 = stk.pop()) || !isEmpty(v1)) {
  2224. stk.clear();
  2225. stk.push(!isEmpty(v2) ? v2 : v1);
  2226. return;
  2227. }
  2228. else stk.push(null);
  2229. break;
  2230. case REGEX:
  2231. stk.push(java.util.regex.Pattern.compile(java.lang.String.valueOf(stk.pop()))
  2232. .matcher(java.lang.String.valueOf(stk.pop())).matches());
  2233. break;
  2234. case INSTANCEOF:
  2235. stk.push(((Class) stk.pop()).isInstance(stk.pop()));
  2236. break;
  2237. case CONVERTABLE_TO:
  2238. stk.push(org.mvel2.DataConversion.canConvert(stk.peek2().getClass(), (Class) stk.pop2()));
  2239. break;
  2240. case CONTAINS:
  2241. stk.push(containsCheck(stk.peek2(), stk.pop2()));
  2242. break;
  2243. case SOUNDEX:
  2244. stk.push(soundex(java.lang.String.valueOf(stk.pop()))
  2245. .equals(soundex(java.lang.String.valueOf(stk.pop()))));
  2246. break;
  2247. case SIMILARITY:
  2248. stk.push(similarity(java.lang.String.valueOf(stk.pop()), java.lang.String.valueOf(stk.pop())));
  2249. break;
  2250. default:
  2251. reduceNumeric(operator);
  2252. }
  2253. }
  2254. catch (ClassCastException e) {
  2255. throw new CompileException("syntax error or incompatable types", expr, st, e);
  2256. }
  2257. catch (ArithmeticException e) {
  2258. throw new CompileException("arithmetic error: " + e.getMessage(), expr, st, e);
  2259. }
  2260. catch (Exception e) {
  2261. throw new CompileException("failed to subEval expression", expr, st, e);
  2262. }
  2263. }
  2264. private void reduceNumeric(int operator) {
  2265. Object op1 = stk.peek2();
  2266. Object op2 = stk.pop2();
  2267. if (op1 instanceof Integer) {
  2268. if (op2 instanceof Integer) {
  2269. reduce((Integer) op1, operator, (Integer) op2);
  2270. }
  2271. else {
  2272. reduce((Integer) op1, operator, (Long) op2);
  2273. }
  2274. }
  2275. else {
  2276. if (op2 instanceof Integer) {
  2277. reduce((Long) op1, operator, (Integer) op2);
  2278. }
  2279. else {
  2280. reduce((Long) op1, operator, (Long) op2);
  2281. }
  2282. }
  2283. }
  2284. private void reduce(int op1, int operator, int op2) {
  2285. switch (operator) {
  2286. case BW_AND:
  2287. stk.push(op1 & op2);
  2288. break;
  2289. case BW_OR:
  2290. stk.push(op1 | op2);
  2291. break;
  2292. case BW_XOR:
  2293. stk.push(op1 ^ op2);
  2294. break;
  2295. case BW_SHIFT_LEFT:
  2296. stk.push(op1 << op2);
  2297. break;
  2298. case BW_USHIFT_LEFT:
  2299. int iv2 = op1;
  2300. if (iv2 < 0) iv2 *= -1;
  2301. stk.push(iv2 << op2);
  2302. break;
  2303. case BW_SHIFT_RIGHT:
  2304. stk.push(op1 >> op2);
  2305. break;
  2306. case BW_USHIFT_RIGHT:
  2307. stk.push(op1 >>> op2);
  2308. break;
  2309. }
  2310. }
  2311. private void reduce(int op1, int operator, long op2) {
  2312. switch (operator) {
  2313. case BW_AND:
  2314. stk.push(op1 & op2);
  2315. break;
  2316. case BW_OR:
  2317. stk.push(op1 | op2);
  2318. break;
  2319. case BW_XOR:
  2320. stk.push(op1 ^ op2);
  2321. break;
  2322. case BW_SHIFT_LEFT:
  2323. stk.push(op1 << op2);
  2324. break;
  2325. case BW_USHIFT_LEFT:
  2326. int iv2 = op1;
  2327. if (iv2 < 0) iv2 *= -1;
  2328. stk.push(iv2 << op2);
  2329. break;
  2330. case BW_SHIFT_RIGHT:
  2331. stk.push(op1 >> op2);
  2332. break;
  2333. case BW_USHIFT_RIGHT:
  2334. stk.push(op1 >>> op2);
  2335. break;
  2336. }
  2337. }
  2338. private void reduce(long op1, int operator, int op2) {
  2339. switch (operator) {
  2340. case BW_AND:
  2341. stk.push(op1 & op2);
  2342. break;
  2343. case BW_OR:
  2344. stk.push(op1 | op2);
  2345. break;
  2346. case BW_XOR:
  2347. stk.push(op1 ^ op2);
  2348. break;
  2349. case BW_SHIFT_LEFT:
  2350. stk.push(op1 << op2);
  2351. break;
  2352. case BW_USHIFT_LEFT:
  2353. long iv2 = op1;
  2354. if (iv2 < 0) iv2 *= -1;
  2355. stk.push(iv2 << op2);
  2356. break;
  2357. case BW_SHIFT_RIGHT:
  2358. stk.push(op1 >> op2);
  2359. break;
  2360. case BW_USHIFT_RIGHT:
  2361. stk.push(op1 >>> op2);
  2362. break;
  2363. }
  2364. }
  2365. private void reduce(long op1, int operator, long op2) {
  2366. switch (operator) {
  2367. case BW_AND:
  2368. stk.push(op1 & op2);
  2369. break;
  2370. case BW_OR:
  2371. stk.push(op1 | op2);
  2372. break;
  2373. case BW_XOR:
  2374. stk.push(op1 ^ op2);
  2375. break;
  2376. case BW_SHIFT_LEFT:
  2377. stk.push(op1 << op2);
  2378. break;
  2379. case BW_USHIFT_LEFT:
  2380. long iv2 = op1;
  2381. if (iv2 < 0) iv2 *= -1;
  2382. stk.push(iv2 << op2);
  2383. break;
  2384. case BW_SHIFT_RIGHT:
  2385. stk.push(op1 >> op2);
  2386. break;
  2387. case BW_USHIFT_RIGHT:
  2388. stk.push(op1 >>> op2);
  2389. break;
  2390. }
  2391. }
  2392. public int getCursor() {
  2393. return cursor;
  2394. }
  2395. public char[] getExpression() {
  2396. return expr;
  2397. }
  2398. private static int asInt(final Object o) {
  2399. return (Integer) o;
  2400. }
  2401. public void setPCtx(ParserContext pCtx) {
  2402. this.debugSymbols = (this.pCtx = pCtx).isDebugSymbols();
  2403. }
  2404. }