|
23 | 23 | import org.antlr.v4.tool.LeftRecursiveRule;
|
24 | 24 | import org.antlr.v4.tool.LexerGrammar;
|
25 | 25 | import org.antlr.v4.tool.Rule;
|
26 |
| -import org.antlr.v4.tool.ast.AltAST; |
27 |
| -import org.antlr.v4.tool.ast.GrammarAST; |
28 |
| -import org.antlr.v4.tool.ast.TerminalAST; |
| 26 | +import org.antlr.v4.tool.ast.*; |
29 | 27 |
|
30 | 28 | import java.util.ArrayList;
|
31 | 29 | import java.util.Collection;
|
@@ -70,12 +68,14 @@ public void process() {
|
70 | 68 | // methods affect fields, but no side-effects outside this object
|
71 | 69 | // So, call order sensitive
|
72 | 70 | // First collect all rules for later use in checkForLabelConflict()
|
73 |
| - if (g.rules != null) { |
74 |
| - for (Rule r : g.rules.values()) nameToRuleMap.put(r.name, r); |
75 |
| - } |
76 |
| - checkReservedNames(g.rules.values()); |
| 71 | + Collection<Rule> rules = g.rules.values(); |
| 72 | + for (Rule r : rules) nameToRuleMap.put(r.name, r); |
| 73 | + checkReservedNames(rules); |
77 | 74 | checkActionRedefinitions(collector.namedActions);
|
78 |
| - checkForLabelConflicts(g.rules.values()); |
| 75 | + checkForLabelConflicts(rules); |
| 76 | + if (g instanceof LexerGrammar) { |
| 77 | + checkActionLocation(rules); |
| 78 | + } |
79 | 79 | }
|
80 | 80 |
|
81 | 81 | public void checkActionRedefinitions(List<GrammarAST> actions) {
|
@@ -479,4 +479,86 @@ public void checkForQualifiedRuleIssues(Grammar g, List<GrammarAST> qualifiedRul
|
479 | 479 | }
|
480 | 480 | }
|
481 | 481 | }
|
| 482 | + |
| 483 | + private void checkActionLocation(Collection<Rule> rules) { |
| 484 | + HashMap<RuleAST, Boolean> calculatedRules = new HashMap<>(); |
| 485 | + for (Rule rule : rules) { |
| 486 | + checkActionLocation(rule.ast, calculatedRules, false); |
| 487 | + } |
| 488 | + } |
| 489 | + |
| 490 | + private void checkActionLocation(GrammarAST node, HashMap<RuleAST, Boolean> calculatedRules, boolean isPredicateDetected) { |
| 491 | + if (node instanceof RuleAST) { |
| 492 | + RuleAST ruleAST = (RuleAST) node; |
| 493 | + checkActionLocation((GrammarAST) ruleAST.getChild(ruleAST.getChildCount() - 1), calculatedRules, isPredicateDetected); |
| 494 | + } |
| 495 | + else if (node instanceof BlockAST) { |
| 496 | + for (Object child : node.getChildren()) { |
| 497 | + checkActionLocation((GrammarAST)child, calculatedRules, isPredicateDetected); |
| 498 | + } |
| 499 | + } |
| 500 | + else if (node instanceof AltAST) { |
| 501 | + List<?> children = node.getChildren(); |
| 502 | + boolean isPredicateDetectedInSequence = isPredicateDetected; |
| 503 | + for (int i = children.size() - 1; i >= 0; i--) { |
| 504 | + GrammarAST child = (GrammarAST)children.get(i); |
| 505 | + checkActionLocation(child, calculatedRules, isPredicateDetectedInSequence); |
| 506 | + if (!isPredicateDetectedInSequence && containsSemanticsPredicate(child, calculatedRules)) { |
| 507 | + isPredicateDetectedInSequence = true; |
| 508 | + } |
| 509 | + } |
| 510 | + } |
| 511 | + else if (node instanceof PlusBlockAST || node instanceof StarBlockAST) { |
| 512 | + GrammarAST firstChild = (GrammarAST) node.getChild(0); |
| 513 | + // Warn about action in closure if there is predicate inside closure |
| 514 | + checkActionLocation(firstChild, calculatedRules, |
| 515 | + isPredicateDetected || containsSemanticsPredicate(firstChild, calculatedRules)); |
| 516 | + } |
| 517 | + else if (node instanceof OptionalBlockAST) { |
| 518 | + checkActionLocation((GrammarAST) node.getChild(0), calculatedRules, isPredicateDetected); |
| 519 | + } |
| 520 | + else if (node instanceof ActionAST && !(node instanceof PredAST)) { |
| 521 | + if (isPredicateDetected) { |
| 522 | + ActionAST actionAST = (ActionAST) node; |
| 523 | + errMgr.grammarError(ErrorType.ACTION_SHOULD_BE_PLACED_AFTER_PREDICATES, g.fileName, actionAST.token, actionAST.token.getText()); |
| 524 | + } |
| 525 | + } |
| 526 | + } |
| 527 | + |
| 528 | + private boolean containsSemanticsPredicate(GrammarAST node, HashMap<RuleAST, Boolean> calculatedRules) { |
| 529 | + if (node instanceof RuleAST) { |
| 530 | + RuleAST ruleAST = (RuleAST) node; |
| 531 | + Boolean calculatedValue = calculatedRules.get(ruleAST); |
| 532 | + if (calculatedValue != null) { |
| 533 | + return calculatedValue; |
| 534 | + } |
| 535 | + |
| 536 | + calculatedRules.put(ruleAST, false); // Prevent endless recursion |
| 537 | + boolean result = containsSemanticsPredicate((GrammarAST) ruleAST.getChild(ruleAST.getChildCount() - 1), calculatedRules); |
| 538 | + calculatedRules.put(ruleAST, result); |
| 539 | + } |
| 540 | + else if (node instanceof PredAST) { |
| 541 | + return true; |
| 542 | + } |
| 543 | + else if (node instanceof TerminalAST) { |
| 544 | + if (node.token.getType() == ANTLRLexer.TOKEN_REF) { |
| 545 | + Rule refRule = nameToRuleMap.get(node.token.getText()); |
| 546 | + if (refRule != null) { |
| 547 | + return containsSemanticsPredicate(refRule.ast, calculatedRules); |
| 548 | + } |
| 549 | + } |
| 550 | + } |
| 551 | + else { |
| 552 | + List<?> children = node.getChildren(); |
| 553 | + if (children != null) { |
| 554 | + for (Object child : children) { |
| 555 | + if (containsSemanticsPredicate((GrammarAST) child, calculatedRules)) { |
| 556 | + return true; |
| 557 | + } |
| 558 | + } |
| 559 | + } |
| 560 | + } |
| 561 | + |
| 562 | + return false; |
| 563 | + } |
482 | 564 | }
|
0 commit comments