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