1/***************************************************************************** 2 * Licensed to the Apache Software Foundation (ASF) under one * 3 * or more contributor license agreements. See the NOTICE file * 4 * distributed with this work for additional information * 5 * regarding copyright ownership. The ASF licenses this file * 6 * to you under the Apache License, Version 2.0 (the * 7 * "License"); you may not use this file except in compliance * 8 * with the License. You may obtain a copy of the License at * 9 * * 10 * http://www.apache.org/licenses/LICENSE-2.0 * 11 * * 12 * Unless required by applicable law or agreed to in writing, * 13 * software distributed under the License is distributed on an * 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 15 * KIND, either express or implied. See the License for the * 16 * specific language governing permissions and limitations * 17 * under the License. * 18 * * 19 * This file is part of the BeanShell Java Scripting distribution. * 20 * Documentation and updates may be found at http://www.beanshell.org/ * 21 * Patrick Niemeyer (pat@pat.net) * 22 * Author of Learning Java, O'Reilly & Associates * 23 * * 24 *****************************************************************************/ 25 26/* 27 Notes: 28 There is probably a lot of room for improvement in here. 29 All of the syntactic lookaheads have been commented with: 30 SYNTACTIC_LOOKAHEAD 31 These are probably expensive and we may want to start weeding them out 32 where possible. 33*/ 34 35options { 36 JAVA_UNICODE_ESCAPE=true; 37 STATIC=false; 38 MULTI=true; 39 NODE_DEFAULT_VOID=true; 40 NODE_SCOPE_HOOK=true; 41 NODE_PREFIX="BSH"; 42 /* Print grammar debugging info as we parse 43 DEBUG_PARSER=true; 44 */ 45 /* Print detailed lookahead debugging info 46 DEBUG_LOOKAHEAD=true; 47 */ 48 49 /* 50 There are weird issues related to this flag. 51 The default value, true, creates line level error detail in the parse 52 exceptions. However it gives us strange LookaheadSuccess Errors thrown 53 on some syntax errors. 54 http://groups-beta.google.com/group/comp.compilers.tools.javacc/browse_thread/thread/14d3471883f8794f/ecf7b1d81151839c?q=Parser$LookaheadSuccess&rnum=1&hl=en#ecf7b1d81151839c 55 I have manually hacked the Parser.java to catch LookaheadSuccess in 56 the jj_rescan_token() method... The bug report indicates that this 57 is fixed, apparently in some post javacc 3.2 version. 58 */ 59 //ERROR_REPORTING=false; 60 61 // This breaks something for interactive use on the command line, 62 // but may be useful in non-interactive use. 63 //CACHE_TOKENS=true; 64} 65 66PARSER_BEGIN(Parser) 67package bsh; 68 69import java.io.*; 70import java.util.Vector; 71 72/** 73 This is the BeanShell parser. It is used internally by the Interpreter 74 class (which is probably what you are looking for). The parser knows 75 only how to parse the structure of the language, it does not understand 76 names, commands, etc. 77 <p> 78 You can use the Parser from the command line to do basic structural 79 validation of BeanShell files without actually executing them. e.g. 80 <code><pre> 81 java bsh.Parser [ -p ] file [ file ] [ ... ] 82 </pre></code> 83 <p> 84 The -p option causes the abstract syntax to be printed. 85 <p> 86 87 From code you'd use the Parser like this: 88 <p 89 <code><pre> 90 Parser parser = new Parser(in); 91 while( !(eof=parser.Line()) ) { 92 SimpleNode node = parser.popNode(); 93 // use the node, etc. (See bsh.BSH* classes) 94 } 95 </pre></code> 96*/ 97public class Parser 98{ 99 boolean retainComments = false; 100 101 public void setRetainComments( boolean b ) { 102 retainComments = b; 103 } 104 105 void jjtreeOpenNodeScope(Node n) { 106 ((SimpleNode)n).firstToken = getToken(1); 107 } 108 109 void jjtreeCloseNodeScope(Node n) { 110 ((SimpleNode)n).lastToken = getToken(0); 111 } 112 113 /** 114 Re-initialize the input stream and token source. 115 */ 116 void reInitInput( Reader in ) { 117 ReInit(in); 118 } 119 120 public SimpleNode popNode() 121 { 122 if ( jjtree.nodeArity() > 0) // number of child nodes 123 return (SimpleNode)jjtree.popNode(); 124 else 125 return null; 126 } 127 128 /** 129 Explicitly re-initialize just the token reader. 130 This seems to be necessary to avoid certain looping errors when 131 reading bogus input. See Interpreter. 132 */ 133 void reInitTokenInput( Reader in ) { 134 jj_input_stream.ReInit( in, 135 jj_input_stream.getEndLine(), 136 jj_input_stream.getEndColumn() ); 137 } 138 139 public static void main( String [] args ) 140 throws IOException, ParseException 141 { 142 boolean print = false; 143 int i=0; 144 if ( args[0].equals("-p") ) { 145 i++; 146 print=true; 147 } 148 for(; i< args.length; i++) { 149 Reader in = new FileReader(args[i]); 150 Parser parser = new Parser(in); 151 parser.setRetainComments(true); 152 while( !parser.Line()/*eof*/ ) 153 if ( print ) 154 System.out.println( parser.popNode() ); 155 } 156 } 157 158 /** 159 Lookahead for the enhanced for statement. 160 Expect "for" "(" and then see whether we hit ":" or a ";" first. 161 */ 162 boolean isRegularForStatement() 163 { 164 int curTok = 1; 165 Token tok; 166 tok = getToken(curTok++); 167 if ( tok.kind != FOR ) return false; 168 tok = getToken(curTok++); 169 if ( tok.kind != LPAREN ) return false; 170 while (true) 171 { 172 tok = getToken(curTok++); 173 switch (tok.kind) { 174 case COLON: 175 return false; 176 case SEMICOLON: 177 return true; 178 case EOF: 179 return false; 180 } 181 } 182 } 183 184 /** 185 Generate a ParseException with the specified message, pointing to the 186 current token. 187 The auto-generated Parser.generateParseException() method does not 188 provide line number info, therefore we do this. 189 */ 190 ParseException createParseException( String message, Exception e ) 191 { 192 Token errortok = token; 193 int line = errortok.beginLine, column = errortok.beginColumn; 194 String mess = (errortok.kind == 0) ? tokenImage[0] : errortok.image; 195 return new ParseException( "Parse error at line " + line 196 + ", column " + column + " : " + message, e ); 197 } 198 199 int parseInt(String s) throws NumberFormatException { 200 int radix; 201 int i; 202 if( s.startsWith("0x") || s.startsWith("0X") ) { 203 radix = 16; 204 i = 2; 205 } else if( s.startsWith("0") && s.length() > 1 ) { 206 radix = 8; 207 i = 1; 208 } else { 209 radix = 10; 210 i = 0; 211 } 212 int result = 0; 213 int len = s.length(); 214 for( ; i<len; i++ ) { 215 if( result < 0 ) 216 throw new NumberFormatException("Number too big for integer type: "+s); 217 result *= radix; 218 int digit = Character.digit(s.charAt(i),radix); 219 if( digit < 0 ) 220 throw new NumberFormatException("Invalid integer type: "+s); 221 result += digit; 222 } 223 return result; 224 } 225 226 long parseLong(String s) throws NumberFormatException { 227 int radix; 228 int i; 229 if( s.startsWith("0x") || s.startsWith("0X") ) { 230 radix = 16; 231 i = 2; 232 } else if( s.startsWith("0") && s.length() > 1 ) { 233 radix = 8; 234 i = 1; 235 } else { 236 radix = 10; 237 i = 0; 238 } 239 long result = 0; 240 int len = s.length(); 241 for( ; i<len; i++ ) { 242 if( result < 0L ) 243 throw new NumberFormatException("Number too big for long type: "+s); 244 result *= radix; 245 int digit = Character.digit(s.charAt(i),radix); 246 if( digit < 0 ) 247 throw new NumberFormatException("Invalid long type: "+s); 248 result += digit; 249 } 250 return result; 251 } 252} 253 254PARSER_END(Parser) 255 256SKIP : /* WHITE SPACE */ 257{ 258 " " | "\t" | "\r" | "\f" 259 | "\n" 260 | < NONPRINTABLE: (["\u0000"-"\u0020", "\u0080"-"\u00ff"])+ > 261} 262 263SPECIAL_TOKEN : /* COMMENTS */ 264{ 265/* 266 SINGLE_LINE_COMMENT includes a hack to accept SLC at the end of a file 267 with no terminanting linefeed. This is actually illegal according to 268 spec, but comes up often enough to warrant it... (especially in eval()). 269*/ 270 <SINGLE_LINE_COMMENT: "//" (~["\n","\r"])* ("\n"|"\r"|"\r\n")? > 271 272| <HASH_BANG_COMMENT: "#!" (~["\n","\r"])* ("\n"|"\r"|"\r\n")> 273 274 /* Moved FORMAL_COMMENT to a real token. Modified MULTI_LINE_COMMENT to not 275 catch formal comments (require no star after star) */ 276| <MULTI_LINE_COMMENT: 277 "/*" (~["*"])+ "*" ("*" | (~["*","/"] (~["*"])* "*"))* "/"> 278} 279 280TOKEN : /* RESERVED WORDS AND LITERALS */ 281{ 282< ABSTRACT : "abstract" > 283| < BOOLEAN: "boolean" > 284| < BREAK: "break" > 285| < CLASS: "class" > 286| < BYTE: "byte" > 287| < CASE: "case" > 288| < CATCH: "catch" > 289| < CHAR: "char" > 290| < CONST: "const" > 291| < CONTINUE: "continue" > 292| < _DEFAULT: "default" > 293| < DO: "do" > 294| < DOUBLE: "double" > 295| < ELSE: "else" > 296| < ENUM: "enum" > 297| < EXTENDS: "extends" > 298| < FALSE: "false" > 299| < FINAL: "final" > 300| < FINALLY: "finally" > 301| < FLOAT: "float" > 302| < FOR: "for" > 303| < GOTO: "goto" > 304| < IF: "if" > 305| < IMPLEMENTS: "implements" > 306| < IMPORT: "import" > 307| < INSTANCEOF: "instanceof" > 308| < INT: "int" > 309| < INTERFACE: "interface" > 310| < LONG: "long" > 311| < NATIVE: "native" > 312| < NEW: "new" > 313| < NULL: "null" > 314| < PACKAGE: "package" > 315| < PRIVATE: "private" > 316| < PROTECTED: "protected" > 317| < PUBLIC: "public" > 318| < RETURN: "return" > 319| < SHORT: "short" > 320| < STATIC: "static" > 321| < STRICTFP : "strictfp" > 322| < SWITCH: "switch" > 323| < SYNCHRONIZED: "synchronized" > 324| < TRANSIENT: "transient" > 325| < THROW: "throw" > 326| < THROWS: "throws" > 327| < TRUE: "true" > 328| < TRY: "try" > 329| < VOID: "void" > 330| < VOLATILE: "volatile" > 331| < WHILE: "while" > 332} 333 334TOKEN : /* LITERALS */ 335{ 336 < INTEGER_LITERAL: 337 <DECIMAL_LITERAL> (["l","L"])? 338 | <HEX_LITERAL> (["l","L"])? 339 | <OCTAL_LITERAL> (["l","L"])? 340 > 341| 342 < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* > 343| 344 < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ > 345| 346 < #OCTAL_LITERAL: "0" (["0"-"7"])* > 347| 348 < FLOATING_POINT_LITERAL: 349 (["0"-"9"])+ "." (["0"-"9"])* (<EXPONENT>)? (["f","F","d","D"])? 350 | "." (["0"-"9"])+ (<EXPONENT>)? (["f","F","d","D"])? 351 | (["0"-"9"])+ <EXPONENT> (["f","F","d","D"])? 352 | (["0"-"9"])+ (<EXPONENT>)? ["f","F","d","D"] 353 > 354| 355 < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > 356| 357 < CHARACTER_LITERAL: 358 "'" 359 ( (~["'","\\","\n","\r"]) 360 | ("\\" 361 ( ["n","t","b","r","f","\\","'","\""] 362 | ["0"-"7"] ( ["0"-"7"] )? 363 | ["0"-"3"] ["0"-"7"] ["0"-"7"] 364 ) 365 ) 366 ) 367 "'" 368 > 369| 370 < STRING_LITERAL: 371 "\"" 372 ( (~["\"","\\","\n","\r"]) 373 | ("\\" 374 ( ["n","t","b","r","f","\\","'","\""] 375 | ["0"-"7"] ( ["0"-"7"] )? 376 | ["0"-"3"] ["0"-"7"] ["0"-"7"] 377 ) 378 ) 379 )* 380 "\"" 381 > 382| 383 < LONG_STRING_LITERAL: 384 "\"\"\"" 385 ( ~["\""] ("\"")? ("\"")? )* 386 "\"\"\"" 387 > 388| 389 < FORMAL_COMMENT: 390 "/**" (~["*"])* "*" ("*" | (~["*","/"] (~["*"])* "*"))* "/" 391 > 392} 393 394TOKEN : /* IDENTIFIERS */ 395{ 396 < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* > 397| 398 < #LETTER: 399 [ 400 "\u0024", 401 "\u0041"-"\u005a", 402 "\u005f", 403 "\u0061"-"\u007a", 404 "\u00c0"-"\u00d6", 405 "\u00d8"-"\u00f6", 406 "\u00f8"-"\u00ff", 407 "\u0100"-"\u1fff", 408 "\u3040"-"\u318f", 409 "\u3300"-"\u337f", 410 "\u3400"-"\u3d2d", 411 "\u4e00"-"\u9fff", 412 "\uf900"-"\ufaff" 413 ] 414 > 415| 416 < #DIGIT: 417 [ 418 "\u0030"-"\u0039", 419 "\u0660"-"\u0669", 420 "\u06f0"-"\u06f9", 421 "\u0966"-"\u096f", 422 "\u09e6"-"\u09ef", 423 "\u0a66"-"\u0a6f", 424 "\u0ae6"-"\u0aef", 425 "\u0b66"-"\u0b6f", 426 "\u0be7"-"\u0bef", 427 "\u0c66"-"\u0c6f", 428 "\u0ce6"-"\u0cef", 429 "\u0d66"-"\u0d6f", 430 "\u0e50"-"\u0e59", 431 "\u0ed0"-"\u0ed9", 432 "\u1040"-"\u1049" 433 ] 434 > 435} 436 437TOKEN : /* SEPARATORS */ 438{ 439 < LPAREN: "(" > 440| < RPAREN: ")" > 441| < LBRACE: "{" > 442| < RBRACE: "}" > 443| < LBRACKET: "[" > 444| < RBRACKET: "]" > 445| < SEMICOLON: ";" > 446| < COMMA: "," > 447| < DOT: "." > 448} 449 450TOKEN : /* OPERATORS */ 451{ 452 < ASSIGN: "=" > 453| < GT: ">" > 454| < GTX: "@gt" > 455| < LT: "<" > 456| < LTX: "@lt" > 457| < BANG: "!" > 458| < TILDE: "~" > 459| < HOOK: "?" > 460| < COLON: ":" > 461| < EQ: "==" > 462| < LE: "<=" > 463| < LEX: "@lteq" > 464| < GE: ">=" > 465| < GEX: "@gteq" > 466| < NE: "!=" > 467| < BOOL_OR: "||" > 468| < BOOL_ORX: "@or" > 469| < BOOL_AND: "&&" > 470| < BOOL_ANDX: "@and" > 471| < INCR: "++" > 472| < DECR: "--" > 473| < PLUS: "+" > 474| < MINUS: "-" > 475| < STAR: "*" > 476| < SLASH: "/" > 477| < BIT_AND: "&" > 478| < BIT_ANDX: "@bitwise_and" > 479| < BIT_OR: "|" > 480| < BIT_ORX: "@bitwise_or" > 481| < XOR: "^" > 482| < MOD: "%" > 483| < LSHIFT: "<<" > 484| < LSHIFTX: "@left_shift" > 485| < RSIGNEDSHIFT: ">>" > 486| < RSIGNEDSHIFTX: "@right_shift" > 487| < RUNSIGNEDSHIFT: ">>>" > 488| < RUNSIGNEDSHIFTX: "@right_unsigned_shift" > 489| < PLUSASSIGN: "+=" > 490| < MINUSASSIGN: "-=" > 491| < STARASSIGN: "*=" > 492| < SLASHASSIGN: "/=" > 493| < ANDASSIGN: "&=" > 494| < ANDASSIGNX: "@and_assign" > 495| < ORASSIGN: "|=" > 496| < ORASSIGNX: "@or_assign" > 497| < XORASSIGN: "^=" > 498| < MODASSIGN: "%=" > 499| < LSHIFTASSIGN: "<<=" > 500| < LSHIFTASSIGNX: "@left_shift_assign" > 501| < RSIGNEDSHIFTASSIGN: ">>=" > 502| < RSIGNEDSHIFTASSIGNX: "@right_shift_assign" > 503| < RUNSIGNEDSHIFTASSIGN: ">>>=" > 504| < RUNSIGNEDSHIFTASSIGNX: "@right_unsigned_shift_assign" > 505} 506 507 508/* 509 Thanks to Sreenivasa Viswanadha for suggesting how to get rid of expensive 510 lookahead here. 511*/ 512boolean Line() : 513{} 514{ 515 <EOF> { 516 Interpreter.debug("End of File!"); 517 return true; 518 } 519| 520 BlockStatement() { 521 return false; 522 } 523} 524 525/***************************************** 526 * THE JAVA LANGUAGE GRAMMAR STARTS HERE * 527 *****************************************/ 528 529/* 530 Gather modifiers for a class, method, or field. 531 I lookahead is true then we are being called as part of a lookahead and we 532 should not enforce any rules. Otherwise we validate based on context 533 (field, method, class) 534*/ 535Modifiers Modifiers( int context, boolean lookahead ) : 536{ 537 Modifiers mods = null; 538} 539{ 540 ( 541 ( 542 "private" | "protected" | "public" | "synchronized" | "final" 543 | "native" | "transient" | "volatile" | "abstract" | "static" 544 | "strictfp" 545 ) { 546 if ( !lookahead ) 547 try { 548 if ( mods == null ) mods = new Modifiers(); 549 mods.addModifier( context, getToken(0).image ); 550 } catch ( IllegalStateException e ) { 551 throw createParseException( e.getMessage(), e ); 552 } 553 } 554 )* { 555 return mods; 556 } 557} 558 559 560/** 561*/ 562void ClassDeclaration() #ClassDeclaration : 563{ 564 Modifiers mods; 565 Token name; 566 int numInterfaces; 567} 568{ 569 mods = Modifiers( Modifiers.CLASS, false ) 570 ( "class" | "interface" { jjtThis.isInterface=true; } ) 571 name=<IDENTIFIER> 572 [ "extends" AmbiguousName() { jjtThis.extend = true; } ] 573 [ "implements" numInterfaces=NameList() 574 { jjtThis.numInterfaces=numInterfaces; } ] 575 Block() 576 { 577 jjtThis.modifiers = mods; 578 jjtThis.name = name.image; 579 } 580} 581 582void MethodDeclaration() #MethodDeclaration : 583{ 584 Token t = null; 585 Modifiers mods; 586 int count; 587} 588{ 589 mods = Modifiers( Modifiers.METHOD, false ) { jjtThis.modifiers = mods; } 590( 591 LOOKAHEAD( <IDENTIFIER> "(" ) 592 t = <IDENTIFIER> { jjtThis.name = t.image; } 593| 594 ReturnType() 595 t = <IDENTIFIER> { jjtThis.name = t.image; } 596) 597 FormalParameters() 598 [ "throws" count=NameList() { jjtThis.numThrows=count; } ] 599 ( Block() | ";" ) 600} 601 602void PackageDeclaration () #PackageDeclaration: 603{ } 604{ 605 "package" AmbiguousName() 606} 607 608void ImportDeclaration() #ImportDeclaration : 609{ 610 Token s = null; 611 Token t = null; 612} 613{ 614 LOOKAHEAD( 3 ) 615 [ s = "static" ] "import" AmbiguousName() [ t = "." "*" ] ";" 616 { 617 if ( s != null ) jjtThis.staticImport = true; 618 if ( t != null ) jjtThis.importPackage = true; 619 } 620 | 621 // bsh super import statement 622 "import" "*" ";" { 623 jjtThis.superImport = true; 624 } 625} 626 627void VariableDeclarator() #VariableDeclarator : 628{ 629 Token t; 630} 631{ 632 t=<IDENTIFIER> [ "=" VariableInitializer() ] 633 { 634 jjtThis.name = t.image; 635 } 636} 637 638/* 639this originally handled postfix array dimensions... 640 641void VariableDeclaratorId() #VariableDeclaratorId : 642{ Token t; } 643{ 644 t=<IDENTIFIER> { jjtThis.name = t.image; } 645 ( "[" "]" { jjtThis.addUndefinedDimension(); } )* 646} 647*/ 648 649void VariableInitializer() : 650{} 651{ 652 ArrayInitializer() 653| 654 Expression() 655} 656 657void ArrayInitializer() #ArrayInitializer : 658{} 659{ 660 "{" [ VariableInitializer() 661 ( LOOKAHEAD(2) "," VariableInitializer() )* ] [ "," ] "}" 662} 663 664void FormalParameters() #FormalParameters : 665{} 666{ 667 "(" [ FormalParameter() ( "," FormalParameter() )* ] ")" 668} 669 670void FormalParameter() #FormalParameter : 671{ Token t; } 672{ 673 // added [] to Type for bsh. Removed [ final ] - is that legal? 674 LOOKAHEAD(2) ["final"] Type() [ TypeArguments() ] t=<IDENTIFIER> { jjtThis.name = t.image; } 675| 676 t=<IDENTIFIER> { jjtThis.name = t.image; } 677} 678 679 680/* 681 Type, name and expression syntax follows. 682*/ 683void Type() #Type : 684{ } 685{ 686 /* 687 The embedded lookahead is (was?) necessary to disambiguate for 688 PrimaryPrefix. ( )* is a choice point. It took me a while to 689 figure out where to put that. This stuff is annoying. 690 */ 691 ( PrimitiveType() | AmbiguousName() ) 692 ( LOOKAHEAD(2) "[" "]" { jjtThis.addArrayDimension(); } )* 693} 694 695/* 696 Originally called ResultType in the grammar 697*/ 698void ReturnType() #ReturnType : 699{ } 700{ 701 "void" { jjtThis.isVoid = true; } 702| 703 Type() [ TypeArguments() ] 704} 705 706void PrimitiveType() #PrimitiveType : 707{ } { 708 "boolean" { jjtThis.type = Boolean.TYPE; } 709| "char" { jjtThis.type = Character.TYPE; } 710| "byte" { jjtThis.type = Byte.TYPE; } 711| "short" { jjtThis.type = Short.TYPE; } 712| "int" { jjtThis.type = Integer.TYPE; } 713| "long" { jjtThis.type = Long.TYPE; } 714| "float" { jjtThis.type = Float.TYPE; } 715| "double" { jjtThis.type = Double.TYPE; } 716} 717 718void AmbiguousName() #AmbiguousName : 719/* 720 A lookahead of 2 is required below since "Name" can be followed by a ".*" 721 when used in the context of an "ImportDeclaration". 722*/ 723{ 724 Token t; 725 StringBuilder s; 726} 727{ 728 t = <IDENTIFIER> { 729 s = new StringBuilder(t.image); 730 } 731 ( LOOKAHEAD(2) "." t = <IDENTIFIER> { s.append("."+t.image); } )* { 732 jjtThis.text = s.toString(); 733 } 734} 735 736int NameList() : 737{ int count = 0; } 738{ 739 AmbiguousName() { ++count; } ( "," AmbiguousName() { ++count; } )* 740 { return count; } 741} 742 743/* 744 * Expression syntax follows. 745 */ 746void Expression() : 747{ } 748{ 749 /** 750 SYNTACTIC_LOOKAHEAD 751 Note: the original grammar was cheating here and we've fixed that, 752 but at the expense of another syntactic lookahead. 753 */ 754 LOOKAHEAD( PrimaryExpression() AssignmentOperator() ) 755 Assignment() 756| 757 ConditionalExpression() 758} 759 760void Assignment() #Assignment : 761{ int op ; } 762{ 763 PrimaryExpression() 764 op = AssignmentOperator() { jjtThis.operator = op; } 765 // Add this for blocks, e.g. foo = { }; 766 //( Expression() | Block() ) 767 Expression() 768} 769 770int AssignmentOperator() : 771{ Token t; } 772{ 773 ( "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "&=" | "^=" | "|=" | 774 "<<=" | "@left_shift_assign" | ">>=" | "@right_shift_assign" | 775 ">>>=" | "@right_unsigned_shift_assign" ) 776 { 777 t = getToken(0); 778 return t.kind; 779 } 780} 781 782void ConditionalExpression() : 783{ } 784{ 785 ConditionalOrExpression() [ "?" Expression() ":" ConditionalExpression() 786 #TernaryExpression(3) ] 787} 788 789void ConditionalOrExpression() : 790{ Token t=null; } 791{ 792 ConditionalAndExpression() 793 ( ( t = "||" | t = "@or" ) 794 ConditionalAndExpression() 795 { jjtThis.kind = t.kind; } #BinaryExpression(2) )* 796} 797 798void ConditionalAndExpression() : 799{ Token t=null; } 800{ 801 InclusiveOrExpression() 802 ( ( t = "&&" | t = "@and" ) 803 InclusiveOrExpression() 804 { jjtThis.kind = t.kind; } #BinaryExpression(2) )* 805} 806 807void InclusiveOrExpression() : 808{ Token t=null; } 809{ 810 ExclusiveOrExpression() 811 ( ( t = "|" | t = "@bitwise_or" ) 812 ExclusiveOrExpression() 813 { jjtThis.kind = t.kind; } #BinaryExpression(2) )* 814} 815 816void ExclusiveOrExpression() : 817{ Token t=null; } 818{ 819 AndExpression() ( t="^" AndExpression() 820 { jjtThis.kind = t.kind; } #BinaryExpression(2) )* 821} 822 823void AndExpression() : 824{ Token t=null; } 825{ 826 EqualityExpression() 827 ( ( t = "&" | t = "@bitwise_and" ) 828 EqualityExpression() 829 { jjtThis.kind = t.kind; } #BinaryExpression(2) )* 830} 831 832void EqualityExpression() : 833{ Token t = null; } 834{ 835 InstanceOfExpression() ( ( t= "==" | t= "!=" ) InstanceOfExpression() 836 { jjtThis.kind = t.kind; } #BinaryExpression(2) 837 )* 838} 839 840void InstanceOfExpression() : 841{ Token t = null; } 842{ 843 RelationalExpression() 844 [ t = "instanceof" Type() { jjtThis.kind = t.kind; } #BinaryExpression(2) ] 845} 846 847void RelationalExpression() : 848{ Token t = null; } 849{ 850 ShiftExpression() 851 ( ( t = "<" | t = "@lt" | t = ">" | t = "@gt" | 852 t = "<=" | t = "@lteq" | t = ">=" | t = "@gteq" ) 853 ShiftExpression() 854 { jjtThis.kind = t.kind; } #BinaryExpression(2) )* 855} 856 857void ShiftExpression() : 858{ Token t = null; } 859{ 860 AdditiveExpression() 861 ( ( t = "<<" | t = "@left_shift" | t = ">>" | t = "@right_shift" | 862 t = ">>>" | t = "@right_unsigned_shift" ) 863 AdditiveExpression() 864 { jjtThis.kind = t.kind; } #BinaryExpression(2) )* 865} 866 867void AdditiveExpression() : 868{ Token t = null; } 869{ 870 MultiplicativeExpression() 871 ( ( t= "+" | t= "-" ) MultiplicativeExpression() { jjtThis.kind = t.kind; } 872 #BinaryExpression(2) 873 )* 874} 875 876void MultiplicativeExpression() : 877{ Token t = null; } 878{ 879 UnaryExpression() ( ( t= "*" | t= "/" | t= "%" ) 880 UnaryExpression() { jjtThis.kind = t.kind; } #BinaryExpression(2) )* 881} 882 883void UnaryExpression() : 884{ Token t = null; } 885{ 886 ( t="+" | t="-" ) UnaryExpression() 887 { jjtThis.kind = t.kind; } #UnaryExpression(1) 888| 889 PreIncrementExpression() 890| 891 PreDecrementExpression() 892| 893 UnaryExpressionNotPlusMinus() 894} 895 896void PreIncrementExpression() : 897{ Token t = null; } 898{ 899 t="++" PrimaryExpression() 900 { jjtThis.kind = t.kind; } #UnaryExpression(1) 901} 902 903void PreDecrementExpression() : 904{ Token t = null; } 905{ 906 t="--" PrimaryExpression() 907 { jjtThis.kind = t.kind; } #UnaryExpression(1) 908} 909 910void UnaryExpressionNotPlusMinus() : 911{ Token t = null; } 912{ 913 ( t="~" | t="!" ) UnaryExpression() 914 { jjtThis.kind = t.kind; } #UnaryExpression(1) 915| 916 // SYNTACTIC_LOOKAHEAD 917 LOOKAHEAD( CastLookahead() ) CastExpression() 918| 919 PostfixExpression() 920} 921 922// This production is to determine lookahead only. 923void CastLookahead() : { } 924{ 925 LOOKAHEAD(2) "(" PrimitiveType() 926| 927// SYNTACTIC_LOOKAHEAD 928 LOOKAHEAD( "(" AmbiguousName() "[" ) "(" AmbiguousName() "[" "]" 929| 930 "(" AmbiguousName() ")" ( "~" | "!" | "(" | <IDENTIFIER> | /* "this" | "super" | */ "new" | Literal() ) 931} 932 933void PostfixExpression() : 934{ Token t = null; } 935{ 936// SYNTACTIC_LOOKAHEAD 937 LOOKAHEAD( PrimaryExpression() ("++"|"--") ) 938 PrimaryExpression() 939 ( t="++" | t="--" ) { 940 jjtThis.kind = t.kind; jjtThis.postfix = true; } #UnaryExpression(1) 941| 942 PrimaryExpression() 943} 944 945void CastExpression() #CastExpression : 946{ } 947{ 948// SYNTACTIC_LOOKAHEAD 949 LOOKAHEAD( "(" PrimitiveType() ) "(" Type() ")" UnaryExpression() 950| 951 "(" Type() ")" UnaryExpressionNotPlusMinus() 952} 953 954void PrimaryExpression() #PrimaryExpression : { } 955{ 956 PrimaryPrefix() ( PrimarySuffix() )* 957} 958 959void MethodInvocation() #MethodInvocation : { } 960{ 961 AmbiguousName() Arguments() 962} 963 964void PrimaryPrefix() : { } 965{ 966 Literal() 967| 968 "(" Expression() ")" 969| 970 AllocationExpression() 971| 972 // SYNTACTIC_LOOKAHEAD 973 LOOKAHEAD( MethodInvocation() ) 974 MethodInvocation() 975| 976 LOOKAHEAD( Type() "." "class" ) 977 Type() 978| 979 AmbiguousName() 980 981/* 982| 983 LOOKAHEAD( "void" "." "class" ) 984*/ 985} 986 987void PrimarySuffix() #PrimarySuffix : 988{ 989 Token t = null; 990} 991{ 992 LOOKAHEAD(2) 993 "." "class" { 994 jjtThis.operation = BSHPrimarySuffix.CLASS; 995 } 996| 997 "[" Expression() "]" { 998 jjtThis.operation = BSHPrimarySuffix.INDEX; 999 } 1000| 1001 // Field access or method invocation 1002 "." t = <IDENTIFIER> [ Arguments() ] { 1003 jjtThis.operation = BSHPrimarySuffix.NAME; 1004 jjtThis.field = t.image; 1005 } 1006| 1007 "{" Expression() "}" { 1008 jjtThis.operation = BSHPrimarySuffix.PROPERTY; 1009 } 1010/* 1011 For inner classes 1012| 1013 LOOKAHEAD(2) 1014 "." AllocationExpression() 1015*/ 1016} 1017 1018void Literal() #Literal : 1019{ 1020 Token x; 1021 boolean b; 1022 String literal; 1023 char ch; 1024} 1025{ 1026 x = <INTEGER_LITERAL> 1027 { 1028 literal = x.image; 1029 ch = literal.charAt(literal.length()-1); 1030 if(ch == 'l' || ch == 'L') 1031 { 1032 literal = literal.substring(0,literal.length()-1); 1033 try { 1034 jjtThis.value = new Primitive( parseLong(literal) ); 1035 } catch ( NumberFormatException e ) { 1036 throw createParseException( e.getMessage(), e ); 1037 } 1038 } 1039 else 1040 try { 1041 jjtThis.value = new Primitive( parseInt(literal) ); 1042 } catch ( NumberFormatException e ) { 1043 throw createParseException( e.getMessage(), e ); 1044 } 1045 } 1046| 1047 x = <FLOATING_POINT_LITERAL> 1048 { 1049 literal = x.image; 1050 ch = literal.charAt(literal.length()-1); 1051 if(ch == 'f' || ch == 'F') 1052 { 1053 literal = literal.substring(0,literal.length()-1); 1054 jjtThis.value = new Primitive( new Float( literal ).floatValue() ); 1055 } 1056 else 1057 { 1058 if(ch == 'd' || ch == 'D') 1059 literal = literal.substring(0,literal.length()-1); 1060 1061 jjtThis.value = new Primitive( new Double( literal ).doubleValue() ); 1062 } 1063 } 1064| 1065 x = <CHARACTER_LITERAL> { 1066 try { 1067 jjtThis.charSetup( x.image.substring(1, x.image.length() - 1) ); 1068 } catch ( Exception e ) { 1069 throw createParseException("Error parsing character: "+x.image, e); 1070 } 1071 } 1072| 1073 x = <STRING_LITERAL> { 1074 try { 1075 jjtThis.stringSetup( x.image.substring(1, x.image.length() - 1) ); 1076 } catch ( Exception e ) { 1077 throw createParseException("Error parsing string: "+x.image, e); 1078 } 1079 } 1080| 1081 x = <LONG_STRING_LITERAL> { 1082 try { 1083 jjtThis.value = x.image.substring(3, x.image.length() - 3); 1084 } catch ( Exception e ) { 1085 throw createParseException("Error parsing long string: "+x.image, e); 1086 } 1087 } 1088| 1089 b = BooleanLiteral() { 1090 jjtThis.value = b ? Primitive.TRUE : Primitive.FALSE; } 1091| 1092 NullLiteral() { 1093 jjtThis.value = Primitive.NULL; 1094} 1095| 1096 VoidLiteral() { 1097 jjtThis.value = Primitive.VOID; } 1098} 1099 1100boolean BooleanLiteral() : 1101{} 1102{ 1103 "true" { return true; } 1104| 1105 "false" { return false; } 1106} 1107 1108void NullLiteral() : 1109{} 1110{ 1111 "null" 1112} 1113 1114void VoidLiteral() : 1115{} 1116{ 1117 "void" 1118} 1119 1120void Arguments() #Arguments : 1121{ } 1122{ 1123 "(" [ ArgumentList() ] ")" 1124} 1125 1126// leave these on the stack for Arguments() to handle 1127void ArgumentList() : 1128{ } 1129{ 1130 Expression() 1131 ( "," Expression() )* 1132} 1133 1134void TypeArguments() : 1135{ } 1136{ 1137 "<" <IDENTIFIER> ">" 1138} 1139 1140void AllocationExpression() #AllocationExpression : 1141{ } 1142{ 1143 LOOKAHEAD(2) 1144 "new" PrimitiveType() ArrayDimensions() 1145| 1146 "new" AmbiguousName() [ TypeArguments() ] 1147 ( 1148 ArrayDimensions() 1149 | 1150 // SYNTACTIC_LOOKAHEAD 1151 Arguments() [ LOOKAHEAD(2) Block() ] 1152 ) 1153} 1154 1155void ArrayDimensions() #ArrayDimensions : 1156{} 1157{ 1158 // e.g. int [4][3][][]; 1159 LOOKAHEAD(2) 1160 ( LOOKAHEAD(2) "[" Expression() "]" { jjtThis.addDefinedDimension(); } )+ 1161 ( LOOKAHEAD(2) "[" "]" { jjtThis.addUndefinedDimension(); } )* 1162| 1163 // e.g. int [][] { {1,2}, {3,4} }; 1164 ( "[" "]" { jjtThis.addUndefinedDimension(); } )+ ArrayInitializer() 1165} 1166 1167 1168/* 1169 * Statement syntax follows. 1170 */ 1171 1172void Statement() : { } 1173{ 1174 LOOKAHEAD(2) 1175 LabeledStatement() 1176| 1177 Block() 1178| 1179 EmptyStatement() 1180| 1181 StatementExpression() ";" 1182| 1183 SwitchStatement() 1184| 1185 IfStatement() 1186| 1187 WhileStatement() 1188| 1189 DoStatement() 1190| 1191 LOOKAHEAD ( { isRegularForStatement() } ) 1192 ForStatement() 1193| 1194 EnhancedForStatement() 1195| 1196 BreakStatement() 1197| 1198 ContinueStatement() 1199| 1200 ReturnStatement() 1201| 1202 SynchronizedStatement() 1203| 1204 ThrowStatement() 1205| 1206 TryStatement() 1207} 1208 1209void LabeledStatement() : 1210{} 1211{ 1212 <IDENTIFIER> ":" Statement() 1213} 1214 1215void Block() #Block : 1216{} 1217{ 1218 "{" ( BlockStatement() )* "}" 1219} 1220 1221void BlockStatement() : 1222{ 1223} 1224{ 1225 LOOKAHEAD( Modifiers( Modifiers.FIELD, true ) ( "class" | "interface" ) ) 1226 ClassDeclaration() 1227| 1228 LOOKAHEAD ( Modifiers( Modifiers.METHOD, true ) 1229 ReturnType() <IDENTIFIER> "(" 1230 ) 1231 MethodDeclaration() 1232| 1233 LOOKAHEAD ( Modifiers( Modifiers.METHOD, true ) 1234 <IDENTIFIER> FormalParameters() [ "throws" NameList() ] "{" 1235 ) 1236 MethodDeclaration() 1237| 1238 // SYNTACTIC_LOOKAHEAD 1239 LOOKAHEAD( Modifiers( Modifiers.FIELD, true ) Type() [ TypeArguments() ] <IDENTIFIER> ) 1240 TypedVariableDeclaration() ";" 1241| 1242 Statement() 1243 1244| 1245 // Allow BeanShell imports in any block 1246 ImportDeclaration() 1247| 1248 // Allow BeanShell package declarations in any block 1249 PackageDeclaration() 1250| 1251 FormalComment() 1252} 1253 1254void FormalComment() #FormalComment( retainComments ) : 1255{ 1256 Token t; 1257} 1258{ 1259 t=<FORMAL_COMMENT> { 1260 jjtThis.text=t.image; 1261 } 1262} 1263 1264void EmptyStatement() : 1265{} 1266{ 1267 ";" 1268} 1269 1270void StatementExpression() : 1271{ } 1272{ 1273 /* 1274 This is looser than normal Java to simplify the grammar. This allows 1275 us to type arbitrary expressions on the command line, e.g. "1+1;" 1276 We should turn this off in the implementation in strict java mode. 1277 */ 1278 Expression() 1279/* 1280 // This was the original Java grammar. 1281 1282 // Original comment: 1283 // The last expansion of this production accepts more than the legal 1284 // Java expansions for StatementExpression. 1285 PreIncrementExpression() 1286 | 1287 PreDecrementExpression() 1288 | 1289 // SYNTACTIC_LOOKAHEAD 1290 LOOKAHEAD( PrimaryExpression() AssignmentOperator() ) 1291 Assignment() { } 1292 | 1293 PostfixExpression() 1294*/ 1295 1296} 1297 1298void SwitchStatement() #SwitchStatement : 1299{} 1300{ 1301 "switch" "(" Expression() ")" "{" 1302 ( SwitchLabel() ( BlockStatement() )* )* 1303 "}" 1304} 1305 1306void SwitchLabel() #SwitchLabel : 1307{} 1308{ 1309 "case" Expression() ":" 1310| 1311 "default" ":" { jjtThis.isDefault = true; } 1312} 1313 1314void IfStatement() #IfStatement : 1315/* 1316 * The disambiguating algorithm of JavaCC automatically binds dangling 1317 * else's to the innermost if statement. The LOOKAHEAD specification 1318 * is to tell JavaCC that we know what we are doing. 1319 */ 1320{} 1321{ 1322 "if" "(" Expression() ")" Statement() [ LOOKAHEAD(1) "else" Statement() ] 1323} 1324 1325void WhileStatement() #WhileStatement : 1326{} 1327{ 1328 "while" "(" Expression() ")" Statement() 1329} 1330 1331/* 1332 Do statement is just a While statement with a special hook to execute 1333 at least once. 1334*/ 1335void DoStatement() #WhileStatement : 1336{} 1337{ 1338 "do" Statement() "while" "(" Expression() ")" ";" 1339 { jjtThis.isDoStatement=true; } 1340} 1341 1342void ForStatement() #ForStatement : 1343{ Token t = null; } 1344{ 1345 "for" "(" [ ForInit() { jjtThis.hasForInit=true; } ] 1346 ";" [ Expression() { jjtThis.hasExpression=true; } ] 1347 ";" [ ForUpdate() { jjtThis.hasForUpdate=true; } ] ")" 1348 Statement() 1349} 1350 1351/* 1352 The new JDK1.5 enhanced for statement. 1353 e.g. for( int a : arrayOfInts ) { } 1354 We also support loose typing of the iterator var for BeanShell 1355 e.g. for( a : arrayOfInts ) { } 1356*/ 1357void EnhancedForStatement() #EnhancedForStatement : 1358{ Token t = null; } 1359{ 1360 LOOKAHEAD( 4 ) // look ahead for the ":" before deciding 1361 "for" "(" t=<IDENTIFIER> ":" Expression() ")" 1362 Statement() { jjtThis.varName = t.image; } 1363 | 1364 "for" "(" Type() t=<IDENTIFIER> ":" Expression() ")" 1365 Statement() { jjtThis.varName = t.image; } 1366} 1367 1368void ForInit() : 1369{ Token t = null; } 1370{ 1371// SYNTACTIC_LOOKAHEAD 1372 LOOKAHEAD( Modifiers( Modifiers.FIELD, true ) Type() <IDENTIFIER> ) 1373 TypedVariableDeclaration() 1374| 1375 StatementExpressionList() 1376} 1377 1378/** 1379 Declared a typed variable. 1380 Untyped variables are not declared per-se but are handled by the part 1381 of the grammar that deals with assignments. 1382*/ 1383void TypedVariableDeclaration() #TypedVariableDeclaration : 1384{ 1385 Token t = null; 1386 Modifiers mods; 1387} 1388{ 1389 mods = Modifiers( Modifiers.FIELD, false ) 1390 Type() [ TypeArguments() ] VariableDeclarator() ( "," VariableDeclarator() )* 1391 { 1392 jjtThis.modifiers = mods; 1393 } 1394} 1395 1396void StatementExpressionList() #StatementExpressionList : 1397{} 1398{ 1399 StatementExpression() ( "," StatementExpression() )* 1400} 1401 1402void ForUpdate() : 1403{} 1404{ 1405 StatementExpressionList() 1406} 1407 1408void BreakStatement() #ReturnStatement : 1409{} 1410{ 1411 "break" [ <IDENTIFIER> ] ";" { jjtThis.kind = BREAK; } 1412} 1413 1414void ContinueStatement() #ReturnStatement : 1415{} 1416{ 1417 "continue" [ <IDENTIFIER> ] ";" { jjtThis.kind = CONTINUE; } 1418} 1419 1420void ReturnStatement() #ReturnStatement : 1421{} 1422{ 1423 "return" [ Expression() ] ";" { jjtThis.kind = RETURN; } 1424} 1425 1426void SynchronizedStatement() #Block : 1427{ 1428} 1429{ 1430 "synchronized" "(" Expression() ")" Block() { 1431 jjtThis.isSynchronized=true; 1432 } 1433} 1434 1435void ThrowStatement() #ThrowStatement : 1436{} 1437{ 1438 "throw" Expression() ";" 1439} 1440 1441void TryStatement() #TryStatement: 1442/* 1443 Semantic check required here to make sure that at least one 1444 finally/catch is present. (You can have a try with finally and no catch). 1445 */ 1446{ boolean closed = false; } 1447{ 1448 "try" Block() 1449 ( "catch" "(" FormalParameter() ")" Block() { closed = true; } )* 1450 [ "finally" Block() { closed = true; } ] 1451 { 1452 if ( !closed ) throw generateParseException(); 1453 } 1454} 1455