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