/plugins/JavaSideKick/trunk/src/sidekick/java/parser/Tiger.jj

# · Unknown · 2649 lines · 2441 code · 208 blank · 0 comment · 0 complexity · 104dece9ebe7fbe45d652ab08aab8575 MD5 · raw file

Large files are truncated click here to view the full file

  1. /*
  2. Per Sreenivasa Viswanadha (as posted on the javacc user mailing list), the
  3. original java 1.5 grammar is licensed under the BSD license, so this modified
  4. grammar is also.
  5. Copyright (c) 2005, Dale Anson
  6. All rights reserved.
  7. Redistribution and use in source and binary forms, with or without modification,
  8. are permitted provided that the following conditions are met:
  9. * Redistributions of source code must retain the above copyright notice,
  10. this list of conditions and the following disclaimer.
  11. * Redistributions in binary form must reproduce the above copyright notice,
  12. this list of conditions and the following disclaimer in the documentation
  13. and/or other materials provided with the distribution.
  14. * Neither the name of the <ORGANIZATION> nor the names of its contributors
  15. may be used to endorse or promote products derived from this software without
  16. specific prior written permission.
  17. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  18. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  19. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  21. ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  22. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  23. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  24. ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  26. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. options {
  29. JAVA_UNICODE_ESCAPE = true;
  30. //ERROR_REPORTING = true;
  31. STATIC = false;
  32. //DEBUG_PARSER = true;
  33. }
  34. PARSER_BEGIN(TigerParser)
  35. package sidekick.java.parser;
  36. import sidekick.util.*;
  37. import sidekick.java.node.*;
  38. import sidekick.java.util.Log;
  39. import java.io.*;
  40. import java.util.*;
  41. /**
  42. * Based on grammar to parse Java version 1.5 written by Sreenivasa Viswanadha,
  43. * parses a java file for the JavaSideKick plugin to provide a java code
  44. * browser that works with java 1.5. I've also updated this file so it will
  45. * parse javacc files for JavaSideKick too, that makes it a lot easier to edit
  46. * files such as this.
  47. * <p>
  48. * Example usage to parse a java file:<p>
  49. * <code>
  50. * TigerParser parser = new TigerParser(filename);<br>
  51. * CUNode root = parser.getJavaRootNode();<br>
  52. * </code>
  53. * or to parse a javacc file:<br>
  54. * <code>
  55. * TigerParser parser = new TigerParser(filename);<br>
  56. * CUNode root = parser.getJavaCCRootNode();<br>
  57. * </code>
  58. * Calling either of the above causes the file to be parsed into
  59. * TigerNodes, of which, CUNode is the top-level. The TigerNodes have a parent/
  60. * child relastionship, which naturally forms a tree structure.
  61. * <p>
  62. * To turn this .jj file into a .java file, run <code>javacc Tiger.jj</code>
  63. * from the directory that contains this file. Javacc will produce a number of
  64. * .java files, Be careful -- not all files in the directory are produced by
  65. * javacc, in particular ModifierSet.java and Token.java are required files and
  66. * are NOT produced by javacc. So the sequence is:<br>
  67. * .jj -> javacc -> .java -> javac -> .class
  68. * <p>
  69. * References like JLS X.X are section numbers in the Java Language Specification
  70. * Third Edition.
  71. */
  72. public class TigerParser
  73. {
  74. // accumulates counts of classes, interfaces, methods and fields.
  75. private Results results = new Results();
  76. private InputStream inputStream = null;
  77. /**
  78. * Constructor for TigerParser. Note that JavaSideKick does not use this
  79. * constructor -- since the options for building the parser have both
  80. * USER_TOKEN_MANAGER and USER_CHAR_STREAM set to false (these are the
  81. * default values so are not explicitly set), javacc will create a
  82. * constructor "public TigerParser(InputStream)". It is that constructor
  83. * that JavaSideKick uses.
  84. * @param fileName name of the file to parse
  85. */
  86. public TigerParser(String filename)
  87. {
  88. this(System.in);
  89. try {
  90. inputStream = new FileInputStream(new File(filename));
  91. ReInit(inputStream);
  92. }
  93. catch(Exception e) {
  94. e.printStackTrace();
  95. }
  96. }
  97. TigerParser(JavaCharStream stream) {
  98. jj_input_stream = stream;
  99. token_source = new TigerParserTokenManager(jj_input_stream);
  100. token = new Token();
  101. jj_ntk = -1;
  102. }
  103. /**
  104. * @return the accumulated counts of classes, interfaces, methods, and fields.
  105. */
  106. public Results getResults() {
  107. return results;
  108. }
  109. public Location getLocation(Token t) {
  110. if (t == null)
  111. return new Location(0, 0);
  112. return new Location(t.beginLine, t.beginColumn);
  113. }
  114. public Location getEndLocation(Token t) {
  115. if (t == null)
  116. return new Location(0, 0);
  117. return new Location(t.endLine, t.endColumn + 1);
  118. }
  119. public Location getLocation(Modifier m) {
  120. if (m == null)
  121. return new Location(0, 0);
  122. if (m.beginLine == -1)
  123. m.beginLine = 0;
  124. return new Location(m.beginLine, m.beginColumn);
  125. }
  126. /**
  127. * @param pe ParseException, if null, one will be generated.
  128. * @param kinds One or more token types (see TigerParserConstants for
  129. * definitions). Skipping will stop at the first token type found in
  130. * this list.
  131. * @return the token that was skipped to. This could be null since there is
  132. * a limit of checking no more than 100 tokens. If a token of the requested
  133. * kind is not found within the next 100 tokens, null will be returned.
  134. */
  135. public Token error_skipto(ParseException pe, int... kinds) {
  136. if (pe == null) {
  137. pe = generateParseException(); // generate the exception object.
  138. }
  139. addException(pe);
  140. return error_skipto(kinds);
  141. }
  142. private Token error_skipto(int... kinds) {
  143. Token t = null;
  144. int i = 0;
  145. do {
  146. i++;
  147. if (i > 100) {
  148. return t;
  149. }
  150. t = getNextToken();
  151. for (int type : kinds) {
  152. if (t.kind == type) {
  153. return t;
  154. }
  155. }
  156. } while (t != null);
  157. return t;
  158. }
  159. private List<ErrorNode> exceptions = new ArrayList<ErrorNode>();
  160. private void addException(ParseException pe) {
  161. //pe.printStackTrace();
  162. ErrorNode en = new ErrorNode(pe);
  163. exceptions.add(en);
  164. }
  165. public List<ErrorNode> getErrors() {
  166. return exceptions;
  167. }
  168. public void adjustModifier(Modifier m, Token t) {
  169. if (m.beginLine < t.beginLine) {
  170. m.beginLine = t.beginLine;
  171. }
  172. if (m.beginLine == t.beginLine && (t.beginColumn < m.beginColumn || m.beginColumn == -1)) {
  173. m.beginColumn = t.beginColumn;
  174. }
  175. if (m.endLine < t.endLine) {
  176. m.endLine = t.endLine;
  177. }
  178. if (m.endLine == t.endLine && m.endColumn < t.endColumn) {
  179. m.endColumn = t.endColumn;
  180. }
  181. }
  182. public void setTabSize(int size) {
  183. jj_input_stream.setTabSize(size);
  184. }
  185. public int getTabSize() {
  186. return jj_input_stream.getTabSize(0);
  187. }
  188. /*
  189. * Returns true if the next token is not in the FOLLOW list of "expansion".
  190. * It is used to decide when the end of an "expansion" has been reached.
  191. */
  192. private boolean notTailOfExpansionUnit() {
  193. Token t;
  194. t = getToken(1);
  195. if (t.kind == BIT_OR || t.kind == COMMA || t.kind == RPAREN || t.kind == RBRACE || t.kind == RBRACKET) return false;
  196. return true;
  197. }
  198. /* returns a list as a comma separated string */
  199. private String toString(List list) {
  200. if (list != null) {
  201. StringBuffer sb = new StringBuffer();
  202. for (Iterator it = list.iterator(); it.hasNext(); ) {
  203. Object o = it.next();
  204. if (o == null) {
  205. o = "null";
  206. }
  207. sb.append(o.toString());
  208. if (it.hasNext()) {
  209. sb.append(",");
  210. }
  211. }
  212. return sb.toString();
  213. }
  214. return "";
  215. }
  216. private String toTigerString(List<TigerNode> list) {
  217. if (list != null) {
  218. StringBuffer sb = new StringBuffer();
  219. for (Iterator it = list.iterator(); it.hasNext(); ) {
  220. TigerNode tn = (TigerNode)it.next();
  221. sb.append(tn.getName()).append(", ");
  222. if (tn.getChildCount() > 0) {
  223. sb.append(toTigerString(tn.getChildren()));
  224. }
  225. }
  226. return sb.toString();
  227. }
  228. return "";
  229. }
  230. }
  231. PARSER_END(TigerParser)
  232. /********************************************
  233. * THE JAVA TOKEN SPECIFICATION STARTS HERE *
  234. ********************************************/
  235. /* WHITE SPACE */
  236. SKIP :
  237. {
  238. " "
  239. | "\t"
  240. | "\n"
  241. | "\r"
  242. | "\f"
  243. }
  244. /* COMMENTS */
  245. MORE :
  246. {
  247. /* danson, added backup(2) for special handling of single line comments at
  248. the end of a file, see <IN_SINGLE_LINE_COMMENT> special token below */
  249. "//" { input_stream.backup(2); } : IN_SINGLE_LINE_COMMENT
  250. |
  251. <"/**" ~["/"]> { input_stream.backup(1); } : IN_FORMAL_COMMENT
  252. |
  253. "/*" : IN_MULTI_LINE_COMMENT
  254. }
  255. <IN_SINGLE_LINE_COMMENT>
  256. SPECIAL_TOKEN :
  257. {
  258. /* this is the original, I've replaced with the next line to allow a single
  259. line comment at the end of a java file without a new line following the
  260. comment. The java language specification says that single line comments
  261. must be followed by an end-of-line marker (see section 3.4), so this new rule
  262. relaxes that requirement slightly by allowing the line terminator to be
  263. optional. This only makes sense when the comment is the last line of the
  264. source file, all other single line comments will have a line terminator. This
  265. request was posted as a bug against JBrowse, I don't see any problem with
  266. allowing it, especially since Sun's compiler doesn't complain. */
  267. /* <SINGLE_LINE_COMMENT: "\n" | "\r" | "\r\n" > : DEFAULT */
  268. < SINGLE_LINE_COMMENT: "//"(~["\n","\r"])* ("\n"|"\r"|"\r\n")? > : DEFAULT
  269. }
  270. <IN_FORMAL_COMMENT>
  271. SPECIAL_TOKEN :
  272. {
  273. <FORMAL_COMMENT: "*/" > : DEFAULT
  274. }
  275. <IN_MULTI_LINE_COMMENT>
  276. SPECIAL_TOKEN :
  277. {
  278. <MULTI_LINE_COMMENT: "*/" > : DEFAULT
  279. }
  280. <IN_SINGLE_LINE_COMMENT,IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>
  281. MORE :
  282. {
  283. < ~[] >
  284. }
  285. /* RESERVED WORDS AND LITERALS */
  286. TOKEN :
  287. {
  288. < ABSTRACT: "abstract" >
  289. | < ASSERT: "assert" >
  290. | < BOOLEAN: "boolean" >
  291. | < BREAK: "break" >
  292. | < BYTE: "byte" >
  293. | < CASE: "case" >
  294. | < CATCH: "catch" >
  295. | < CHAR: "char" >
  296. | < CLASS: "class" >
  297. | < CONST: "const" >
  298. | < CONTINUE: "continue" >
  299. | < _DEFAULT: "default" >
  300. | < DO: "do" >
  301. | < DOUBLE: "double" >
  302. | < ELSE: "else" >
  303. | < ENUM: "enum" >
  304. | < EXTENDS: "extends" >
  305. | < FALSE: "false" >
  306. | < FINAL: "final" >
  307. | < FINALLY: "finally" >
  308. | < FLOAT: "float" >
  309. | < FOR: "for" >
  310. | < GOTO: "goto" >
  311. | < IF: "if" >
  312. | < IMPLEMENTS: "implements" >
  313. | < IMPORT: "import" >
  314. | < INSTANCEOF: "instanceof" >
  315. | < INT: "int" >
  316. | < INTERFACE: "interface" >
  317. | < LONG: "long" >
  318. | < NATIVE: "native" >
  319. | < NEW: "new" >
  320. | < NULL: "null" >
  321. | < PACKAGE: "package">
  322. | < PRIVATE: "private" >
  323. | < PROTECTED: "protected" >
  324. | < PUBLIC: "public" >
  325. | < RETURN: "return" >
  326. | < SHORT: "short" >
  327. | < STATIC: "static" >
  328. | < STRICTFP: "strictfp" >
  329. | < SUPER: "super" >
  330. | < SWITCH: "switch" >
  331. | < SYNCHRONIZED: "synchronized" >
  332. | < THIS: "this" >
  333. | < THROW: "throw" >
  334. | < THROWS: "throws" >
  335. | < TRANSIENT: "transient" >
  336. | < TRUE: "true" >
  337. | < TRY: "try" >
  338. | < VOID: "void" >
  339. | < VOLATILE: "volatile" >
  340. | < WHILE: "while" >
  341. }
  342. /* JAVACC RESERVED WORDS: These are the only tokens in JavaCC but not in Java */
  343. /* danson, using these keywords as tokens causes problems when parsing java
  344. code, so I'm replacing these keyworks with semantic lookahead as described in
  345. the javacc faq. */
  346. /*
  347. TOKEN :
  348. {
  349. < _OPTIONS: "options" >
  350. | < _LOOKAHEAD: "LOOKAHEAD" >
  351. | < _IGNORE_CASE: "IGNORE_CASE" >
  352. | < _PARSER_BEGIN: "PARSER_BEGIN" >
  353. | < _PARSER_END: "PARSER_END" >
  354. | < _JAVACODE: "JAVACODE" >
  355. | < _TOKEN: "TOKEN" >
  356. | < _SPECIAL_TOKEN: "SPECIAL_TOKEN" >
  357. | < _MORE: "MORE" >
  358. | < _SKIP: "SKIP" >
  359. | < _TOKEN_MGR_DECLS: "TOKEN_MGR_DECLS" >
  360. | < _EOF: "EOF" >
  361. }
  362. */
  363. /* LITERALS */
  364. // DONE: Java 7 allows a binary literal.
  365. // DONE: Java 7 allows underscores in numeric literals to improve readability, e.g.
  366. // long creditCardNumber = 1234_5678_9012_3456L;
  367. TOKEN :
  368. {
  369. < INTEGER_LITERAL:
  370. <DECIMAL_LITERAL> (["l","L"])?
  371. | <HEX_LITERAL> (["l","L"])?
  372. | <BINARY_LITERAL> (["l","L"])?
  373. | <OCTAL_LITERAL> (["l","L"])?
  374. >
  375. |
  376. < #DECIMAL_LITERAL: ["1"-"9"] ((["_"])*["0"-"9"])* >
  377. |
  378. < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])((["_"])*["0"-"9","a"-"f","A"-"F"])* >
  379. |
  380. < #BINARY_LITERAL: "0" ["b","B"] (["0"-"1"])((["_"])*["0"-"1"])* > // new for Java 7
  381. |
  382. < #OCTAL_LITERAL: "0" ((["_"])*(["0"-"7"]))* >
  383. |
  384. < FLOATING_POINT_LITERAL:
  385. (["0"-"9"])((["_"])*(["0"-"9"]))* "." ((["0"-"9"])((["_"])*(["0"-"9"]))*)* (<EXPONENT>)? (["f","F","d","D"])?
  386. | "." (["0"-"9"])((["_"])*(["0"-"9"]))* (<EXPONENT>)? (["f","F","d","D"])?
  387. | (["0"-"9"])((["_"])*(["0"-"9"]))* <EXPONENT> (["f","F","d","D"])?
  388. | (["0"-"9"])((["_"])*(["0"-"9"]))* (<EXPONENT>)? ["f","F","d","D"]
  389. >
  390. |
  391. < #EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
  392. |
  393. < CHARACTER_LITERAL:
  394. "'"
  395. ( (~["'","\\","\n","\r"])
  396. | ("\\"
  397. ( ["n","t","b","r","f","\\","'","\""]
  398. | ["0"-"7"] ( ["0"-"7"] )?
  399. | ["0"-"3"] ["0"-"7"] ["0"-"7"]
  400. )
  401. )
  402. )
  403. "'"
  404. >
  405. |
  406. < STRING_LITERAL:
  407. "\""
  408. ( (~["\"","\\","\n","\r"])
  409. | ("\\"
  410. ( ["n","t","b","r","f","\\","'","\""]
  411. | ["0"-"7"] ( ["0"-"7"] )?
  412. | ["0"-"3"] ["0"-"7"] ["0"-"7"]
  413. )
  414. )
  415. )*
  416. "\""
  417. >
  418. }
  419. /* IDENTIFIERS */
  420. TOKEN :
  421. {
  422. < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* >
  423. |
  424. < #LETTER:
  425. [
  426. "\u0024",
  427. "\u0041"-"\u005a",
  428. "\u005f",
  429. "\u0061"-"\u007a",
  430. "\u00c0"-"\u00d6",
  431. "\u00d8"-"\u00f6",
  432. "\u00f8"-"\u00ff",
  433. "\u0100"-"\u1fff",
  434. "\u3040"-"\u318f",
  435. "\u3300"-"\u337f",
  436. "\u3400"-"\u3d2d",
  437. "\u4e00"-"\u9fff",
  438. "\uf900"-"\ufaff"
  439. ]
  440. >
  441. |
  442. < #DIGIT:
  443. [
  444. "\u0030"-"\u0039",
  445. "\u0660"-"\u0669",
  446. "\u06f0"-"\u06f9",
  447. "\u0966"-"\u096f",
  448. "\u09e6"-"\u09ef",
  449. "\u0a66"-"\u0a6f",
  450. "\u0ae6"-"\u0aef",
  451. "\u0b66"-"\u0b6f",
  452. "\u0be7"-"\u0bef",
  453. "\u0c66"-"\u0c6f",
  454. "\u0ce6"-"\u0cef",
  455. "\u0d66"-"\u0d6f",
  456. "\u0e50"-"\u0e59",
  457. "\u0ed0"-"\u0ed9",
  458. "\u1040"-"\u1049"
  459. ]
  460. >
  461. }
  462. /* SEPARATORS */
  463. TOKEN :
  464. {
  465. < LPAREN: "(" >
  466. | < RPAREN: ")" >
  467. | < LBRACE: "{" >
  468. | < RBRACE: "}" >
  469. | < LBRACKET: "[" >
  470. | < RBRACKET: "]" >
  471. | < SEMICOLON: ";" >
  472. | < COMMA: "," >
  473. | < DOT: "." >
  474. | < AT: "@" >
  475. }
  476. /* OPERATORS */
  477. TOKEN :
  478. {
  479. < ASSIGN: "=" >
  480. | < LT: "<" >
  481. | < BANG: "!" >
  482. | < TILDE: "~" >
  483. | < HOOK: "?" >
  484. | < COLON: ":" >
  485. | < EQ: "==" >
  486. | < LE: "<=" >
  487. | < GE: ">=" >
  488. | < NE: "!=" >
  489. | < SC_OR: "||" >
  490. | < SC_AND: "&&" >
  491. | < INCR: "++" >
  492. | < DECR: "--" >
  493. | < PLUS: "+" >
  494. | < MINUS: "-" >
  495. | < STAR: "*" >
  496. | < SLASH: "/" >
  497. | < BIT_AND: "&" >
  498. | < BIT_OR: "|" >
  499. | < XOR: "^" >
  500. | < REM: "%" >
  501. | < LSHIFT: "<<" >
  502. | < PLUSASSIGN: "+=" >
  503. | < MINUSASSIGN: "-=" >
  504. | < STARASSIGN: "*=" >
  505. | < SLASHASSIGN: "/=" >
  506. | < ANDASSIGN: "&=" >
  507. | < ORASSIGN: "|=" >
  508. | < XORASSIGN: "^=" >
  509. | < REMASSIGN: "%=" >
  510. | < LSHIFTASSIGN: "<<=" >
  511. | < RSIGNEDSHIFTASSIGN: ">>=" >
  512. | < RUNSIGNEDSHIFTASSIGN: ">>>=" >
  513. | < ELLIPSIS: "..." >
  514. }
  515. /* >'s need special attention due to generics syntax. */
  516. TOKEN :
  517. {
  518. < RUNSIGNEDSHIFT: ">>>" >
  519. {
  520. matchedToken.kind = GT;
  521. ((Token.GTToken)matchedToken).realKind = RUNSIGNEDSHIFT;
  522. input_stream.backup(2);
  523. }
  524. | < RSIGNEDSHIFT: ">>" >
  525. {
  526. matchedToken.kind = GT;
  527. ((Token.GTToken)matchedToken).realKind = RSIGNEDSHIFT;
  528. input_stream.backup(1);
  529. }
  530. | < GT: ">" >
  531. }
  532. /************************************************
  533. * THE JAVACC GRAMMAR SPECIFICATION STARTS HERE *
  534. ************************************************/
  535. CUNode getJavaCCRootNode(int tab_size) :
  536. {
  537. setTabSize(tab_size);
  538. CUNode n = new CUNode();
  539. List<TigerNode> children = null;
  540. }
  541. {
  542. children = javacc_input()
  543. {
  544. if (children != null ) {
  545. for (TigerNode child : children) {
  546. if (child instanceof CUNode) {
  547. for (ImportNode in : ((CUNode)child).getImportNodes()) {
  548. n.addImport(in);
  549. }
  550. }
  551. n.addChild(child);
  552. }
  553. }
  554. return n;
  555. }
  556. }
  557. List<TigerNode> javacc_input() :
  558. {
  559. List<TigerNode> children = new ArrayList<TigerNode>();
  560. TigerNode options_node = null;
  561. Token parser_node_start_t = null;
  562. Token parser_node_end_t = null;
  563. Token cunode_start_t = null;
  564. Token cunode_end_t = null;
  565. TigerNode production_node = null;
  566. CUNode cunode = null;
  567. CUNode parser_node = new CUNode(){public int getOrdinal(){return TigerNode.PARSER;}};
  568. }
  569. {
  570. options_node=javacc_options()
  571. // special handling for javacc keyword
  572. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("PARSER_BEGIN")} )
  573. parser_node_start_t=<IDENTIFIER> "(" identifier() cunode_start_t=")"
  574. cunode=CompilationUnit(getTabSize())
  575. // special handling for javacc keyword
  576. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("PARSER_END")} )
  577. cunode_end_t=<IDENTIFIER> "(" identifier() parser_node_end_t=")"
  578. ( production_node=production() { children.add(production_node); } )+
  579. <EOF>
  580. {
  581. if (options_node != null) {
  582. children.add(options_node);
  583. }
  584. if (parser_node_start_t != null && parser_node_end_t != null ) {
  585. parser_node.setName("PARSER");
  586. parser_node.setStartLocation(getLocation(parser_node_start_t));
  587. parser_node.setEndLocation(getEndLocation(parser_node_end_t));
  588. children.add(parser_node);
  589. if (cunode != null) {
  590. cunode.setStartLocation(getLocation(cunode_start_t));
  591. cunode.setEndLocation(getEndLocation(cunode_end_t));
  592. if (cunode.getChildren() != null ) {
  593. for (Iterator it = cunode.getChildren().iterator(); it.hasNext(); ) {
  594. parser_node.addChild((TigerNode)it.next());
  595. }
  596. }
  597. else {
  598. parser_node.addChild(cunode);
  599. }
  600. for (ImportNode in : cunode.getImportNodes()) {
  601. parser_node.addImport(in);
  602. }
  603. }
  604. }
  605. return children;
  606. }
  607. }
  608. TigerNode javacc_options() :
  609. {
  610. TigerNode tn = new TigerNode() {public int getOrdinal() { return TigerNode.OPTIONS;}};
  611. Token start_t = null;
  612. Token end_t = null;
  613. }
  614. {
  615. // special handling for javacc keyword
  616. [
  617. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("options")} )
  618. (start_t=<IDENTIFIER> "{" ( option_binding() )+ end_t="}" )
  619. ]
  620. //[ start_t=<_OPTIONS> "{" ( option_binding() )+ end_t="}" ]
  621. {
  622. tn.setName("options");
  623. tn.setStartLocation(getLocation(start_t));
  624. tn.setEndLocation(getEndLocation(end_t));
  625. return tn;
  626. }
  627. }
  628. void option_binding() :
  629. {}
  630. {
  631. (
  632. // special handling for javacc keyword
  633. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("LOOKAHEAD")} )
  634. identifier()
  635. |
  636. // special handling for javacc keyword
  637. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("IGNORE_CASE")} )
  638. identifier()
  639. |
  640. <IDENTIFIER>
  641. |
  642. "static" )
  643. "="
  644. ( IntegerLiteral() | BooleanLiteral() | StringLiteral() )
  645. ";"
  646. }
  647. TigerNode production() :
  648. {
  649. TigerNode tn = null;
  650. }
  651. {
  652. (
  653. LOOKAHEAD(1)
  654. /*
  655. * Since JAVACODE is both a JavaCC reserved word and a Java identifier,
  656. * we need to give preference to "javacode_production" over
  657. * "bnf_production".
  658. */
  659. tn=javacode_production()
  660. |
  661. LOOKAHEAD(1)
  662. /*
  663. * Since SKIP, TOKEN, etc. are both JavaCC reserved words and Java
  664. * identifiers, we need to give preference to "regular_expression_production"
  665. * over "bnf_production".
  666. */
  667. tn=regular_expr_production()
  668. |
  669. LOOKAHEAD(1)
  670. /*
  671. * Since TOKEN_MGR_DECLS is both a JavaCC reserved word and a Java identifier,
  672. * we need to give preference to "token_manager_decls" over
  673. * "bnf_production".
  674. */
  675. tn=token_manager_decls()
  676. |
  677. tn=bnf_production()
  678. )
  679. {
  680. return tn;
  681. }
  682. }
  683. TigerNode javacode_production() :
  684. {
  685. JavaCodeProductionNode mn = new JavaCodeProductionNode();
  686. List<TigerNode> params = null;
  687. Token start_t = null;
  688. Type resultType = null;
  689. String identifier = "";
  690. BlockNode bn = null;
  691. }
  692. {
  693. // special handling for javacc keyword
  694. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("JAVACODE")} )
  695. start_t=<IDENTIFIER>
  696. resultType=ResultType() identifier=identifier() params=FormalParameters()
  697. [ LOOKAHEAD(2) "throws" Name() ( "," Name() )* ]
  698. [ LOOKAHEAD(2) node_descriptor() ]
  699. bn=Block()
  700. {
  701. if (start_t != null){
  702. mn.setStartLocation(getLocation(start_t));
  703. }
  704. if (resultType != null) {
  705. mn.setReturnType(resultType);
  706. }
  707. mn.setName(identifier);
  708. if (params != null) {
  709. mn.setFormalParams(params);
  710. }
  711. if (bn != null) {
  712. mn.setEndLocation(bn.getEndLocation());
  713. mn.addChild(bn);
  714. }
  715. return mn;
  716. }
  717. }
  718. TigerNode bnf_production() :
  719. {
  720. BNFProductionNode mn = new BNFProductionNode();
  721. List<TigerNode> params = null;
  722. Type resultType = null;
  723. String identifier = "";
  724. Token end_t = null;
  725. BlockNode java_block = null;
  726. }
  727. {
  728. resultType=ResultType() identifier=identifier() params=FormalParameters()
  729. [ "throws" Name() ( "," Name() )* ]
  730. [ node_descriptor() ]
  731. ":"
  732. java_block = Block()
  733. "{" expansion_choices() end_t="}"
  734. {
  735. if (resultType != null) {
  736. mn.setStartLocation(resultType.getStartLocation());
  737. mn.setReturnType(resultType);
  738. }
  739. mn.setName(identifier);
  740. if (params != null) {
  741. mn.setFormalParams(params);
  742. }
  743. if (end_t != null ) {
  744. mn.setEndLocation(getEndLocation(end_t));
  745. }
  746. if (java_block != null) {
  747. mn.addChild(java_block);
  748. }
  749. return mn;
  750. }
  751. }
  752. TigerNode regular_expr_production() :
  753. {
  754. Token start_t = null;
  755. Token end_t = null;
  756. Token t = null;
  757. TigerNode tn = new RegexProductionNode();
  758. Token kind = null;
  759. StringBuffer lexical_state_list = new StringBuffer();
  760. }
  761. {
  762. [
  763. LOOKAHEAD(2) start_t="<" "*" ">" {lexical_state_list.append("<*>"); }
  764. |
  765. start_t="<" t=<IDENTIFIER> {lexical_state_list.append("<").append(t.image);} ( "," t=<IDENTIFIER> {lexical_state_list.append(",").append(t.image);} )* ">" {lexical_state_list.append(">");}
  766. ]
  767. kind=regexpr_kind() [ "["
  768. // special handling for javacc keyword
  769. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("IGNORE_CASE")} )
  770. <IDENTIFIER>
  771. "]"
  772. ] ":"
  773. "{" regexpr_spec() ( "|" regexpr_spec() )* end_t="}"
  774. {
  775. if (lexical_state_list.length() > 0) {
  776. tn.setName(lexical_state_list.toString());
  777. }
  778. else if (kind != null) {
  779. tn.setName(kind.image);
  780. }
  781. tn.setStartLocation(getLocation(start_t == null ? kind : start_t));
  782. tn.setEndLocation(getEndLocation(end_t));
  783. return tn;
  784. }
  785. }
  786. TigerNode token_manager_decls() :
  787. {
  788. Token start_t = null;
  789. BlockNode bn = null;
  790. }
  791. {
  792. // special handling for javacc keyword
  793. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("TOKEN_MGR_DECLS")} )
  794. start_t=<IDENTIFIER> ":" bn=TokenMgrDeclBlock()
  795. {
  796. TigerNode tn = new TokenMgrDeclProductionNode();
  797. tn.setName(start_t.image);
  798. tn.setStartLocation(getLocation(start_t));
  799. if (bn != null) {
  800. tn.setEndLocation(bn.getEndLocation());
  801. tn.addChildren(bn.getChildren());
  802. }
  803. return tn;
  804. }
  805. }
  806. Token regexpr_kind() :
  807. {
  808. Token t = null;
  809. }
  810. {
  811. (
  812. // special handling for javacc keyword
  813. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("TOKEN")} )
  814. t=<IDENTIFIER>
  815. |
  816. // special handling for javacc keyword
  817. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("SPECIAL_TOKEN")} )
  818. t=<IDENTIFIER>
  819. |
  820. // special handling for javacc keyword
  821. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("SKIP")} )
  822. t=<IDENTIFIER>
  823. |
  824. // special handling for javacc keyword
  825. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("MORE")} )
  826. t=<IDENTIFIER>
  827. )
  828. {
  829. return t;
  830. }
  831. }
  832. void regexpr_spec() :
  833. {}
  834. {
  835. regular_expression() [ Block() ] [ ":" <IDENTIFIER> ]
  836. }
  837. void expansion_choices() :
  838. {}
  839. {
  840. expansion() ( "|" expansion() )*
  841. }
  842. void expansion() :
  843. {}
  844. {
  845. ( LOOKAHEAD(1)
  846. // special handling for javacc keyword
  847. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("LOOKAHEAD")} )
  848. <IDENTIFIER> "(" local_lookahead() ")"
  849. )?
  850. ( LOOKAHEAD(0, { notTailOfExpansionUnit() } )
  851. expansion_unit()
  852. [ node_descriptor() ]
  853. )+
  854. }
  855. void local_lookahead() :
  856. {
  857. boolean commaAtEnd = false, emptyLA = true;
  858. }
  859. {
  860. [
  861. /*
  862. * The lookahead of 1 is to turn off the warning message that lets
  863. * us know that an expansion choice can also start with an integer
  864. * literal because a primary expression can do the same. But we
  865. * know that this is what we want.
  866. */
  867. LOOKAHEAD(1)
  868. IntegerLiteral()
  869. {
  870. emptyLA = false;
  871. }
  872. ]
  873. [ LOOKAHEAD(0, { !emptyLA && (getToken(1).kind != RPAREN) } )
  874. ","
  875. {
  876. commaAtEnd = true;
  877. }
  878. ]
  879. [ LOOKAHEAD(0, { getToken(1).kind != RPAREN && getToken(1).kind != LBRACE } )
  880. expansion_choices()
  881. {
  882. emptyLA = false; commaAtEnd = false;
  883. }
  884. ]
  885. [ LOOKAHEAD(0, { !emptyLA && !commaAtEnd && (getToken(1).kind != RPAREN) } )
  886. ","
  887. {
  888. commaAtEnd = true;
  889. }
  890. ]
  891. [ LOOKAHEAD(0, { emptyLA || commaAtEnd } )
  892. "{" Expression() "}"
  893. ]
  894. }
  895. void expansion_unit() :
  896. {}
  897. {
  898. LOOKAHEAD(1)
  899. /*
  900. * We give this priority over primary expressions which use LOOKAHEAD as the
  901. * name of its identifier.
  902. */
  903. // special handling for javacc keyword
  904. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("LOOKAHEAD")} )
  905. <IDENTIFIER> "(" local_lookahead() ")"
  906. |
  907. Block()
  908. |
  909. "[" expansion_choices() "]"
  910. |
  911. "try" "{" expansion_choices() "}"
  912. ( "catch" "(" Name() <IDENTIFIER> ")" Block() )*
  913. [ "finally" Block() ]
  914. |
  915. LOOKAHEAD( identifier() | StringLiteral() | "<" | PrimaryExpression() "=" )
  916. [
  917. LOOKAHEAD(PrimaryExpression() "=")
  918. PrimaryExpression() "="
  919. ]
  920. ( regular_expression() | identifier() Arguments() )
  921. |
  922. "(" expansion_choices() ")" ( "+" | "*" | "?" )?
  923. }
  924. void regular_expression() :
  925. {}
  926. {
  927. StringLiteral()
  928. |
  929. LOOKAHEAD(3)
  930. "<" [ [ "#" ] identifier() ":" ] complex_regular_expression_choices() ">"
  931. |
  932. LOOKAHEAD(2)
  933. "<" identifier() ">"
  934. |
  935. "<"
  936. // special handling for javacc keyword
  937. LOOKAHEAD( {getToken(1).kind == IDENTIFIER && getToken(1).image.equals("EOF")} )
  938. <IDENTIFIER>
  939. ">"
  940. }
  941. void complex_regular_expression_choices() :
  942. {}
  943. {
  944. complex_regular_expression() ( "|" complex_regular_expression() )*
  945. }
  946. void complex_regular_expression() :
  947. {}
  948. {
  949. ( complex_regular_expression_unit() )+
  950. }
  951. void complex_regular_expression_unit() :
  952. {}
  953. {
  954. StringLiteral()
  955. |
  956. "<" identifier() ">"
  957. |
  958. character_list()
  959. |
  960. "(" complex_regular_expression_choices() ")" ( "+" | "*" | "?" )?
  961. }
  962. void character_list() :
  963. {}
  964. {
  965. [ "~" ] "[" [ character_descriptor() ( "," character_descriptor() )* ] "]"
  966. }
  967. void character_descriptor() :
  968. {}
  969. {
  970. StringLiteral() [ "-" StringLiteral() ]
  971. }
  972. String identifier() :
  973. {
  974. Token t = null;
  975. }
  976. {
  977. t=<IDENTIFIER>
  978. {
  979. return t.image;
  980. }
  981. }
  982. /**********************************************
  983. * THE JJTREE PRODUCTIONS START HERE *
  984. **********************************************/
  985. void node_descriptor() :
  986. {}
  987. {
  988. "#" ( <IDENTIFIER> | <VOID> )
  989. [
  990. LOOKAHEAD(1)
  991. "(" [ ">" ] node_descriptor_expression() ")"
  992. ]
  993. }
  994. JAVACODE
  995. void node_descriptor_expression()
  996. {
  997. Token tok;
  998. int nesting = 1;
  999. while (true) {
  1000. tok = getToken(1);
  1001. if (tok.kind == 0) {
  1002. throw new ParseException();
  1003. }
  1004. if (tok.kind == LPAREN) nesting++;
  1005. if (tok.kind == RPAREN) {
  1006. nesting--;
  1007. if (nesting == 0) break;
  1008. }
  1009. tok = getNextToken();
  1010. }
  1011. }
  1012. /* javacc productions */
  1013. void IntegerLiteral() :
  1014. {}
  1015. {
  1016. <INTEGER_LITERAL>
  1017. }
  1018. void StringLiteral() :
  1019. {}
  1020. {
  1021. <STRING_LITERAL>
  1022. }
  1023. /*****************************************
  1024. * THE JAVA LANGUAGE GRAMMAR STARTS HERE *
  1025. *****************************************/
  1026. /*
  1027. * Program structuring syntax follows.
  1028. */
  1029. CUNode getJavaRootNode(int tab_size) :
  1030. {
  1031. CUNode n = null;
  1032. }
  1033. {
  1034. n = JavaCompilationUnit(tab_size)
  1035. {
  1036. return n;
  1037. }
  1038. }
  1039. /**
  1040. * Main entry point for parsing the PARSER section in javacc files. Use
  1041. * JavaCompilationUnit as main entry point for parsing java files.
  1042. * @return a CUNode, which is parent or root node of all other nodes.
  1043. */
  1044. CUNode CompilationUnit(int tab_size):
  1045. {
  1046. setTabSize(tab_size);
  1047. CUNode n = new CUNode();
  1048. TigerNode a;
  1049. String packageName = "";
  1050. ImportNode in = null;
  1051. }
  1052. {
  1053. try {
  1054. (
  1055. [ packageName=PackageDeclaration() ]
  1056. ( in=ImportDeclaration() { n.addImport(in); } )*
  1057. // TypeDeclaration, this will be one or more classes or
  1058. // interfaces, add these as child nodes of the root node
  1059. (
  1060. a=TypeDeclaration()
  1061. { n.addChild(a); }
  1062. )*
  1063. )
  1064. }
  1065. catch(ParseException pe) {
  1066. error_skipto(pe, SEMICOLON, RBRACE, PUBLIC, PRIVATE, PROTECTED, CLASS, INTERFACE, ENUM);
  1067. }
  1068. {
  1069. n.setPackageName(packageName);
  1070. return n;
  1071. }
  1072. }
  1073. /**
  1074. * Main entry point for parsing java files.
  1075. * @return a CUNode, which is parent or root node of all other nodes.
  1076. */
  1077. CUNode JavaCompilationUnit(int tab_size):
  1078. {
  1079. CUNode n = null;
  1080. Token end_t = null;
  1081. }
  1082. {
  1083. try {
  1084. (
  1085. n = CompilationUnit(tab_size)
  1086. // read the whole file
  1087. end_t=<EOF>
  1088. )
  1089. }
  1090. catch(ParseException pe) {
  1091. error_skipto(pe, SEMICOLON, RBRACE);
  1092. }
  1093. {
  1094. if (end_t != null) {
  1095. n.setEndLocation(getEndLocation(end_t));
  1096. }
  1097. return n;
  1098. }
  1099. }
  1100. String PackageDeclaration():
  1101. {
  1102. NameNode name = null;
  1103. }
  1104. {
  1105. try {
  1106. "package" name=Name() ";" ////
  1107. }
  1108. catch(ParseException pe) {
  1109. error_skipto(pe, SEMICOLON, IMPORT, CLASS, INTERFACE, ENUM, PUBLIC, PRIVATE, PROTECTED);
  1110. }
  1111. {
  1112. if (name == null) {
  1113. return "";
  1114. }
  1115. return name.getFullyQualifiedTypeName();
  1116. }
  1117. }
  1118. /**
  1119. * @return just the package name, without the 'import' or 'static' or '.*', e.g.
  1120. * "import java.util.*;" will return "java.util". A fully qualified import will
  1121. * return the full classname, e.g. "import java.util.List;" will return
  1122. * "java.util.List", this is also the case with static imports, e.g.
  1123. * "import static java.lang.Math.PI;" will return "java.lang.Math.PI".
  1124. */
  1125. ImportNode ImportDeclaration():
  1126. {
  1127. NameNode name = null;
  1128. Token st = null;
  1129. Token et = null;
  1130. }
  1131. {
  1132. try {
  1133. st="import" [ "static" ] name=Name() [ "." "*" ] et=";" ////
  1134. }
  1135. catch(ParseException pe) {
  1136. error_skipto(pe, SEMICOLON, IMPORT, CLASS, INTERFACE, ENUM, PUBLIC, PRIVATE, PROTECTED);
  1137. }
  1138. {
  1139. if (name == null) {
  1140. return null;
  1141. }
  1142. ImportNode in = new ImportNode(name.getFullyQualifiedTypeName());
  1143. in.setStartLocation(getLocation(st));
  1144. in.setEndLocation(getEndLocation(et));
  1145. return in;
  1146. }
  1147. }
  1148. /*
  1149. * Modifiers. We match all modifiers in a single rule to reduce the chances of
  1150. * syntax errors for simple modifier mistakes. It will also enable us to give
  1151. * better error messages.
  1152. */
  1153. Modifier Modifiers():
  1154. {
  1155. int modifiers = 0;
  1156. Token t = null;
  1157. Modifier m = new Modifier();
  1158. }
  1159. {
  1160. (
  1161. LOOKAHEAD(2)
  1162. (
  1163. t="public" { modifiers |= ModifierSet.PUBLIC; adjustModifier(m, t);}
  1164. |
  1165. t="static" { modifiers |= ModifierSet.STATIC; adjustModifier(m, t);}
  1166. |
  1167. t="protected" { modifiers |= ModifierSet.PROTECTED; adjustModifier(m, t);}
  1168. |
  1169. t="private" { modifiers |= ModifierSet.PRIVATE; adjustModifier(m, t);}
  1170. |
  1171. t="final" { modifiers |= ModifierSet.FINAL; adjustModifier(m, t);}
  1172. |
  1173. t="abstract" { modifiers |= ModifierSet.ABSTRACT; adjustModifier(m, t);}
  1174. |
  1175. t="synchronized" { modifiers |= ModifierSet.SYNCHRONIZED; adjustModifier(m, t);}
  1176. |
  1177. t="native" { modifiers |= ModifierSet.NATIVE; adjustModifier(m, t);}
  1178. |
  1179. t="transient" { modifiers |= ModifierSet.TRANSIENT; adjustModifier(m, t);}
  1180. |
  1181. t="volatile" { modifiers |= ModifierSet.VOLATILE; adjustModifier(m, t);}
  1182. |
  1183. t="strictfp" { modifiers |= ModifierSet.STRICTFP; adjustModifier(m, t);}
  1184. |
  1185. // TODO: Annotation doesn't work as a modifier, at least, not the way the
  1186. // Modifer and ModifierSet classes are set up. Need to figure out how to
  1187. // represent Annotation as a modifier.
  1188. Annotation()
  1189. )
  1190. )*
  1191. {
  1192. m.modifiers = modifiers;
  1193. return m;
  1194. }
  1195. }
  1196. /*
  1197. * Declaration syntax follows.
  1198. */
  1199. // Handle classes, interfaces, enums, and annotations.
  1200. TigerNode TypeDeclaration():
  1201. {
  1202. Modifier modifier;
  1203. TigerNode tn = null;
  1204. }
  1205. {
  1206. try {
  1207. ";" /// is this the semi-colon that I need to handle at the end of a class??
  1208. |
  1209. modifier = Modifiers()
  1210. (
  1211. tn=ClassOrInterfaceDeclaration(modifier)
  1212. |
  1213. tn=EnumDeclaration(modifier)
  1214. |
  1215. tn=AnnotationTypeDeclaration(modifier) { tn = null; }
  1216. )
  1217. }
  1218. catch(ParseException pe) {
  1219. error_skipto(pe, SEMICOLON, RBRACE, PUBLIC, PRIVATE, PROTECTED); // TODO: rethrow?
  1220. }
  1221. {
  1222. return tn;
  1223. }
  1224. }
  1225. /**
  1226. * @return a ClassNode or an InterfaceNode
  1227. */
  1228. TigerNode ClassOrInterfaceDeclaration(Modifier m):
  1229. {
  1230. boolean isInterface = false;
  1231. Token t = null;
  1232. TigerNode kids = null; // only need the children of this node
  1233. String type_params = "";
  1234. List<TigerNode> extends_list = null;
  1235. List<TigerNode> implements_list = null;
  1236. Token type = null;
  1237. }
  1238. {
  1239. try {
  1240. ( type="class" | type="interface" { isInterface = true; } )
  1241. t=<IDENTIFIER>
  1242. [ type_params = TypeParameters() ]
  1243. [ extends_list=ExtendsList(isInterface) ]
  1244. [ implements_list=ImplementsList(isInterface) ]
  1245. kids=ClassOrInterfaceBody(isInterface)
  1246. /* danson, added this check for trailing semi-colon. Apparently, this has been
  1247. legal since the beginning of Java, some sort of a C hold-over. Sun's latest
  1248. Java 1.5 compiler doesn't mind it, but this parser whined if the class has a
  1249. semi-colon after the last }. The original Java1.5.jj file that this parser
  1250. is based on does NOT whine, so I've done something to change the base behaviour.
  1251. See below, I probably broke this in ClassOrInterfaceBody. */
  1252. [ LOOKAHEAD(2) <SEMICOLON> ]
  1253. }
  1254. catch(ParseException pe) {
  1255. error_skipto(pe, SEMICOLON, CLASS, INTERFACE, ENUM, PUBLIC, PRIVATE, PROTECTED);
  1256. }
  1257. {
  1258. ClassNode node = isInterface ? new InterfaceNode(t.image, m.modifiers) : new ClassNode(t.image, m.modifiers);
  1259. if (isInterface) {
  1260. results.incInterfaceCount();
  1261. }
  1262. else {
  1263. results.incClassCount();
  1264. }
  1265. if (m.beginColumn > -1) {
  1266. node.setStartLocation(getLocation(m) );
  1267. }
  1268. else {
  1269. node.setStartLocation(getLocation(type));
  1270. }
  1271. if (kids != null)
  1272. node.setEndLocation(kids.getEndLocation());
  1273. // add the child nodes, don't need the 'kids' node itself, it's just a holder
  1274. // for the nodes I want (although I do want the end location).
  1275. if (kids != null && kids.getChildren() != null)
  1276. node.addChildren(kids.getChildren());
  1277. node.setTypeParams(type_params);
  1278. node.setExtendsList(extends_list);
  1279. node.setImplementsList(implements_list);
  1280. return node;
  1281. }
  1282. }
  1283. /**
  1284. * @return a list of sidekick.java.node.Types representing items in an 'extends'
  1285. * list, e.g. the "Bar" in "public class Foo extends Bar"
  1286. */
  1287. List<TigerNode> ExtendsList(boolean isInterface):
  1288. {
  1289. boolean extendsMoreThanOne = false;
  1290. List<TigerNode> list = new ArrayList<TigerNode>(); // a list of Types
  1291. Type type_s = null;
  1292. Type type_a = null;
  1293. }
  1294. {
  1295. try {
  1296. "extends" type_s=ClassOrInterfaceType() { list.add(type_s); }
  1297. ( "," type_a=ClassOrInterfaceType() { extendsMoreThanOne = true; list.add(type_a); } )*
  1298. }
  1299. catch(ParseException pe) {
  1300. error_skipto(pe, SEMICOLON, RBRACE);
  1301. }
  1302. {
  1303. if (extendsMoreThanOne && !isInterface)
  1304. throw new ParseException("A class cannot extend more than one other class");
  1305. return list;
  1306. }
  1307. }
  1308. /**
  1309. * @return a list of sidekick.java.node.Types representing items in an 'implements'
  1310. * list, e.g. the "Bar" and "Serializable" in "public class Foo implements Bar, Serializable"
  1311. */
  1312. List<TigerNode> ImplementsList(boolean isInterface):
  1313. {
  1314. List<TigerNode> list = new ArrayList<TigerNode>();
  1315. Type type_s = null;
  1316. Type type_a = null;
  1317. }
  1318. {
  1319. try {
  1320. "implements" type_s=ClassOrInterfaceType() { list.add(type_s); }
  1321. ( "," type_a=ClassOrInterfaceType() { list.add(type_a); } )*
  1322. }
  1323. catch(ParseException pe) {
  1324. error_skipto(pe, SEMICOLON, RBRACE);
  1325. }
  1326. {
  1327. if (isInterface)
  1328. throw new ParseException("An interface cannot implement other interfaces");
  1329. return list;
  1330. }
  1331. }
  1332. /**
  1333. * @return an EnumNode
  1334. */
  1335. TigerNode EnumDeclaration(Modifier m):
  1336. {
  1337. Token t = null;
  1338. Token start_t = null;
  1339. TigerNode body = null;
  1340. }
  1341. {
  1342. try {
  1343. start_t="enum" t=<IDENTIFIER>
  1344. [ ImplementsList(false) ]
  1345. body=EnumBody()
  1346. }
  1347. catch(ParseException pe) {
  1348. if (t == null) {
  1349. // handle the case where old code used 'enum' as a variable name
  1350. ParseException e = new ParseException("Parse error at line " + start_t.beginLine + ", column " + start_t.beginColumn + ". Encountered: 'enum' as an identifier, 'enum' is a keyword.");
  1351. addException(e);
  1352. return null;
  1353. }
  1354. else
  1355. error_skipto(pe, SEMICOLON, RBRACE);
  1356. }
  1357. {
  1358. if (t == null) {
  1359. // handle the case where old code used 'enum' as a variable name
  1360. ParseException e = new ParseException("Parse error at line " + start_t.beginLine + ", column " + start_t.beginColumn + ". Encountered: 'enum' as an identifier, 'enum' is a keyword.");
  1361. addException(e);
  1362. return null;
  1363. }
  1364. EnumNode node = new EnumNode(t.image, m.modifiers);
  1365. if (start_t != null) {
  1366. if (m.beginColumn == -1)
  1367. node.setStartLocation(getLocation(start_t));
  1368. else
  1369. node.setStartLocation(getLocation(m));
  1370. }
  1371. node.setEndLocation(body.getEndLocation());
  1372. node.addChildren(body.getChildren());
  1373. return node;
  1374. }
  1375. }
  1376. // returns the end location of the enum body
  1377. TigerNode EnumBody():
  1378. {
  1379. Token start_t = null;
  1380. Token end_t = null;
  1381. List<TigerNode> constants = new ArrayList<TigerNode>();
  1382. TigerNode constant;
  1383. }
  1384. {
  1385. try {
  1386. start_t="{"
  1387. constant = EnumConstant(){constants.add(constant);} ( "," constant=EnumConstant(){if (constant != null) constants.add(constant);} )*
  1388. [ ";" ( ClassOrInterfaceBodyDeclaration(false) )* ]
  1389. end_t="}"
  1390. }
  1391. catch(ParseException pe) {
  1392. error_skipto(pe, SEMICOLON, RBRACE);
  1393. end_t = start_t;
  1394. }
  1395. {
  1396. TigerNode node = new TigerNode();
  1397. node.setStartLocation(getLocation(start_t));
  1398. node.setEndLocation(getEndLocation(end_t));
  1399. node.addChildren(constants);
  1400. return node;
  1401. }
  1402. }
  1403. /**
  1404. * @return an enum constant value
  1405. */
  1406. TigerNode EnumConstant():
  1407. {
  1408. Token t = null;
  1409. TigerNode node = null;
  1410. }
  1411. {
  1412. try {
  1413. t=<IDENTIFIER> [ Arguments() ] [ ClassOrInterfaceBody(false) ]
  1414. }
  1415. catch(ParseException pe) {
  1416. // This grammar expects commas to be followed by EnumConstants, but
  1417. // neither javac nor the language spec says it has to, so something like
  1418. // public enum {ACE, BASE, CASE, } is allowed.
  1419. // TODO: rethrow the exception? This is poor coding practice. Or maybe
  1420. // let PMD find it?
  1421. return null;
  1422. }
  1423. {
  1424. if (t != null) {
  1425. node = new TigerNode();
  1426. node.setName(t.image);
  1427. node.setStartLocation(getLocation(t));
  1428. node.setEndLocation(getEndLocation(t));
  1429. }
  1430. return node;
  1431. }
  1432. }
  1433. /**
  1434. * @return a string representing a generics type, e.g. the "<String>" in
  1435. * "List<String> list = new List();", the string will contain the angle brackets.
  1436. */
  1437. String TypeParameters():
  1438. {
  1439. String s = "<";
  1440. String a = "";
  1441. }
  1442. {
  1443. try {
  1444. (
  1445. "<"
  1446. (
  1447. a=TypeParameter()
  1448. { s += a; }
  1449. )?
  1450. (
  1451. ","
  1452. { s += ","; }
  1453. a=TypeParameter()
  1454. { s += a; }
  1455. )*
  1456. ">"
  1457. )
  1458. }
  1459. catch(ParseException pe) {
  1460. error_skipto(pe, SEMICOLON, GT);
  1461. }
  1462. {
  1463. return s + ">";
  1464. }
  1465. }
  1466. String TypeParameter():
  1467. {
  1468. String s = "";
  1469. Token t = null;
  1470. }
  1471. {
  1472. try {
  1473. (
  1474. t=<IDENTIFIER> [ s=TypeBound() ]
  1475. )
  1476. }
  1477. catch(ParseException pe) {
  1478. error_skipto(pe, SEMICOLON);
  1479. }
  1480. {
  1481. StringBuffer sb = new StringBuffer();
  1482. if (t.image != null)
  1483. sb.append(t.image);
  1484. if (s.length() > 0)
  1485. sb.append(" ").append(s);
  1486. return sb.toString();
  1487. }
  1488. }
  1489. String TypeBound():
  1490. {
  1491. String s = "extends";
  1492. Type type_s = null;
  1493. Type type_a = null;
  1494. }
  1495. {
  1496. try {
  1497. (
  1498. "extends"
  1499. type_a=ClassOrInterfaceType()
  1500. { s += " " + type_a.toString(); }
  1501. (
  1502. "&"
  1503. { s += " & "; }
  1504. type_a=ClassOrInterfaceType()
  1505. { s += type_a.toString(); }
  1506. )*
  1507. )
  1508. }
  1509. catch(ParseException pe) {
  1510. error_skipto(pe, SEMICOLON); // TODO: rethrow?
  1511. }
  1512. {
  1513. return s;
  1514. }
  1515. }
  1516. /**
  1517. * @return a node representing the contents of a Class or Interface body. The
  1518. * returned node is simply a holder for the contents, it is the children of this
  1519. * node that are useful as they are the methods and fields of the class or
  1520. * interface.
  1521. */
  1522. TigerNode ClassOrInterfaceBody(boolean isInterface):
  1523. {
  1524. TigerNode parent = new TigerNode("", -1);
  1525. TigerNode child = null;
  1526. Token start_t = null;
  1527. Token end_t = null;
  1528. }
  1529. {
  1530. try {
  1531. (
  1532. start_t="{"
  1533. (
  1534. child=ClassOrInterfaceBodyDeclaration(isInterface)
  1535. { if (child != null) parent.addChild(child); }
  1536. )*
  1537. end_t="}"
  1538. )
  1539. }
  1540. catch(ParseException pe) {
  1541. error_skipto(pe, SEMICOLON, RBRACE, PUBLIC, PRIVATE, PROTECTED);
  1542. }
  1543. {
  1544. if (start_t != null) {
  1545. parent.setStartLocation(getLocation(start_t));
  1546. }
  1547. if (end_t != null) {
  1548. parent.setEndLocation(getEndLocation(end_t));
  1549. }
  1550. else if (child != null) {
  1551. parent.setEndLocation(child.getEndLocation());
  1552. }
  1553. return parent.getChildren() != null ? parent : null;
  1554. }
  1555. }
  1556. /**
  1557. * @return one of several different nodes, could be a ClassNode, EnumNode,
  1558. * ConstructorNode, FieldNode, MethodNode, or an InterfaceNode.
  1559. */
  1560. TigerNode ClassOrInterfaceBodyDeclaration(boolean isInterface):
  1561. {
  1562. // see note above (~ line 510), I think my changes here have broken the test for a
  1563. // trailing ; after a class body.
  1564. boolean isNestedInterface = false;
  1565. Modifier m;
  1566. TigerNode a = null;
  1567. TigerNode initializer = null;
  1568. String type_params = null;
  1569. }
  1570. {
  1571. try {
  1572. (
  1573. LOOKAHEAD(2)
  1574. initializer=Initializer()
  1575. {
  1576. if (isInterface)
  1577. throw new ParseException("An interface cannot have initializers");
  1578. if (initializer != null)
  1579. return initializer;
  1580. }
  1581. |
  1582. m = Modifiers() // Just get all the modifiers out of the way. If you want to do
  1583. // more checks, pass the modifiers down to the member
  1584. (
  1585. a=ClassOrInterfaceDeclaration(m)
  1586. |
  1587. a=EnumDeclaration(m)
  1588. |
  1589. LOOKAHEAD( [ TypeParameters() ] <IDENTIFIER> "(" )
  1590. a=ConstructorDeclaration(m)
  1591. |
  1592. LOOKAHEAD( Type() <IDENTIFIER> ( "[" "]" )* ( "," | "=" | ";" ) )
  1593. a=FieldDeclaration(m)
  1594. |
  1595. a=MethodDeclaration(m)
  1596. |
  1597. a=AnnotationTypeDeclaration(m)
  1598. )
  1599. |
  1600. ";" /// is this the trailing semi-colon??
  1601. )
  1602. }
  1603. catch(ParseException pe) {
  1604. //error_skipto(pe, SEMICOLON, PUBLIC, PRIVATE, PROTECTED); // TODO: rethrow?
  1605. throw pe;
  1606. }
  1607. {
  1608. return a;
  1609. }
  1610. }
  1611. /**
  1612. * @return a FieldNode
  1613. */
  1614. FieldNode FieldDeclaration(Modifier m):
  1615. {
  1616. Type type = null;
  1617. FieldNode fn = null;
  1618. VariableDeclarator name = null;
  1619. VariableDeclarator a;
  1620. Token et = null;
  1621. }
  1622. {
  1623. try {
  1624. (
  1625. // Modifiers are already matched in the caller
  1626. /// might need to change this, I'm collecting multiple declarations into a single
  1627. /// field, which seems to be okay, e.g. I'm putting "int x = 0, y = 6" into a
  1628. /// field with Type "int" and name "x, y". It might be better to create individual
  1629. /// nodes for each, so for this example, this method could return 2 fields, one
  1630. /// for "int x" and one for "int y".
  1631. type=Type() name=VariableDeclarator()
  1632. {
  1633. fn = new FieldNode(name.getName(), m.modifiers, type);
  1634. fn.addChild(name);
  1635. if (fn.isPrimitive())
  1636. results.incPrimitiveFieldCount();
  1637. else
  1638. results.incReferenceFieldCount();
  1639. }
  1640. (
  1641. "," a=VariableDeclarator()
  1642. {
  1643. fn.setName(fn.getName() + ", " + a.getName());
  1644. fn.addChild(a);
  1645. }
  1646. )*
  1647. et=";"
  1648. )
  1649. }
  1650. catch(ParseException pe) {
  1651. //error_skipto(pe, SEMICOLON);
  1652. throw pe;
  1653. }
  1654. {
  1655. if (m.beginColumn == -1)
  1656. fn.setStartLocation(type.getStartLocation());
  1657. else
  1658. fn.setStartLocation(getLocation(m)); //type.getStartLocation());
  1659. fn.setEndLocation(getEndLocation(et));
  1660. return fn;
  1661. }
  1662. }
  1663. /*
  1664. Represents a variable declaration. The returned node represents the LHS,
  1665. the children of the node represent the RHS.
  1666. */
  1667. VariableDeclarator VariableDeclarator():
  1668. {
  1669. VariableDeclarator s = null;
  1670. List<TigerNode> list = null;
  1671. }
  1672. {
  1673. try {
  1674. (
  1675. s=VariableDeclaratorId() [ "=" list=VariableInitializer() ]
  1676. )
  1677. }
  1678. catch(ParseException pe) {
  1679. //error_skipto(pe, SEMICOLON);
  1680. throw pe;
  1681. }
  1682. {
  1683. if (list != null) {
  1684. s.addChildren(list);
  1685. }
  1686. return s;
  1687. }
  1688. }
  1689. VariableDeclarator VariableDeclaratorId():
  1690. {
  1691. Token t = null;
  1692. }
  1693. {
  1694. try {
  1695. t=<IDENTIFIER> ( "[" "]" )*
  1696. }
  1697. catch(ParseException pe) {
  1698. //error_skipto(pe, SEMICOLON);
  1699. throw pe;
  1700. }
  1701. {
  1702. if ( t == null)
  1703. return null;
  1704. VariableDeclarator tn = new VariableDeclarator(t.image);
  1705. tn.setStartLocation(new Location(t.beginLine, t.beginColumn));
  1706. tn.setEndLocation(new Location(t.beginLine, t.beginColumn + t.image.length()));
  1707. return tn;
  1708. }
  1709. }
  1710. List<TigerNode> VariableInitializer():
  1711. {
  1712. List<TigerNode> list = new ArrayList<TigerNode>();
  1713. List<TigerNode> a = null;
  1714. }
  1715. {
  1716. try {
  1717. a=ArrayInitializer()
  1718. |
  1719. a=Expression()
  1720. }
  1721. catch(ParseException pe) {
  1722. //error_skipto(pe, SEMICOLON);
  1723. throw pe;
  1724. }
  1725. {
  1726. if (a != null)
  1727. list.addAll(a);
  1728. return list;
  1729. }
  1730. }
  1731. List<TigerNode> ArrayInitializer():
  1732. {
  1733. List<TigerNode> list = new ArrayList<TigerNode>();
  1734. List<TigerNode> a = null;
  1735. List<TigerNode> b = null;
  1736. }
  1737. {
  1738. try {
  1739. "{" [ a=VariableInitializer() { if (a != null) list.addAll(a); } ( LOOKAHEAD(2) "," a=VariableInitializer() { if (b != null) list.addAll(b); } )* ] [ "," ] "}"
  1740. }
  1741. catch(ParseException pe) {
  1742. //error_skipto(pe, SEMICOLON);
  1743. throw pe;
  1744. }
  1745. {
  1746. return list;
  1747. }
  1748. }
  1749. /**
  1750. * @return a MethodNode
  1751. */
  1752. TigerNode MethodDeclaration(Modifier m):
  1753. {
  1754. String type_params = "";
  1755. Type return_type = null;
  1756. MethodNode m_node = null;
  1757. List<TigerNode> name_list = null;
  1758. int line_number = -1;
  1759. BlockNode block = null;
  1760. Location endLoc = null;
  1761. Token t = null;
  1762. }
  1763. {
  1764. try {
  1765. (
  1766. // Modifiers already matched in the caller!
  1767. [ type_params = TypeParameters() ]
  1768. return_type = ResultType()
  1769. m_node = MethodDeclarator() [ "throws" name_list=NameList() ]
  1770. ( block = Block() | t=";" )
  1771. )
  1772. }
  1773. catch(ParseException pe) {
  1774. error_skipto(pe, SEMICOLON, RBRACE, PUBLIC, PRIVATE, PROTECTED);
  1775. }
  1776. {
  1777. if (m_node == null)
  1778. return null;
  1779. MethodNode node = new MethodNode();
  1780. node.setName(m_node.getName());
  1781. if (m.beginColumn == -1)
  1782. node.setStartLocation(new Location(m_node.getStartLocation().line, 0));
  1783. else
  1784. node.setStartLocation(getLocation(m));
  1785. node.setModifiers(m.modif