1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.jexl3.parser;
18
19 import java.io.BufferedReader;
20 import java.io.IOException;
21 import java.io.StringReader;
22 import java.util.ArrayDeque;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Deque;
27 import java.util.HashSet;
28 import java.util.IdentityHashMap;
29 import java.util.Iterator;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.TreeMap;
35 import java.util.concurrent.atomic.AtomicInteger;
36 import java.util.concurrent.atomic.AtomicReference;
37
38 import org.apache.commons.jexl3.JexlEngine;
39 import org.apache.commons.jexl3.JexlException;
40 import org.apache.commons.jexl3.JexlFeatures;
41 import org.apache.commons.jexl3.JexlInfo;
42 import org.apache.commons.jexl3.JxltEngine;
43 import org.apache.commons.jexl3.internal.LexicalScope;
44 import org.apache.commons.jexl3.internal.Scope;
45 import org.apache.commons.jexl3.internal.TemplateEngine;
46 import org.apache.commons.jexl3.introspection.JexlUberspect;
47
48
49
50
51 public abstract class JexlParser extends StringParser implements JexlScriptParser {
52
53
54
55
56 public interface LexicalUnit {
57
58
59
60
61
62 boolean declareSymbol(int symbol);
63
64
65
66 LexicalScope getLexicalScope();
67
68
69
70
71 int getSymbolCount();
72
73
74
75
76
77 boolean hasSymbol(int symbol);
78
79 boolean isConstant(int symbol);
80
81 void setConstant(int symbol);
82 }
83
84
85
86 public static final String PRAGMA_OPTIONS = "jexl.options";
87
88
89
90 public static final String PRAGMA_JEXLNS = "jexl.namespace.";
91
92
93
94 public static final String PRAGMA_MODULE = "jexl.module.";
95
96
97
98 public static final String PRAGMA_IMPORT = "jexl.import";
99
100
101
102 private static final Set<Class<? extends JexlNode>> ASSIGN_NODES = new HashSet<>(
103 Arrays.asList(
104 ASTAssignment.class,
105 ASTSetAddNode.class,
106 ASTSetSubNode.class,
107 ASTSetMultNode.class,
108 ASTSetDivNode.class,
109 ASTSetModNode.class,
110 ASTSetAndNode.class,
111 ASTSetOrNode.class,
112 ASTSetXorNode.class,
113 ASTSetShiftLeftNode.class,
114 ASTSetShiftRightNode.class,
115 ASTSetShiftRightUnsignedNode.class,
116 ASTIncrementGetNode.class,
117 ASTDecrementGetNode.class,
118 ASTGetDecrementNode.class,
119 ASTGetIncrementNode.class
120 )
121 );
122
123
124
125
126
127 protected static Token errorToken(final Token... tokens) {
128 for (final Token token : tokens) {
129 if (token != null && token.image != null && !token.image.isEmpty()) {
130 return token;
131 }
132 }
133 return null;
134 }
135
136
137
138
139
140
141 protected static String readSourceLine(final String src, final int lineno) {
142 String msg = "";
143 if (src != null && lineno >= 0) {
144 try {
145 final BufferedReader reader = new BufferedReader(new StringReader(src));
146 for (int l = 0; l < lineno; ++l) {
147 msg = reader.readLine();
148 }
149 } catch (final IOException xio) {
150
151 }
152 }
153 return msg;
154 }
155
156
157
158
159
160 protected static String stringify(final Iterable<String> lstr) {
161 return String.join(".", lstr);
162 }
163
164
165
166 protected final FeatureController featureController;
167
168
169
170 protected JexlInfo info;
171
172
173
174 protected String source;
175
176
177
178
179
180 protected final AtomicReference<Scope> scopeReference;
181
182
183
184 protected final Deque<Scope> scopes;
185
186
187
188 protected Map<String, Object> pragmas;
189
190
191
192 protected final AtomicReference<JexlUberspect.ClassConstantResolver> fqcnResolver;
193
194
195
196
197 protected final List<String> imports;
198
199
200 void addImport(final String importName) {
201 if (importName != null && !importName.isEmpty() && !imports.contains(importName)) {
202 imports.add(importName);
203 }
204 }
205
206 Object resolveConstant(final String name) {
207 JexlUberspect.ClassConstantResolver resolver = fqcnResolver.get();
208 if (resolver == null) {
209 final JexlEngine engine = JexlEngine.getThreadEngine();
210 if (engine instanceof JexlUberspect.ConstantResolverFactory) {
211 resolver = ((JexlUberspect.ConstantResolverFactory) engine).createConstantResolver(imports);
212 fqcnResolver.set(resolver);
213 }
214 }
215 return resolver != null
216 ? resolver.resolveConstant(name)
217 : JexlEngine.TRY_FAILED;
218 }
219
220
221
222
223 protected boolean autoSemicolon = true;
224
225
226
227 protected Set<String> namespaces;
228
229
230
231 protected AtomicInteger loopCount;
232
233
234
235 protected final Deque<Integer> loopCounts;
236
237
238
239 protected final AtomicReference<LexicalUnit> blockReference;
240
241
242
243 protected final Deque<LexicalUnit> blocks;
244
245
246
247 protected final Map<LexicalUnit, Scope> blockScopes;
248
249
250
251 protected final JexlParser parent;
252
253
254
255
256
257
258
259 protected JexlParser() {
260 this(null);
261 }
262
263
264
265
266
267
268
269 protected JexlParser(final JexlParser parser) {
270 this.info = null;
271 this.source = null;
272 if (parser != null) {
273 parent = parser;
274 featureController = parser.featureController;
275 scopeReference = parser.scopeReference;
276 scopes = parser.scopes;
277 pragmas = parser.pragmas;
278 namespaces = parser.namespaces;
279 loopCount = parser.loopCount;
280 loopCounts = parser.loopCounts;
281 blockReference = parser.blockReference;
282 blocks = parser.blocks;
283 blockScopes = parser.blockScopes;
284 fqcnResolver = parser.fqcnResolver;
285 imports = parser.imports;
286 autoSemicolon = parser.autoSemicolon;
287 } else {
288 parent = null;
289 featureController = new FeatureController(JexlEngine.DEFAULT_FEATURES);
290 scopeReference = new AtomicReference<>();
291 blockReference = new AtomicReference<>();
292 fqcnResolver = new AtomicReference<>();
293 loopCount = new AtomicInteger();
294 scopes = new ArrayDeque<>();
295 loopCounts = new ArrayDeque<>();
296 blocks = new ArrayDeque<>();
297 blockScopes = new IdentityHashMap<>();
298 imports = new ArrayList<>();
299 }
300 }
301
302
303
304
305 public static final Object NIL = new Object() {
306
307 @Override
308 public String toString() {
309 return "null";
310 }};
311
312
313
314
315 public static final Object DFLT = new Object() {
316
317 @Override
318 public String toString() {
319 return "default";
320 }};
321
322
323
324
325 public static final Object NAN = new Object() {
326
327 @Override
328 public String toString() {
329 return "NaN";
330 }};
331
332
333
334
335
336
337
338 static Object switchCode(final Object value) {
339 if (value == null) {
340 return NIL;
341 }
342 if (value instanceof Double && ((Double) value).isNaN()) {
343 return NAN;
344 }
345 return value;
346 }
347
348
349
350
351
352
353
354 static Object switchDecode(final Object value) {
355 if (value == NIL) {
356 return null;
357 }
358 if (value == NAN) {
359 return Double.NaN;
360 }
361 return value;
362 }
363
364
365
366
367 protected SwitchSet switchSet() {
368 return new SwitchSet();
369 }
370
371 protected class SwitchSet implements Iterable<Object> {
372 private final Set<Object> values = new LinkedHashSet<>();
373
374
375
376
377
378 void addAll(final Collection<Object> values) {
379 values.forEach(this::add);
380 }
381
382
383
384
385
386
387 void add(final Object value) {
388 final Object code = switchCode(value);
389 if (!values.add(code)) {
390 throw new JexlException.Parsing(info, "duplicate constant value: " + value);
391 }
392 }
393
394 void clear() {
395 values.clear();
396 }
397
398 boolean isEmpty() {
399 return values.isEmpty();
400 }
401
402 int size() {
403 return values.size();
404 }
405
406 @Override
407 public Iterator<Object> iterator() {
408 return new Iterator<Object>() {
409 private final Iterator<Object> iter = values.iterator();
410
411 @Override
412 public boolean hasNext() {
413 return iter.hasNext();
414 }
415
416 @Override
417 public Object next() {
418 return switchDecode(iter.next());
419 }
420
421 @Override
422 public void remove() {
423 iter.remove();
424 }
425 };
426 }
427 }
428
429
430
431
432
433
434 public void allowRegisters(final boolean registers) {
435 featureController.setFeatures(new JexlFeatures(featureController.getFeatures()).register(registers));
436 }
437
438
439
440
441
442
443
444 protected boolean allowVariable(final String image) {
445 final JexlFeatures features = getFeatures();
446 if (!features.supportsLocalVar()) {
447 return false;
448 }
449 if (features.isReservedName(image)) {
450 return false;
451 }
452 return true;
453 }
454
455
456
457
458
459
460 protected void checkLambda(final Token token) {
461 final String arrow = token.image;
462 if ("->".equals(arrow)) {
463 if (!getFeatures().supportsThinArrow()) {
464 throwFeatureException(JexlFeatures.THIN_ARROW, token);
465 }
466 return;
467 }
468 if ("=>".equals(arrow) && !getFeatures().supportsFatArrow()) {
469 throwFeatureException(JexlFeatures.FAT_ARROW, token);
470 }
471 }
472
473
474
475
476
477
478
479
480 protected String checkVariable(final ASTIdentifier identifier, final String name) {
481 final Scope scope = scopeReference.get();
482 if (scope != null) {
483 final Integer symbol = scope.getSymbol(name);
484 if (symbol != null) {
485 identifier.setLexical(scope.isLexical(symbol));
486 boolean declared = true;
487 if (scope.isCapturedSymbol(symbol)) {
488
489 identifier.setCaptured(true);
490 } else {
491 LexicalUnit unit = getUnit();
492 declared = unit.hasSymbol(symbol);
493
494 if (!declared) {
495 for (final LexicalUnit u : blocks) {
496 if (u.hasSymbol(symbol)) {
497 unit = u;
498 declared = true;
499 break;
500 }
501 }
502 }
503 if (declared) {
504
505 if (unit.isConstant(symbol)) {
506 identifier.setConstant(true);
507 }
508 } else if (info instanceof JexlNode.Info) {
509 declared = isSymbolDeclared((JexlNode.Info) info, symbol);
510 }
511 }
512 identifier.setSymbol(symbol, name);
513 if (!declared) {
514 if (getFeatures().isLexicalShade()) {
515
516 throw new JexlException.Parsing(info, name + ": variable is not declared").clean();
517 }
518 identifier.setShaded(true);
519 }
520 }
521 }
522 return name;
523 }
524
525
526
527
528
529
530 protected void cleanup(final JexlFeatures features) {
531 info = null;
532 source = null;
533 if (parent == null) {
534 scopeReference.set(null);
535 scopes.clear();
536 pragmas = null;
537 namespaces = null;
538 fqcnResolver.set(null);
539 imports.clear();
540 loopCounts.clear();
541 loopCount.set(0);
542 blocks.clear();
543 blockReference.set(null);
544 blockScopes.clear();
545 setFeatures(features);
546 }
547 }
548
549
550
551
552 protected void controlPragmaAnywhere() {
553 final JexlFeatures features = getFeatures();
554 if (features.supportsPragma() && !features.supportsPragmaAnywhere()) {
555 featureController.setFeatures(new JexlFeatures(featureController.getFeatures()).pragma(false));
556 }
557 }
558
559
560
561
562
563
564
565 protected void declareFunction(final ASTVar variable, final Token token) {
566 final String name = token.image;
567
568 Scope scope = scopeReference.get();
569 if (scope == null) {
570 scope = new Scope(null);
571 scopeReference.set(scope);
572 }
573 final int symbol = scope.declareVariable(name);
574 variable.setSymbol(symbol, name);
575 variable.setLexical(true);
576 if (scope.isCapturedSymbol(symbol)) {
577 variable.setCaptured(true);
578 }
579
580 if (declareSymbol(symbol)) {
581 scope.addLexical(symbol);
582 final LexicalUnit block = getUnit();
583 block.setConstant(symbol);
584 } else {
585 if (getFeatures().isLexical()) {
586 throw new JexlException(variable, name + ": variable is already declared");
587 }
588 variable.setRedefined(true);
589 }
590 }
591
592
593
594
595
596
597
598
599
600
601
602 protected void declareParameter(final Token token, final boolean lexical, final boolean constant) {
603 final String identifier = token.image;
604 if (!allowVariable(identifier)) {
605 throwFeatureException(JexlFeatures.LOCAL_VAR, token);
606 }
607 Scope scope = scopeReference.get();
608 if (scope == null) {
609 scope = new Scope(null, (String[]) null);
610 scopeReference.set(scope);
611 }
612 final int symbol = scope.declareParameter(identifier);
613
614
615 final LexicalUnit block = getUnit();
616 if (!block.declareSymbol(symbol)) {
617 if (lexical || getFeatures().isLexical()) {
618 final JexlInfo xinfo = info.at(token.beginLine, token.beginColumn);
619 throw new JexlException.Parsing(xinfo, identifier + ": parameter is already declared").clean();
620 }
621 } else if (lexical) {
622 scope.addLexical(symbol);
623 if (constant) {
624 block.setConstant(symbol);
625 }
626 }
627 }
628
629
630
631
632
633
634
635 protected void declarePragma(final String key, final Object value) {
636 final JexlFeatures features = getFeatures();
637 if (!features.supportsPragma()) {
638 throwFeatureException(JexlFeatures.PRAGMA, getToken(0));
639 }
640 if (PRAGMA_IMPORT.equals(key) && !features.supportsImportPragma()) {
641 throwFeatureException(JexlFeatures.IMPORT_PRAGMA, getToken(0));
642 }
643 if (pragmas == null) {
644 pragmas = new TreeMap<>();
645 }
646
647 final String[] nsprefixes = { PRAGMA_JEXLNS, PRAGMA_MODULE };
648 for(final String nsprefix : nsprefixes) {
649 if (key.startsWith(nsprefix)) {
650 if (!features.supportsNamespacePragma()) {
651 throwFeatureException(JexlFeatures.NS_PRAGMA, getToken(0));
652 }
653 final String nsname = key.substring(nsprefix.length());
654 if (!nsname.isEmpty()) {
655 if (namespaces == null) {
656 namespaces = new HashSet<>();
657 }
658 namespaces.add(nsname);
659 }
660 break;
661 }
662 }
663
664 if (value == null) {
665 pragmas.putIfAbsent(key, null);
666 } else {
667 pragmas.merge(key, value, (previous, newValue) -> {
668 if (previous instanceof Set<?>) {
669 ((Set<Object>) previous).add(newValue);
670 return previous;
671 }
672 final Set<Object> values = new LinkedHashSet<>();
673 values.add(previous);
674 values.add(newValue);
675 return values;
676 });
677 }
678 }
679
680
681
682
683
684
685
686
687 private boolean declareSymbol(final int symbol) {
688 for (final LexicalUnit lu : blocks) {
689 if (lu.hasSymbol(symbol)) {
690 return false;
691 }
692
693 if (lu instanceof ASTJexlLambda) {
694 break;
695 }
696 }
697 final LexicalUnit block = getUnit();
698 return block == null || block.declareSymbol(symbol);
699 }
700
701
702
703
704
705
706
707
708
709
710
711
712 protected void declareVariable(final ASTVar variable, final Token token, final boolean lexical, final boolean constant) {
713 final String name = token.image;
714 if (!allowVariable(name)) {
715 throwFeatureException(JexlFeatures.LOCAL_VAR, token);
716 }
717 Scope scope = scopeReference.get();
718 if (scope == null) {
719 scope = new Scope(null);
720 scopeReference.set(scope);
721 }
722 final int symbol = scope.declareVariable(name);
723 variable.setSymbol(symbol, name);
724 variable.setLexical(lexical);
725 variable.setConstant(constant);
726 if (scope.isCapturedSymbol(symbol)) {
727 variable.setCaptured(true);
728 }
729
730 if (!declareSymbol(symbol)) {
731 if (lexical || scope.isLexical(symbol) || getFeatures().isLexical()) {
732 final JexlInfo location = info.at(token.beginLine, token.beginColumn);
733 throw new JexlException.Parsing(location, name + ": variable is already declared").clean();
734 }
735
736 variable.setRedefined(true);
737 } else if (lexical) {
738 scope.addLexical(symbol);
739 if (constant) {
740 getUnit().setConstant(symbol);
741 }
742 }
743 }
744
745
746
747
748
749
750 protected JexlFeatures getFeatures() {
751 return featureController.getFeatures();
752 }
753
754
755
756
757
758
759
760
761
762 protected Scope getScope() {
763 return scopeReference.get();
764 }
765
766
767
768
769
770
771
772 protected abstract Token getToken(int index);
773
774
775
776
777
778 protected LexicalUnit getUnit() {
779 return blockReference.get();
780 }
781
782
783
784
785
786
787
788 @SuppressWarnings("unused")
789 protected void Identifier(final boolean top) throws ParseException {
790
791 }
792
793
794
795
796
797
798
799 private boolean isConstant(final int symbol) {
800 if (symbol >= 0) {
801 final LexicalUnit block = getUnit();
802 if (block != null && block.hasSymbol(symbol)) {
803 return block.isConstant(symbol);
804 }
805 Scope blockScope = blockScopes.get(block);
806 int lexical = symbol;
807 for (final LexicalUnit unit : blocks) {
808 final Scope unitScope = blockScopes.get(unit);
809
810 if (blockScope != unitScope) {
811 final int declared = blockScope.getCaptureDeclaration(lexical);
812 if (declared >= 0) {
813 lexical = declared;
814 }
815 if (unitScope != null) {
816 blockScope = unitScope;
817 }
818 }
819 if (unit.hasSymbol(lexical)) {
820 return unit.isConstant(lexical);
821 }
822 }
823 }
824 return false;
825 }
826
827
828
829
830
831
832
833 private boolean isNamespace(final String name) {
834
835 if ("jexl".equals(name) || "$jexl".equals(name)) {
836 return true;
837 }
838 final Set<String> ns = namespaces;
839
840 if (ns != null && ns.contains(name)) {
841 return true;
842 }
843
844 return getFeatures().namespaceTest().test(name);
845 }
846
847
848
849
850
851
852
853
854
855
856
857
858
859 protected boolean isNamespaceFuncall(final Token ns, final Token colon, final Token fun, final Token paren) {
860
861 if (!":".equals(colon.image)) {
862 return false;
863 }
864 if (!"(".equals(paren.image)) {
865 return false;
866 }
867
868 if (featureController.getFeatures().supportsNamespaceIdentifier()) {
869 return colon.beginColumn - 1 == ns.endColumn
870 && colon.endColumn == fun.beginColumn - 1;
871 }
872
873
874
875 if (isVariable(ns.image) || isVariable(fun.image)) {
876
877 return colon.beginColumn - 1 == ns.endColumn
878 && (colon.endColumn == fun.beginColumn - 1 || isNamespace(ns.image));
879 }
880 return true;
881 }
882
883
884
885
886
887
888
889
890
891 private boolean isSymbolDeclared(final JexlNode.Info info, final int symbol) {
892 JexlNode walk = info.getNode();
893 while(walk != null) {
894 if (walk instanceof JexlParser.LexicalUnit) {
895 final LexicalScope scope = ((JexlParser.LexicalUnit) walk).getLexicalScope();
896 if (scope != null && scope.hasSymbol(symbol)) {
897 return true;
898 }
899
900 if (walk instanceof ASTJexlLambda) {
901 break;
902 }
903 }
904 walk = walk.jjtGetParent();
905 }
906 return false;
907 }
908
909
910
911
912
913
914
915 protected boolean isVariable(final String name) {
916 final Scope scope = scopeReference.get();
917 return scope != null && scope.getSymbol(name) != null;
918 }
919
920
921
922
923
924
925
926
927
928
929
930 protected boolean isAmbiguousStatement(final int semicolon) {
931 if (autoSemicolon) {
932 final Token current = getToken(0);
933 final Token next = getToken(1);
934 if (current != null && next != null && current.endLine != next.beginLine) {
935
936 return false;
937 }
938 }
939 return !getFeatures().supportsAmbiguousStatement();
940 }
941
942
943
944
945
946
947
948
949
950 protected void jjtreeCloseNodeScope(final JexlNode node) {
951 if (node instanceof ASTAmbiguous) {
952 throwAmbiguousException(node);
953 }
954 if (node instanceof ASTJexlScript) {
955 if (node instanceof ASTJexlLambda && !getFeatures().supportsLambda()) {
956 throwFeatureException(JexlFeatures.LAMBDA, node.jexlInfo());
957 }
958 final ASTJexlScript script = (ASTJexlScript) node;
959
960 final Scope scope = scopeReference.get();
961 if (script.getScope() != scope) {
962 script.setScope(scope);
963 }
964 } else if (ASSIGN_NODES.contains(node.getClass())) {
965 final JexlNode lv = node.jjtGetChild(0);
966 if (!lv.isLeftValue()) {
967 JexlInfo xinfo = lv.jexlInfo();
968 xinfo = info.at(xinfo.getLine(), xinfo.getColumn());
969 final String msg = readSourceLine(source, xinfo.getLine());
970 throw new JexlException.Assignment(xinfo, msg).clean();
971 }
972 if (lv instanceof ASTIdentifier && !(lv instanceof ASTVar)) {
973 final ASTIdentifier varName = (ASTIdentifier) lv;
974 if (isConstant(varName.getSymbol())) {
975 JexlInfo xinfo = lv.jexlInfo();
976 xinfo = info.at(xinfo.getLine(), xinfo.getColumn());
977 throw new JexlException.Assignment(xinfo, varName.getName()).clean();
978 }
979 }
980 }
981
982 featureController.controlNode(node);
983 }
984
985
986
987
988
989
990
991
992
993 @Override
994 public ASTJexlScript jxltParse(final JexlInfo info, final JexlFeatures features, final String src, final Scope scope) {
995 return new Parser(this).parse(info, features, src, scope);
996 }
997
998
999
1000
1001
1002
1003
1004
1005
1006 static JxltEngine.Expression parseInterpolation(final JexlInfo info, final String src, final Scope scope) {
1007 final JexlEngine jexl = JexlEngine.getThreadEngine();
1008 if (jexl != null) {
1009
1010
1011 final JxltEngine jxlt = jexl.createJxltEngine(true, -1, '$', '#');
1012 if (jxlt instanceof TemplateEngine) {
1013 return ((TemplateEngine) jxlt).createExpression(info, src, scope);
1014 }
1015 }
1016 throw new IllegalStateException("engine is not a accessible");
1017 }
1018
1019
1020
1021
1022
1023
1024 protected void jjtreeOpenNodeScope(final JexlNode node) {
1025
1026 }
1027
1028
1029
1030
1031
1032
1033 protected void beginLambda(final ASTJexlScript jjtThis) {
1034 jjtThis.setFeatures(getFeatures());
1035 pushScope();
1036 pushUnit(jjtThis);
1037 }
1038
1039
1040
1041
1042
1043
1044 protected void endLambda(final ASTJexlScript jjtThis) {
1045 popUnit(jjtThis);
1046 popScope();
1047 }
1048
1049
1050
1051
1052 protected void popScope() {
1053 final Scope scope = scopes.isEmpty() ? null : scopes.pop();
1054 scopeReference.set(scope);
1055 if (!loopCounts.isEmpty()) {
1056 loopCount.set(loopCounts.pop());
1057 }
1058 }
1059
1060
1061
1062
1063
1064
1065 protected void popUnit(final LexicalUnit unit) {
1066 final LexicalUnit block = blockReference.get();
1067 if (block == unit){
1068 blockScopes.remove(unit);
1069 blockReference.set(blocks.isEmpty()? null : blocks.pop());
1070 }
1071 }
1072
1073
1074
1075
1076 protected void pushScope() {
1077 Scope scope = scopeReference.get();
1078 if (scope != null) {
1079 scopes.push(scope);
1080 }
1081 scope = new Scope(scope, (String[]) null);
1082 scopeReference.set(scope);
1083 loopCounts.push(loopCount.getAndSet(0));
1084 }
1085
1086
1087
1088
1089
1090
1091 protected void pushUnit(final LexicalUnit unit) {
1092 final Scope scope = scopeReference.get();
1093 blockScopes.put(unit, scope);
1094 final LexicalUnit block = blockReference.get();
1095 if (block != null) {
1096 blocks.push(block);
1097 }
1098 blockReference.set(unit);
1099 }
1100
1101
1102
1103
1104
1105
1106 protected void pushLoop() {
1107 loopCounts.push(loopCount.getAndSet(0));
1108 }
1109
1110
1111
1112
1113 protected void popLoop() {
1114 if (!loopCounts.isEmpty()) {
1115 loopCount.set(loopCounts.pop());
1116 }
1117 }
1118
1119
1120
1121
1122
1123
1124 protected void setFeatures(final JexlFeatures features) {
1125 this.featureController.setFeatures(features);
1126 }
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137 protected void throwAmbiguousException(final JexlNode node) {
1138 final JexlInfo begin = node.jexlInfo(info.getName());
1139 final Token t = getToken(0);
1140 final JexlInfo end = info.at(t.beginLine, t.endColumn);
1141 final String msg = readSourceLine(source, end.getLine());
1142 throw new JexlException.Ambiguous(begin, end, msg).clean();
1143 }
1144
1145
1146
1147
1148
1149
1150
1151
1152 protected void throwFeatureException(final int feature, final JexlInfo info) {
1153 final String msg = info != null ? readSourceLine(source, info.getLine()) : null;
1154 throw new JexlException.Feature(info, feature, msg).clean();
1155 }
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165 protected void throwFeatureException(final int feature, final Token trigger) {
1166 Token token = trigger;
1167 if (token == null) {
1168 token = getToken(0);
1169 if (token == null) {
1170 throw new JexlException.Parsing(null, JexlFeatures.stringify(feature)).clean();
1171 }
1172 }
1173 final JexlInfo xinfo = info.at(token.beginLine, token.beginColumn);
1174 throwFeatureException(feature, xinfo);
1175 }
1176
1177
1178
1179
1180
1181
1182
1183 protected void throwParsingException(final Token parsed) {
1184 JexlInfo xinfo = null;
1185 String msg = "unrecoverable state";
1186 Token token = parsed;
1187 if (token == null) {
1188 token = getToken(0);
1189 }
1190 if (token != null) {
1191 xinfo = info.at(token.beginLine, token.beginColumn);
1192 msg = token.image;
1193 }
1194 throw new JexlException.Parsing(xinfo, msg).clean();
1195 }
1196 }