woot! new visitor does away with BasicSemanticTriggers.g and Refs.g

[git-p4: depot-paths = "//depot/code/antlr4/main/": change = 8861]
This commit is contained in:
parrt 2011-07-14 19:25:08 -08:00
parent f36b1ad875
commit b9ef4f9bee
11 changed files with 277 additions and 340 deletions

View File

@ -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);
}

View File

@ -1,7 +1,7 @@
grammar T;
options {output=AST;}
a : 'var' (ID ':' type ';')+ -> ^('var' ^(':' ID type)*) ;
a : 'var' (ID ':' type ';')+ -> ^('var' ^(':' ID<Foo> type)*) ;
type returns [int i] : ID;
ID : 'a'..'z'+ ;
INT : '0'..'9'+;

View File

@ -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;

View File

@ -786,9 +786,8 @@ range
;
terminal
: // Args are only valid for lexer rules
TOKEN_REF ARG_ACTION? elementOptions? -> ^(TOKEN_REF<TerminalAST> ARG_ACTION<ActionAST>? elementOptions?)
| STRING_LITERAL elementOptions? -> ^(STRING_LITERAL<TerminalAST> elementOptions?)
: TOKEN_REF elementOptions? -> ^(TOKEN_REF<TerminalAST> elementOptions?)
| STRING_LITERAL elementOptions? -> ^(STRING_LITERAL<TerminalAST> elementOptions?)
;
// Terminals may be adorned with certain options when

View File

@ -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
;

View File

@ -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--;}))
;

View File

@ -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<GrammarAST> options = firstPrequel.getNodesWithType(OPTIONS);
List<GrammarAST> imports = firstPrequel.getNodesWithType(IMPORT);
List<GrammarAST> 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<ASTNodeName> 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<String, String> 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,

View File

@ -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);}
;

View File

@ -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<GrammarAST> shallow = new ArrayList<GrammarAST>();
List<GrammarAST> deep = new ArrayList<GrammarAST>();
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);

View File

@ -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;

View File

@ -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);