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() { public void sanityCheckParentAndChildIndexes() {
sanityCheckParentAndChildIndexes(null, -1); sanityCheckParentAndChildIndexes(null, -1);
} }

View File

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

View File

@ -432,6 +432,8 @@ public class Tool {
lexerRulesRoot.getChildren().add(0, litRule); // add first lexerRulesRoot.getChildren().add(0, litRule); // add first
} }
lexerRulesRoot.freshenParentAndChildIndexesDeeply();
System.out.println("after ="+combinedAST.toStringTree()); System.out.println("after ="+combinedAST.toStringTree());
System.out.println("lexer ="+lexerAST.toStringTree()); System.out.println("lexer ="+lexerAST.toStringTree());
return lexerAST; return lexerAST;

View File

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

View File

@ -334,8 +334,6 @@ range
terminal terminal
: ^(STRING_LITERAL elementOptions) : ^(STRING_LITERAL elementOptions)
| STRING_LITERAL | STRING_LITERAL
| ^(TOKEN_REF ARG_ACTION elementOptions)
| ^(TOKEN_REF ARG_ACTION)
| ^(TOKEN_REF elementOptions) | ^(TOKEN_REF elementOptions)
| TOKEN_REF | TOKEN_REF
; ;

View File

@ -70,6 +70,7 @@ options {
package org.antlr.v4.parse; package org.antlr.v4.parse;
import org.antlr.v4.tool.*; import org.antlr.v4.tool.*;
import org.antlr.v4.runtime.tree.CommonTree; // use updated v4 one not v3 import org.antlr.v4.runtime.tree.CommonTree; // use updated v4 one not v3
import java.lang.reflect.Method;
} }
@members { @members {
@ -83,29 +84,66 @@ public int rewriteEBNFLevel = 0;
public boolean inRewrite; public boolean inRewrite;
public boolean currentOuterAltHasRewrite; public boolean currentOuterAltHasRewrite;
public void visitGrammar(ErrorManager errMgr) { public GrammarTreeVisitor() { this(null); }
try { grammarSpec(); }
catch (org.antlr.runtime.RecognitionException re) { public ErrorManager getErrorManager() { return null; }
errMgr.grammarError(ErrorType.INTERNAL_ERROR,
null, re.token, re); 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);
} }
} catch (Exception e) {
public void visitRewrite(ErrorManager errMgr) { ErrorManager errMgr = getErrorManager();
try { rewrite(); } if ( errMgr==null ) System.err.println("can't find rule "+ruleName+
catch (org.antlr.runtime.RecognitionException re) { " or tree structure error: "+t.toStringTree()
errMgr.grammarError(ErrorType.INTERNAL_ERROR, );
null, re.token, re); else errMgr.toolError(ErrorType.INTERNAL_ERROR, e);
}
}
public void visitRewriteEBNF(ErrorManager errMgr) {
try { rewriteTreeEbnf(); }
catch (org.antlr.runtime.RecognitionException re) {
errMgr.grammarError(ErrorType.INTERNAL_ERROR,
null, re.token, re);
} }
} }
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 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 rewriteStringRef(GrammarAST ast, GrammarAST options) { }
public void rewriteRuleRef(GrammarAST ast) { } public void rewriteRuleRef(GrammarAST ast) { }
public void rewriteLabelRef(GrammarAST ast) { } public void rewriteLabelRef(GrammarAST ast) { }
@ -113,9 +151,19 @@ public void rewriteAction(GrammarAST ast) { }
} }
grammarSpec 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 prequelConstruct
: optionsSpec : optionsSpec
| delegateGrammars | delegateGrammars
@ -130,9 +178,15 @@ optionsSpec
option option
: ^(ASSIGN ID optionValue) : ^(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 : ID
| STRING_LITERAL | STRING_LITERAL
| INT | INT
@ -144,8 +198,8 @@ delegateGrammars
; ;
delegateGrammar delegateGrammar
: ^(ASSIGN ID ID) : ^(ASSIGN label=ID id=ID) {importGrammar($label, $id);}
| ID | id=ID {importGrammar(null, $id);}
; ;
tokensSpec tokensSpec
@ -153,7 +207,7 @@ tokensSpec
; ;
tokenSpec tokenSpec
: ^(ASSIGN ID STRING_LITERAL) : ^(ASSIGN ID STRING_LITERAL) {tokenAlias($ID, $STRING_LITERAL);}
| ID | ID
; ;
@ -166,14 +220,15 @@ action
; ;
rules 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? DOC_COMMENT? ruleModifiers? ARG_ACTION?
ruleReturns? rulePrequel* ruleBlock exceptionGroup ruleReturns? rulePrequel* ruleBlock exceptionGroup
{finishRule($RULE, $ID);}
) )
; ;
@ -246,7 +301,15 @@ ruleBlock
; ;
alternative 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 EPSILON)
| ^(ALT element+) | ^(ALT element+)
; ;
@ -260,8 +323,8 @@ element
| SEMPRED | SEMPRED
| GATED_SEMPRED | GATED_SEMPRED
| treeSpec | treeSpec
| ^(ROOT astOperand) | ^(ROOT astOperand) {rootOp($ROOT, $astOperand.start);}
| ^(BANG astOperand) | ^(BANG astOperand) {bangOp($BANG, $astOperand.start);}
| ^(NOT blockSet) | ^(NOT blockSet)
| ^(NOT block) | ^(NOT block)
; ;
@ -301,8 +364,8 @@ ebnfSuffix
atom: range atom: range
| ^(DOT ID terminal) | ^(DOT ID terminal)
| ^(DOT ID ruleref) | ^(DOT ID ruleref)
| ^(WILDCARD elementOptions) | ^(WILDCARD elementOptions) {wildcardRef($WILDCARD, $elementOptions.start);}
| WILDCARD | WILDCARD {wildcardRef($WILDCARD, null);}
| terminal | terminal
| blockSet | blockSet
| ruleref | ruleref
@ -322,7 +385,7 @@ block
; ;
ruleref ruleref
: ^(RULE_REF ARG_ACTION?) : ^(RULE_REF ARG_ACTION?) {ruleRef($RULE_REF, $ARG_ACTION);}
; ;
range range
@ -331,30 +394,29 @@ range
terminal terminal
: ^(STRING_LITERAL elementOptions) : ^(STRING_LITERAL elementOptions)
| STRING_LITERAL {stringRef($STRING_LITERAL, $elementOptions.start);}
| ^(TOKEN_REF ARG_ACTION elementOptions) | STRING_LITERAL {stringRef($STRING_LITERAL, null);}
| ^(TOKEN_REF ARG_ACTION) | ^(TOKEN_REF elementOptions) {tokenRef($TOKEN_REF, $elementOptions.start);}
| ^(TOKEN_REF elementOptions) | TOKEN_REF {tokenRef($TOKEN_REF, null);}
| TOKEN_REF
; ;
elementOptions elementOptions
: ^(ELEMENT_OPTIONS elementOption+) : ^(ELEMENT_OPTIONS elementOption[(TerminalAST)$start.getParent()]+)
; ;
elementOption elementOption[TerminalAST t]
: ID : ID {terminalOption(t, $ID, null);}
| ^(ASSIGN ID ID) | ^(ASSIGN id=ID v=ID) {terminalOption(t, $id, $v);}
| ^(ASSIGN ID STRING_LITERAL) | ^(ASSIGN ID v=STRING_LITERAL) {terminalOption(t, $ID, $v);}
; ;
rewrite rewrite
: predicatedRewrite* nakedRewrite : {discoverRewrites($start);} predicatedRewrite* nakedRewrite {finishRewrites($start);}
; ;
predicatedRewrite predicatedRewrite
: ^(ST_RESULT SEMPRED rewriteAlt) : ^(ST_RESULT SEMPRED {discoverSTRewrite($start);} rewriteAlt)
| ^(RESULT SEMPRED rewriteAlt) | ^(RESULT SEMPRED {discoverTreeRewrite($start);} rewriteAlt)
; ;
nakedRewrite nakedRewrite
@ -381,17 +443,29 @@ rewriteTreeElement
; ;
rewriteTreeAtom rewriteTreeAtom
: ^(TOKEN_REF elementOptions ARG_ACTION) {rewriteTokenRef($start,$elementOptions.start,$ARG_ACTION);} : ^(TOKEN_REF rewriteElementOptions ARG_ACTION)
| ^(TOKEN_REF elementOptions) {rewriteTokenRef($start,$elementOptions.start,null);} {rewriteTokenRef($start,$rewriteElementOptions.start,$ARG_ACTION);}
| ^(TOKEN_REF ARG_ACTION) {rewriteTokenRef($start,null,$ARG_ACTION);} | ^(TOKEN_REF rewriteElementOptions) {rewriteTokenRef($start,$rewriteElementOptions.start,null);}
| TOKEN_REF {rewriteTokenRef($start,null,null);} | ^(TOKEN_REF ARG_ACTION) {rewriteTokenRef($start,null,$ARG_ACTION);}
| RULE_REF {rewriteRuleRef($start);} | TOKEN_REF {rewriteTokenRef($start,null,null);}
| ^(STRING_LITERAL elementOptions) {rewriteStringRef($start,$elementOptions.start);} | RULE_REF {rewriteRuleRef($start);}
| STRING_LITERAL {rewriteStringRef($start,null);} | ^(STRING_LITERAL rewriteElementOptions)
| LABEL {rewriteLabelRef($start);} {rewriteStringRef($start,$rewriteElementOptions.start);}
| ACTION {rewriteAction($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 rewriteTreeEbnf
: ^(ebnfSuffix ^(REWRITE_BLOCK {rewriteEBNFLevel++;} rewriteTreeAlt {rewriteEBNFLevel--;})) : ^(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.runtime.Token;
import org.antlr.v4.misc.Utils; import org.antlr.v4.misc.Utils;
import org.antlr.v4.parse.ANTLRParser; import org.antlr.v4.parse.*;
import org.antlr.v4.tool.*; import org.antlr.v4.tool.*;
import org.stringtemplate.v4.misc.MultiMap; import org.stringtemplate.v4.misc.MultiMap;
import java.io.File; import java.io.File;
import java.util.*; 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 * FILE_AND_GRAMMAR_NAME_DIFFER
* LEXER_RULES_NOT_ALLOWED * LEXER_RULES_NOT_ALLOWED
@ -62,7 +65,7 @@ import java.util.*;
* *
* TODO: 1 action per lex rule * TODO: 1 action per lex rule
*/ */
public class BasicSemanticChecks { public class BasicSemanticChecks extends GrammarTreeVisitor {
public static final Set legalLexerOptions = public static final Set legalLexerOptions =
new HashSet() { new HashSet() {
{ {
@ -151,6 +154,110 @@ public class BasicSemanticChecks {
this.errMgr = g.tool.errMgr; 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) { void checkGrammarName(Token nameToken) {
if ( g.implicitLexer==null ) return; if ( g.implicitLexer==null ) return;
String fullyQualifiedName = nameToken.getInputStream().getSourceName(); 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 */ /** Check option is appropriate for grammar, rule, subrule */
boolean checkOptions(GrammarAST parent, boolean checkOptions(GrammarAST parent,
Token optionID, String value) Token optionID, String value)
@ -299,9 +394,8 @@ public class BasicSemanticChecks {
} }
/** Check option is appropriate for token; parent is ELEMENT_OPTIONS */ /** Check option is appropriate for token; parent is ELEMENT_OPTIONS */
boolean checkTokenOptions(GrammarAST parent, boolean checkTokenOptions(GrammarAST ID, String value) {
Token optionID, String value) Token optionID = ID.token;
{
String fileName = optionID.getInputStream().getSourceName(); String fileName = optionID.getInputStream().getSourceName();
// don't care about ID<ASTNodeName> options // don't care about ID<ASTNodeName> options
if ( value!=null && !legalTokenOptions.contains(optionID.getText()) ) { if ( value!=null && !legalTokenOptions.contains(optionID.getText()) ) {
@ -312,18 +406,12 @@ public class BasicSemanticChecks {
return false; return false;
} }
// example (ALT_REWRITE (ALT (ID (ELEMENT_OPTIONS Foo))) (-> (ALT ID)) // 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, g.tool.errMgr.grammarError(ErrorType.HETERO_ILLEGAL_IN_REWRITE_ALT,
fileName, fileName,
optionID); optionID);
} }
// TODO: extra checks depending on terminal kind? // TODO: extra checks depending on terminal kind?
switch ( parent.getType() ) {
case ANTLRParser.TOKEN_REF :
case ANTLRParser.STRING_LITERAL :
case ANTLRParser.WILDCARD :
}
return true; return true;
} }
@ -395,8 +483,7 @@ public class BasicSemanticChecks {
} }
void checkRewriteOk(Map<String, String> options, GrammarAST elementRoot) { void checkRewriteOk(Map<String, String> options, GrammarAST elementRoot) {
RuleAST rule = (RuleAST)elementRoot.getAncestor(ANTLRParser.RULE); String ruleName = currentRule.getChild(0).getText();
String ruleName = rule.getChild(0).getText();
String fileName = elementRoot.token.getInputStream().getSourceName(); String fileName = elementRoot.token.getInputStream().getSourceName();
if ( options!=null && options.get("output")==null ) { if ( options!=null && options.get("output")==null ) {
g.tool.errMgr.grammarWarning(ErrorType.REWRITE_OR_OP_WITH_NO_OUTPUT_OPTION, 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; package org.antlr.v4.semantics;
import org.antlr.runtime.tree.TreeNodeStream;
import org.antlr.v4.parse.GrammarTreeVisitor; import org.antlr.v4.parse.GrammarTreeVisitor;
import org.antlr.v4.tool.GrammarAST; import org.antlr.v4.tool.*;
import java.util.*; import java.util.*;
public class RewriteRefs extends GrammarTreeVisitor { public class RewriteRefs extends GrammarTreeVisitor {
List<GrammarAST> shallow = new ArrayList<GrammarAST>(); List<GrammarAST> shallow = new ArrayList<GrammarAST>();
List<GrammarAST> deep = new ArrayList<GrammarAST>(); List<GrammarAST> deep = new ArrayList<GrammarAST>();
public int desiredShallowLevel; int desiredShallowLevel;
Grammar g;
public RewriteRefs(TreeNodeStream input, int desiredShallowLevel) { public RewriteRefs(Grammar g, int desiredShallowLevel) {
super(input);
this.desiredShallowLevel = desiredShallowLevel; this.desiredShallowLevel = desiredShallowLevel;
this.g = g;
} }
public void track(GrammarAST t) { public void track(GrammarAST t) {
@ -50,6 +50,9 @@ public class RewriteRefs extends GrammarTreeVisitor {
if ( rewriteEBNFLevel==desiredShallowLevel ) shallow.add(t); if ( rewriteEBNFLevel==desiredShallowLevel ) shallow.add(t);
} }
@Override
public ErrorManager getErrorManager() { return g.tool.errMgr; }
@Override @Override
public void rewriteTokenRef(GrammarAST ast, GrammarAST options, GrammarAST arg) { public void rewriteTokenRef(GrammarAST ast, GrammarAST options, GrammarAST arg) {
track(ast); track(ast);

View File

@ -77,9 +77,11 @@ public class SemanticPipeline {
} }
// DO BASIC / EASY SEMANTIC CHECKS // DO BASIC / EASY SEMANTIC CHECKS
nodes.reset(); // nodes.reset();
BasicSemanticTriggers basics = new BasicSemanticTriggers(nodes,g); // BasicSemanticTriggers basics = new BasicSemanticTriggers(nodes,g);
basics.downup(g.ast); // basics.downup(g.ast);
BasicSemanticChecks basics = new BasicSemanticChecks(g);
basics.process();
// don't continue if we get errors in this basic check // don't continue if we get errors in this basic check
if ( false ) return; if ( false ) return;

View File

@ -29,7 +29,6 @@
package org.antlr.v4.semantics; package org.antlr.v4.semantics;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.antlr.v4.parse.ANTLRParser; import org.antlr.v4.parse.ANTLRParser;
import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.Token;
import org.antlr.v4.tool.*; import org.antlr.v4.tool.*;
@ -117,10 +116,9 @@ public class UseDefAnalyzer {
int desiredShallowLevel, int desiredShallowLevel,
boolean deep) boolean deep)
{ {
CommonTreeNodeStream nodes = new CommonTreeNodeStream(root); RewriteRefs collector = new RewriteRefs(g, desiredShallowLevel);
RewriteRefs collector = new RewriteRefs(nodes, desiredShallowLevel); if ( root.getType()==ANTLRParser.RESULT ) collector.visitRewrite(root);
if ( root.getType()==ANTLRParser.RESULT ) collector.visitRewrite(g.tool.errMgr); else collector.visitRewriteEBNF(root);
else collector.visitRewriteEBNF(g.tool.errMgr);
System.out.println("from "+root.toStringTree()); System.out.println("from "+root.toStringTree());
System.out.println("shallow: "+collector.shallow); System.out.println("shallow: "+collector.shallow);
System.out.println("deep: "+collector.deep); System.out.println("deep: "+collector.deep);