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:
parent
f36b1ad875
commit
b9ef4f9bee
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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'+;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
;
|
||||
|
|
|
@ -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--;}))
|
||||
;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);}
|
||||
;
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue