1/* 2 [The "BSD license"] 3 Copyright (c) 2010 Terence Parr 4 All rights reserved. 5 6 Redistribution and use in source and binary forms, with or without 7 modification, are permitted provided that the following conditions 8 are met: 9 1. Redistributions of source code must retain the above copyright 10 notice, this list of conditions and the following disclaimer. 11 2. Redistributions in binary form must reproduce the above copyright 12 notice, this list of conditions and the following disclaimer in the 13 documentation and/or other materials provided with the distribution. 14 3. The name of the author may not be used to endorse or promote products 15 derived from this software without specific prior written permission. 16 17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27*/ 28lexer grammar ActionTranslator; 29options { 30 language=Java; 31 filter=true; // try all non-fragment rules in order specified 32 // output=template; TODO: can we make tokens return templates somehow? 33} 34 35@header { 36package org.antlr.grammar.v3; 37import org.stringtemplate.v4.ST; 38import org.antlr.runtime.*; 39import org.antlr.tool.*; 40import org.antlr.codegen.*; 41 42import org.antlr.runtime.*; 43import java.util.List; 44import java.util.ArrayList; 45import org.antlr.grammar.v3.ANTLRParser; 46 47} 48 49@members { 50public List<Object> chunks = new ArrayList<Object>(); 51Rule enclosingRule; 52int outerAltNum; 53Grammar grammar; 54CodeGenerator generator; 55Token actionToken; 56 57 public ActionTranslator(CodeGenerator generator, 58 String ruleName, 59 GrammarAST actionAST) 60 { 61 this(new ANTLRStringStream(actionAST.token.getText())); 62 this.generator = generator; 63 this.grammar = generator.grammar; 64 this.enclosingRule = grammar.getLocallyDefinedRule(ruleName); 65 this.actionToken = actionAST.token; 66 this.outerAltNum = actionAST.outerAltNum; 67 } 68 69 public ActionTranslator(CodeGenerator generator, 70 String ruleName, 71 Token actionToken, 72 int outerAltNum) 73 { 74 this(new ANTLRStringStream(actionToken.getText())); 75 this.generator = generator; 76 grammar = generator.grammar; 77 this.enclosingRule = grammar.getRule(ruleName); 78 this.actionToken = actionToken; 79 this.outerAltNum = outerAltNum; 80 } 81 82/** Return a list of strings and ST objects that 83 * represent the translated action. 84 */ 85public List<Object> translateToChunks() { 86 // System.out.println("###\naction="+action); 87 Token t; 88 do { 89 t = nextToken(); 90 } while ( t.getType()!= Token.EOF ); 91 return chunks; 92} 93 94public String translate() { 95 List<Object> theChunks = translateToChunks(); 96 //System.out.println("chunks="+a.chunks); 97 StringBuilder buf = new StringBuilder(); 98 for (int i = 0; i < theChunks.size(); i++) { 99 Object o = theChunks.get(i); 100 if ( o instanceof ST ) buf.append(((ST)o).render()); 101 else buf.append(o); 102 } 103 //System.out.println("translated: "+buf.toString()); 104 return buf.toString(); 105} 106 107public List<Object> translateAction(String action) { 108 String rname = null; 109 if ( enclosingRule!=null ) { 110 rname = enclosingRule.name; 111 } 112 ActionTranslator translator = 113 new ActionTranslator(generator, 114 rname, 115 new CommonToken(ANTLRParser.ACTION,action),outerAltNum); 116 return translator.translateToChunks(); 117} 118 119public boolean isTokenRefInAlt(String id) { 120 return enclosingRule.getTokenRefsInAlt(id, outerAltNum)!=null; 121} 122public boolean isRuleRefInAlt(String id) { 123 return enclosingRule.getRuleRefsInAlt(id, outerAltNum)!=null; 124} 125public Grammar.LabelElementPair getElementLabel(String id) { 126 return enclosingRule.getLabel(id); 127} 128 129public void checkElementRefUniqueness(String ref, boolean isToken) { 130 List<GrammarAST> refs = null; 131 if ( isToken ) { 132 refs = enclosingRule.getTokenRefsInAlt(ref, outerAltNum); 133 } 134 else { 135 refs = enclosingRule.getRuleRefsInAlt(ref, outerAltNum); 136 } 137 if ( refs!=null && refs.size()>1 ) { 138 ErrorManager.grammarError(ErrorManager.MSG_NONUNIQUE_REF, 139 grammar, 140 actionToken, 141 ref); 142 } 143} 144 145/** For \$rulelabel.name, return the Attribute found for name. It 146 * will be a predefined property or a return value. 147 */ 148public Attribute getRuleLabelAttribute(String ruleName, String attrName) { 149 Rule r = grammar.getRule(ruleName); 150 AttributeScope scope = r.getLocalAttributeScope(attrName); 151 if ( scope!=null && !scope.isParameterScope ) { 152 return scope.getAttribute(attrName); 153 } 154 return null; 155} 156 157AttributeScope resolveDynamicScope(String scopeName) { 158 if ( grammar.getGlobalScope(scopeName)!=null ) { 159 return grammar.getGlobalScope(scopeName); 160 } 161 Rule scopeRule = grammar.getRule(scopeName); 162 if ( scopeRule!=null ) { 163 return scopeRule.ruleScope; 164 } 165 return null; // not a valid dynamic scope 166} 167 168protected ST template(String name) { 169 ST st = generator.getTemplates().getInstanceOf(name); 170 chunks.add(st); 171 return st; 172} 173 174 175} 176 177/** $x.y x is enclosing rule, y is a return value, parameter, or 178 * predefined property. 179 * 180 * r[int i] returns [int j] 181 * : {$r.i, $r.j, $r.start, $r.stop, $r.st, $r.tree} 182 * ; 183 */ 184SET_ENCLOSING_RULE_SCOPE_ATTR 185 : '$' x=ID '.' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';' 186 {enclosingRule!=null && 187 $x.text.equals(enclosingRule.name) && 188 enclosingRule.getLocalAttributeScope($y.text)!=null}? 189 //{System.out.println("found \$rule.attr");} 190 { 191 ST st = null; 192 AttributeScope scope = enclosingRule.getLocalAttributeScope($y.text); 193 if ( scope.isPredefinedRuleScope ) { 194 if ( $y.text.equals("st") || $y.text.equals("tree") ) { 195 st = template("ruleSetPropertyRef_"+$y.text); 196 grammar.referenceRuleLabelPredefinedAttribute($x.text); 197 st.add("scope", $x.text); 198 st.add("attr", $y.text); 199 st.add("expr", translateAction($expr.text)); 200 } else { 201 ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR, 202 grammar, 203 actionToken, 204 $x.text, 205 $y.text); 206 } 207 } 208 else if ( scope.isPredefinedLexerRuleScope ) { 209 // this is a better message to emit than the previous one... 210 ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR, 211 grammar, 212 actionToken, 213 $x.text, 214 $y.text); 215 } 216 else if ( scope.isParameterScope ) { 217 st = template("parameterSetAttributeRef"); 218 st.add("attr", scope.getAttribute($y.text)); 219 st.add("expr", translateAction($expr.text)); 220 } 221 else { // must be return value 222 st = template("returnSetAttributeRef"); 223 st.add("ruleDescriptor", enclosingRule); 224 st.add("attr", scope.getAttribute($y.text)); 225 st.add("expr", translateAction($expr.text)); 226 } 227 } 228 ; 229ENCLOSING_RULE_SCOPE_ATTR 230 : '$' x=ID '.' y=ID {enclosingRule!=null && 231 $x.text.equals(enclosingRule.name) && 232 enclosingRule.getLocalAttributeScope($y.text)!=null}? 233 //{System.out.println("found \$rule.attr");} 234 { 235 if ( isRuleRefInAlt($x.text) ) { 236 ErrorManager.grammarError(ErrorManager.MSG_RULE_REF_AMBIG_WITH_RULE_IN_ALT, 237 grammar, 238 actionToken, 239 $x.text); 240 } 241 ST st = null; 242 AttributeScope scope = enclosingRule.getLocalAttributeScope($y.text); 243 if ( scope.isPredefinedRuleScope ) { 244 st = template("rulePropertyRef_"+$y.text); 245 grammar.referenceRuleLabelPredefinedAttribute($x.text); 246 st.add("scope", $x.text); 247 st.add("attr", $y.text); 248 } 249 else if ( scope.isPredefinedLexerRuleScope ) { 250 // perhaps not the most precise error message to use, but... 251 ErrorManager.grammarError(ErrorManager.MSG_RULE_HAS_NO_ARGS, 252 grammar, 253 actionToken, 254 $x.text); 255 } 256 else if ( scope.isParameterScope ) { 257 st = template("parameterAttributeRef"); 258 st.add("attr", scope.getAttribute($y.text)); 259 } 260 else { // must be return value 261 st = template("returnAttributeRef"); 262 st.add("ruleDescriptor", enclosingRule); 263 st.add("attr", scope.getAttribute($y.text)); 264 } 265 } 266 ; 267 268/** Setting $tokenlabel.attr or $tokenref.attr where attr is predefined property of a token is an error. */ 269SET_TOKEN_SCOPE_ATTR 270 : '$' x=ID '.' y=ID WS? '=' 271 {enclosingRule!=null && input.LA(1)!='=' && 272 (enclosingRule.getTokenLabel($x.text)!=null|| 273 isTokenRefInAlt($x.text)) && 274 AttributeScope.tokenScope.getAttribute($y.text)!=null}? 275 //{System.out.println("found \$tokenlabel.attr or \$tokenref.attr");} 276 { 277 ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR, 278 grammar, 279 actionToken, 280 $x.text, 281 $y.text); 282 } 283 ; 284 285/** $tokenlabel.attr or $tokenref.attr where attr is predefined property of a token. 286 * If in lexer grammar, only translate for strings and tokens (rule refs) 287 */ 288TOKEN_SCOPE_ATTR 289 : '$' x=ID '.' y=ID {enclosingRule!=null && 290 (enclosingRule.getTokenLabel($x.text)!=null|| 291 isTokenRefInAlt($x.text)) && 292 AttributeScope.tokenScope.getAttribute($y.text)!=null && 293 (grammar.type!=Grammar.LEXER || 294 getElementLabel($x.text).elementRef.token.getType()==ANTLRParser.TOKEN_REF || 295 getElementLabel($x.text).elementRef.token.getType()==ANTLRParser.STRING_LITERAL)}? 296 // {System.out.println("found \$tokenlabel.attr or \$tokenref.attr");} 297 { 298 String label = $x.text; 299 if ( enclosingRule.getTokenLabel($x.text)==null ) { 300 // \$tokenref.attr gotta get old label or compute new one 301 checkElementRefUniqueness($x.text, true); 302 label = enclosingRule.getElementLabel($x.text, outerAltNum, generator); 303 if ( label==null ) { 304 ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF, 305 grammar, 306 actionToken, 307 "\$"+$x.text+"."+$y.text); 308 label = $x.text; 309 } 310 } 311 ST st = template("tokenLabelPropertyRef_"+$y.text); 312 st.add("scope", label); 313 st.add("attr", AttributeScope.tokenScope.getAttribute($y.text)); 314 } 315 ; 316 317/** Setting $rulelabel.attr or $ruleref.attr where attr is a predefined property is an error 318 * This must also fail, if we try to access a local attribute's field, like $tree.scope = localObject 319 * That must be handled by LOCAL_ATTR below. ANTLR only concerns itself with the top-level scope 320 * attributes declared in scope {} or parameters, return values and the like. 321 */ 322SET_RULE_SCOPE_ATTR 323@init { 324Grammar.LabelElementPair pair=null; 325String refdRuleName=null; 326} 327 : '$' x=ID '.' y=ID WS? '=' {enclosingRule!=null && input.LA(1)!='='}? 328 { 329 pair = enclosingRule.getRuleLabel($x.text); 330 refdRuleName = $x.text; 331 if ( pair!=null ) { 332 refdRuleName = pair.referencedRuleName; 333 } 334 } 335 // supercomplicated because I can't exec the above action. 336 // This asserts that if it's a label or a ref to a rule proceed but only if the attribute 337 // is valid for that rule's scope 338 {(enclosingRule.getRuleLabel($x.text)!=null || isRuleRefInAlt($x.text)) && 339 getRuleLabelAttribute(enclosingRule.getRuleLabel($x.text)!=null?enclosingRule.getRuleLabel($x.text).referencedRuleName:$x.text,$y.text)!=null}? 340 //{System.out.println("found set \$rulelabel.attr or \$ruleref.attr: "+$x.text+"."+$y.text);} 341 { 342 ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR, 343 grammar, 344 actionToken, 345 $x.text, 346 $y.text); 347 } 348 ; 349 350/** $rulelabel.attr or $ruleref.attr where attr is a predefined property*/ 351RULE_SCOPE_ATTR 352@init { 353Grammar.LabelElementPair pair=null; 354String refdRuleName=null; 355} 356 : '$' x=ID '.' y=ID {enclosingRule!=null}? 357 { 358 pair = enclosingRule.getRuleLabel($x.text); 359 refdRuleName = $x.text; 360 if ( pair!=null ) { 361 refdRuleName = pair.referencedRuleName; 362 } 363 } 364 // supercomplicated because I can't exec the above action. 365 // This asserts that if it's a label or a ref to a rule proceed but only if the attribute 366 // is valid for that rule's scope 367 {(enclosingRule.getRuleLabel($x.text)!=null || isRuleRefInAlt($x.text)) && 368 getRuleLabelAttribute(enclosingRule.getRuleLabel($x.text)!=null?enclosingRule.getRuleLabel($x.text).referencedRuleName:$x.text,$y.text)!=null}? 369 //{System.out.println("found \$rulelabel.attr or \$ruleref.attr: "+$x.text+"."+$y.text);} 370 { 371 String label = $x.text; 372 if ( pair==null ) { 373 // \$ruleref.attr gotta get old label or compute new one 374 checkElementRefUniqueness($x.text, false); 375 label = enclosingRule.getElementLabel($x.text, outerAltNum, generator); 376 if ( label==null ) { 377 ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF, 378 grammar, 379 actionToken, 380 "\$"+$x.text+"."+$y.text); 381 label = $x.text; 382 } 383 } 384 ST st; 385 Rule refdRule = grammar.getRule(refdRuleName); 386 AttributeScope scope = refdRule.getLocalAttributeScope($y.text); 387 if ( scope.isPredefinedRuleScope ) { 388 st = template("ruleLabelPropertyRef_"+$y.text); 389 grammar.referenceRuleLabelPredefinedAttribute(refdRuleName); 390 st.add("scope", label); 391 st.add("attr", $y.text); 392 } 393 else if ( scope.isPredefinedLexerRuleScope ) { 394 st = template("lexerRuleLabelPropertyRef_"+$y.text); 395 grammar.referenceRuleLabelPredefinedAttribute(refdRuleName); 396 st.add("scope", label); 397 st.add("attr", $y.text); 398 } 399 else if ( scope.isParameterScope ) { 400 // TODO: error! 401 } 402 else { 403 st = template("ruleLabelRef"); 404 st.add("referencedRule", refdRule); 405 st.add("scope", label); 406 st.add("attr", scope.getAttribute($y.text)); 407 } 408 } 409 ; 410 411 412/** $label either a token label or token/rule list label like label+=expr */ 413LABEL_REF 414 : '$' ID {enclosingRule!=null && 415 getElementLabel($ID.text)!=null && 416 enclosingRule.getRuleLabel($ID.text)==null}? 417 // {System.out.println("found \$label");} 418 { 419 ST st; 420 Grammar.LabelElementPair pair = getElementLabel($ID.text); 421 if ( pair.type==Grammar.RULE_LIST_LABEL || 422 pair.type==Grammar.TOKEN_LIST_LABEL || 423 pair.type==Grammar.WILDCARD_TREE_LIST_LABEL ) 424 { 425 st = template("listLabelRef"); 426 } 427 else { 428 st = template("tokenLabelRef"); 429 } 430 st.add("label", $ID.text); 431 } 432 ; 433 434/** $tokenref in a non-lexer grammar */ 435ISOLATED_TOKEN_REF 436 : '$' ID {grammar.type!=Grammar.LEXER && enclosingRule!=null && isTokenRefInAlt($ID.text)}? 437 //{System.out.println("found \$tokenref");} 438 { 439 String label = enclosingRule.getElementLabel($ID.text, outerAltNum, generator); 440 checkElementRefUniqueness($ID.text, true); 441 if ( label==null ) { 442 ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF, 443 grammar, 444 actionToken, 445 $ID.text); 446 } 447 else { 448 ST st = template("tokenLabelRef"); 449 st.add("label", label); 450 } 451 } 452 ; 453 454/** $lexerruleref from within the lexer */ 455ISOLATED_LEXER_RULE_REF 456 : '$' ID {grammar.type==Grammar.LEXER && 457 enclosingRule!=null && 458 isRuleRefInAlt($ID.text)}? 459 //{System.out.println("found \$lexerruleref");} 460 { 461 String label = enclosingRule.getElementLabel($ID.text, outerAltNum, generator); 462 checkElementRefUniqueness($ID.text, false); 463 if ( label==null ) { 464 ErrorManager.grammarError(ErrorManager.MSG_FORWARD_ELEMENT_REF, 465 grammar, 466 actionToken, 467 $ID.text); 468 } 469 else { 470 ST st = template("lexerRuleLabel"); 471 st.add("label", label); 472 } 473 } 474 ; 475 476/** $y return value, parameter, predefined rule property, or token/rule 477 * reference within enclosing rule's outermost alt. 478 * y must be a "local" reference; i.e., it must be referring to 479 * something defined within the enclosing rule. 480 * 481 * r[int i] returns [int j] 482 * : {$i, $j, $start, $stop, $st, $tree} 483 * ; 484 * 485 * TODO: this might get the dynamic scope's elements too.!!!!!!!!! 486 */ 487SET_LOCAL_ATTR 488 : '$' ID WS? '=' expr=ATTR_VALUE_EXPR ';' {enclosingRule!=null 489 && enclosingRule.getLocalAttributeScope($ID.text)!=null 490 && !enclosingRule.getLocalAttributeScope($ID.text).isPredefinedLexerRuleScope}? 491 //{System.out.println("found set \$localattr");} 492 { 493 ST st; 494 AttributeScope scope = enclosingRule.getLocalAttributeScope($ID.text); 495 if ( scope.isPredefinedRuleScope ) { 496 if ($ID.text.equals("tree") || $ID.text.equals("st")) { 497 st = template("ruleSetPropertyRef_"+$ID.text); 498 grammar.referenceRuleLabelPredefinedAttribute(enclosingRule.name); 499 st.add("scope", enclosingRule.name); 500 st.add("attr", $ID.text); 501 st.add("expr", translateAction($expr.text)); 502 } else { 503 ErrorManager.grammarError(ErrorManager.MSG_WRITE_TO_READONLY_ATTR, 504 grammar, 505 actionToken, 506 $ID.text, 507 ""); 508 } 509 } 510 else if ( scope.isParameterScope ) { 511 st = template("parameterSetAttributeRef"); 512 st.add("attr", scope.getAttribute($ID.text)); 513 st.add("expr", translateAction($expr.text)); 514 } 515 else { 516 st = template("returnSetAttributeRef"); 517 st.add("ruleDescriptor", enclosingRule); 518 st.add("attr", scope.getAttribute($ID.text)); 519 st.add("expr", translateAction($expr.text)); 520 } 521 } 522 ; 523LOCAL_ATTR 524 : '$' ID {enclosingRule!=null && enclosingRule.getLocalAttributeScope($ID.text)!=null}? 525 //{System.out.println("found \$localattr");} 526 { 527 ST st; 528 AttributeScope scope = enclosingRule.getLocalAttributeScope($ID.text); 529 if ( scope.isPredefinedRuleScope ) { 530 st = template("rulePropertyRef_"+$ID.text); 531 grammar.referenceRuleLabelPredefinedAttribute(enclosingRule.name); 532 st.add("scope", enclosingRule.name); 533 st.add("attr", $ID.text); 534 } 535 else if ( scope.isPredefinedLexerRuleScope ) { 536 st = template("lexerRulePropertyRef_"+$ID.text); 537 st.add("scope", enclosingRule.name); 538 st.add("attr", $ID.text); 539 } 540 else if ( scope.isParameterScope ) { 541 st = template("parameterAttributeRef"); 542 st.add("attr", scope.getAttribute($ID.text)); 543 } 544 else { 545 st = template("returnAttributeRef"); 546 st.add("ruleDescriptor", enclosingRule); 547 st.add("attr", scope.getAttribute($ID.text)); 548 } 549 } 550 ; 551 552/** $x::y the only way to access the attributes within a dynamic scope 553 * regardless of whether or not you are in the defining rule. 554 * 555 * scope Symbols { List names; } 556 * r 557 * scope {int i;} 558 * scope Symbols; 559 * : {$r::i=3;} s {$Symbols::names;} 560 * ; 561 * s : {$r::i; $Symbols::names;} 562 * ; 563 */ 564SET_DYNAMIC_SCOPE_ATTR 565 : '$' x=ID '::' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';' 566 {resolveDynamicScope($x.text)!=null && 567 resolveDynamicScope($x.text).getAttribute($y.text)!=null}? 568 //{System.out.println("found set \$scope::attr "+ $x.text + "::" + $y.text + " to " + $expr.text);} 569 { 570 AttributeScope scope = resolveDynamicScope($x.text); 571 if ( scope!=null ) { 572 ST st = template("scopeSetAttributeRef"); 573 st.add("scope", $x.text); 574 st.add("attr", scope.getAttribute($y.text)); 575 st.add("expr", translateAction($expr.text)); 576 } 577 else { 578 // error: invalid dynamic attribute 579 } 580 } 581 ; 582 583DYNAMIC_SCOPE_ATTR 584 : '$' x=ID '::' y=ID 585 {resolveDynamicScope($x.text)!=null && 586 resolveDynamicScope($x.text).getAttribute($y.text)!=null}? 587 //{System.out.println("found \$scope::attr "+ $x.text + "::" + $y.text);} 588 { 589 AttributeScope scope = resolveDynamicScope($x.text); 590 if ( scope!=null ) { 591 ST st = template("scopeAttributeRef"); 592 st.add("scope", $x.text); 593 st.add("attr", scope.getAttribute($y.text)); 594 } 595 else { 596 // error: invalid dynamic attribute 597 } 598 } 599 ; 600 601 602ERROR_SCOPED_XY 603 : '$' x=ID '::' y=ID 604 { 605 chunks.add(getText()); 606 generator.issueInvalidScopeError($x.text,$y.text, 607 enclosingRule,actionToken, 608 outerAltNum); 609 } 610 ; 611 612/** To access deeper (than top of stack) scopes, use the notation: 613 * 614 * $x[-1]::y previous (just under top of stack) 615 * $x[-i]::y top of stack - i where the '-' MUST BE PRESENT; 616 * i.e., i cannot simply be negative without the '-' sign! 617 * $x[i]::y absolute index i (0..size-1) 618 * $x[0]::y is the absolute 0 indexed element (bottom of the stack) 619 */ 620DYNAMIC_NEGATIVE_INDEXED_SCOPE_ATTR 621 : '$' x=ID '[' '-' expr=SCOPE_INDEX_EXPR ']' '::' y=ID 622 // {System.out.println("found \$scope[-...]::attr");} 623 { 624 ST st = template("scopeAttributeRef"); 625 st.add("scope", $x.text); 626 st.add("attr", resolveDynamicScope($x.text).getAttribute($y.text)); 627 st.add("negIndex", $expr.text); 628 } 629 ; 630 631DYNAMIC_ABSOLUTE_INDEXED_SCOPE_ATTR 632 : '$' x=ID '[' expr=SCOPE_INDEX_EXPR ']' '::' y=ID 633 // {System.out.println("found \$scope[...]::attr");} 634 { 635 ST st = template("scopeAttributeRef"); 636 st.add("scope", $x.text); 637 st.add("attr", resolveDynamicScope($x.text).getAttribute($y.text)); 638 st.add("index", $expr.text); 639 } 640 ; 641 642fragment 643SCOPE_INDEX_EXPR 644 : (~']')+ 645 ; 646 647/** $r y is a rule's dynamic scope or a global shared scope. 648 * Isolated $rulename is not allowed unless it has a dynamic scope *and* 649 * there is no reference to rulename in the enclosing alternative, 650 * which would be ambiguous. See TestAttributes.testAmbiguousRuleRef() 651 */ 652ISOLATED_DYNAMIC_SCOPE 653 : '$' ID {resolveDynamicScope($ID.text)!=null}? 654 // {System.out.println("found isolated \$scope where scope is a dynamic scope");} 655 { 656 ST st = template("isolatedDynamicScopeRef"); 657 st.add("scope", $ID.text); 658 } 659 ; 660 661// antlr.g then codegen.g does these first two currently. 662// don't want to duplicate that code. 663 664/** %foo(a={},b={},...) ctor */ 665TEMPLATE_INSTANCE 666 : '%' ID '(' ( WS? ARG (',' WS? ARG)* WS? )? ')' 667 // {System.out.println("found \%foo(args)");} 668 { 669 String action = getText().substring(1,getText().length()); 670 String ruleName = "<outside-of-rule>"; 671 if ( enclosingRule!=null ) { 672 ruleName = enclosingRule.name; 673 } 674 ST st = 675 generator.translateTemplateConstructor(ruleName, 676 outerAltNum, 677 actionToken, 678 action); 679 if ( st!=null ) { 680 chunks.add(st); 681 } 682 } 683 ; 684 685/** %({name-expr})(a={},...) indirect template ctor reference */ 686INDIRECT_TEMPLATE_INSTANCE 687 : '%' '(' ACTION ')' '(' ( WS? ARG (',' WS? ARG)* WS? )? ')' 688 // {System.out.println("found \%({...})(args)");} 689 { 690 String action = getText().substring(1,getText().length()); 691 ST st = 692 generator.translateTemplateConstructor(enclosingRule.name, 693 outerAltNum, 694 actionToken, 695 action); 696 chunks.add(st); 697 } 698 ; 699 700fragment 701ARG : ID '=' ACTION 702 ; 703 704/** %{expr}.y = z; template attribute y of ST-typed expr to z */ 705SET_EXPR_ATTRIBUTE 706 : '%' a=ACTION '.' ID WS? '=' expr=ATTR_VALUE_EXPR ';' 707 // {System.out.println("found \%{expr}.y = z;");} 708 { 709 ST st = template("actionSetAttribute"); 710 String action = $a.text; 711 action = action.substring(1,action.length()-1); // stuff inside {...} 712 st.add("st", translateAction(action)); 713 st.add("attrName", $ID.text); 714 st.add("expr", translateAction($expr.text)); 715 } 716 ; 717 718/* %x.y = z; set template attribute y of x (always set never get attr) 719 * to z [languages like python without ';' must still use the 720 * ';' which the code generator is free to remove during code gen] 721 */ 722SET_ATTRIBUTE 723 : '%' x=ID '.' y=ID WS? '=' expr=ATTR_VALUE_EXPR ';' 724 // {System.out.println("found \%x.y = z;");} 725 { 726 ST st = template("actionSetAttribute"); 727 st.add("st", $x.text); 728 st.add("attrName", $y.text); 729 st.add("expr", translateAction($expr.text)); 730 } 731 ; 732 733/** Don't allow an = as first char to prevent $x == 3; kind of stuff. */ 734fragment 735ATTR_VALUE_EXPR 736 : ~'=' (~';')* 737 ; 738 739/** %{string-expr} anonymous template from string expr */ 740TEMPLATE_EXPR 741 : '%' a=ACTION 742 // {System.out.println("found \%{expr}");} 743 { 744 ST st = template("actionStringConstructor"); 745 String action = $a.text; 746 action = action.substring(1,action.length()-1); // stuff inside {...} 747 st.add("stringExpr", translateAction(action)); 748 } 749 ; 750 751fragment 752ACTION 753 : '{' (options {greedy=false;}:.)* '}' 754 ; 755 756ESC : '\\' '$' {chunks.add("\$");} 757 | '\\' '%' {chunks.add("\%");} 758 | '\\' ~('$'|'%') {chunks.add(getText());} 759 ; 760 761ERROR_XY 762 : '$' x=ID '.' y=ID 763 { 764 chunks.add(getText()); 765 generator.issueInvalidAttributeError($x.text,$y.text, 766 enclosingRule,actionToken, 767 outerAltNum); 768 } 769 ; 770 771ERROR_X 772 : '$' x=ID 773 { 774 chunks.add(getText()); 775 generator.issueInvalidAttributeError($x.text, 776 enclosingRule,actionToken, 777 outerAltNum); 778 } 779 ; 780 781UNKNOWN_SYNTAX 782 : '$' 783 { 784 chunks.add(getText()); 785 // shouldn't need an error here. Just accept \$ if it doesn't look like anything 786 } 787 | '%' (ID|'.'|'('|')'|','|'{'|'}'|'"')* 788 { 789 chunks.add(getText()); 790 ErrorManager.grammarError(ErrorManager.MSG_INVALID_TEMPLATE_ACTION, 791 grammar, 792 actionToken, 793 getText()); 794 } 795 ; 796 797TEXT: ~('$'|'%'|'\\')+ {chunks.add(getText());} 798 ; 799 800fragment 801ID : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'_'|'0'..'9')* 802 ; 803 804fragment 805INT : '0'..'9'+ 806 ; 807 808fragment 809WS : (' '|'\t'|'\n'|'\r')+ 810 ; 811