diff --git a/runtime/Java/src/org/antlr/v4/runtime/tree/BaseTree.java b/runtime/Java/src/org/antlr/v4/runtime/tree/BaseTree.java index 5e99a19fc..7b0aaba23 100644 --- a/runtime/Java/src/org/antlr/v4/runtime/tree/BaseTree.java +++ b/runtime/Java/src/org/antlr/v4/runtime/tree/BaseTree.java @@ -252,6 +252,20 @@ public abstract class BaseTree implements Tree { } } + public void freshenParentAndChildIndexesDeeply() { + freshenParentAndChildIndexesDeeply(0); + } + + public void freshenParentAndChildIndexesDeeply(int offset) { + int n = getChildCount(); + for (int c = offset; c < n; c++) { + BaseTree child = (BaseTree)getChild(c); + child.setChildIndex(c); + child.setParent(this); + child.freshenParentAndChildIndexesDeeply(); + } + } + public void sanityCheckParentAndChildIndexes() { sanityCheckParentAndChildIndexes(null, -1); } diff --git a/tool/playground/T.g b/tool/playground/T.g index 69584e464..1f4f21a7b 100644 --- a/tool/playground/T.g +++ b/tool/playground/T.g @@ -1,7 +1,7 @@ grammar T; options {output=AST;} -a : 'var' (ID ':' type ';')+ -> ^('var' ^(':' ID type)*) ; +a : 'var' (ID ':' type ';')+ -> ^('var' ^(':' ID type)*) ; type returns [int i] : ID; ID : 'a'..'z'+ ; INT : '0'..'9'+; diff --git a/tool/src/org/antlr/v4/Tool.java b/tool/src/org/antlr/v4/Tool.java index d3f626d12..179ae0e0f 100644 --- a/tool/src/org/antlr/v4/Tool.java +++ b/tool/src/org/antlr/v4/Tool.java @@ -432,6 +432,8 @@ public class Tool { lexerRulesRoot.getChildren().add(0, litRule); // add first } + lexerRulesRoot.freshenParentAndChildIndexesDeeply(); + System.out.println("after ="+combinedAST.toStringTree()); System.out.println("lexer ="+lexerAST.toStringTree()); return lexerAST; diff --git a/tool/src/org/antlr/v4/parse/ANTLRParser.g b/tool/src/org/antlr/v4/parse/ANTLRParser.g index 2fe215747..01d56bf84 100644 --- a/tool/src/org/antlr/v4/parse/ANTLRParser.g +++ b/tool/src/org/antlr/v4/parse/ANTLRParser.g @@ -786,9 +786,8 @@ range ; terminal - : // Args are only valid for lexer rules - TOKEN_REF ARG_ACTION? elementOptions? -> ^(TOKEN_REF ARG_ACTION? elementOptions?) - | STRING_LITERAL elementOptions? -> ^(STRING_LITERAL elementOptions?) + : TOKEN_REF elementOptions? -> ^(TOKEN_REF elementOptions?) + | STRING_LITERAL elementOptions? -> ^(STRING_LITERAL elementOptions?) ; // Terminals may be adorned with certain options when diff --git a/tool/src/org/antlr/v4/parse/ASTVerifier.g b/tool/src/org/antlr/v4/parse/ASTVerifier.g index 95779220f..728823960 100644 --- a/tool/src/org/antlr/v4/parse/ASTVerifier.g +++ b/tool/src/org/antlr/v4/parse/ASTVerifier.g @@ -334,8 +334,6 @@ range terminal : ^(STRING_LITERAL elementOptions) | STRING_LITERAL - | ^(TOKEN_REF ARG_ACTION elementOptions) - | ^(TOKEN_REF ARG_ACTION) | ^(TOKEN_REF elementOptions) | TOKEN_REF ; diff --git a/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g b/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g index 3fdc82cee..c307bcbf3 100644 --- a/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g +++ b/tool/src/org/antlr/v4/parse/GrammarTreeVisitor.g @@ -70,6 +70,7 @@ options { package org.antlr.v4.parse; import org.antlr.v4.tool.*; import org.antlr.v4.runtime.tree.CommonTree; // use updated v4 one not v3 +import java.lang.reflect.Method; } @members { @@ -83,29 +84,66 @@ public int rewriteEBNFLevel = 0; public boolean inRewrite; public boolean currentOuterAltHasRewrite; -public void visitGrammar(ErrorManager errMgr) { - try { grammarSpec(); } - catch (org.antlr.runtime.RecognitionException re) { - errMgr.grammarError(ErrorType.INTERNAL_ERROR, - null, re.token, re); +public GrammarTreeVisitor() { this(null); } + +public ErrorManager getErrorManager() { return null; } + +public void visitGrammar(GrammarAST t) { visit(t, "grammarSpec"); } +public void visitRewrite(GrammarAST t) { visit(t, "rewrite"); } +public void visitRewriteEBNF(GrammarAST t) { visit(t, "rewriteTreeEbnf"); } +public void visit(GrammarAST t, String ruleName) { + CommonTreeNodeStream nodes = new CommonTreeNodeStream(t); + setTreeNodeStream(nodes); + try { + Method m = getClass().getMethod(ruleName); + m.invoke(this); } -} -public void visitRewrite(ErrorManager errMgr) { - try { rewrite(); } - catch (org.antlr.runtime.RecognitionException re) { - errMgr.grammarError(ErrorType.INTERNAL_ERROR, - null, re.token, re); - } -} -public void visitRewriteEBNF(ErrorManager errMgr) { - try { rewriteTreeEbnf(); } - catch (org.antlr.runtime.RecognitionException re) { - errMgr.grammarError(ErrorType.INTERNAL_ERROR, - null, re.token, re); + catch (Exception e) { + ErrorManager errMgr = getErrorManager(); + if ( errMgr==null ) System.err.println("can't find rule "+ruleName+ + " or tree structure error: "+t.toStringTree() + ); + else errMgr.toolError(ErrorType.INTERNAL_ERROR, e); } } +public void discoverGrammar(GrammarRootAST root, GrammarAST ID) { } +public void finishPrequels(GrammarAST firstPrequel) { } +public void finishGrammar(GrammarRootAST root, GrammarAST ID) { } + +public void grammarOption(GrammarAST ID, String value) { } +public void ruleOption(GrammarAST ID, String value) { } +public void blockOption(GrammarAST ID, String value) { } +public void tokenAlias(GrammarAST ID, GrammarAST literal) { } + +public void importGrammar(GrammarAST label, GrammarAST ID) { } + +public void modeDef(GrammarAST m, GrammarAST ID) { } + +public void discoverRules(GrammarAST rules) { } +public void finishRules(GrammarAST rule) { } +public void discoverRule(GrammarAST rule, GrammarAST ID) { } +public void finishRule(GrammarAST rule, GrammarAST ID) { } +public void discoverAlt(GrammarAST alt) { } +public void finishAlt(GrammarAST alt) { } +public void discoverAltWithRewrite(GrammarAST alt) { } +public void finishAltWithRewrite(GrammarAST alt) { } +public void discoverSTRewrite(GrammarAST rew) { } +public void discoverTreeRewrite(GrammarAST rew) { } + +public void ruleRef(GrammarAST ref, GrammarAST arg) { } +public void tokenRef(GrammarAST ref, GrammarAST options) { } +public void terminalOption(TerminalAST t, GrammarAST ID, GrammarAST value) { } +public void stringRef(GrammarAST ref, GrammarAST options) { } +public void wildcardRef(GrammarAST ref, GrammarAST options) { } + +public void rootOp(GrammarAST op, GrammarAST opnd) { } +public void bangOp(GrammarAST op, GrammarAST opnd) { } + +public void discoverRewrites(GrammarAST result) { } +public void finishRewrites(GrammarAST result) { } public void rewriteTokenRef(GrammarAST ast, GrammarAST options, GrammarAST arg) { } +public void rewriteTerminalOption(TerminalAST t, GrammarAST ID, GrammarAST value) { } public void rewriteStringRef(GrammarAST ast, GrammarAST options) { } public void rewriteRuleRef(GrammarAST ast) { } public void rewriteLabelRef(GrammarAST ast) { } @@ -113,9 +151,19 @@ public void rewriteAction(GrammarAST ast) { } } grammarSpec - : ^(GRAMMAR ID {grammarName=$ID.text;} DOC_COMMENT? prequelConstruct* rules mode*) + : ^( GRAMMAR ID {grammarName=$ID.text;} DOC_COMMENT? + {discoverGrammar((GrammarRootAST)$GRAMMAR, $ID);} + prequelConstructs + {finishPrequels($prequelConstructs.start);} + rules mode* + {finishGrammar((GrammarRootAST)$GRAMMAR, $ID);} + ) ; +prequelConstructs + : prequelConstruct* + ; + prequelConstruct : optionsSpec | delegateGrammars @@ -130,9 +178,15 @@ optionsSpec option : ^(ASSIGN ID optionValue) + { + if ( inContext("RULE") ) ruleOption($ID, $optionValue.v); + else if ( inContext("BLOCK") ) blockOption($ID, $optionValue.v); + else grammarOption($ID, $optionValue.v); + } ; -optionValue +optionValue returns [String v] +@init {$v = $start.token.getText();} : ID | STRING_LITERAL | INT @@ -144,8 +198,8 @@ delegateGrammars ; delegateGrammar - : ^(ASSIGN ID ID) - | ID + : ^(ASSIGN label=ID id=ID) {importGrammar($label, $id);} + | id=ID {importGrammar(null, $id);} ; tokensSpec @@ -153,7 +207,7 @@ tokensSpec ; tokenSpec - : ^(ASSIGN ID STRING_LITERAL) + : ^(ASSIGN ID STRING_LITERAL) {tokenAlias($ID, $STRING_LITERAL);} | ID ; @@ -166,14 +220,15 @@ action ; rules - : ^(RULES rule*) + : ^(RULES {discoverRules($RULES);} rule* {finishRules($RULES);}) ; -mode: ^( MODE ID rule+ ) ; +mode: ^( MODE ID {modeDef($MODE, $ID);} rule+ ) ; -rule: ^( RULE ID {currentRuleName=$ID.text; currentRule=$RULE;} +rule: ^( RULE ID {currentRuleName=$ID.text; currentRule=$RULE; discoverRule($RULE, $ID);} DOC_COMMENT? ruleModifiers? ARG_ACTION? ruleReturns? rulePrequel* ruleBlock exceptionGroup + {finishRule($RULE, $ID);} ) ; @@ -246,7 +301,15 @@ ruleBlock ; alternative - : ^(ALT_REWRITE {inRewrite=true;} alternative rewrite) +@init { + if ( $start.getType()==ALT_REWRITE ) discoverAltWithRewrite($start); + else discoverAlt($start); +} +@after { + if ( $start.getType()==ALT_REWRITE ) finishAltWithRewrite($start); + else finishAlt($start); +} + : ^(ALT_REWRITE alternative {inRewrite=true;} rewrite) | ^(ALT EPSILON) | ^(ALT element+) ; @@ -260,8 +323,8 @@ element | SEMPRED | GATED_SEMPRED | treeSpec - | ^(ROOT astOperand) - | ^(BANG astOperand) + | ^(ROOT astOperand) {rootOp($ROOT, $astOperand.start);} + | ^(BANG astOperand) {bangOp($BANG, $astOperand.start);} | ^(NOT blockSet) | ^(NOT block) ; @@ -301,8 +364,8 @@ ebnfSuffix atom: range | ^(DOT ID terminal) | ^(DOT ID ruleref) - | ^(WILDCARD elementOptions) - | WILDCARD + | ^(WILDCARD elementOptions) {wildcardRef($WILDCARD, $elementOptions.start);} + | WILDCARD {wildcardRef($WILDCARD, null);} | terminal | blockSet | ruleref @@ -322,7 +385,7 @@ block ; ruleref - : ^(RULE_REF ARG_ACTION?) + : ^(RULE_REF ARG_ACTION?) {ruleRef($RULE_REF, $ARG_ACTION);} ; range @@ -331,30 +394,29 @@ range terminal : ^(STRING_LITERAL elementOptions) - | STRING_LITERAL - | ^(TOKEN_REF ARG_ACTION elementOptions) - | ^(TOKEN_REF ARG_ACTION) - | ^(TOKEN_REF elementOptions) - | TOKEN_REF + {stringRef($STRING_LITERAL, $elementOptions.start);} + | STRING_LITERAL {stringRef($STRING_LITERAL, null);} + | ^(TOKEN_REF elementOptions) {tokenRef($TOKEN_REF, $elementOptions.start);} + | TOKEN_REF {tokenRef($TOKEN_REF, null);} ; elementOptions - : ^(ELEMENT_OPTIONS elementOption+) + : ^(ELEMENT_OPTIONS elementOption[(TerminalAST)$start.getParent()]+) ; -elementOption - : ID - | ^(ASSIGN ID ID) - | ^(ASSIGN ID STRING_LITERAL) +elementOption[TerminalAST t] + : ID {terminalOption(t, $ID, null);} + | ^(ASSIGN id=ID v=ID) {terminalOption(t, $id, $v);} + | ^(ASSIGN ID v=STRING_LITERAL) {terminalOption(t, $ID, $v);} ; rewrite - : predicatedRewrite* nakedRewrite + : {discoverRewrites($start);} predicatedRewrite* nakedRewrite {finishRewrites($start);} ; predicatedRewrite - : ^(ST_RESULT SEMPRED rewriteAlt) - | ^(RESULT SEMPRED rewriteAlt) + : ^(ST_RESULT SEMPRED {discoverSTRewrite($start);} rewriteAlt) + | ^(RESULT SEMPRED {discoverTreeRewrite($start);} rewriteAlt) ; nakedRewrite @@ -381,17 +443,29 @@ rewriteTreeElement ; rewriteTreeAtom - : ^(TOKEN_REF elementOptions ARG_ACTION) {rewriteTokenRef($start,$elementOptions.start,$ARG_ACTION);} - | ^(TOKEN_REF elementOptions) {rewriteTokenRef($start,$elementOptions.start,null);} - | ^(TOKEN_REF ARG_ACTION) {rewriteTokenRef($start,null,$ARG_ACTION);} - | TOKEN_REF {rewriteTokenRef($start,null,null);} - | RULE_REF {rewriteRuleRef($start);} - | ^(STRING_LITERAL elementOptions) {rewriteStringRef($start,$elementOptions.start);} - | STRING_LITERAL {rewriteStringRef($start,null);} - | LABEL {rewriteLabelRef($start);} - | ACTION {rewriteAction($start);} + : ^(TOKEN_REF rewriteElementOptions ARG_ACTION) + {rewriteTokenRef($start,$rewriteElementOptions.start,$ARG_ACTION);} + | ^(TOKEN_REF rewriteElementOptions) {rewriteTokenRef($start,$rewriteElementOptions.start,null);} + | ^(TOKEN_REF ARG_ACTION) {rewriteTokenRef($start,null,$ARG_ACTION);} + | TOKEN_REF {rewriteTokenRef($start,null,null);} + | RULE_REF {rewriteRuleRef($start);} + | ^(STRING_LITERAL rewriteElementOptions) + {rewriteStringRef($start,$rewriteElementOptions.start);} + | STRING_LITERAL {rewriteStringRef($start,null);} + | LABEL {rewriteLabelRef($start);} + | ACTION {rewriteAction($start);} ; +rewriteElementOptions + : ^(ELEMENT_OPTIONS rewriteElementOption[(TerminalAST)$start.getParent()]+) + ; + +rewriteElementOption[TerminalAST t] + : ID {rewriteTerminalOption(t, $ID, null);} + | ^(ASSIGN id=ID v=ID) {rewriteTerminalOption(t, $id, $v);} + | ^(ASSIGN ID v=STRING_LITERAL) {rewriteTerminalOption(t, $ID, $v);} + ; + rewriteTreeEbnf : ^(ebnfSuffix ^(REWRITE_BLOCK {rewriteEBNFLevel++;} rewriteTreeAlt {rewriteEBNFLevel--;})) ; diff --git a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java index d03d9274d..17db606be 100644 --- a/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java +++ b/tool/src/org/antlr/v4/semantics/BasicSemanticChecks.java @@ -31,14 +31,17 @@ package org.antlr.v4.semantics; import org.antlr.runtime.Token; import org.antlr.v4.misc.Utils; -import org.antlr.v4.parse.ANTLRParser; +import org.antlr.v4.parse.*; import org.antlr.v4.tool.*; import org.stringtemplate.v4.misc.MultiMap; import java.io.File; import java.util.*; -/** No side-effects; BasicSemanticTriggers.g invokes check rules for these: +/** No side-effects except for setting options into the appropriate node. + * TODO: make the side effects into a separate pass this + * + * Invokes check rules for these: * * FILE_AND_GRAMMAR_NAME_DIFFER * LEXER_RULES_NOT_ALLOWED @@ -62,7 +65,7 @@ import java.util.*; * * TODO: 1 action per lex rule */ -public class BasicSemanticChecks { +public class BasicSemanticChecks extends GrammarTreeVisitor { public static final Set legalLexerOptions = new HashSet() { { @@ -151,6 +154,110 @@ public class BasicSemanticChecks { this.errMgr = g.tool.errMgr; } + public void process() { visitGrammar(g.ast); } + + // Routines to route visitor traffic to the checking routines + + @Override + public void discoverGrammar(GrammarRootAST root, GrammarAST ID) { + checkGrammarName(ID.token); + } + + @Override + public void finishGrammar(GrammarRootAST root, GrammarAST ID) { + checkTreeFilterOptions(root, g.ast.getOptions()); + } + + @Override + public void finishPrequels(GrammarAST firstPrequel) { + List options = firstPrequel.getNodesWithType(OPTIONS); + List imports = firstPrequel.getNodesWithType(IMPORT); + List tokens = firstPrequel.getNodesWithType(TOKENS); + checkNumPrequels(options, imports, tokens); + } + + @Override + public void importGrammar(GrammarAST label, GrammarAST ID) { + checkImport(ID.token); + } + + @Override + public void discoverRules(GrammarAST rules) { + checkNumRules(rules); + } + + @Override + public void modeDef(GrammarAST m, GrammarAST ID) { + checkMode(ID.token); + } + + @Override + public void grammarOption(GrammarAST ID, String value) { + boolean ok = checkOptions(g.ast, ID.token, value); + if ( ok ) g.ast.setOption(ID.getText(), value); + } + + @Override + public void discoverRule(GrammarAST rule, GrammarAST ID) { + checkInvalidRuleDef(ID.token); + } + + @Override + public void ruleRef(GrammarAST ref, GrammarAST arg) { + checkInvalidRuleRef(ref.token); + } + + @Override + public void terminalOption(TerminalAST t, GrammarAST ID, GrammarAST value) { + String v = null; + if ( value!=null ) v = value.getText(); + boolean ok = checkTokenOptions(ID, v); + if ( ok ) { + if ( v!=null ) { + t.setOption(ID.getText(), v); + } + else { + t.setOption(TerminalAST.defaultTokenOption, v); + } + } + } + + @Override + public void discoverAltWithRewrite(GrammarAST alt) { + GrammarAST firstNode = (GrammarAST)alt.getChild(0); + checkRewriteForMultiRootAltInTreeGrammar(g.ast.getOptions(), + firstNode.token, + currentOuterAltNumber); + } + + @Override + public void rootOp(GrammarAST op, GrammarAST opnd) { + checkASTOps(g.ast.getOptions(), op, opnd); + } + + @Override + public void bangOp(GrammarAST op, GrammarAST opnd) { + checkASTOps(g.ast.getOptions(), op, opnd); + } + + @Override + public void discoverTreeRewrite(GrammarAST rew) { + checkRewriteOk(g.ast.getOptions(), rew); + } + + @Override + public void discoverSTRewrite(GrammarAST rew) { + checkRewriteOk(g.ast.getOptions(), rew); + } + + @Override + public void wildcardRef(GrammarAST ref, GrammarAST options) { + checkWildcardRoot(ref.token); + } + + // Routines to do the actual work of checking issues with a grammar. + // They are triggered by the visitor methods above. + void checkGrammarName(Token nameToken) { if ( g.implicitLexer==null ) return; String fullyQualifiedName = nameToken.getInputStream().getSourceName(); @@ -241,18 +348,6 @@ public class BasicSemanticChecks { } } - /** At this point, we can only rule out obvious problems like ID[3] - * in parser. Might be illegal too in later stage when we see ID - * isn't a fragment. - */ - void checkTokenArgs(Token tokenID) { - String fileName = tokenID.getInputStream().getSourceName(); - if ( !g.isLexer() ) { - g.tool.errMgr.grammarError(ErrorType.ARGS_ON_TOKEN_REF, - fileName, tokenID, tokenID.getText()); - } - } - /** Check option is appropriate for grammar, rule, subrule */ boolean checkOptions(GrammarAST parent, Token optionID, String value) @@ -299,9 +394,8 @@ public class BasicSemanticChecks { } /** Check option is appropriate for token; parent is ELEMENT_OPTIONS */ - boolean checkTokenOptions(GrammarAST parent, - Token optionID, String value) - { + boolean checkTokenOptions(GrammarAST ID, String value) { + Token optionID = ID.token; String fileName = optionID.getInputStream().getSourceName(); // don't care about ID options if ( value!=null && !legalTokenOptions.contains(optionID.getText()) ) { @@ -312,18 +406,12 @@ public class BasicSemanticChecks { return false; } // example (ALT_REWRITE (ALT (ID (ELEMENT_OPTIONS Foo))) (-> (ALT ID)) - if ( parent.hasAncestor(ANTLRParser.ALT_REWRITE) ) { + if ( inRewrite ) { g.tool.errMgr.grammarError(ErrorType.HETERO_ILLEGAL_IN_REWRITE_ALT, fileName, optionID); - } // TODO: extra checks depending on terminal kind? - switch ( parent.getType() ) { - case ANTLRParser.TOKEN_REF : - case ANTLRParser.STRING_LITERAL : - case ANTLRParser.WILDCARD : - } return true; } @@ -395,8 +483,7 @@ public class BasicSemanticChecks { } void checkRewriteOk(Map options, GrammarAST elementRoot) { - RuleAST rule = (RuleAST)elementRoot.getAncestor(ANTLRParser.RULE); - String ruleName = rule.getChild(0).getText(); + String ruleName = currentRule.getChild(0).getText(); String fileName = elementRoot.token.getInputStream().getSourceName(); if ( options!=null && options.get("output")==null ) { g.tool.errMgr.grammarWarning(ErrorType.REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION, diff --git a/tool/src/org/antlr/v4/semantics/BasicSemanticTriggers.g b/tool/src/org/antlr/v4/semantics/BasicSemanticTriggers.g deleted file mode 100644 index f36d1eb69..000000000 --- a/tool/src/org/antlr/v4/semantics/BasicSemanticTriggers.g +++ /dev/null @@ -1,240 +0,0 @@ -/* - [The "BSD license"] - Copyright (c) 2010 Terence Parr - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/** Triggers for the basic semantics of the input. Side-effects: - * Set token, block, rule options in the tree. Load field option - * with grammar options. Only legal options are set. - */ -tree grammar BasicSemanticTriggers; -options { - language = Java; - tokenVocab = ANTLRParser; - ASTLabelType = GrammarAST; - filter = true; - //superClass = 'org.antlr.v4.runtime.tree.TreeFilter'; -} - -// Include the copyright in this source and also the generated source -@header { -/* - [The "BSD license"] - Copyright (c) 2010 Terence Parr - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products - derived from this software without specific prior written permission. - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -package org.antlr.v4.semantics; -import org.antlr.v4.tool.*; -} - -@members { -// TODO: SHOULD we fix up grammar AST to remove errors? Like kill refs to bad rules? -// that is, rewrite tree? maybe all passes are filters until code gen, which needs -// tree grammar. 'course we won't try codegen if errors. -public String name; -GrammarASTWithOptions root; -Grammar g; // which grammar are we checking -BasicSemanticChecks checker; -public BasicSemanticTriggers(TreeNodeStream input, Grammar g) { - this(input); - this.g = g; - checker = new BasicSemanticChecks(g); -} -} - -topdown // do these on way down so options and such are set first - : grammarSpec - | rules - | mode - | option - | rule - | tokenAlias - | rewrite - ; - -bottomup // do these "inside to outside" of expressions. - : multiElementAltInTreeGrammar - | astOps - | ruleref - | tokenRefWithArgs - | elementOption - | checkGrammarOptions // do after we see everything - | wildcardRoot - ; - -grammarSpec - : ^( GRAMMAR ID DOC_COMMENT? - { - name = $ID.text; - checker.checkGrammarName($ID.token); - root = (GrammarRootAST)$start; - } - prequelConstructs ^(RULES .*) - ) - ; - -checkGrammarOptions // when we get back to root - : GRAMMAR - {checker.checkTreeFilterOptions((GrammarRootAST)$GRAMMAR, - root.getOptions());} - ; - -/* -grammarType -@init {gtype = $start.getType(); root = (GrammarASTWithOptions)$start;} - : LEXER_GRAMMAR | PARSER_GRAMMAR | TREE_GRAMMAR | COMBINED_GRAMMAR - ; - */ - -prequelConstructs - : ( ^(o+=OPTIONS .+) - | ^(i+=IMPORT delegateGrammar+) - | ^(t+=TOKENS .+) - )* - {checker.checkNumPrequels($o, $i, $t);} - ; - -delegateGrammar - : ( ^(ASSIGN ID id=ID) - | id=ID - ) - {checker.checkImport($id.token);} - ; - -rules : RULES {checker.checkNumRules($RULES);} ; - -mode : ^(MODE ID .*) {checker.checkMode($ID.token);} ; - -option // TODO: put in grammar, or rule, or block - : {inContext("OPTIONS")}? ^(ASSIGN o=ID optionValue) - { - GrammarAST parent = (GrammarAST)$start.getParent(); // OPTION - GrammarAST parentWithOptionKind = (GrammarAST)parent.getParent(); - boolean ok = checker.checkOptions(parentWithOptionKind, - $ID.token, $optionValue.v); - // store options into XXX_GRAMMAR, RULE, BLOCK nodes - if ( ok ) { - ((GrammarASTWithOptions)parentWithOptionKind).setOption($o.text, $optionValue.v); - } - } - ; - -optionValue returns [String v] -@init {$v = $start.token.getText();} - : ID - | STRING_LITERAL - | INT - | STAR - ; - -rule: ^( RULE r=ID .*) {checker.checkInvalidRuleDef($r.token);} - ; - -ruleref - : RULE_REF {checker.checkInvalidRuleRef($RULE_REF.token);} - ; - -tokenAlias - : {inContext("TOKENS")}? ^(ASSIGN ID STRING_LITERAL) - {checker.checkTokenAlias($ID.token);} - ; - -tokenRefWithArgs - : {!inContext("RESULT ...")}? // if not on right side of -> - ^(TOKEN_REF ARG_ACTION) - {checker.checkTokenArgs($TOKEN_REF.token);} - ; - -elementOption - : {!inContext("RESULT ...")}? // not on right side of -> - ^( ELEMENT_OPTIONS - ( ^(ASSIGN o=ID value=ID) - | ^(ASSIGN o=ID value=STRING_LITERAL) - | o=ID - ) - ) - { - boolean ok = checker.checkTokenOptions((GrammarAST)$o.getParent(), - $o.token, $value.text); - if ( ok ) { - if ( value!=null ) { - TerminalAST terminal = (TerminalAST)$start.getParent(); - terminal.setOption($o.text, $value.text); - } - else { - TerminalAST terminal = (TerminalAST)$start.getParent(); - terminal.setOption(TerminalAST.defaultTokenOption, $o.text); - } - } - } - ; - -// (ALT_REWRITE (ALT A B) ^( ALT ^( A B ) ) or ( ALT A ) -multiElementAltInTreeGrammar - : {inContext("ALT_REWRITE")}? - ^( ALT ~(SEMPRED|ACTION) ~(SEMPRED|ACTION)+ ) // > 1 element at outer level - { - int altNum = $start.getParent().getChildIndex() + 1; // alts are 1..n - GrammarAST firstNode = (GrammarAST)$start.getChild(0); - checker.checkRewriteForMultiRootAltInTreeGrammar(root.getOptions(), - firstNode.token, - altNum); - } - ; - -// Check stuff like (^ A) (! r) -astOps - : ^(ROOT el=.) {checker.checkASTOps(root.getOptions(), $start, $el);} - | ^(BANG el=.) {checker.checkASTOps(root.getOptions(), $start, $el);} - ; - -rewrite - : (RESULT|ST_RESULT) - {checker.checkRewriteOk(root.getOptions(),$start);} - ; - -wildcardRoot - : ^(TREE_BEGIN WILDCARD .*) - {checker.checkWildcardRoot($WILDCARD.token);} - ; diff --git a/tool/src/org/antlr/v4/semantics/RewriteRefs.java b/tool/src/org/antlr/v4/semantics/RewriteRefs.java index 60c7762ef..f97efbab0 100644 --- a/tool/src/org/antlr/v4/semantics/RewriteRefs.java +++ b/tool/src/org/antlr/v4/semantics/RewriteRefs.java @@ -29,20 +29,20 @@ package org.antlr.v4.semantics; -import org.antlr.runtime.tree.TreeNodeStream; import org.antlr.v4.parse.GrammarTreeVisitor; -import org.antlr.v4.tool.GrammarAST; +import org.antlr.v4.tool.*; import java.util.*; public class RewriteRefs extends GrammarTreeVisitor { List shallow = new ArrayList(); List deep = new ArrayList(); - public int desiredShallowLevel; + int desiredShallowLevel; + Grammar g; - public RewriteRefs(TreeNodeStream input, int desiredShallowLevel) { - super(input); + public RewriteRefs(Grammar g, int desiredShallowLevel) { this.desiredShallowLevel = desiredShallowLevel; + this.g = g; } public void track(GrammarAST t) { @@ -50,6 +50,9 @@ public class RewriteRefs extends GrammarTreeVisitor { if ( rewriteEBNFLevel==desiredShallowLevel ) shallow.add(t); } + @Override + public ErrorManager getErrorManager() { return g.tool.errMgr; } + @Override public void rewriteTokenRef(GrammarAST ast, GrammarAST options, GrammarAST arg) { track(ast); diff --git a/tool/src/org/antlr/v4/semantics/SemanticPipeline.java b/tool/src/org/antlr/v4/semantics/SemanticPipeline.java index 00ed2078c..042156509 100644 --- a/tool/src/org/antlr/v4/semantics/SemanticPipeline.java +++ b/tool/src/org/antlr/v4/semantics/SemanticPipeline.java @@ -77,9 +77,11 @@ public class SemanticPipeline { } // DO BASIC / EASY SEMANTIC CHECKS - nodes.reset(); - BasicSemanticTriggers basics = new BasicSemanticTriggers(nodes,g); - basics.downup(g.ast); +// nodes.reset(); +// BasicSemanticTriggers basics = new BasicSemanticTriggers(nodes,g); +// basics.downup(g.ast); + BasicSemanticChecks basics = new BasicSemanticChecks(g); + basics.process(); // don't continue if we get errors in this basic check if ( false ) return; diff --git a/tool/src/org/antlr/v4/semantics/UseDefAnalyzer.java b/tool/src/org/antlr/v4/semantics/UseDefAnalyzer.java index f8a14d093..96cefef4f 100644 --- a/tool/src/org/antlr/v4/semantics/UseDefAnalyzer.java +++ b/tool/src/org/antlr/v4/semantics/UseDefAnalyzer.java @@ -29,7 +29,6 @@ package org.antlr.v4.semantics; -import org.antlr.runtime.tree.CommonTreeNodeStream; import org.antlr.v4.parse.ANTLRParser; import org.antlr.v4.runtime.Token; import org.antlr.v4.tool.*; @@ -117,10 +116,9 @@ public class UseDefAnalyzer { int desiredShallowLevel, boolean deep) { - CommonTreeNodeStream nodes = new CommonTreeNodeStream(root); - RewriteRefs collector = new RewriteRefs(nodes, desiredShallowLevel); - if ( root.getType()==ANTLRParser.RESULT ) collector.visitRewrite(g.tool.errMgr); - else collector.visitRewriteEBNF(g.tool.errMgr); + RewriteRefs collector = new RewriteRefs(g, desiredShallowLevel); + if ( root.getType()==ANTLRParser.RESULT ) collector.visitRewrite(root); + else collector.visitRewriteEBNF(root); System.out.println("from "+root.toStringTree()); System.out.println("shallow: "+collector.shallow); System.out.println("deep: "+collector.deep);