blob: 7615833a1562ca38d01f3c29228d0a781248d28d [file] [log] [blame]
/*
[The "BSD license"]
Copyright (c) 2011 Terence Parr
All rights reserved.
Grammar conversion to ANTLR v3:
Copyright (c) 2011 Sam Harwell
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.
*/
/** Walk a grammar and generate code by gradually building up
* a bigger and bigger ST.
*
* Terence Parr
* University of San Francisco
* June 15, 2004
*/
tree grammar CodeGenTreeWalker;
options {
tokenVocab = ANTLR;
ASTLabelType=GrammarAST;
}
@header {
package org.antlr.grammar.v3;
import org.antlr.analysis.*;
import org.antlr.misc.*;
import org.antlr.tool.*;
import org.antlr.codegen.*;
import java.util.HashSet;
import java.util.Set;
import java.util.Collection;
import org.antlr.runtime.BitSet;
import org.antlr.runtime.DFA;
import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroup;
}
@members {
protected static final int RULE_BLOCK_NESTING_LEVEL = 0;
protected static final int OUTER_REWRITE_NESTING_LEVEL = 0;
private String currentRuleName = null;
protected int blockNestingLevel = 0;
protected int rewriteBlockNestingLevel = 0;
private int outerAltNum = 0;
protected ST currentBlockST = null;
protected boolean currentAltHasASTRewrite = false;
protected int rewriteTreeNestingLevel = 0;
protected HashSet<Object> rewriteRuleRefs = null;
public String getCurrentRuleName() {
return currentRuleName;
}
public void setCurrentRuleName(String value) {
currentRuleName = value;
}
public int getOuterAltNum() {
return outerAltNum;
}
public void setOuterAltNum(int value) {
outerAltNum = value;
}
@Override
public void reportError(RecognitionException ex) {
Token token = null;
if (ex instanceof MismatchedTokenException) {
token = ((MismatchedTokenException)ex).token;
} else if (ex instanceof NoViableAltException) {
token = ((NoViableAltException)ex).token;
}
ErrorManager.syntaxError(
ErrorManager.MSG_SYNTAX_ERROR,
grammar,
token,
"codegen: " + ex.toString(),
ex );
}
public final void reportError(String s) {
System.out.println("codegen: error: " + s);
}
protected CodeGenerator generator;
protected Grammar grammar;
protected STGroup templates;
/** The overall lexer/parser template; simulate dynamically scoped
* attributes by making this an instance var of the walker.
*/
protected ST recognizerST;
protected ST outputFileST;
protected ST headerFileST;
protected String outputOption = "";
protected final ST getWildcardST(GrammarAST elementAST, GrammarAST ast_suffix, String label) {
String name = "wildcard";
if (grammar.type == Grammar.LEXER) {
name = "wildcardChar";
}
return getTokenElementST(name, name, elementAST, ast_suffix, label);
}
protected final ST getRuleElementST( String name,
String ruleTargetName,
GrammarAST elementAST,
GrammarAST ast_suffix,
String label ) {
Rule r = grammar.getRule( currentRuleName );
String suffix = getSTSuffix(elementAST, ast_suffix, label);
if ( !r.isSynPred ) {
name += suffix;
}
// if we're building trees and there is no label, gen a label
// unless we're in a synpred rule.
if ( ( grammar.buildAST() || suffix.length() > 0 ) && label == null &&
( r == null || !r.isSynPred ) ) {
// we will need a label to do the AST or tracking, make one
label = generator.createUniqueLabel( ruleTargetName );
CommonToken labelTok = new CommonToken( ANTLRParser.ID, label );
grammar.defineRuleRefLabel( currentRuleName, labelTok, elementAST );
}
ST elementST = templates.getInstanceOf( name );
if ( label != null ) {
elementST.add( "label", label );
}
return elementST;
}
protected final ST getTokenElementST( String name,
String elementName,
GrammarAST elementAST,
GrammarAST ast_suffix,
String label ) {
boolean tryUnchecked = false;
if (name == "matchSet" && elementAST.enclosingRuleName != null && elementAST.enclosingRuleName.length() > 0 && Rule.getRuleType(elementAST.enclosingRuleName) == Grammar.LEXER)
{
if ( ( elementAST.getParent().getType() == ANTLRLexer.ALT && elementAST.getParent().getParent().getParent().getType() == RULE && elementAST.getParent().getParent().getChildCount() == 2 )
|| ( elementAST.getParent().getType() == ANTLRLexer.NOT && elementAST.getParent().getParent().getParent().getParent().getType() == RULE && elementAST.getParent().getParent().getParent().getChildCount() == 2 ) ) {
// single alt at the start of the rule needs to be checked
} else {
tryUnchecked = true;
}
}
String suffix = getSTSuffix( elementAST, ast_suffix, label );
// if we're building trees and there is no label, gen a label
// unless we're in a synpred rule.
Rule r = grammar.getRule( currentRuleName );
if ( ( grammar.buildAST() || suffix.length() > 0 ) && label == null &&
( r == null || !r.isSynPred ) )
{
label = generator.createUniqueLabel( elementName );
CommonToken labelTok = new CommonToken( ANTLRParser.ID, label );
grammar.defineTokenRefLabel( currentRuleName, labelTok, elementAST );
}
ST elementST = null;
if ( tryUnchecked && templates.isDefined( name + "Unchecked" + suffix ) )
elementST = templates.getInstanceOf( name + "Unchecked" + suffix );
if ( elementST == null )
elementST = templates.getInstanceOf( name + suffix );
if ( label != null )
{
elementST.add( "label", label );
}
return elementST;
}
public final boolean isListLabel(String label) {
boolean hasListLabel = false;
if ( label != null ) {
Rule r = grammar.getRule( currentRuleName );
//String stName = null;
if ( r != null )
{
Grammar.LabelElementPair pair = r.getLabel( label );
if ( pair != null &&
( pair.type == Grammar.TOKEN_LIST_LABEL ||
pair.type == Grammar.RULE_LIST_LABEL ||
pair.type == Grammar.WILDCARD_TREE_LIST_LABEL ) )
{
hasListLabel = true;
}
}
}
return hasListLabel;
}
/** Return a non-empty template name suffix if the token is to be
* tracked, added to a tree, or both.
*/
protected final String getSTSuffix(GrammarAST elementAST, GrammarAST ast_suffix, String label) {
if ( grammar.type == Grammar.LEXER )
{
return "";
}
// handle list label stuff; make element use "Track"
String operatorPart = "";
String rewritePart = "";
String listLabelPart = "";
Rule ruleDescr = grammar.getRule( currentRuleName );
if ( ast_suffix != null && !ruleDescr.isSynPred )
{
if ( ast_suffix.getType() == ANTLRParser.ROOT )
{
operatorPart = "RuleRoot";
}
else if ( ast_suffix.getType() == ANTLRParser.BANG )
{
operatorPart = "Bang";
}
}
if ( currentAltHasASTRewrite && elementAST.getType() != WILDCARD )
{
rewritePart = "Track";
}
if ( isListLabel( label ) )
{
listLabelPart = "AndListLabel";
}
String STsuffix = operatorPart + rewritePart + listLabelPart;
//JSystem.@out.println("suffix = "+STsuffix);
return STsuffix;
}
/** Convert rewrite AST lists to target labels list */
protected final List<String> getTokenTypesAsTargetLabels(Collection<GrammarAST> refs)
{
if ( refs == null || refs.size() == 0 )
return null;
List<String> labels = new ArrayList<String>( refs.size() );
for ( GrammarAST t : refs )
{
String label;
if ( t.getType() == ANTLRParser.RULE_REF || t.getType() == ANTLRParser.TOKEN_REF || t.getType() == ANTLRParser.LABEL)
{
label = t.getText();
}
else
{
// must be char or String literal
label = generator.getTokenTypeAsTargetLabel(grammar.getTokenType(t.getText()));
}
labels.add( label );
}
return labels;
}
public final void init( Grammar g ) {
this.grammar = g;
this.generator = grammar.getCodeGenerator();
this.templates = generator.getTemplates();
}
}
public
grammar_[Grammar g,
ST recognizerST,
ST outputFileST,
ST headerFileST]
@init
{
if ( state.backtracking == 0 )
{
init(g);
this.recognizerST = recognizerST;
this.outputFileST = outputFileST;
this.headerFileST = headerFileST;
String superClass = (String)g.getOption("superClass");
outputOption = (String)g.getOption("output");
if ( superClass!=null ) recognizerST.add("superClass", superClass);
if ( g.type!=Grammar.LEXER ) {
Object lt = g.getOption("ASTLabelType");
if ( lt!=null ) recognizerST.add("ASTLabelType", lt);
}
if ( g.type==Grammar.TREE_PARSER && g.getOption("ASTLabelType")==null ) {
ErrorManager.grammarWarning(ErrorManager.MSG_MISSING_AST_TYPE_IN_TREE_GRAMMAR,
g,
null,
g.name);
}
if ( g.type!=Grammar.TREE_PARSER ) {
Object lt = g.getOption("TokenLabelType");
if ( lt!=null ) recognizerST.add("labelType", lt);
}
$recognizerST.add("numRules", grammar.getRules().size());
$outputFileST.add("numRules", grammar.getRules().size());
$headerFileST.add("numRules", grammar.getRules().size());
}
}
: ( ^( LEXER_GRAMMAR grammarSpec )
| ^( PARSER_GRAMMAR grammarSpec )
| ^( TREE_GRAMMAR grammarSpec )
| ^( COMBINED_GRAMMAR grammarSpec )
)
;
attrScope
: ^( 'scope' ID ( ^(AMPERSAND .*) )* ACTION )
;
grammarSpec
: name=ID
( cmt=DOC_COMMENT
{
outputFileST.add("docComment", $cmt.text);
headerFileST.add("docComment", $cmt.text);
}
)?
{
recognizerST.add("name", grammar.getRecognizerName());
outputFileST.add("name", grammar.getRecognizerName());
headerFileST.add("name", grammar.getRecognizerName());
recognizerST.add("scopes", grammar.getGlobalScopes());
headerFileST.add("scopes", grammar.getGlobalScopes());
}
( ^(OPTIONS .*) )?
( ^(IMPORT .*) )?
( ^(TOKENS .*) )?
(attrScope)*
( ^(AMPERSAND .*) )*
rules[recognizerST]
;
rules[ST recognizerST]
@init
{
String ruleName = ((GrammarAST)input.LT(1)).getChild(0).getText();
boolean generated = grammar.generateMethodForRule(ruleName);
}
: ( ( options {k=1;} :
{generated}? =>
rST=rule
{
if ( $rST.code != null )
{
recognizerST.add("rules", $rST.code);
outputFileST.add("rules", $rST.code);
headerFileST.add("rules", $rST.code);
}
}
| ^(RULE .*)
| ^(PREC_RULE .*) // ignore
)
{{
if ( input.LA(1) == RULE )
{
ruleName = ((GrammarAST)input.LT(1)).getChild(0).getText();
//System.Diagnostics.Debug.Assert( ruleName == ((GrammarAST)input.LT(1)).enclosingRuleName );
generated = grammar.generateMethodForRule(ruleName);
}
}}
)+
;
rule returns [ST code=null]
@init
{
String initAction = null;
// get the dfa for the BLOCK
GrammarAST block2=(GrammarAST)$start.getFirstChildWithType(BLOCK);
org.antlr.analysis.DFA dfa = block2.getLookaheadDFA();
// init blockNestingLevel so it's block level RULE_BLOCK_NESTING_LEVEL
// for alts of rule
blockNestingLevel = RULE_BLOCK_NESTING_LEVEL-1;
Rule ruleDescr = grammar.getRule($start.getChild(0).getText());
currentRuleName = $start.getChild(0).getText();
// For syn preds, we don't want any AST code etc... in there.
// Save old templates ptr and restore later. Base templates include Dbg.
STGroup saveGroup = templates;
if ( ruleDescr.isSynPred )
{
templates = generator.getBaseTemplates();
}
String description = "";
}
: ^( RULE id=ID
{assert currentRuleName == $id.text;}
(mod=modifier)?
^(ARG (ARG_ACTION)?)
^(RET (ARG_ACTION)?)
(throwsSpec)?
( ^(OPTIONS .*) )?
(ruleScopeSpec)?
( ^(AMPERSAND .*) )*
b=block["ruleBlock", dfa]
{
description =
grammar.grammarTreeToString((GrammarAST)$start.getFirstChildWithType(BLOCK),
false);
description =
generator.target.getTargetStringLiteralFromString(description);
$b.code.add("description", description);
// do not generate lexer rules in combined grammar
String stName = null;
if ( ruleDescr.isSynPred )
{
stName = "synpredRule";
}
else if ( grammar.type==Grammar.LEXER )
{
if ( currentRuleName.equals(Grammar.ARTIFICIAL_TOKENS_RULENAME) )
{
stName = "tokensRule";
}
else
{
stName = "lexerRule";
}
}
else
{
if ( !(grammar.type==Grammar.COMBINED &&
Rule.getRuleType(currentRuleName) == Grammar.LEXER) )
{
stName = "rule";
}
}
$code = templates.getInstanceOf(stName);
if ( $code.getName().equals("/rule") )
{
$code.add("emptyRule", grammar.isEmptyRule(block2));
}
$code.add("ruleDescriptor", ruleDescr);
String memo = (String)grammar.getBlockOption($start,"memoize");
if ( memo==null )
{
memo = (String)grammar.getOption("memoize");
}
if ( memo!=null && memo.equals("true") &&
(stName.equals("rule")||stName.equals("lexerRule")) )
{
$code.add("memoize", memo!=null && memo.equals("true"));
}
}
(exceptionGroup[$code])?
EOR
)
{
if ( $code!=null )
{
if ( grammar.type==Grammar.LEXER )
{
boolean naked =
currentRuleName.equals(Grammar.ARTIFICIAL_TOKENS_RULENAME) ||
($mod.start!=null&&$mod.start.getText().equals(Grammar.FRAGMENT_RULE_MODIFIER));
$code.add("nakedBlock", naked);
}
else
{
description = grammar.grammarTreeToString($start,false);
description = generator.target.getTargetStringLiteralFromString(description);
$code.add("description", description);
}
Rule theRule = grammar.getRule(currentRuleName);
generator.translateActionAttributeReferencesForSingleScope(
theRule,
theRule.getActions()
);
$code.add("ruleName", currentRuleName);
$code.add("block", $b.code);
if ( initAction!=null )
{
$code.add("initAction", initAction);
}
}
}
;
finally { templates = saveGroup; }
modifier
: 'protected'
| 'public'
| 'private'
| 'fragment'
;
throwsSpec
: ^('throws' ID+)
;
ruleScopeSpec
: ^( 'scope' ( ^(AMPERSAND .*) )* (ACTION)? ( ID )* )
;
block[String blockTemplateName, org.antlr.analysis.DFA dfa]
returns [ST code=null]
options { k=1; }
@init
{
int altNum = 0;
blockNestingLevel++;
if ( state.backtracking == 0 )
{
ST decision = null;
if ( $dfa != null )
{
$code = templates.getInstanceOf($blockTemplateName);
decision = generator.genLookaheadDecision(recognizerST,$dfa);
$code.add("decision", decision);
$code.add("decisionNumber", $dfa.getDecisionNumber());
$code.add("maxK",$dfa.getMaxLookaheadDepth());
$code.add("maxAlt",$dfa.getNumberOfAlts());
}
else
{
$code = templates.getInstanceOf($blockTemplateName+"SingleAlt");
}
$code.add("blockLevel", blockNestingLevel);
$code.add("enclosingBlockLevel", blockNestingLevel-1);
altNum = 1;
if ( this.blockNestingLevel==RULE_BLOCK_NESTING_LEVEL ) {
this.outerAltNum=1;
}
}
}
: {$start.getSetValue()!=null}? => setBlock
{
$code.add("alts",$setBlock.code);
}
| ^( BLOCK
( ^(OPTIONS .*) )? // ignore
( alt=alternative rew=rewrite
{
if ( this.blockNestingLevel==RULE_BLOCK_NESTING_LEVEL )
{
this.outerAltNum++;
}
// add the rewrite code as just another element in the alt :)
// (unless it's a " -> ..." rewrite
// ( -> ... )
GrammarAST firstRewriteAST = (GrammarAST)$rew.start.findFirstType(REWRITE);
boolean etc =
$rew.start.getType()==REWRITES &&
firstRewriteAST.getChild(0)!=null &&
firstRewriteAST.getChild(0).getType()==ETC;
if ( $rew.code!=null && !etc )
{
$alt.code.add("rew", $rew.code);
}
// add this alt to the list of alts for this block
$code.add("alts",$alt.code);
$alt.code.add("altNum", altNum);
$alt.code.add("outerAlt", blockNestingLevel==RULE_BLOCK_NESTING_LEVEL);
altNum++;
}
)+
EOB
)
;
finally { blockNestingLevel--; }
setBlock returns [ST code=null]
@init
{
ST setcode = null;
if ( state.backtracking == 0 )
{
if ( blockNestingLevel==RULE_BLOCK_NESTING_LEVEL && grammar.buildAST() )
{
Rule r = grammar.getRule(currentRuleName);
currentAltHasASTRewrite = r.hasRewrite(outerAltNum);
if ( currentAltHasASTRewrite )
{
r.trackTokenReferenceInAlt($start, outerAltNum);
}
}
}
}
: ^(s=BLOCK .*)
{
int i = ((CommonToken)$s.getToken()).getTokenIndex();
if ( blockNestingLevel==RULE_BLOCK_NESTING_LEVEL )
{
setcode = getTokenElementST("matchRuleBlockSet", "set", $s, null, null);
}
else
{
setcode = getTokenElementST("matchSet", "set", $s, null, null);
}
setcode.add("elementIndex", i);
//if ( grammar.type!=Grammar.LEXER )
//{
// generator.generateLocalFOLLOW($s,"set",currentRuleName,i);
//}
setcode.add("s",
generator.genSetExpr(templates,$s.getSetValue(),1,false));
ST altcode=templates.getInstanceOf("alt");
altcode.addAggr("elements.{el,line,pos}",
setcode,
$s.getLine(),
$s.getCharPositionInLine() + 1
);
altcode.add("altNum", 1);
altcode.add("outerAlt", blockNestingLevel==RULE_BLOCK_NESTING_LEVEL);
if ( !currentAltHasASTRewrite && grammar.buildAST() )
{
altcode.add("autoAST", true);
}
altcode.add("treeLevel", rewriteTreeNestingLevel);
$code = altcode;
}
;
setAlternative
: ^(ALT setElement+ EOA)
;
exceptionGroup[ST ruleST]
: ( exceptionHandler[$ruleST] )+ (finallyClause[$ruleST])?
| finallyClause[$ruleST]
;
exceptionHandler[ST ruleST]
: ^('catch' ARG_ACTION ACTION)
{
List chunks = generator.translateAction(currentRuleName,$ACTION);
$ruleST.addAggr("exceptions.{decl,action}",$ARG_ACTION.text,chunks);
}
;
finallyClause[ST ruleST]
: ^('finally' ACTION)
{
List chunks = generator.translateAction(currentRuleName,$ACTION);
$ruleST.add("finally",chunks);
}
;
alternative returns [ST code]
@init
{
if ( state.backtracking == 0 )
{
$code = templates.getInstanceOf("alt");
if ( blockNestingLevel==RULE_BLOCK_NESTING_LEVEL && grammar.buildAST() )
{
Rule r = grammar.getRule(currentRuleName);
currentAltHasASTRewrite = r.hasRewrite(outerAltNum);
}
String description = grammar.grammarTreeToString($start, false);
description = generator.target.getTargetStringLiteralFromString(description);
$code.add("description", description);
$code.add("treeLevel", rewriteTreeNestingLevel);
if ( !currentAltHasASTRewrite && grammar.buildAST() )
{
$code.add("autoAST", true);
}
}
}
: ^( a=ALT
(
e=element[null,null]
{
if (e != null && e.code != null)
{
$code.addAggr("elements.{el,line,pos}",
$e.code,
$e.start.getLine(),
$e.start.getCharPositionInLine() + 1
);
}
}
)+
EOA
)
;
element[GrammarAST label, GrammarAST astSuffix] returns [ST code=null]
options { k=1; }
@init
{
IntSet elements=null;
GrammarAST ast = null;
}
: ^(ROOT e=element[label,$ROOT])
{ $code = $e.code; }
| ^(BANG e=element[label,$BANG])
{ $code = $e.code; }
| ^( n=NOT notElement[$n, $label, $astSuffix] )
{ $code = $notElement.code; }
| ^( ASSIGN alabel=ID e=element[$alabel,$astSuffix] )
{ $code = $e.code; }
| ^( PLUS_ASSIGN label2=ID e=element[$label2,$astSuffix] )
{ $code = $e.code; }
| ^(CHAR_RANGE a=CHAR_LITERAL b=CHAR_LITERAL)
{
$code = templates.getInstanceOf("charRangeRef");
String low = generator.target.getTargetCharLiteralFromANTLRCharLiteral(generator,$a.text);
String high = generator.target.getTargetCharLiteralFromANTLRCharLiteral(generator,$b.text);
$code.add("a", low);
$code.add("b", high);
if ( label!=null )
{
$code.add("label", $label.getText());
}
}
| ({((GrammarAST)input.LT(1)).getSetValue()==null}? (BLOCK|OPTIONAL|CLOSURE|POSITIVE_CLOSURE)) => /*{$start.getSetValue()==null}?*/ ebnf
{ $code = $ebnf.code; }
| atom[null, $label, $astSuffix]
{ $code = $atom.code; }
| tree_
{ $code = $tree_.code; }
| element_action
{ $code = $element_action.code; }
| (sp=SEMPRED|sp=GATED_SEMPRED)
{
$code = templates.getInstanceOf("validateSemanticPredicate");
$code.add("pred", generator.translateAction(currentRuleName,$sp));
String description = generator.target.getTargetStringLiteralFromString($sp.text);
$code.add("description", description);
}
| SYN_SEMPRED // used only in lookahead; don't generate validating pred
| ^(SYNPRED .*)
| ^(BACKTRACK_SEMPRED .*)
| EPSILON
;
element_action returns [ST code=null]
: act=ACTION
{
$code = templates.getInstanceOf("execAction");
$code.add("action", generator.translateAction(currentRuleName,$act));
}
| act2=FORCED_ACTION
{
$code = templates.getInstanceOf("execForcedAction");
$code.add("action", generator.translateAction(currentRuleName,$act2));
}
;
notElement[GrammarAST n, GrammarAST label, GrammarAST astSuffix] returns [ST code=null]
@init
{
IntSet elements=null;
String labelText = null;
if ( label!=null )
{
labelText = label.getText();
}
}
: ( assign_c=CHAR_LITERAL
{
int ttype=0;
if ( grammar.type==Grammar.LEXER )
{
ttype = Grammar.getCharValueFromGrammarCharLiteral($assign_c.text);
}
else
{
ttype = grammar.getTokenType($assign_c.text);
}
elements = grammar.complement(ttype);
}
| assign_s=STRING_LITERAL
{
int ttype=0;
if ( grammar.type==Grammar.LEXER )
{
// TODO: error!
}
else
{
ttype = grammar.getTokenType($assign_s.text);
}
elements = grammar.complement(ttype);
}
| assign_t=TOKEN_REF
{
int ttype = grammar.getTokenType($assign_t.text);
elements = grammar.complement(ttype);
}
| ^(assign_st=BLOCK .*)
{
elements = $assign_st.getSetValue();
elements = grammar.complement(elements);
}
)
{
$code = getTokenElementST("matchSet",
"set",
(GrammarAST)$n.getChild(0),
astSuffix,
labelText);
$code.add("s",generator.genSetExpr(templates,elements,1,false));
int i = ((CommonToken)n.getToken()).getTokenIndex();
$code.add("elementIndex", i);
if ( grammar.type!=Grammar.LEXER )
{
generator.generateLocalFOLLOW(n,"set",currentRuleName,i);
}
}
;
ebnf returns [ST code=null]
@init
{
org.antlr.analysis.DFA dfa=null;
GrammarAST b = (GrammarAST)$start.getChild(0);
GrammarAST eob = (GrammarAST)b.getLastChild(); // loops will use EOB DFA
}
: ( { dfa = $start.getLookaheadDFA(); }
blk=block["block", dfa]
{ $code = $blk.code; }
| { dfa = $start.getLookaheadDFA(); }
^( OPTIONAL blk=block["optionalBlock", dfa] )
{ $code = $blk.code; }
| { dfa = eob.getLookaheadDFA(); }
^( CLOSURE blk=block["closureBlock", dfa] )
{ $code = $blk.code; }
| { dfa = eob.getLookaheadDFA(); }
^( POSITIVE_CLOSURE blk=block["positiveClosureBlock", dfa] )
{ $code = $blk.code; }
)
{
String description = grammar.grammarTreeToString($start, false);
description = generator.target.getTargetStringLiteralFromString(description);
$code.add("description", description);
}
;
tree_ returns [ST code]
@init
{
rewriteTreeNestingLevel++;
GrammarAST rootSuffix = null;
if ( state.backtracking == 0 )
{
$code = templates.getInstanceOf("tree");
NFAState afterDOWN = (NFAState)$start.NFATreeDownState.transition(0).target;
LookaheadSet s = grammar.LOOK(afterDOWN);
if ( s.member(Label.UP) ) {
// nullable child list if we can see the UP as the next token
// we need an "if ( input.LA(1)==Token.DOWN )" gate around
// the child list.
$code.add("nullableChildList", "true");
}
$code.add("enclosingTreeLevel", rewriteTreeNestingLevel-1);
$code.add("treeLevel", rewriteTreeNestingLevel);
Rule r = grammar.getRule(currentRuleName);
if ( grammar.buildAST() && !r.hasRewrite(outerAltNum) ) {
rootSuffix = new GrammarAST(ROOT,"ROOT");
}
}
}
: ^( TREE_BEGIN
el=element[null,rootSuffix]
{
$code.addAggr("root.{el,line,pos}",
$el.code,
$el.start.getLine(),
$el.start.getCharPositionInLine() + 1
);
}
// push all the immediately-following actions out before children
// so actions aren't guarded by the "if (input.LA(1)==Token.DOWN)"
// guard in generated code.
( (element_action) =>
act=element_action
{
$code.addAggr("actionsAfterRoot.{el,line,pos}",
$act.code,
$act.start.getLine(),
$act.start.getCharPositionInLine() + 1
);
}
)*
( el=element[null,null]
{
$code.addAggr("children.{el,line,pos}",
$el.code,
$el.start.getLine(),
$el.start.getCharPositionInLine() + 1
);
}
)*
)
;
finally { rewriteTreeNestingLevel--; }
atom[GrammarAST scope, GrammarAST label, GrammarAST astSuffix]
returns [ST code=null]
@init
{
String labelText=null;
if ( state.backtracking == 0 )
{
if ( label!=null )
{
labelText = label.getText();
}
if ( grammar.type!=Grammar.LEXER &&
($start.getType()==RULE_REF||$start.getType()==TOKEN_REF||
$start.getType()==CHAR_LITERAL||$start.getType()==STRING_LITERAL) )
{
Rule encRule = grammar.getRule(((GrammarAST)$start).enclosingRuleName);
if ( encRule!=null && encRule.hasRewrite(outerAltNum) && astSuffix!=null )
{
ErrorManager.grammarError(ErrorManager.MSG_AST_OP_IN_ALT_WITH_REWRITE,
grammar,
((GrammarAST)$start).getToken(),
((GrammarAST)$start).enclosingRuleName,
outerAltNum);
astSuffix = null;
}
}
}
}
: ^( r=RULE_REF (rarg=ARG_ACTION)? )
{
grammar.checkRuleReference(scope, $r, $rarg, currentRuleName);
String scopeName = null;
if ( scope!=null ) {
scopeName = scope.getText();
}
Rule rdef = grammar.getRule(scopeName, $r.text);
// don't insert label=r() if $label.attr not used, no ret value, ...
if ( !rdef.getHasReturnValue() ) {
labelText = null;
}
$code = getRuleElementST("ruleRef", $r.text, $r, astSuffix, labelText);
$code.add("rule", rdef);
if ( scope!=null ) { // scoped rule ref
Grammar scopeG = grammar.composite.getGrammar(scope.getText());
$code.add("scope", scopeG);
}
else if ( rdef.grammar != this.grammar ) { // nonlocal
// if rule definition is not in this grammar, it's nonlocal
List<Grammar> rdefDelegates = rdef.grammar.getDelegates();
if ( rdefDelegates.contains(this.grammar) ) {
$code.add("scope", rdef.grammar);
}
else {
// defining grammar is not a delegate, scope all the
// back to root, which has delegate methods for all
// rules. Don't use scope if we are root.
if ( this.grammar != rdef.grammar.composite.delegateGrammarTreeRoot.grammar ) {
$code.add("scope",
rdef.grammar.composite.delegateGrammarTreeRoot.grammar);
}
}
}
if ( $rarg!=null ) {
List args = generator.translateAction(currentRuleName,$rarg);
$code.add("args", args);
}
int i = ((CommonToken)r.getToken()).getTokenIndex();
$code.add("elementIndex", i);
generator.generateLocalFOLLOW($r,$r.text,currentRuleName,i);
$r.code = $code;
}
| ^( t=TOKEN_REF (targ=ARG_ACTION)? )
{
if ( currentAltHasASTRewrite && $t.terminalOptions!=null &&
$t.terminalOptions.get(Grammar.defaultTokenOption)!=null )
{
ErrorManager.grammarError(ErrorManager.MSG_HETERO_ILLEGAL_IN_REWRITE_ALT,
grammar,
((GrammarAST)($t)).getToken(),
$t.text);
}
grammar.checkRuleReference(scope, $t, $targ, currentRuleName);
if ( grammar.type==Grammar.LEXER )
{
if ( grammar.getTokenType($t.text)==Label.EOF )
{
$code = templates.getInstanceOf("lexerMatchEOF");
}
else
{
$code = templates.getInstanceOf("lexerRuleRef");
if ( isListLabel(labelText) )
{
$code = templates.getInstanceOf("lexerRuleRefAndListLabel");
}
String scopeName = null;
if ( scope!=null )
{
scopeName = scope.getText();
}
Rule rdef2 = grammar.getRule(scopeName, $t.text);
$code.add("rule", rdef2);
if ( scope!=null )
{ // scoped rule ref
Grammar scopeG = grammar.composite.getGrammar(scope.getText());
$code.add("scope", scopeG);
}
else if ( rdef2.grammar != this.grammar )
{ // nonlocal
// if rule definition is not in this grammar, it's nonlocal
$code.add("scope", rdef2.grammar);
}
if ( $targ!=null )
{
List args = generator.translateAction(currentRuleName,$targ);
$code.add("args", args);
}
}
int i = ((CommonToken)$t.getToken()).getTokenIndex();
$code.add("elementIndex", i);
if ( label!=null )
$code.add("label", labelText);
}
else
{
$code = getTokenElementST("tokenRef", $t.text, $t, astSuffix, labelText);
String tokenLabel =
generator.getTokenTypeAsTargetLabel(grammar.getTokenType(t.getText()));
$code.add("token",tokenLabel);
if ( !currentAltHasASTRewrite && $t.terminalOptions!=null )
{
$code.add("terminalOptions", $t.terminalOptions);
}
int i = ((CommonToken)$t.getToken()).getTokenIndex();
$code.add("elementIndex", i);
generator.generateLocalFOLLOW($t,tokenLabel,currentRuleName,i);
}
$t.code = $code;
}
| c=CHAR_LITERAL
{
if ( grammar.type==Grammar.LEXER )
{
$code = templates.getInstanceOf("charRef");
$code.add("char",
generator.target.getTargetCharLiteralFromANTLRCharLiteral(generator,$c.text));
if ( label!=null )
{
$code.add("label", labelText);
}
}
else { // else it's a token type reference
$code = getTokenElementST("tokenRef", "char_literal", $c, astSuffix, labelText);
String tokenLabel = generator.getTokenTypeAsTargetLabel(grammar.getTokenType($c.text));
$code.add("token",tokenLabel);
if ( $c.terminalOptions!=null ) {
$code.add("terminalOptions",$c.terminalOptions);
}
int i = ((CommonToken)$c.getToken()).getTokenIndex();
$code.add("elementIndex", i);
generator.generateLocalFOLLOW($c,tokenLabel,currentRuleName,i);
}
}
| s=STRING_LITERAL
{
int i = ((CommonToken)$s.getToken()).getTokenIndex();
if ( grammar.type==Grammar.LEXER )
{
$code = templates.getInstanceOf("lexerStringRef");
$code.add("string",
generator.target.getTargetStringLiteralFromANTLRStringLiteral(generator,$s.text));
$code.add("elementIndex", i);
if ( label!=null )
{
$code.add("label", labelText);
}
}
else
{
// else it's a token type reference
$code = getTokenElementST("tokenRef", "string_literal", $s, astSuffix, labelText);
String tokenLabel =
generator.getTokenTypeAsTargetLabel(grammar.getTokenType($s.text));
$code.add("token",tokenLabel);
if ( $s.terminalOptions!=null )
{
$code.add("terminalOptions",$s.terminalOptions);
}
$code.add("elementIndex", i);
generator.generateLocalFOLLOW($s,tokenLabel,currentRuleName,i);
}
}
| w=WILDCARD
{
$code = getWildcardST($w,astSuffix,labelText);
$code.add("elementIndex", ((CommonToken)$w.getToken()).getTokenIndex());
}
| ^(DOT ID a=atom[$ID, label, astSuffix]) // scope override on rule or token
{ $code = $a.code; }
| set[label,astSuffix]
{ $code = $set.code; }
;
ast_suffix
: ROOT
| BANG
;
set[GrammarAST label, GrammarAST astSuffix] returns [ST code=null]
@init
{
String labelText=null;
if ( $label!=null )
{
labelText = $label.getText();
}
}
: ^(s=BLOCK .*) // only care that it's a BLOCK with setValue!=null
{
$code = getTokenElementST("matchSet", "set", $s, astSuffix, labelText);
int i = ((CommonToken)$s.getToken()).getTokenIndex();
$code.add("elementIndex", i);
if ( grammar.type!=Grammar.LEXER )
{
generator.generateLocalFOLLOW($s,"set",currentRuleName,i);
}
$code.add("s", generator.genSetExpr(templates,$s.getSetValue(),1,false));
}
;
setElement
: CHAR_LITERAL
| TOKEN_REF
| STRING_LITERAL
| ^(CHAR_RANGE CHAR_LITERAL CHAR_LITERAL)
;
// REWRITE stuff
rewrite returns [ST code=null]
@init
{
if ( state.backtracking == 0 )
{
if ( $start.getType()==REWRITES )
{
if ( generator.grammar.buildTemplate() )
{
$code = templates.getInstanceOf("rewriteTemplate");
}
else
{
$code = templates.getInstanceOf("rewriteCode");
$code.add("treeLevel", OUTER_REWRITE_NESTING_LEVEL);
$code.add("rewriteBlockLevel", OUTER_REWRITE_NESTING_LEVEL);
$code.add("referencedElementsDeep",
getTokenTypesAsTargetLabels($start.rewriteRefsDeep));
Set<String> tokenLabels =
grammar.getLabels($start.rewriteRefsDeep, Grammar.TOKEN_LABEL);
Set<String> tokenListLabels =
grammar.getLabels($start.rewriteRefsDeep, Grammar.TOKEN_LIST_LABEL);
Set<String> ruleLabels =
grammar.getLabels($start.rewriteRefsDeep, Grammar.RULE_LABEL);
Set<String> ruleListLabels =
grammar.getLabels($start.rewriteRefsDeep, Grammar.RULE_LIST_LABEL);
Set<String> wildcardLabels =
grammar.getLabels($start.rewriteRefsDeep, Grammar.WILDCARD_TREE_LABEL);
Set<String> wildcardListLabels =
grammar.getLabels($start.rewriteRefsDeep, Grammar.WILDCARD_TREE_LIST_LABEL);
// just in case they ref $r for "previous value", make a stream
// from retval.tree
ST retvalST = templates.getInstanceOf("prevRuleRootRef");
ruleLabels.add(retvalST.render());
$code.add("referencedTokenLabels", tokenLabels);
$code.add("referencedTokenListLabels", tokenListLabels);
$code.add("referencedRuleLabels", ruleLabels);
$code.add("referencedRuleListLabels", ruleListLabels);
$code.add("referencedWildcardLabels", wildcardLabels);
$code.add("referencedWildcardListLabels", wildcardListLabels);
}
}
else
{
$code = templates.getInstanceOf("noRewrite");
$code.add("treeLevel", OUTER_REWRITE_NESTING_LEVEL);
$code.add("rewriteBlockLevel", OUTER_REWRITE_NESTING_LEVEL);
}
}
}
: ^( REWRITES
(
{rewriteRuleRefs = new HashSet<Object>();}
^( r=REWRITE (pred=SEMPRED)? alt=rewrite_alternative)
{
rewriteBlockNestingLevel = OUTER_REWRITE_NESTING_LEVEL;
List predChunks = null;
if ( $pred!=null )
{
//predText = #pred.getText();
predChunks = generator.translateAction(currentRuleName,$pred);
}
String description =
grammar.grammarTreeToString($r,false);
description = generator.target.getTargetStringLiteralFromString(description);
$code.addAggr("alts.{pred,alt,description}",
predChunks,
alt,
description);
pred=null;
}
)*
)
|
;
rewrite_block[String blockTemplateName] returns [ST code=null]
@init
{
rewriteBlockNestingLevel++;
ST save_currentBlockST = currentBlockST;
if ( state.backtracking == 0 )
{
$code = templates.getInstanceOf(blockTemplateName);
currentBlockST = $code;
$code.add("rewriteBlockLevel", rewriteBlockNestingLevel);
}
}
: ^( BLOCK
{
currentBlockST.add("referencedElementsDeep",
getTokenTypesAsTargetLabels($BLOCK.rewriteRefsDeep));
currentBlockST.add("referencedElements",
getTokenTypesAsTargetLabels($BLOCK.rewriteRefsShallow));
}
alt=rewrite_alternative
EOB
)
{
$code.add("alt", $alt.code);
}
;
finally { rewriteBlockNestingLevel--; currentBlockST = save_currentBlockST; }
rewrite_alternative returns [ST code=null]
: {generator.grammar.buildAST()}?
^( a=ALT {$code=templates.getInstanceOf("rewriteElementList");}
( (
el=rewrite_element
{$code.addAggr("elements.{el,line,pos}",
$el.code,
$el.start.getLine(),
$el.start.getCharPositionInLine() + 1
);
}
)+
| EPSILON
{$code.addAggr("elements.{el,line,pos}",
templates.getInstanceOf("rewriteEmptyAlt"),
$a.getLine(),
$a.getCharPositionInLine() + 1
);
}
)
EOA
)
| {generator.grammar.buildTemplate()}? rewrite_template
{ $code = $rewrite_template.code; }
| // reproduce same input (only AST at moment)
ETC
;
rewrite_element returns [ST code=null]
@init
{
IntSet elements=null;
GrammarAST ast = null;
}
: rewrite_atom[false]
{ $code = $rewrite_atom.code; }
| rewrite_ebnf
{ $code = $rewrite_ebnf.code; }
| rewrite_tree
{ $code = $rewrite_tree.code; }
;
rewrite_ebnf returns [ST code=null]
: ^( OPTIONAL rewrite_block["rewriteOptionalBlock"] )
{ $code = $rewrite_block.code; }
{
String description = grammar.grammarTreeToString($start, false);
description = generator.target.getTargetStringLiteralFromString(description);
$code.add("description", description);
}
| ^( CLOSURE rewrite_block["rewriteClosureBlock"] )
{ $code = $rewrite_block.code; }
{
String description = grammar.grammarTreeToString($start, false);
description = generator.target.getTargetStringLiteralFromString(description);
$code.add("description", description);
}
| ^( POSITIVE_CLOSURE rewrite_block["rewritePositiveClosureBlock"] )
{ $code = $rewrite_block.code; }
{
String description = grammar.grammarTreeToString($start, false);
description = generator.target.getTargetStringLiteralFromString(description);
$code.add("description", description);
}
;
rewrite_tree returns [ST code]
@init
{
rewriteTreeNestingLevel++;
if ( state.backtracking == 0 )
{
$code = templates.getInstanceOf("rewriteTree");
$code.add("treeLevel", rewriteTreeNestingLevel);
$code.add("enclosingTreeLevel", rewriteTreeNestingLevel-1);
}
}
: ^( TREE_BEGIN
r=rewrite_atom[true]
{
$code.addAggr("root.{el,line,pos}",
$r.code,
$r.start.getLine(),
$r.start.getCharPositionInLine() + 1
);
}
(
el=rewrite_element
{
$code.addAggr("children.{el,line,pos}",
$el.code,
$el.start.getLine(),
$el.start.getCharPositionInLine() + 1
);
}
)*
)
{
String description = grammar.grammarTreeToString($start, false);
description = generator.target.getTargetStringLiteralFromString(description);
$code.add("description", description);
}
;
finally { rewriteTreeNestingLevel--; }
rewrite_atom[boolean isRoot] returns [ST code=null]
: r=RULE_REF
{
String ruleRefName = $r.text;
String stName = "rewriteRuleRef";
if ( isRoot )
{
stName += "Root";
}
$code = templates.getInstanceOf(stName);
$code.add("rule", ruleRefName);
if ( grammar.getRule(ruleRefName)==null )
{
ErrorManager.grammarError(ErrorManager.MSG_UNDEFINED_RULE_REF,
grammar,
((GrammarAST)($r)).getToken(),
ruleRefName);
$code = new ST(""); // blank; no code gen
}
else if ( grammar.getRule(currentRuleName)
.getRuleRefsInAlt(ruleRefName,outerAltNum)==null )
{
ErrorManager.grammarError(ErrorManager.MSG_REWRITE_ELEMENT_NOT_PRESENT_ON_LHS,
grammar,
((GrammarAST)($r)).getToken(),
ruleRefName);
$code = new ST(""); // blank; no code gen
}
else
{
// track all rule refs as we must copy 2nd ref to rule and beyond
if ( !rewriteRuleRefs.contains(ruleRefName) )
{
rewriteRuleRefs.add(ruleRefName);
}
}
}
|
( ^(tk=TOKEN_REF (arg=ARG_ACTION)?)
| cl=CHAR_LITERAL
| sl=STRING_LITERAL
)
{
GrammarAST term = $tk;
if (term == null) term = $cl;
if (term == null) term = $sl;
String tokenName = $start.getToken().getText();
String stName = "rewriteTokenRef";
Rule rule = grammar.getRule(currentRuleName);
Collection<String> tokenRefsInAlt = rule.getTokenRefsInAlt(outerAltNum);
boolean createNewNode = !tokenRefsInAlt.contains(tokenName) || $arg!=null;
if ( createNewNode )
{
stName = "rewriteImaginaryTokenRef";
}
if ( isRoot )
{
stName += "Root";
}
$code = templates.getInstanceOf(stName);
$code.add("terminalOptions",term.terminalOptions);
if ( $arg!=null )
{
List args = generator.translateAction(currentRuleName,$arg);
$code.add("args", args);
}
$code.add("elementIndex", ((CommonToken)$start.getToken()).getTokenIndex());
int ttype = grammar.getTokenType(tokenName);
String tok = generator.getTokenTypeAsTargetLabel(ttype);
$code.add("token", tok);
if ( grammar.getTokenType(tokenName)==Label.INVALID )
{
ErrorManager.grammarError(ErrorManager.MSG_UNDEFINED_TOKEN_REF_IN_REWRITE,
grammar,
((GrammarAST)($start)).getToken(),
tokenName);
$code = new ST(""); // blank; no code gen
}
}
| LABEL
{
String labelName = $LABEL.text;
Rule rule = grammar.getRule(currentRuleName);
Grammar.LabelElementPair pair = rule.getLabel(labelName);
if ( labelName.equals(currentRuleName) )
{
// special case; ref to old value via $ rule
if ( rule.hasRewrite(outerAltNum) &&
rule.getRuleRefsInAlt(outerAltNum).contains(labelName) )
{
ErrorManager.grammarError(ErrorManager.MSG_RULE_REF_AMBIG_WITH_RULE_IN_ALT,
grammar,
((GrammarAST)($LABEL)).getToken(),
labelName);
}
ST labelST = templates.getInstanceOf("prevRuleRootRef");
$code = templates.getInstanceOf("rewriteRuleLabelRef"+(isRoot?"Root":""));
$code.add("label", labelST);
}
else if ( pair==null )
{
ErrorManager.grammarError(ErrorManager.MSG_UNDEFINED_LABEL_REF_IN_REWRITE,
grammar,
((GrammarAST)($LABEL)).getToken(),
labelName);
$code = new ST("");
}
else
{
String stName = null;
switch ( pair.type )
{
case Grammar.TOKEN_LABEL :
stName = "rewriteTokenLabelRef";
break;
case Grammar.WILDCARD_TREE_LABEL :
stName = "rewriteWildcardLabelRef";
break;
case Grammar.WILDCARD_TREE_LIST_LABEL:
stName = "rewriteRuleListLabelRef"; // acts like rule ref list for ref
break;
case Grammar.RULE_LABEL :
stName = "rewriteRuleLabelRef";
break;
case Grammar.TOKEN_LIST_LABEL :
stName = "rewriteTokenListLabelRef";
break;
case Grammar.RULE_LIST_LABEL :
stName = "rewriteRuleListLabelRef";
break;
}
if ( isRoot )
{
stName += "Root";
}
$code = templates.getInstanceOf(stName);
$code.add("label", labelName);
}
}
| ACTION
{
// actions in rewrite rules yield a tree object
String actText = $ACTION.text;
List chunks = generator.translateAction(currentRuleName,$ACTION);
$code = templates.getInstanceOf("rewriteNodeAction"+(isRoot?"Root":""));
$code.add("action", chunks);
}
;
public
rewrite_template returns [ST code=null]
: ^( ALT EPSILON EOA ) {$code=templates.getInstanceOf("rewriteEmptyTemplate");}
| ^( TEMPLATE (id=ID|ind=ACTION)
{
if ( $id!=null && $id.text.equals("template") )
{
$code = templates.getInstanceOf("rewriteInlineTemplate");
}
else if ( $id!=null )
{
$code = templates.getInstanceOf("rewriteExternalTemplate");
$code.add("name", $id.text);
}
else if ( $ind!=null )
{ // must be \%({expr})(args)
$code = templates.getInstanceOf("rewriteIndirectTemplate");
List chunks=generator.translateAction(currentRuleName,$ind);
$code.add("expr", chunks);
}
}
^( ARGLIST
( ^( ARG arg=ID a=ACTION
{
// must set alt num here rather than in define.g
// because actions like \%foo(name={\$ID.text}) aren't
// broken up yet into trees.
$a.outerAltNum = this.outerAltNum;
List chunks = generator.translateAction(currentRuleName,$a);
$code.addAggr("args.{name,value}", $arg.text, chunks);
}
)
)*
)
( DOUBLE_QUOTE_STRING_LITERAL
{
String sl = $DOUBLE_QUOTE_STRING_LITERAL.text;
String t = sl.substring( 1, sl.length() - 1 ); // strip quotes
t = generator.target.getTargetStringLiteralFromString(t);
$code.add("template",t);
}
| DOUBLE_ANGLE_STRING_LITERAL
{
String sl = $DOUBLE_ANGLE_STRING_LITERAL.text;
String t = sl.substring( 2, sl.length() - 2 ); // strip double angle quotes
t = generator.target.getTargetStringLiteralFromString(t);
$code.add("template",t);
}
)?
)
| act=ACTION
{
// set alt num for same reason as ARGLIST above
$act.outerAltNum = this.outerAltNum;
$code=templates.getInstanceOf("rewriteAction");
$code.add("action",
generator.translateAction(currentRuleName,$act));
}
;