aboutsummaryrefslogtreecommitdiff
path: root/tool/src/main/antlr3/org/antlr/grammar/v3/CodeGenTreeWalker.g
diff options
context:
space:
mode:
Diffstat (limited to 'tool/src/main/antlr3/org/antlr/grammar/v3/CodeGenTreeWalker.g')
-rw-r--r--tool/src/main/antlr3/org/antlr/grammar/v3/CodeGenTreeWalker.g1608
1 files changed, 1608 insertions, 0 deletions
diff --git a/tool/src/main/antlr3/org/antlr/grammar/v3/CodeGenTreeWalker.g b/tool/src/main/antlr3/org/antlr/grammar/v3/CodeGenTreeWalker.g
new file mode 100644
index 0000000..7615833
--- /dev/null
+++ b/tool/src/main/antlr3/org/antlr/grammar/v3/CodeGenTreeWalker.g
@@ -0,0 +1,1608 @@
+/*
+ [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));
+ }
+ ;