/* * Copyright © 2002 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * California 95054, U.S.A. All rights reserved. Sun Microsystems, Inc. has * intellectual property rights relating to technology embodied in the product * that is described in this document. In particular, and without limitation, * these intellectual property rights may include one or more of the U.S. * patents listed at http://www.sun.com/patents and one or more additional * patents or pending patent applications in the U.S. and in other countries. * U.S. Government Rights - Commercial software. Government users are subject * to the Sun Microsystems, Inc. standard license agreement and applicable * provisions of the FAR and its supplements. Use is subject to license terms. * Sun, Sun Microsystems, the Sun logo and Java are trademarks or registered * trademarks of Sun Microsystems, Inc. in the U.S. and other countries. This * product is covered and controlled by U.S. Export Control laws and may be * subject to the export or import laws in other countries. Nuclear, missile, * chemical biological weapons or nuclear maritime end uses or end users, * whether direct or indirect, are strictly prohibited. Export or reexport * to countries subject to U.S. embargo or to entities identified on U.S. * export exclusion lists, including, but not limited to, the denied persons * and specially designated nationals lists is strictly prohibited. */ package csjavacc.parser; import java.util.Hashtable; import csjavacc.struct.Action; import csjavacc.struct.BNFProduction; import csjavacc.struct.Choice; import csjavacc.struct.Expansion; import csjavacc.struct.CSCodeProduction; import csjavacc.struct.Lookahead; import csjavacc.struct.NonTerminal; import csjavacc.struct.RegularExpression; import csjavacc.struct.TryBlock; import csjavacc.struct.ZeroOrMore; import csjavacc.struct.ZeroOrOne; public class ParseEngine extends CSJavaCCGlobals { static private java.io.PrintWriter ostr; static private int gensymindex = 0; static private int indentamt; static private boolean jj2LA; /** * These lists are used to maintain expansions for which code generation * in phase 2 and phase 3 is required. Whenever a call is generated to * a phase 2 or phase 3 routine, a corresponding entry is added here if * it has not already been added. * The phase 3 routines have been optimized in version 0.7pre2. Essentially * only those methods (and only those portions of these methods) are * generated that are required. The lookahead amount is used to determine * this. This change requires the use of a hash table because it is now * possible for the same phase 3 routine to be requested multiple times * with different lookaheads. The hash table provides a easily searchable * capability to determine the previous requests. * The phase 3 routines now are performed in a two step process - the first * step gathers the requests (replacing requests with lower lookaheads with * those requiring larger lookaheads). The second step then generates these * methods. * This optimization and the hashtable makes it look like we do not need * the flag "phase3done" any more. But this has not been removed yet. */ static private java.util.Vector phase2list = new java.util.Vector(); static private java.util.Vector phase3list = new java.util.Vector(); static private java.util.Hashtable phase3table = new java.util.Hashtable(); /** * The phase 1 routines generates their output into String's and dumps * these String's once for each method. These String's contain the * special characters '\u0001' to indicate a positive indent, and '\u0002' * to indicate a negative indent. '\n' is used to indicate a line terminator. * The characters '\u0003' and '\u0004' are used to delineate portions of * text where '\n's should not be followed by an indentation. */ /** * Returns true if there is a CSCODE production that the argument expansion * may directly expand to (without consuming tokens). */ static private boolean csCodeCheck(Expansion exp) { if (exp instanceof RegularExpression) { return false; } else if (exp instanceof NonTerminal) { NormalProduction prod = ((NonTerminal)exp).prod; if (prod instanceof CSCodeProduction) { return true; } else { return csCodeCheck(prod.expansion); } } else if (exp instanceof Choice) { Choice ch = (Choice)exp; for (int i = 0; i < ch.choices.size(); i++) { if (csCodeCheck((Expansion)(ch.choices.elementAt(i)))) { return true; } } return false; } else if (exp instanceof Sequence) { Sequence seq = (Sequence)exp; for (int i = 0; i < seq.units.size(); i++) { if (csCodeCheck((Expansion)(seq.units.elementAt(i)))) { return true; } else if (!Semanticize.emptyExpansionExists((Expansion)(seq.units.elementAt(i)))) { return false; } } return false; } else if (exp instanceof OneOrMore) { OneOrMore om = (OneOrMore)exp; return csCodeCheck(om.expansion); } else if (exp instanceof ZeroOrMore) { ZeroOrMore zm = (ZeroOrMore)exp; return csCodeCheck(zm.expansion); } else if (exp instanceof ZeroOrOne) { ZeroOrOne zo = (ZeroOrOne)exp; return csCodeCheck(zo.expansion); } else if (exp instanceof TryBlock) { TryBlock tb = (TryBlock)exp; return csCodeCheck(tb.exp); } else { return false; } } /** * An array used to store the first sets generated by the following method. * A true entry means that the corresponding token is in the first set. */ static private boolean[] firstSet; /** * Sets up the array "firstSet" above based on the Expansion argument * passed to it. Since this is a recursive function, it assumes that * "firstSet" has been reset before the first call. */ static private void genFirstSet(Expansion exp) { if (exp instanceof RegularExpression) { firstSet[((RegularExpression)exp).ordinal] = true; } else if (exp instanceof NonTerminal) { genFirstSet(((BNFProduction)(((NonTerminal)exp).prod)).expansion); } else if (exp instanceof Choice) { Choice ch = (Choice)exp; for (int i = 0; i < ch.choices.size(); i++) { genFirstSet((Expansion)(ch.choices.elementAt(i))); } } else if (exp instanceof Sequence) { Sequence seq = (Sequence)exp; Object obj = seq.units.elementAt(0); if ((obj instanceof Lookahead) && (((Lookahead)obj).action_tokens.size() != 0)) { jj2LA = true; } for (int i = 0; i < seq.units.size(); i++) { genFirstSet((Expansion)(seq.units.elementAt(i))); if (!Semanticize.emptyExpansionExists((Expansion)(seq.units.elementAt(i)))) { break; } } } else if (exp instanceof OneOrMore) { OneOrMore om = (OneOrMore)exp; genFirstSet(om.expansion); } else if (exp instanceof ZeroOrMore) { ZeroOrMore zm = (ZeroOrMore)exp; genFirstSet(zm.expansion); } else if (exp instanceof ZeroOrOne) { ZeroOrOne zo = (ZeroOrOne)exp; genFirstSet(zo.expansion); } else if (exp instanceof TryBlock) { TryBlock tb = (TryBlock)exp; genFirstSet(tb.exp); } } /** * Constants used in the following method "buildLookaheadChecker". */ static final int NOOPENSTM = 0; static final int OPENIF = 1; static final int OPENSWITCH = 2; /** * This method takes two parameters - an array of Lookahead's * "conds", and an array of String's "actions". "actions" contains * exactly one element more than "conds". "actions" are Java source * code, and "conds" translate to conditions - so lets say * "f(conds[i])" is true if the lookahead required by "conds[i]" is * indeed the case. This method returns a string corresponding to * the Java code for: * * if (f(conds[0]) actions[0] * else if (f(conds[1]) actions[1] * . . . * else actions[action.length-1] * * A particular action entry ("actions[i]") can be null, in which * case, a noop is generated for that action. */ static String buildLookaheadChecker(Lookahead[] conds, String[] actions) { // The state variables. int state = NOOPENSTM; int indentAmt = 0; boolean[] casedValues = new boolean[tokenCount]; String retval = ""; Lookahead la; Token t = null; int tokenMaskSize = (tokenCount-1)/32 + 1; int[] tokenMask = null; // Iterate over all the conditions. int index = 0; java.util.List needsBreak = new java.util.ArrayList(); while (index < conds.length) { la = conds[index]; jj2LA = false; if ((la.amount == 0) || Semanticize.emptyExpansionExists(la.la_expansion) || csCodeCheck(la.la_expansion) ) { // This handles the following cases: // . If syntactic lookahead is not wanted (and hence explicitly specified // as 0). // . If it is possible for the lookahead expansion to recognize the empty // string - in which case the lookahead trivially passes. // . If the lookahead expansion has a JAVACODE production that it directly // expands to - in which case the lookahead trivially passes. if (la.action_tokens.size() == 0) { // In addition, if there is no semantic lookahead, then the // lookahead trivially succeeds. So break the main loop and // treat this case as the default last action. break; } else { // This case is when there is only semantic lookahead // (without any preceding syntactic lookahead). In this // case, an "if" statement is generated. switch (state) { case NOOPENSTM: retval += "\n" + "if ("; indentAmt++; break; case OPENIF: retval += "\u0002\n" + "} else if ("; break; case OPENSWITCH: retval += "\u0002\n" + "default:" + "\u0001"; if (Options.getErrorReporting()) { retval += "\njj_la1[" + maskindex + "] = jj_gen;"; maskindex++; } needsBreak.add(indentAmt); maskVals.addElement(tokenMask); retval += "\n" + "if ("; indentAmt++; } printTokenSetup((Token)(la.action_tokens.elementAt(0))); for (java.util.Enumeration anEnum = la.action_tokens.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); retval += printToken(t); } retval += printTrailingComments(t); retval += ") {\u0001" + actions[index]; state = OPENIF; } } else if (la.amount == 1 && la.action_tokens.size() == 0) { // Special optimal processing when the lookahead is exactly 1, and there // is no semantic lookahead. if (firstSet == null) { firstSet = new boolean[tokenCount]; } for (int i = 0; i < tokenCount; i++) { firstSet[i] = false; } // jj2LA is set to false at the beginning of the containing "if" statement. // It is checked immediately after the end of the same statement to determine // if lookaheads are to be performed using calls to the jj2 methods. genFirstSet(la.la_expansion); // genFirstSet may find that semantic attributes are appropriate for the next // token. In which case, it sets jj2LA to true. if (!jj2LA) { // This case is if there is no applicable semantic lookahead and the lookahead // is one (excluding the earlier cases such as JAVACODE, etc.). switch (state) { case OPENIF: retval += "\u0002\n" + "} else {\u0001"; // Control flows through to next case. case NOOPENSTM: retval += "\n" + "switch ("; if (Options.getCacheTokens()) { retval += "jj_nt.kind) {\u0001"; } else { retval += "(jj_ntk==-1)?jj_init_ntk():jj_ntk) {\u0001"; } for (int i = 0; i < tokenCount; i++) { casedValues[i] = false; } indentAmt++; tokenMask = new int[tokenMaskSize]; for (int i = 0; i < tokenMaskSize; i++) { tokenMask[i] = 0; } // Don't need to do anything if state is OPENSWITCH. } for (int i = 0; i < tokenCount; i++) { if (firstSet[i]) { if (!casedValues[i]) { casedValues[i] = true; retval += "\u0002\ncase "; int j1 = i/32; int j2 = i%32; tokenMask[j1] |= 1 << j2; String s = (String)(names_of_tokens.get(new Integer(i))); if (s == null) { retval += i; } else { retval += s; } retval += ":\u0001"; } } } retval += actions[index]; retval += "\nbreak;"; state = OPENSWITCH; } } else { // This is the case when lookahead is determined through calls to // jj2 methods. The other case is when lookahead is 1, but semantic // attributes need to be evaluated. Hence this crazy control structure. jj2LA = true; } if (jj2LA) { // In this case lookahead is determined by the jj2 methods. switch (state) { case NOOPENSTM: retval += "\n" + "if ("; indentAmt++; break; case OPENIF: retval += "\u0002\n" + "} else if ("; break; case OPENSWITCH: retval += "\u0002\n" + "default:" + "\u0001"; if (Options.getErrorReporting()) { retval += "\njj_la1[" + maskindex + "] = jj_gen;"; maskindex++; } needsBreak.add(indentAmt); maskVals.addElement(tokenMask); retval += "\n" + "if ("; indentAmt++; } jj2index++; // At this point, la.la_expansion.internal_name must be "". la.la_expansion.internal_name = "_" + jj2index; phase2list.addElement(la); retval += "jj_2" + la.la_expansion.internal_name + "(" + la.amount + ")"; if (la.action_tokens.size() != 0) { // In addition, there is also a semantic lookahead. So concatenate // the semantic check with the syntactic one. retval += " && ("; printTokenSetup((Token)(la.action_tokens.elementAt(0))); for (java.util.Enumeration anEnum = la.action_tokens.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); retval += printToken(t); } retval += printTrailingComments(t); retval += ")"; } retval += ") {\u0001" + actions[index]; state = OPENIF; } index++; } // Generate code for the default case. Note this may not // be the last entry of "actions" if any condition can be // statically determined to be always "true". switch (state) { case NOOPENSTM: retval += actions[index]; break; case OPENIF: retval += "\u0002\n" + "} else {\u0001" + actions[index]; break; case OPENSWITCH: retval += "\u0002\n" + "default:" + "\u0001"; if (Options.getErrorReporting()) { retval += "\njj_la1[" + maskindex + "] = jj_gen;"; maskVals.addElement(tokenMask); maskindex++; } retval += actions[index]; // New for CS... need to break empty default cases retval += "break;\n"; } for (int i = 0; i < indentAmt; i++){ retval += "\u0002\n}"; if(needsBreak.contains(indentAmt-i-1)){ needsBreak.remove((Object)(indentAmt-i-1)); retval += "break;"; } } return retval; } static void dumpFormattedString(String str) { char ch = ' '; char prevChar; boolean indentOn = true; for (int i = 0; i < str.length(); i++) { prevChar = ch; ch = str.charAt(i); if (ch == '\n' && prevChar == '\r') { // do nothing - we've already printed a new line for the '\r' // during the previous iteration. } else if (ch == '\n' || ch == '\r') { if (indentOn) { phase1NewLine(); } else { ostr.println(""); } } else if (ch == '\u0001') { indentamt += 2; } else if (ch == '\u0002') { indentamt -= 2; } else if (ch == '\u0003') { indentOn = false; } else if (ch == '\u0004') { indentOn = true; } else { ostr.print(ch); } } } static void buildPhase1Routine(BNFProduction p) { Token t; t = (Token)(p.return_type_tokens.elementAt(0)); boolean voidReturn = false; if (t.kind == CSJavaCCParserConstants.VOID) { voidReturn = true; } printTokenSetup(t); ccol = 1; printLeadingComments(t, ostr); ostr.print(" " + staticOpt() +(p.accessMod != null ? p.accessMod : "public")+ " "); cline = t.beginLine; ccol = t.beginColumn; printTokenOnly(t, ostr); for (int i = 1; i < p.return_type_tokens.size(); i++) { t = (Token)(p.return_type_tokens.elementAt(i)); printToken(t, ostr); } printTrailingComments(t, ostr); ostr.print(" " + p.lhs + "("); if (p.parameter_list_tokens.size() != 0) { printTokenSetup((Token)(p.parameter_list_tokens.elementAt(0))); for (java.util.Enumeration anEnum = p.parameter_list_tokens.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); printToken(t, ostr); } printTrailingComments(t, ostr); } ostr.print(")"); for (java.util.Enumeration anEnum = p.throws_list.elements(); anEnum.hasMoreElements();) { ostr.print(", "); java.util.Vector name = (java.util.Vector)anEnum.nextElement(); for (java.util.Enumeration enum1 = name.elements(); enum1.hasMoreElements();) { t = (Token)enum1.nextElement(); ostr.print(t.image); } } ostr.print(" {"); indentamt = 4; if (Options.getDebugParser()) { ostr.println(""); ostr.println(" trace_call(\"" + p.lhs + "\");"); ostr.print(" try {"); indentamt = 6; } if (p.declaration_tokens.size() != 0) { printTokenSetup((Token)(p.declaration_tokens.elementAt(0))); cline--; for (java.util.Enumeration anEnum = p.declaration_tokens.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); printToken(t, ostr); } printTrailingComments(t, ostr); } String code = phase1ExpansionGen(p.expansion); dumpFormattedString(code); ostr.println(""); if (p.jumpPatched && !voidReturn) { ostr.println(" throw new System.Exception(\"Missing return statement in function\");"); } if (Options.getDebugParser()) { ostr.println(" } finally {"); ostr.println(" trace_return(\"" + p.lhs + "\");"); ostr.println(" }"); } ostr.println(" }"); ostr.println(""); } static void phase1NewLine() { ostr.println(""); for (int i = 0; i < indentamt; i++) { ostr.print(" "); } } static String phase1ExpansionGen(Expansion e) { String retval = ""; Token t = null; Lookahead[] conds; String[] actions; if (e instanceof RegularExpression) { RegularExpression e_nrw = (RegularExpression)e; retval += "\n"; if (e_nrw.lhsTokens.size() != 0) { printTokenSetup((Token)(e_nrw.lhsTokens.elementAt(0))); for (java.util.Enumeration anEnum = e_nrw.lhsTokens.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); retval += printToken(t); } retval += printTrailingComments(t); retval += " = "; } String tail = e_nrw.rhsToken == null ? ");" : ")." + e_nrw.rhsToken.image + ";"; if (e_nrw.label.equals("")) { Object label = names_of_tokens.get(new Integer(e_nrw.ordinal)); if (label != null) { retval += "jj_consume_token(" + (String)label + tail; } else { retval += "jj_consume_token(" + e_nrw.ordinal + tail; } } else { retval += "jj_consume_token(" + e_nrw.label + tail; } } else if (e instanceof NonTerminal) { NonTerminal e_nrw = (NonTerminal)e; retval += "\n"; if (e_nrw.lhsTokens.size() != 0) { printTokenSetup((Token)(e_nrw.lhsTokens.elementAt(0))); for (java.util.Enumeration anEnum = e_nrw.lhsTokens.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); retval += printToken(t); } retval += printTrailingComments(t); retval += " = "; } retval += e_nrw.name + "("; if (e_nrw.argument_tokens.size() != 0) { printTokenSetup((Token)(e_nrw.argument_tokens.elementAt(0))); for (java.util.Enumeration anEnum = e_nrw.argument_tokens.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); retval += printToken(t); } retval += printTrailingComments(t); } retval += ");"; } else if (e instanceof Action) { Action e_nrw = (Action)e; retval += "\u0003\n"; if (e_nrw.action_tokens.size() != 0) { printTokenSetup((Token)(e_nrw.action_tokens.elementAt(0))); ccol = 1; for (java.util.Enumeration anEnum = e_nrw.action_tokens.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); retval += printToken(t); } retval += printTrailingComments(t); } retval += "\u0004"; } else if (e instanceof Choice) { Choice e_nrw = (Choice)e; conds = new Lookahead[e_nrw.choices.size()]; actions = new String[e_nrw.choices.size() + 1]; actions[e_nrw.choices.size()] = "\n" + "jj_consume_token(-1);\n" + "throw new ParseException();"; // In previous line, the "throw" never throws an exception since the // evaluation of jj_consume_token(-1) causes ParseException to be // thrown first. Sequence nestedSeq; for (int i = 0; i < e_nrw.choices.size(); i++) { nestedSeq = (Sequence)(e_nrw.choices.elementAt(i)); actions[i] = phase1ExpansionGen(nestedSeq); conds[i] = (Lookahead)(nestedSeq.units.elementAt(0)); } retval = buildLookaheadChecker(conds, actions); } else if (e instanceof Sequence) { Sequence e_nrw = (Sequence)e; // We skip the first element in the following iteration since it is the // Lookahead object. for (int i = 1; i < e_nrw.units.size(); i++) { retval += phase1ExpansionGen((Expansion)(e_nrw.units.elementAt(i))); } } else if (e instanceof OneOrMore) { OneOrMore e_nrw = (OneOrMore)e; Expansion nested_e = e_nrw.expansion; Lookahead la; if (nested_e instanceof Sequence) { la = (Lookahead)(((Sequence)nested_e).units.elementAt(0)); } else { la = new Lookahead(); la.amount = Options.getLookahead(); la.la_expansion = nested_e; } retval += "\n"; int labelIndex = ++gensymindex; // Removed for CS fix... //retval += "label_" + labelIndex + ":\n"; retval += "while (true) {\u0001"; retval += phase1ExpansionGen(nested_e); conds = new Lookahead[1]; conds[0] = la; actions = new String[2]; actions[0] = "\n;"; //actions[1] = "\nbreak label_" + labelIndex + ";"; // New for CS fix... actions[1] = "\ngoto label_" + labelIndex + ";"; retval += buildLookaheadChecker(conds, actions); retval += "\u0002\n" + "}\n"; // New for CS fix... also needs an empty statement, in case it comes // at the end of a block/method retval += "label_" + labelIndex + ": ;\n"; } else if (e instanceof ZeroOrMore) { ZeroOrMore e_nrw = (ZeroOrMore)e; Expansion nested_e = e_nrw.expansion; Lookahead la; if (nested_e instanceof Sequence) { la = (Lookahead)(((Sequence)nested_e).units.elementAt(0)); } else { la = new Lookahead(); la.amount = Options.getLookahead(); la.la_expansion = nested_e; } retval += "\n"; int labelIndex = ++gensymindex; // Removed for CS fix... //retval += "label_" + labelIndex + ":\n"; retval += "while (true) {\u0001"; conds = new Lookahead[1]; conds[0] = la; actions = new String[2]; actions[0] = "\n;"; //actions[1] = "\nbreak label_" + labelIndex + ";"; // New for CS fix... actions[1] = "\ngoto label_" + labelIndex + ";"; retval += buildLookaheadChecker(conds, actions); retval += phase1ExpansionGen(nested_e); retval += "\u0002\n" + "}\n"; // New for CS fix... retval += "label_" + labelIndex + ": ;\n"; } else if (e instanceof ZeroOrOne) { ZeroOrOne e_nrw = (ZeroOrOne)e; Expansion nested_e = e_nrw.expansion; Lookahead la; if (nested_e instanceof Sequence) { la = (Lookahead)(((Sequence)nested_e).units.elementAt(0)); } else { la = new Lookahead(); la.amount = Options.getLookahead(); la.la_expansion = nested_e; } conds = new Lookahead[1]; conds[0] = la; actions = new String[2]; actions[0] = phase1ExpansionGen(nested_e); actions[1] = "\n;"; retval += buildLookaheadChecker(conds, actions); } else if (e instanceof TryBlock) { TryBlock e_nrw = (TryBlock)e; Expansion nested_e = e_nrw.exp; java.util.Vector v; retval += "\n"; retval += "try {\u0001"; retval += phase1ExpansionGen(nested_e); retval += "\u0002\n" + "}"; for (int i = 0; i < e_nrw.catchblks.size(); i++) { retval += " catch ("; v = (java.util.Vector)(e_nrw.types.elementAt(i)); if (v.size() != 0) { printTokenSetup((Token)(v.elementAt(0))); for (java.util.Enumeration anEnum = v.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); retval += printToken(t); } retval += printTrailingComments(t); } retval += " "; t = (Token)(e_nrw.ids.elementAt(i)); printTokenSetup(t); retval += printToken(t); retval += printTrailingComments(t); retval += ") {\u0003\n"; v = (java.util.Vector)(e_nrw.catchblks.elementAt(i)); if (v.size() != 0) { printTokenSetup((Token)(v.elementAt(0))); ccol = 1; for (java.util.Enumeration anEnum = v.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); retval += printToken(t); } retval += printTrailingComments(t); } retval += "\u0004\n" + "}"; } if (e_nrw.finallyblk != null) { retval += " finally {\u0003\n"; if (e_nrw.finallyblk.size() != 0) { printTokenSetup((Token)(e_nrw.finallyblk.elementAt(0))); ccol = 1; for (java.util.Enumeration anEnum = e_nrw.finallyblk.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); retval += printToken(t); } retval += printTrailingComments(t); } retval += "\u0004\n" + "}"; } } return retval; } static void buildPhase2Routine(Lookahead la) { Expansion e = la.la_expansion; ostr.println(" " + staticOpt() + "private bool jj_2" + e.internal_name + "(int xla) {"); ostr.println(" jj_la = xla; jj_lastpos = jj_scanpos = token;"); ostr.println(" try { return !jj_3" + e.internal_name + "(); }"); ostr.println(" catch(LookaheadSuccess ls) { return true; }"); if (Options.getErrorReporting()) ostr.println(" finally { jj_save(" + (Integer.parseInt(e.internal_name.substring(1))-1) + ", xla); }"); ostr.println(" }"); ostr.println(""); Phase3Data p3d = new Phase3Data(e, la.amount); phase3list.addElement(p3d); phase3table.put(e, p3d); } static private boolean xsp_declared; static Expansion jj3_expansion; static String genReturn(boolean value) { String retval = (value ? "true" : "false"); if (Options.getDebugLookahead() && jj3_expansion != null) { String tracecode = "trace_return(\"" + ((NormalProduction)jj3_expansion.parent).lhs + "(LOOKAHEAD " + (value ? "FAILED" : "SUCCEEDED") + ")\");"; if (Options.getErrorReporting()) { tracecode = "if (!jj_rescan) " + tracecode; } return "{ " + tracecode + " return " + retval + "; }"; } else { return "return " + retval + ";"; } } private static void generate3R(Expansion e, Phase3Data inf) { Expansion seq = e; if (e.internal_name.equals("")) { while (true) { if (seq instanceof Sequence && ((Sequence)seq).units.size() == 2) { seq = (Expansion)((Sequence)seq).units.elementAt(1); } else if (seq instanceof NonTerminal) { NonTerminal e_nrw = (NonTerminal)seq; NormalProduction ntprod = (NormalProduction)(production_table.get(e_nrw.name)); if (ntprod instanceof CSCodeProduction) { break; // nothing to do here } else { seq = ntprod.expansion; } } else break; } if (seq instanceof RegularExpression) { e.internal_name = "jj_scan_token(" + ((RegularExpression)seq).ordinal + ")"; return; } gensymindex++; //if (gensymindex == 100) //{ //new Error().printStackTrace(); //System.err.println(" ***** seq: " + seq.internal_name + "; size: " + ((Sequence)seq).units.size()); //} e.internal_name = "R_" + gensymindex; } Phase3Data p3d = (Phase3Data)(phase3table.get(e)); if (p3d == null || p3d.count < inf.count) { p3d = new Phase3Data(e, inf.count); phase3list.addElement(p3d); phase3table.put(e, p3d); } } static void setupPhase3Builds(Phase3Data inf) { Expansion e = inf.exp; if (e instanceof RegularExpression) { ; // nothing to here } else if (e instanceof NonTerminal) { // All expansions of non-terminals have the "name" fields set. So // there's no need to check it below for "e_nrw" and "ntexp". In // fact, we rely here on the fact that the "name" fields of both these // variables are the same. NonTerminal e_nrw = (NonTerminal)e; NormalProduction ntprod = (NormalProduction)(production_table.get(e_nrw.name)); if (ntprod instanceof CSCodeProduction) { ; // nothing to do here } else { generate3R(ntprod.expansion, inf); } } else if (e instanceof Choice) { Choice e_nrw = (Choice)e; for (int i = 0; i < e_nrw.choices.size(); i++) { generate3R((Expansion)(e_nrw.choices.elementAt(i)), inf); } } else if (e instanceof Sequence) { Sequence e_nrw = (Sequence)e; // We skip the first element in the following iteration since it is the // Lookahead object. int cnt = inf.count; for (int i = 1; i < e_nrw.units.size(); i++) { Expansion eseq = (Expansion)(e_nrw.units.elementAt(i)); setupPhase3Builds(new Phase3Data(eseq, cnt)); cnt -= minimumSize(eseq); if (cnt <= 0) break; } } else if (e instanceof TryBlock) { TryBlock e_nrw = (TryBlock)e; setupPhase3Builds(new Phase3Data(e_nrw.exp, inf.count)); } else if (e instanceof OneOrMore) { OneOrMore e_nrw = (OneOrMore)e; generate3R(e_nrw.expansion, inf); } else if (e instanceof ZeroOrMore) { ZeroOrMore e_nrw = (ZeroOrMore)e; generate3R(e_nrw.expansion, inf); } else if (e instanceof ZeroOrOne) { ZeroOrOne e_nrw = (ZeroOrOne)e; generate3R(e_nrw.expansion, inf); } } private static String genjj_3Call(Expansion e) { if (e.internal_name.startsWith("jj_scan_token")) return e.internal_name; else return "jj_3" + e.internal_name + "()"; } static Hashtable generated = new Hashtable(); static void buildPhase3Routine(Phase3Data inf, boolean recursive_call) { Expansion e = inf.exp; Token t = null; if (e.internal_name.startsWith("jj_scan_token")) return; if (!recursive_call) { ostr.println(" " + staticOpt() + "private bool jj_3" + e.internal_name + "() {"); xsp_declared = false; if (Options.getDebugLookahead() && e.parent instanceof NormalProduction) { ostr.print(" "); if (Options.getErrorReporting()) { ostr.print("if (!jj_rescan) "); } ostr.println("trace_call(\"" + ((NormalProduction)e.parent).lhs + "(LOOKING AHEAD...)\");"); jj3_expansion = e; } else { jj3_expansion = null; } } if (e instanceof RegularExpression) { RegularExpression e_nrw = (RegularExpression)e; if (e_nrw.label.equals("")) { Object label = names_of_tokens.get(new Integer(e_nrw.ordinal)); if (label != null) { ostr.println(" if (jj_scan_token(" + (String)label + ")) " + genReturn(true)); } else { ostr.println(" if (jj_scan_token(" + e_nrw.ordinal + ")) " + genReturn(true)); } } else { ostr.println(" if (jj_scan_token(" + e_nrw.label + ")) " + genReturn(true)); } //ostr.println(" if (jj_la == 0 && jj_scanpos == jj_lastpos) " + genReturn(false)); } else if (e instanceof NonTerminal) { // All expansions of non-terminals have the "name" fields set. So // there's no need to check it below for "e_nrw" and "ntexp". In // fact, we rely here on the fact that the "name" fields of both these // variables are the same. NonTerminal e_nrw = (NonTerminal)e; NormalProduction ntprod = (NormalProduction)(production_table.get(e_nrw.name)); if (ntprod instanceof CSCodeProduction) { ostr.println(" if (true) { jj_la = 0; jj_scanpos = jj_lastpos; " + genReturn(false) + "}"); } else { Expansion ntexp = ntprod.expansion; //ostr.println(" if (jj_3" + ntexp.internal_name + "()) " + genReturn(true)); ostr.println(" if (" + genjj_3Call(ntexp)+ ") " + genReturn(true)); //ostr.println(" if (jj_la == 0 && jj_scanpos == jj_lastpos) " + genReturn(false)); } } else if (e instanceof Choice) { if (!xsp_declared) { xsp_declared = true; ostr.println(" Token xsp;"); } ostr.println(" xsp = jj_scanpos;"); Sequence nested_seq; Choice e_nrw = (Choice)e; for (int i = 0; i < e_nrw.choices.size(); i++) { nested_seq = (Sequence)(e_nrw.choices.elementAt(i)); Lookahead la = (Lookahead)(nested_seq.units.elementAt(0)); if (la.action_tokens.size() != 0) { // We have semantic lookahead that must be evaluated. ostr.println(" lookingAhead = true;"); ostr.print(" jj_semLA = "); printTokenSetup((Token)(la.action_tokens.elementAt(0))); for (java.util.Enumeration anEnum = la.action_tokens.elements(); anEnum.hasMoreElements();) { t = (Token)anEnum.nextElement(); printToken(t, ostr); } printTrailingComments(t, ostr); ostr.println(";"); ostr.println(" lookingAhead = false;"); } ostr.print(" if ("); if (la.action_tokens.size() != 0) { ostr.print("!jj_semLA || "); } if (i != e_nrw.choices.size() - 1) { //ostr.println("jj_3" + nested_seq.internal_name + "()) {"); ostr.println(genjj_3Call(nested_seq) + ") {"); ostr.println(" jj_scanpos = xsp;"); } else { //ostr.println("jj_3" + nested_seq.internal_name + "()) " + genReturn(true)); ostr.println(genjj_3Call(nested_seq) + ") " + genReturn(true)); //ostr.println(" if (jj_la == 0 && jj_scanpos == jj_lastpos) " + genReturn(false)); } } for (int i = 1; i < e_nrw.choices.size(); i++) { //ostr.println(" } else if (jj_la == 0 && jj_scanpos == jj_lastpos) " + genReturn(false)); ostr.println(" }"); } } else if (e instanceof Sequence) { Sequence e_nrw = (Sequence)e; // We skip the first element in the following iteration since it is the // Lookahead object. int cnt = inf.count; for (int i = 1; i < e_nrw.units.size(); i++) { Expansion eseq = (Expansion)(e_nrw.units.elementAt(i)); buildPhase3Routine(new Phase3Data(eseq, cnt), true); //System.err.println("minimumSize: line: " + eseq.line + ", column: " + eseq.column + ": " + minimumSize(eseq));//Test Code cnt -= minimumSize(eseq); if (cnt <= 0) break; } } else if (e instanceof TryBlock) { TryBlock e_nrw = (TryBlock)e; buildPhase3Routine(new Phase3Data(e_nrw.exp, inf.count), true); } else if (e instanceof OneOrMore) { if (!xsp_declared) { xsp_declared = true; ostr.println(" Token xsp;"); } OneOrMore e_nrw = (OneOrMore)e; Expansion nested_e = e_nrw.expansion; //ostr.println(" if (jj_3" + nested_e.internal_name + "()) " + genReturn(true)); ostr.println(" if (" + genjj_3Call(nested_e) + ") " + genReturn(true)); //ostr.println(" if (jj_la == 0 && jj_scanpos == jj_lastpos) " + genReturn(false)); ostr.println(" while (true) {"); ostr.println(" xsp = jj_scanpos;"); //ostr.println(" if (jj_3" + nested_e.internal_name + "()) { jj_scanpos = xsp; break; }"); ostr.println(" if (" + genjj_3Call(nested_e) + ") { jj_scanpos = xsp; break; }"); //ostr.println(" if (jj_la == 0 && jj_scanpos == jj_lastpos) " + genReturn(false)); ostr.println(" }"); } else if (e instanceof ZeroOrMore) { if (!xsp_declared) { xsp_declared = true; ostr.println(" Token xsp;"); } ZeroOrMore e_nrw = (ZeroOrMore)e; Expansion nested_e = e_nrw.expansion; ostr.println(" while (true) {"); ostr.println(" xsp = jj_scanpos;"); //ostr.println(" if (jj_3" + nested_e.internal_name + "()) { jj_scanpos = xsp; break; }"); ostr.println(" if (" + genjj_3Call(nested_e) + ") { jj_scanpos = xsp; break; }"); //ostr.println(" if (jj_la == 0 && jj_scanpos == jj_lastpos) " + genReturn(false)); ostr.println(" }"); } else if (e instanceof ZeroOrOne) { if (!xsp_declared) { xsp_declared = true; ostr.println(" Token xsp;"); } ZeroOrOne e_nrw = (ZeroOrOne)e; Expansion nested_e = e_nrw.expansion; ostr.println(" xsp = jj_scanpos;"); //ostr.println(" if (jj_3" + nested_e.internal_name + "()) jj_scanpos = xsp;"); ostr.println(" if (" + genjj_3Call(nested_e) + ") jj_scanpos = xsp;"); //ostr.println(" else if (jj_la == 0 && jj_scanpos == jj_lastpos) " + genReturn(false)); } if (!recursive_call) { ostr.println(" " + genReturn(false)); ostr.println(" }"); ostr.println(""); } } static int minimumSize(Expansion e) { return minimumSize(e, Integer.MAX_VALUE); } /* * Returns the minimum number of tokens that can parse to this expansion. */ static int minimumSize(Expansion e, int oldMin) { int retval = 0; // should never be used. Will be bad if it is. if (e.inMinimumSize) { // recursive search for minimum size unnecessary. return Integer.MAX_VALUE; } e.inMinimumSize = true; if (e instanceof RegularExpression) { retval = 1; } else if (e instanceof NonTerminal) { NonTerminal e_nrw = (NonTerminal)e; NormalProduction ntprod = (NormalProduction)(production_table.get(e_nrw.name)); if (ntprod instanceof CSCodeProduction) { retval = Integer.MAX_VALUE; // Make caller think this is unending (for we do not go beyond JAVACODE during // phase3 execution). } else { Expansion ntexp = ntprod.expansion; retval = minimumSize(ntexp); } } else if (e instanceof Choice) { int min = oldMin; Expansion nested_e; Choice e_nrw = (Choice)e; for (int i = 0; min > 1 && i < e_nrw.choices.size(); i++) { nested_e = (Expansion)(e_nrw.choices.elementAt(i)); int min1 = minimumSize(nested_e, min); if (min > min1) min = min1; } retval = min; } else if (e instanceof Sequence) { int min = 0; Sequence e_nrw = (Sequence)e; // We skip the first element in the following iteration since it is the // Lookahead object. for (int i = 1; i < e_nrw.units.size(); i++) { Expansion eseq = (Expansion)(e_nrw.units.elementAt(i)); int mineseq = minimumSize(eseq); if (min == Integer.MAX_VALUE || mineseq == Integer.MAX_VALUE) { min = Integer.MAX_VALUE; // Adding infinity to something results in infinity. } else { min += mineseq; if (min > oldMin) break; } } retval = min; } else if (e instanceof TryBlock) { TryBlock e_nrw = (TryBlock)e; retval = minimumSize(e_nrw.exp); } else if (e instanceof OneOrMore) { OneOrMore e_nrw = (OneOrMore)e; retval = minimumSize(e_nrw.expansion); } else if (e instanceof ZeroOrMore) { retval = 0; } else if (e instanceof ZeroOrOne) { retval = 0; } else if (e instanceof Lookahead) { retval = 0; } else if (e instanceof Action) { retval = 0; } e.inMinimumSize = false; return retval; } static void build(java.io.PrintWriter ps) { NormalProduction p; CSCodeProduction jp; Token t = null; ostr = ps; for (java.util.Enumeration anEnum = bnfproductions.elements(); anEnum.hasMoreElements();) { p = (NormalProduction)anEnum.nextElement(); if (p instanceof CSCodeProduction) { jp = (CSCodeProduction)p; t = (Token)(jp.return_type_tokens.elementAt(0)); printTokenSetup(t); ccol = 1; printLeadingComments(t, ostr); ostr.print(" " + staticOpt() + (p.accessMod != null ? p.accessMod + " " : "")); cline = t.beginLine; ccol = t.beginColumn; printTokenOnly(t, ostr); for (int i = 1; i < jp.return_type_tokens.size(); i++) { t = (Token)(jp.return_type_tokens.elementAt(i)); printToken(t, ostr); } printTrailingComments(t, ostr); ostr.print(" " + jp.lhs + "("); if (jp.parameter_list_tokens.size() != 0) { printTokenSetup((Token)(jp.parameter_list_tokens.elementAt(0))); for (java.util.Enumeration enum1 = jp.parameter_list_tokens.elements(); enum1.hasMoreElements();) { t = (Token)enum1.nextElement(); printToken(t, ostr); } printTrailingComments(t, ostr); } ostr.print(") throws ParseException"); for (java.util.Enumeration enum1 = jp.throws_list.elements(); enum1.hasMoreElements();) { ostr.print(", "); java.util.Vector name = (java.util.Vector)enum1.nextElement(); for (java.util.Enumeration enum2 = name.elements(); enum2.hasMoreElements();) { t = (Token)enum2.nextElement(); ostr.print(t.image); } } ostr.print(" {"); if (Options.getDebugParser()) { ostr.println(""); ostr.println(" trace_call(\"" + jp.lhs + "\");"); ostr.print(" try {"); } if (jp.code_tokens.size() != 0) { printTokenSetup((Token)(jp.code_tokens.elementAt(0))); cline--; for (java.util.Enumeration enum1 = jp.code_tokens.elements(); enum1.hasMoreElements();) { t = (Token)enum1.nextElement(); printToken(t, ostr); } printTrailingComments(t, ostr); } ostr.println(""); if (Options.getDebugParser()) { ostr.println(" } finally {"); ostr.println(" trace_return(\"" + jp.lhs + "\");"); ostr.println(" }"); } ostr.println(" }"); ostr.println(""); } else { buildPhase1Routine((BNFProduction)p); } } for (int phase2index = 0; phase2index < phase2list.size(); phase2index++) { buildPhase2Routine((Lookahead)(phase2list.elementAt(phase2index))); } int phase3index = 0; while (phase3index < phase3list.size()) { for (; phase3index < phase3list.size(); phase3index++) { setupPhase3Builds((Phase3Data)(phase3list.elementAt(phase3index))); } } for (java.util.Enumeration anEnum = phase3table.elements(); anEnum.hasMoreElements();) { buildPhase3Routine((Phase3Data)(anEnum.nextElement()), false); } } public static void reInit() { ostr = null; gensymindex = 0; indentamt = 0; jj2LA = false; phase2list = new java.util.Vector(); phase3list = new java.util.Vector(); phase3table = new java.util.Hashtable(); firstSet = null; xsp_declared = false; jj3_expansion = null; } } /* * This class stored information to pass from phase 2 to phase 3. */ class Phase3Data { /* * This is the expansion to generate the jj3 method for. */ Expansion exp; /* * This is the number of tokens that can still be consumed. This * number is used to limit the number of jj3 methods generated. */ int count; Phase3Data(Expansion e, int c) { exp = e; count = c; } }