diff --git a/tool/playground/A.g4 b/tool/playground/A.g4 index 9c2d6ea73d..ce79583fc5 100644 --- a/tool/playground/A.g4 +++ b/tool/playground/A.g4 @@ -8,5 +8,11 @@ e : e '*' e -> Mult | '(' e ')' -> Parens ; +x : A -> Foo + | B + ; + +y : Y -> Mult ; + INT : [0-9]+ ; WS : [ \t\n]+ -> skip ; diff --git a/tool/playground/ABaseVisitor.java b/tool/playground/ABaseVisitor.java index aa3159fbc1..355023fd63 100644 --- a/tool/playground/ABaseVisitor.java +++ b/tool/playground/ABaseVisitor.java @@ -1,10 +1,10 @@ -import org.antlr.v4.runtime.tree.*; -import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.tree.ParseTreeVisitor; public class ABaseVisitor extends ParseTreeVisitor implements AVisitor { public T visit(AParser.MultContext ctx) { visitChildren(ctx); return null; } public T visit(AParser.ParensContext ctx) { visitChildren(ctx); return null; } public T visit(AParser.sContext ctx) { visitChildren(ctx); return null; } + public T visit(AParser.FooContext ctx) { visitChildren(ctx); return null; } public T visit(AParser.AddContext ctx) { visitChildren(ctx); return null; } public T visit(AParser.IntContext ctx) { visitChildren(ctx); return null; } -} \ No newline at end of file +} diff --git a/tool/playground/AVisitor.java b/tool/playground/AVisitor.java index 5730fa2d6b..4bd72a0f0b 100644 --- a/tool/playground/AVisitor.java +++ b/tool/playground/AVisitor.java @@ -1,10 +1,8 @@ -import org.antlr.v4.runtime.tree.*; -import org.antlr.v4.runtime.Token; - public interface AVisitor { T visit(AParser.MultContext ctx); T visit(AParser.ParensContext ctx); T visit(AParser.sContext ctx); + T visit(AParser.FooContext ctx); T visit(AParser.AddContext ctx); T visit(AParser.IntContext ctx); -} \ No newline at end of file +} diff --git a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java index cea367831f..d39b46cf0d 100644 --- a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java +++ b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java @@ -40,10 +40,7 @@ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT import org.stringtemplate.v4.misc.MultiMap; import java.io.File; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; /** No side-effects except for setting options into the appropriate node. * TODO: make the side effects into a separate pass this @@ -242,6 +239,44 @@ public void elementOption(GrammarASTWithOptions elem, GrammarAST ID, GrammarAST // } } + Map altLabelToRuleName = new HashMap(); + + @Override + public void finishRule(RuleAST rule, GrammarAST ID, GrammarAST block) { + MultiMap ruleToAltLabels = new MultiMap(); + if ( rule.isLexerRule() ) return; + BlockAST blk = (BlockAST)rule.getFirstChildWithType(BLOCK); + int nalts = blk.getChildCount(); + for (int i=0; i< nalts; i++) { + AltAST altAST = (AltAST)blk.getChild(i); + if ( altAST.altLabel!=null ) { + System.out.println("alt label "+altAST.altLabel); + ruleToAltLabels.map(rule.getRuleName(), altAST.altLabel); + String altLabel = altAST.altLabel.getText(); + String prevRuleForLabel = altLabelToRuleName.get(altLabel); + if ( prevRuleForLabel!=null ) { + g.tool.errMgr.grammarError(ErrorType.ALT_LABEL_REDEF, + g.fileName, rule.getToken(), + altLabel, + rule.getRuleName(), + prevRuleForLabel); + } + else { + altLabelToRuleName.put(altLabel, rule.getRuleName()); + } + } + } + System.out.println(rule.getRuleName()+" has "+ nalts +" alts"); + List altLabels = ruleToAltLabels.get(rule.getRuleName()); + int numAltLabels = 0; + if ( altLabels!=null ) numAltLabels = altLabels.size(); + System.out.println("labels="+altLabels); + if ( numAltLabels>0 && nalts != numAltLabels ) { + g.tool.errMgr.grammarError(ErrorType.RULE_WITH_TOO_FEW_ALT_LABELS, + g.fileName, rule.getToken(), rule.getRuleName()); + } + } + // Routines to do the actual work of checking issues with a grammar. // They are triggered by the visitor methods above. diff --git a/tool/src/org/antlr/v4/tool/ErrorType.java b/tool/src/org/antlr/v4/tool/ErrorType.java index af37575091..4b9f831b7e 100644 --- a/tool/src/org/antlr/v4/tool/ErrorType.java +++ b/tool/src/org/antlr/v4/tool/ErrorType.java @@ -139,7 +139,9 @@ public enum ErrorType { ALL_OPS_NEED_SAME_ASSOC(118, "all operators of alt of left-recursive rule must have same associativity", ErrorSeverity.WARNING), LEFT_RECURSION_CYCLES(119, "The following sets of rules are mutually left-recursive }; separator=\", \">]}; separator=\" and \">", ErrorSeverity.ERROR), MODE_NOT_IN_LEXER(120, "lexical modes are only allowed in lexer grammars", ErrorSeverity.ERROR), - CANNOT_FIND_ATTRIBUTE_NAME_IN_DECL(121, "cannot find an attribute name in attribute declaration", ErrorSeverity.ERROR), + CANNOT_FIND_ATTRIBUTE_NAME_IN_DECL(121, "cannot find an attribute name in attribute declaration", ErrorSeverity.ERROR), + RULE_WITH_TOO_FEW_ALT_LABELS(122, "rule : must label all alternatives or none", ErrorSeverity.ERROR), + ALT_LABEL_REDEF(123, "rule alt label redefined in rule , originally in ", ErrorSeverity.ERROR), /** Documentation comment is unterminated */ //UNTERMINATED_DOC_COMMENT(, "", ErrorSeverity.ERROR), diff --git a/tool/src/org/antlr/v4/tool/ast/RuleAST.java b/tool/src/org/antlr/v4/tool/ast/RuleAST.java index 5f47220180..257492538d 100644 --- a/tool/src/org/antlr/v4/tool/ast/RuleAST.java +++ b/tool/src/org/antlr/v4/tool/ast/RuleAST.java @@ -34,8 +34,6 @@ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT import org.antlr.v4.parse.ANTLRParser; public class RuleAST extends GrammarASTWithOptions { - - public RuleAST(GrammarAST node) { super(node); } @@ -43,6 +41,17 @@ public RuleAST(GrammarAST node) { public RuleAST(Token t) { super(t); } public RuleAST(int type) { super(type); } + public boolean isLexerRule() { + String name = getRuleName(); + return name!=null && Character.isUpperCase(name.charAt(0)); + } + + public String getRuleName() { + GrammarAST nameNode = (GrammarAST)getChild(0); + if ( nameNode!=null ) return nameNode.getText(); + return null; + } + @Override public Tree dupNode() { return new RuleAST(this); }