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 org.apache.commons.jexl3.JexlEngine;
20 import org.apache.commons.jexl3.JexlException;
21 import org.apache.commons.jexl3.JexlFeatures;
22 import org.apache.commons.jexl3.JexlInfo;
23 import org.apache.commons.jexl3.internal.LexicalScope;
24 import org.apache.commons.jexl3.internal.Scope;
25
26 import java.io.BufferedReader;
27 import java.io.IOException;
28 import java.io.StringReader;
29 import java.util.ArrayDeque;
30 import java.util.Arrays;
31 import java.util.Deque;
32 import java.util.HashSet;
33 import java.util.IdentityHashMap;
34 import java.util.LinkedHashSet;
35 import java.util.Map;
36 import java.util.Set;
37 import java.util.TreeMap;
38 import java.util.function.Predicate;
39
40
41
42
43
44 public abstract class JexlParser extends StringParser {
45
46
47
48 protected final FeatureController featureController = new FeatureController(JexlEngine.DEFAULT_FEATURES);
49
50
51
52 protected JexlInfo info = null;
53
54
55
56 protected String source = null;
57
58
59
60
61
62 protected Scope scope = null;
63
64
65
66 protected final Deque<Scope> scopes = new ArrayDeque<>();
67
68
69
70 protected Map<String, Object> pragmas = null;
71
72
73
74 protected Set<String> namespaces = null;
75
76
77
78 protected int loopCount = 0;
79
80
81
82 protected final Deque<Integer> loopCounts = new ArrayDeque<>();
83
84
85
86 protected LexicalUnit block = null;
87
88
89
90 protected final Deque<LexicalUnit> blocks = new ArrayDeque<>();
91
92
93
94 protected final Map<LexicalUnit, Scope> blockScopes = new IdentityHashMap<>();
95
96
97
98
99
100 public interface LexicalUnit {
101
102
103
104
105
106 boolean declareSymbol(int symbol);
107 void setConstant(int symbol);
108
109
110
111
112
113
114 boolean hasSymbol(int symbol);
115 boolean isConstant(int symbol);
116
117
118
119
120 int getSymbolCount();
121
122
123
124
125 LexicalScope getLexicalScope();
126 }
127
128
129
130
131
132 protected void cleanup(final JexlFeatures features) {
133 info = null;
134 source = null;
135 scope = null;
136 scopes.clear();
137 pragmas = null;
138 namespaces = null;
139 loopCounts.clear();
140 loopCount = 0;
141 blocks.clear();
142 block = null;
143 blockScopes.clear();
144 this.setFeatures(features);
145 }
146
147
148
149
150
151
152 protected static String stringify(final Iterable<String> lstr) {
153 final StringBuilder strb = new StringBuilder();
154 boolean dot = false;
155 for(final String str : lstr) {
156 if (!dot) {
157 dot = true;
158 } else {
159 strb.append('.');
160 }
161 strb.append(str);
162 }
163 return strb.toString();
164 }
165
166
167
168
169
170
171
172 protected static String readSourceLine(final String src, final int lineno) {
173 String msg = "";
174 if (src != null && lineno >= 0) {
175 try {
176 final BufferedReader reader = new BufferedReader(new StringReader(src));
177 for (int l = 0; l < lineno; ++l) {
178 msg = reader.readLine();
179 }
180 } catch (final IOException xio) {
181
182 }
183 }
184 return msg;
185 }
186
187
188
189
190
191 public void allowRegisters(final boolean registers) {
192 featureController.setFeatures(new JexlFeatures(featureController.getFeatures()).register(registers));
193 }
194
195
196
197
198
199 protected void setFeatures(final JexlFeatures features) {
200 this.featureController.setFeatures(features);
201 }
202
203
204
205
206 protected JexlFeatures getFeatures() {
207 return featureController.getFeatures();
208 }
209
210
211
212
213 protected void controlPragmaAnywhere() {
214 final JexlFeatures features = getFeatures();
215 if (features.supportsPragma() && !features.supportsPragmaAnywhere()) {
216 featureController.setFeatures(new JexlFeatures(featureController.getFeatures()).pragma(false));
217 }
218 }
219
220
221
222
223
224
225
226 protected Scope getScope() {
227 return scope;
228 }
229
230
231
232
233 protected void pushScope() {
234 if (scope != null) {
235 scopes.push(scope);
236 }
237 scope = new Scope(scope, (String[]) null);
238 loopCounts.push(loopCount);
239 loopCount = 0;
240 }
241
242
243
244
245 protected void popScope() {
246 if (!scopes.isEmpty()) {
247 scope = scopes.pop();
248 } else {
249 scope = null;
250 }
251 if (!loopCounts.isEmpty()) {
252 loopCount = loopCounts.pop();
253 }
254 }
255
256
257
258
259
260 protected LexicalUnit getUnit() {
261 return block;
262 }
263
264
265
266
267
268 protected void pushUnit(final LexicalUnit unit) {
269 blockScopes.put(unit, scope);
270 if (block != null) {
271 blocks.push(block);
272 }
273 block = unit;
274 }
275
276
277
278
279
280 protected void popUnit(final LexicalUnit unit) {
281 if (block == unit){
282 blockScopes.remove(unit);
283 if (!blocks.isEmpty()) {
284 block = blocks.pop();
285 } else {
286 block = null;
287 }
288 }
289 }
290
291
292
293
294
295
296
297
298 private boolean isSymbolDeclared(final JexlNode.Info info, final int symbol) {
299 JexlNode walk = info.getNode();
300 while(walk != null) {
301 if (walk instanceof JexlParser.LexicalUnit) {
302 final LexicalScope scope = ((JexlParser.LexicalUnit) walk).getLexicalScope();
303 if (scope != null && scope.hasSymbol(symbol)) {
304 return true;
305 }
306
307 if (walk instanceof ASTJexlLambda) {
308 break;
309 }
310 }
311 walk = walk.jjtGetParent();
312 }
313 return false;
314 }
315
316
317
318
319
320
321 protected boolean isVariable(final String name) {
322 return scope != null && scope.getSymbol(name) != null;
323 }
324
325
326
327
328
329
330
331 protected String checkVariable(final ASTIdentifier identifier, final String name) {
332 if (scope != null) {
333 final Integer symbol = scope.getSymbol(name);
334 if (symbol != null) {
335 identifier.setLexical(scope.isLexical(symbol));
336 boolean declared = true;
337 if (scope.isCapturedSymbol(symbol)) {
338
339 identifier.setCaptured(true);
340 } else {
341 LexicalUnit unit = block;
342 declared = unit.hasSymbol(symbol);
343
344 if (!declared) {
345 for (final LexicalUnit u : blocks) {
346 if (u.hasSymbol(symbol)) {
347 unit = u;
348 declared = true;
349 break;
350 }
351 }
352 }
353 if (declared) {
354
355 if (unit.isConstant(symbol)) {
356 identifier.setConstant(true);
357 }
358 } else if (info instanceof JexlNode.Info) {
359 declared = isSymbolDeclared((JexlNode.Info) info, symbol);
360 }
361 }
362 identifier.setSymbol(symbol, name);
363 if (!declared) {
364 identifier.setShaded(true);
365 if ( getFeatures().isLexicalShade()) {
366
367 throw new JexlException.Parsing(info, name + ": variable is not declared").clean();
368 }
369 }
370 }
371 }
372 return name;
373 }
374
375
376
377
378
379
380 protected boolean allowVariable(final String image) {
381 final JexlFeatures features = getFeatures();
382 if (!features.supportsLocalVar()) {
383 return false;
384 }
385 if (features.isReservedName(image)) {
386 return false;
387 }
388 return true;
389 }
390
391
392
393
394
395
396
397 private boolean declareSymbol(final int symbol) {
398 for (final LexicalUnit lu : blocks) {
399 if (lu.hasSymbol(symbol)) {
400 return false;
401 }
402
403 if (lu instanceof ASTJexlLambda) {
404 break;
405 }
406 }
407 return block == null || block.declareSymbol(symbol);
408 }
409
410
411
412
413
414
415 protected void declareFunction(final ASTVar variable, final Token token) {
416 final String name = token.image;
417
418 if (scope == null) {
419 scope = new Scope(null);
420 }
421 final int symbol = scope.declareVariable(name);
422 variable.setSymbol(symbol, name);
423 variable.setLexical(true);
424 if (scope.isCapturedSymbol(symbol)) {
425 variable.setCaptured(true);
426 }
427
428 if (declareSymbol(symbol)) {
429 scope.addLexical(symbol);
430 block.setConstant(symbol);
431 } else {
432 if (getFeatures().isLexical()) {
433 throw new JexlException(variable, name + ": variable is already declared");
434 }
435 variable.setRedefined(true);
436 }
437 }
438
439
440
441
442
443
444
445
446
447 protected void declareVariable(final ASTVar variable, final Token token, final boolean lexical, final boolean constant) {
448 final String name = token.image;
449 if (!allowVariable(name)) {
450 throwFeatureException(JexlFeatures.LOCAL_VAR, token);
451 }
452 if (scope == null) {
453 scope = new Scope(null);
454 }
455 final int symbol = scope.declareVariable(name);
456 variable.setSymbol(symbol, name);
457 variable.setLexical(lexical);
458 variable.setConstant(constant);
459 if (scope.isCapturedSymbol(symbol)) {
460 variable.setCaptured(true);
461 }
462
463 if (!declareSymbol(symbol)) {
464 if (lexical || scope.isLexical(symbol) || getFeatures().isLexical()) {
465 throw new JexlException.Parsing(variable.jexlInfo(), name + ": variable is already declared").clean();
466 }
467
468 variable.setRedefined(true);
469 } else if (lexical) {
470 scope.addLexical(symbol);
471 if (constant) {
472 block.setConstant(symbol);
473 }
474 }
475 }
476
477
478
479
480
481
482
483
484 protected void declareParameter(final Token token, final boolean lexical, final boolean constant) {
485 final String identifier = token.image;
486 if (!allowVariable(identifier)) {
487 throwFeatureException(JexlFeatures.LOCAL_VAR, token);
488 }
489 if (scope == null) {
490 scope = new Scope(null, (String[]) null);
491 }
492 final int symbol = scope.declareParameter(identifier);
493
494
495 if (!block.declareSymbol(symbol)) {
496 if (lexical || getFeatures().isLexical()) {
497 final JexlInfo xinfo = info.at(token.beginLine, token.beginColumn);
498 throw new JexlException.Parsing(xinfo, identifier + ": parameter is already declared").clean();
499 }
500 } else if (lexical) {
501 scope.addLexical(symbol);
502 if (constant) {
503 block.setConstant(symbol);
504 }
505 }
506 }
507
508
509
510
511 public static final String PRAGMA_OPTIONS = "jexl.options";
512
513
514
515 public static final String PRAGMA_JEXLNS = "jexl.namespace.";
516
517
518
519 public static final String PRAGMA_MODULE = "jexl.module.";
520
521
522
523 public static final String PRAGMA_IMPORT = "jexl.import";
524
525
526
527
528
529
530 protected void declarePragma(final String key, final Object value) {
531 final JexlFeatures features = getFeatures();
532 if (!features.supportsPragma()) {
533 throwFeatureException(JexlFeatures.PRAGMA, getToken(0));
534 }
535 if (PRAGMA_IMPORT.equals(key) && !features.supportsImportPragma()) {
536 throwFeatureException(JexlFeatures.IMPORT_PRAGMA, getToken(0));
537 }
538 if (pragmas == null) {
539 pragmas = new TreeMap<>();
540 }
541
542 final Predicate<String> ns = features.namespaceTest();
543 if (ns != null && key.startsWith(PRAGMA_JEXLNS)) {
544 if (!features.supportsNamespacePragma()) {
545 throwFeatureException(JexlFeatures.NS_PRAGMA, getToken(0));
546 }
547
548 final String nsname = key.substring(PRAGMA_JEXLNS.length());
549 if (!nsname.isEmpty()) {
550 if (namespaces == null) {
551 namespaces = new HashSet<>();
552 }
553 namespaces.add(nsname);
554 }
555 }
556
557 pragmas.merge(key, value, (previous, newValue)->{
558 if (previous instanceof Set<?>) {
559 ((Set<Object>) previous).add(newValue);
560 return previous;
561 }
562 final Set<Object> values = new LinkedHashSet<>();
563 values.add(previous);
564 values.add(newValue);
565 return values;
566 });
567 }
568
569
570
571
572
573
574 protected boolean isDeclaredNamespace(final Token token, final Token colon) {
575
576 if (colon != null && ":".equals(colon.image) && colon.beginColumn - 1 == token.endColumn) {
577 return true;
578 }
579
580 final String name = token.image;
581 if (!isVariable(name)) {
582 final Set<String> ns = namespaces;
583
584 if (ns != null && ns.contains(name)) {
585 return true;
586 }
587
588 if (getFeatures().namespaceTest().test(name)) {
589 return true;
590 }
591 }
592 return false;
593 }
594
595
596
597
598
599
600 protected void Identifier(final boolean top) throws ParseException {
601
602 }
603
604
605
606
607
608
609 protected abstract Token getToken(int index);
610
611
612
613
614 private static final Set<Class<? extends JexlNode>> ASSIGN_NODES = new HashSet<>(
615 Arrays.asList(
616 ASTAssignment.class,
617 ASTSetAddNode.class,
618 ASTSetSubNode.class,
619 ASTSetMultNode.class,
620 ASTSetDivNode.class,
621 ASTSetModNode.class,
622 ASTSetAndNode.class,
623 ASTSetOrNode.class,
624 ASTSetXorNode.class,
625 ASTSetShiftLeftNode.class,
626 ASTSetShiftRightNode.class,
627 ASTSetShiftRightUnsignedNode.class,
628 ASTIncrementGetNode.class,
629 ASTDecrementGetNode.class,
630 ASTGetDecrementNode.class,
631 ASTGetIncrementNode.class
632 )
633 );
634
635
636
637
638
639 protected void jjtreeOpenNodeScope(final JexlNode node) {
640
641
642
643
644 }
645
646
647
648
649
650
651
652
653 protected void jjtreeCloseNodeScope(final JexlNode node) {
654 if (node instanceof ASTAmbiguous) {
655 throwAmbiguousException(node);
656 }
657 if (node instanceof ASTJexlScript) {
658 if (node instanceof ASTJexlLambda && !getFeatures().supportsLambda()) {
659 throwFeatureException(JexlFeatures.LAMBDA, node.jexlInfo());
660 }
661 final ASTJexlScript script = (ASTJexlScript) node;
662
663 if (script.getScope() != scope) {
664 script.setScope(scope);
665 }
666 } else if (ASSIGN_NODES.contains(node.getClass())) {
667 final JexlNode lv = node.jjtGetChild(0);
668 if (!lv.isLeftValue()) {
669 JexlInfo xinfo = lv.jexlInfo();
670 xinfo = info.at(xinfo.getLine(), xinfo.getColumn());
671 final String msg = readSourceLine(source, xinfo.getLine());
672 throw new JexlException.Assignment(xinfo, msg).clean();
673 }
674 if (lv instanceof ASTIdentifier && !(lv instanceof ASTVar)) {
675 final ASTIdentifier var = (ASTIdentifier) lv;
676 if (isConstant(var.getSymbol())) {
677 JexlInfo xinfo = lv.jexlInfo();
678 xinfo = info.at(xinfo.getLine(), xinfo.getColumn());
679 throw new JexlException.Assignment(xinfo, var.getName()).clean();
680 }
681 }
682 }
683
684 featureController.controlNode(node);
685 }
686
687
688
689
690
691
692 private boolean isConstant(int symbol) {
693 if (symbol >= 0) {
694 if (block != null && block.hasSymbol(symbol)) {
695 return block.isConstant(symbol);
696 }
697 Scope blockScope = blockScopes.get(block);
698 int lexical = symbol;
699 for (LexicalUnit unit : blocks) {
700 Scope unitScope = blockScopes.get(unit);
701
702 if (blockScope != unitScope) {
703 int declared = blockScope.getCaptureDeclaration(lexical);
704 if (declared >= 0) {
705 lexical = declared;
706 }
707 if (unitScope != null) {
708 blockScope = unitScope;
709 }
710 }
711 if (unit.hasSymbol(lexical)) {
712 return unit.isConstant(lexical);
713 }
714 }
715 }
716 return false;
717 }
718
719
720
721
722
723 protected void checkLambda(final Token token) {
724 final String arrow = token.image;
725 if ("->".equals(arrow)) {
726 if (!getFeatures().supportsThinArrow()) {
727 throwFeatureException(JexlFeatures.THIN_ARROW, token);
728 }
729 return;
730 }
731 if ("=>".equals(arrow) && !getFeatures().supportsFatArrow()) {
732 throwFeatureException(JexlFeatures.FAT_ARROW, token);
733 }
734 }
735
736
737
738
739
740
741
742 protected void throwAmbiguousException(final JexlNode node) {
743 final JexlInfo begin = node.jexlInfo();
744 final Token t = getToken(0);
745 final JexlInfo end = info.at(t.beginLine, t.endColumn);
746 final String msg = readSourceLine(source, end.getLine());
747 throw new JexlException.Ambiguous(begin, end, msg).clean();
748 }
749
750
751
752
753
754
755
756 protected void throwFeatureException(final int feature, final JexlInfo info) {
757 final String msg = info != null? readSourceLine(source, info.getLine()) : null;
758 throw new JexlException.Feature(info, feature, msg).clean();
759 }
760
761
762
763
764
765
766
767
768 protected void throwFeatureException(final int feature, final Token trigger) {
769 Token token = trigger;
770 if (token == null) {
771 token = this.getToken(0);
772 if (token == null) {
773 throw new JexlException.Parsing(null, JexlFeatures.stringify(feature)).clean();
774 }
775 }
776 final JexlInfo xinfo = info.at(token.beginLine, token.beginColumn);
777 throwFeatureException(feature, xinfo);
778 }
779
780
781
782
783
784
785 protected void throwParsingException(final Token parsed) {
786 JexlInfo xinfo = null;
787 String msg = "unrecoverable state";
788 Token token = parsed;
789 if (token == null) {
790 token = this.getToken(0);
791 }
792 if (token != null) {
793 xinfo = info.at(token.beginLine, token.beginColumn);
794 msg = token.image;
795 }
796 throw new JexlException.Parsing(xinfo, msg).clean();
797 }
798
799
800
801
802
803
804 protected static Token errorToken(final Token... tokens) {
805 for (final Token token : tokens) {
806 if (token != null && token.image != null && !token.image.isEmpty()) {
807 return token;
808 }
809 }
810 return null;
811 }
812 }