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