org.javacc.parser.Semanticize Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ph-javacc-maven-plugin Show documentation
Show all versions of ph-javacc-maven-plugin Show documentation
Maven 3 Plugin for processing JavaCC grammar files.
/* Copyright (c) 2006, Sun Microsystems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Sun Microsystems, Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.javacc.parser;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
public class Semanticize extends JavaCCGlobals
{
static List removeList = new ArrayList ();
static List itemList = new ArrayList ();
static void prepareToRemove (final List vec, final Object item)
{
removeList.add (vec);
itemList.add (item);
}
static void removePreparedItems ()
{
for (int i = 0; i < removeList.size (); i++)
{
final List list = (List) (removeList.get (i));
list.remove (itemList.get (i));
}
removeList.clear ();
itemList.clear ();
}
static public void start () throws MetaParseException
{
if (JavaCCErrors.get_error_count () != 0)
throw new MetaParseException ();
if (Options.getLookahead () > 1 && !Options.getForceLaCheck () && Options.getSanityCheck ())
{
JavaCCErrors.warning ("Lookahead adequacy checking not being performed since option LOOKAHEAD "
+ "is more than 1. Set option FORCE_LA_CHECK to true to force checking.");
}
/*
* The following walks the entire parse tree to convert all LOOKAHEAD's that
* are not at choice points (but at beginning of sequences) and converts
* them to trivial choices. This way, their semantic lookahead specification
* can be evaluated during other lookahead evaluations.
*/
for (final Iterator it = bnfproductions.iterator (); it.hasNext ();)
{
ExpansionTreeWalker.postOrderWalk (((NormalProduction) it.next ()).getExpansion (), new LookaheadFixer ());
}
/*
* The following loop populates "production_table"
*/
for (final Iterator it = bnfproductions.iterator (); it.hasNext ();)
{
final NormalProduction p = (NormalProduction) it.next ();
if (production_table.put (p.getLhs (), p) != null)
{
JavaCCErrors.semantic_error (p, p.getLhs () + " occurs on the left hand side of more than one production.");
}
}
/*
* The following walks the entire parse tree to make sure that all
* non-terminals on RHS's are defined on the LHS.
*/
for (final Iterator it = bnfproductions.iterator (); it.hasNext ();)
{
ExpansionTreeWalker.preOrderWalk (((NormalProduction) it.next ()).getExpansion (),
new ProductionDefinedChecker ());
}
/*
* The following loop ensures that all target lexical states are defined.
* Also piggybacking on this loop is the detection of and in
* token productions. After reporting an error, these entries are removed.
* Also checked are definitions on inline private regular expressions. This
* loop works slightly differently when USER_TOKEN_MANAGER is set to true.
* In this case, occurrences are OK, while regular expression specs
* generate a warning.
*/
for (final Iterator it = rexprlist.iterator (); it.hasNext ();)
{
final TokenProduction tp = (TokenProduction) (it.next ());
final List respecs = tp.respecs;
for (final Iterator it1 = respecs.iterator (); it1.hasNext ();)
{
final RegExprSpec res = (RegExprSpec) (it1.next ());
if (res.nextState != null)
{
if (lexstate_S2I.get (res.nextState) == null)
{
JavaCCErrors.semantic_error (res.nsTok, "Lexical state \"" + res.nextState + "\" has not been defined.");
}
}
if (res.rexp instanceof REndOfFile)
{
// JavaCCErrors.semantic_error(res.rexp, "Badly placed .");
if (tp.lexStates != null)
JavaCCErrors.semantic_error (res.rexp, "EOF action/state change must be specified for all states, "
+ "i.e., <*>TOKEN:.");
if (tp.kind != TokenProduction.TOKEN)
JavaCCErrors.semantic_error (res.rexp, "EOF action/state change can be specified only in a "
+ "TOKEN specification.");
if (nextStateForEof != null || actForEof != null)
JavaCCErrors.semantic_error (res.rexp, "Duplicate action/state change specification for .");
actForEof = res.act;
nextStateForEof = res.nextState;
prepareToRemove (respecs, res);
}
else
if (tp.isExplicit && Options.getUserTokenManager ())
{
JavaCCErrors.warning (res.rexp, "Ignoring regular expression specification since "
+ "option USER_TOKEN_MANAGER has been set to true.");
}
else
if (tp.isExplicit && !Options.getUserTokenManager () && res.rexp instanceof RJustName)
{
JavaCCErrors.warning (res.rexp,
"Ignoring free-standing regular expression reference. " +
"If you really want this, you must give it a different label as >.");
prepareToRemove (respecs, res);
}
else
if (!tp.isExplicit && res.rexp.private_rexp)
{
JavaCCErrors.semantic_error (res.rexp, "Private (#) regular expression cannot be defined within "
+ "grammar productions.");
}
}
}
removePreparedItems ();
/*
* The following loop inserts all names of regular expressions into
* "named_tokens_table" and "ordered_named_tokens". Duplications are flagged
* as errors.
*/
for (final Iterator it = rexprlist.iterator (); it.hasNext ();)
{
final TokenProduction tp = (TokenProduction) (it.next ());
final List respecs = tp.respecs;
for (final Iterator it1 = respecs.iterator (); it1.hasNext ();)
{
final RegExprSpec res = (RegExprSpec) (it1.next ());
if (!(res.rexp instanceof RJustName) && !res.rexp.label.equals (""))
{
final String s = res.rexp.label;
final Object obj = named_tokens_table.put (s, res.rexp);
if (obj != null)
{
JavaCCErrors.semantic_error (res.rexp, "Multiply defined lexical token name \"" + s + "\".");
}
else
{
ordered_named_tokens.add (res.rexp);
}
if (lexstate_S2I.get (s) != null)
{
JavaCCErrors.semantic_error (res.rexp, "Lexical token name \"" +
s +
"\" is the same as " +
"that of a lexical state.");
}
}
}
}
/*
* The following code merges multiple uses of the same string in the same
* lexical state and produces error messages when there are multiple
* explicit occurrences (outside the BNF) of the string in the same lexical
* state, or when within BNF occurrences of a string are duplicates of those
* that occur as non-TOKEN's (SKIP, MORE, SPECIAL_TOKEN) or private regular
* expressions. While doing this, this code also numbers all regular
* expressions (by setting their ordinal values), and populates the table
* "names_of_tokens".
*/
tokenCount = 1;
for (final Iterator it = rexprlist.iterator (); it.hasNext ();)
{
final TokenProduction tp = (TokenProduction) (it.next ());
final List respecs = tp.respecs;
if (tp.lexStates == null)
{
tp.lexStates = new String [lexstate_I2S.size ()];
int i = 0;
for (final Enumeration enum1 = lexstate_I2S.elements (); enum1.hasMoreElements ();)
{
tp.lexStates[i++] = (String) (enum1.nextElement ());
}
}
final Hashtable table[] = new Hashtable [tp.lexStates.length];
for (int i = 0; i < tp.lexStates.length; i++)
{
table[i] = (Hashtable) simple_tokens_table.get (tp.lexStates[i]);
}
for (final Iterator it1 = respecs.iterator (); it1.hasNext ();)
{
final RegExprSpec res = (RegExprSpec) (it1.next ());
if (res.rexp instanceof RStringLiteral)
{
final RStringLiteral sl = (RStringLiteral) res.rexp;
// This loop performs the checks and actions with respect to each
// lexical state.
for (int i = 0; i < table.length; i++)
{
// Get table of all case variants of "sl.image" into table2.
Hashtable table2 = (Hashtable) (table[i].get (sl.image.toUpperCase ()));
if (table2 == null)
{
// There are no case variants of "sl.image" earlier than the
// current one.
// So go ahead and insert this item.
if (sl.ordinal == 0)
{
sl.ordinal = tokenCount++;
}
table2 = new Hashtable ();
table2.put (sl.image, sl);
table[i].put (sl.image.toUpperCase (), table2);
}
else
if (hasIgnoreCase (table2, sl.image))
{ // hasIgnoreCase sets "other" if it is found.
// Since IGNORE_CASE version exists, current one is useless and
// bad.
if (!sl.tpContext.isExplicit)
{
// inline BNF string is used earlier with an IGNORE_CASE.
JavaCCErrors.semantic_error (sl, "String \"" +
sl.image +
"\" can never be matched " +
"due to presence of more general (IGNORE_CASE) regular expression " +
"at line " +
other.getLine () +
", column " +
other.getColumn () +
".");
}
else
{
// give the standard error message.
JavaCCErrors.semantic_error (sl, "Duplicate definition of string token \"" +
sl.image +
"\" " +
"can never be matched.");
}
}
else
if (sl.tpContext.ignoreCase)
{
// This has to be explicit. A warning needs to be given with
// respect
// to all previous strings.
String pos = "";
int count = 0;
for (final Enumeration enum2 = table2.elements (); enum2.hasMoreElements ();)
{
final RegularExpression rexp = (RegularExpression) (enum2.nextElement ());
if (count != 0)
pos += ",";
pos += " line " + rexp.getLine ();
count++;
}
if (count == 1)
{
JavaCCErrors.warning (sl, "String with IGNORE_CASE is partially superceded by string at" +
pos +
".");
}
else
{
JavaCCErrors.warning (sl, "String with IGNORE_CASE is partially superceded by strings at" +
pos +
".");
}
// This entry is legitimate. So insert it.
if (sl.ordinal == 0)
{
sl.ordinal = tokenCount++;
}
table2.put (sl.image, sl);
// The above "put" may override an existing entry (that is not
// IGNORE_CASE) and that's
// the desired behavior.
}
else
{
// The rest of the cases do not involve IGNORE_CASE.
final RegularExpression re = (RegularExpression) table2.get (sl.image);
if (re == null)
{
if (sl.ordinal == 0)
{
sl.ordinal = tokenCount++;
}
table2.put (sl.image, sl);
}
else
if (tp.isExplicit)
{
// This is an error even if the first occurrence was
// implicit.
if (tp.lexStates[i].equals ("DEFAULT"))
{
JavaCCErrors.semantic_error (sl, "Duplicate definition of string token \"" + sl.image + "\".");
}
else
{
JavaCCErrors.semantic_error (sl, "Duplicate definition of string token \"" +
sl.image +
"\" in lexical state \"" +
tp.lexStates[i] +
"\".");
}
}
else
if (re.tpContext.kind != TokenProduction.TOKEN)
{
JavaCCErrors.semantic_error (sl, "String token \"" +
sl.image +
"\" has been defined as a \"" +
TokenProduction.kindImage[re.tpContext.kind] +
"\" token.");
}
else
if (re.private_rexp)
{
JavaCCErrors.semantic_error (sl, "String token \"" +
sl.image +
"\" has been defined as a private regular expression.");
}
else
{
// This is now a legitimate reference to an existing
// RStringLiteral.
// So we assign it a number and take it out of
// "rexprlist".
// Therefore, if all is OK (no errors), then there
// will be only unequal
// string literals in each lexical state. Note that
// the only way
// this can be legal is if this is a string declared
// inline within the
// BNF. Hence, it belongs to only one lexical state -
// namely "DEFAULT".
sl.ordinal = re.ordinal;
prepareToRemove (respecs, res);
}
}
}
}
else
if (!(res.rexp instanceof RJustName))
{
res.rexp.ordinal = tokenCount++;
}
if (!(res.rexp instanceof RJustName) && !res.rexp.label.equals (""))
{
names_of_tokens.put (new Integer (res.rexp.ordinal), res.rexp.label);
}
if (!(res.rexp instanceof RJustName))
{
rexps_of_tokens.put (new Integer (res.rexp.ordinal), res.rexp);
}
}
}
removePreparedItems ();
/*
* The following code performs a tree walk on all regular expressions
* attaching links to "RJustName"s. Error messages are given if undeclared
* names are used, or if "RJustNames" refer to private regular expressions
* or to regular expressions of any kind other than TOKEN. In addition, this
* loop also removes top level "RJustName"s from "rexprlist". This code is
* not executed if Options.getUserTokenManager() is set to true. Instead the
* following block of code is executed.
*/
if (!Options.getUserTokenManager ())
{
final FixRJustNames frjn = new FixRJustNames ();
for (final Iterator it = rexprlist.iterator (); it.hasNext ();)
{
final TokenProduction tp = (TokenProduction) (it.next ());
final List respecs = tp.respecs;
for (final Iterator it1 = respecs.iterator (); it1.hasNext ();)
{
final RegExprSpec res = (RegExprSpec) (it1.next ());
frjn.root = res.rexp;
ExpansionTreeWalker.preOrderWalk (res.rexp, frjn);
if (res.rexp instanceof RJustName)
{
prepareToRemove (respecs, res);
}
}
}
}
removePreparedItems ();
/*
* The following code is executed only if Options.getUserTokenManager() is
* set to true. This code visits all top-level "RJustName"s (ignores
* "RJustName"s nested within regular expressions). Since regular
* expressions are optional in this case, "RJustName"s without corresponding
* regular expressions are given ordinal values here. If "RJustName"s refer
* to a named regular expression, their ordinal values are set to reflect
* this. All but one "RJustName" node is removed from the lists by the end
* of execution of this code.
*/
if (Options.getUserTokenManager ())
{
for (final Iterator it = rexprlist.iterator (); it.hasNext ();)
{
final TokenProduction tp = (TokenProduction) (it.next ());
final List respecs = tp.respecs;
for (final Iterator it1 = respecs.iterator (); it1.hasNext ();)
{
final RegExprSpec res = (RegExprSpec) (it1.next ());
if (res.rexp instanceof RJustName)
{
final RJustName jn = (RJustName) res.rexp;
final RegularExpression rexp = (RegularExpression) named_tokens_table.get (jn.label);
if (rexp == null)
{
jn.ordinal = tokenCount++;
named_tokens_table.put (jn.label, jn);
ordered_named_tokens.add (jn);
names_of_tokens.put (new Integer (jn.ordinal), jn.label);
}
else
{
jn.ordinal = rexp.ordinal;
prepareToRemove (respecs, res);
}
}
}
}
}
removePreparedItems ();
/*
* The following code is executed only if Options.getUserTokenManager() is
* set to true. This loop labels any unlabeled regular expression and prints
* a warning that it is doing so. These labels are added to
* "ordered_named_tokens" so that they may be generated into the
* ...Constants file.
*/
if (Options.getUserTokenManager ())
{
for (final Iterator it = rexprlist.iterator (); it.hasNext ();)
{
final TokenProduction tp = (TokenProduction) (it.next ());
final List respecs = tp.respecs;
for (final Iterator it1 = respecs.iterator (); it1.hasNext ();)
{
final RegExprSpec res = (RegExprSpec) (it1.next ());
final Integer ii = new Integer (res.rexp.ordinal);
if (names_of_tokens.get (ii) == null)
{
JavaCCErrors.warning (res.rexp, "Unlabeled regular expression cannot be referred to by "
+ "user generated token manager.");
}
}
}
}
if (JavaCCErrors.get_error_count () != 0)
throw new MetaParseException ();
// The following code sets the value of the "emptyPossible" field of
// NormalProduction
// nodes. This field is initialized to false, and then the entire list of
// productions is processed. This is repeated as long as at least one item
// got updated from false to true in the pass.
boolean emptyUpdate = true;
while (emptyUpdate)
{
emptyUpdate = false;
for (final Iterator it = bnfproductions.iterator (); it.hasNext ();)
{
final NormalProduction prod = (NormalProduction) it.next ();
if (emptyExpansionExists (prod.getExpansion ()))
{
if (!prod.isEmptyPossible ())
{
emptyUpdate = prod.setEmptyPossible (true);
}
}
}
}
if (Options.getSanityCheck () && JavaCCErrors.get_error_count () == 0)
{
// The following code checks that all ZeroOrMore, ZeroOrOne, and OneOrMore
// nodes
// do not contain expansions that can expand to the empty token list.
for (final Iterator it = bnfproductions.iterator (); it.hasNext ();)
{
ExpansionTreeWalker.preOrderWalk (((NormalProduction) it.next ()).getExpansion (), new EmptyChecker ());
}
// The following code goes through the productions and adds pointers to
// other
// productions that it can expand to without consuming any tokens. Once
// this is
// done, a left-recursion check can be performed.
for (final Iterator it = bnfproductions.iterator (); it.hasNext ();)
{
final NormalProduction prod = (NormalProduction) it.next ();
addLeftMost (prod, prod.getExpansion ());
}
// Now the following loop calls a recursive walk routine that searches for
// actual left recursions. The way the algorithm is coded, once a node has
// been determined to participate in a left recursive loop, it is not
// tried
// in any other loop.
for (final Iterator it = bnfproductions.iterator (); it.hasNext ();)
{
final NormalProduction prod = (NormalProduction) it.next ();
if (prod.getWalkStatus () == 0)
{
prodWalk (prod);
}
}
// Now we do a similar, but much simpler walk for the regular expression
// part of
// the grammar. Here we are looking for any kind of loop, not just left
// recursions,
// so we only need to do the equivalent of the above walk.
// This is not done if option USER_TOKEN_MANAGER is set to true.
if (!Options.getUserTokenManager ())
{
for (final Iterator it = rexprlist.iterator (); it.hasNext ();)
{
final TokenProduction tp = (TokenProduction) (it.next ());
final List respecs = tp.respecs;
for (final Iterator it1 = respecs.iterator (); it1.hasNext ();)
{
final RegExprSpec res = (RegExprSpec) (it1.next ());
final RegularExpression rexp = res.rexp;
if (rexp.walkStatus == 0)
{
rexp.walkStatus = -1;
if (rexpWalk (rexp))
{
loopString = "..." + rexp.label + "... --> " + loopString;
JavaCCErrors.semantic_error (rexp, "Loop in regular expression detected: \"" + loopString + "\"");
}
rexp.walkStatus = 1;
}
}
}
}
/*
* The following code performs the lookahead ambiguity checking.
*/
if (JavaCCErrors.get_error_count () == 0)
{
for (final Iterator it = bnfproductions.iterator (); it.hasNext ();)
{
ExpansionTreeWalker.preOrderWalk (((NormalProduction) it.next ()).getExpansion (), new LookaheadChecker ());
}
}
} // matches "if (Options.getSanityCheck()) {"
if (JavaCCErrors.get_error_count () != 0)
throw new MetaParseException ();
}
public static RegularExpression other;
// Checks to see if the "str" is superceded by another equal (except case)
// string
// in table.
public static boolean hasIgnoreCase (final Hashtable table, final String str)
{
RegularExpression rexp;
rexp = (RegularExpression) (table.get (str));
if (rexp != null && !rexp.tpContext.ignoreCase)
{
return false;
}
for (final Enumeration enumeration = table.elements (); enumeration.hasMoreElements ();)
{
rexp = (RegularExpression) (enumeration.nextElement ());
if (rexp.tpContext.ignoreCase)
{
other = rexp;
return true;
}
}
return false;
}
// returns true if "exp" can expand to the empty string, returns false
// otherwise.
public static boolean emptyExpansionExists (final Expansion exp)
{
if (exp instanceof NonTerminal)
{
return ((NonTerminal) exp).getProd ().isEmptyPossible ();
}
else
if (exp instanceof Action)
{
return true;
}
else
if (exp instanceof RegularExpression)
{
return false;
}
else
if (exp instanceof OneOrMore)
{
return emptyExpansionExists (((OneOrMore) exp).expansion);
}
else
if (exp instanceof ZeroOrMore || exp instanceof ZeroOrOne)
{
return true;
}
else
if (exp instanceof Lookahead)
{
return true;
}
else
if (exp instanceof Choice)
{
for (final Iterator it = ((Choice) exp).getChoices ().iterator (); it.hasNext ();)
{
if (emptyExpansionExists ((Expansion) it.next ()))
{
return true;
}
}
return false;
}
else
if (exp instanceof Sequence)
{
for (final Iterator it = ((Sequence) exp).units.iterator (); it.hasNext ();)
{
if (!emptyExpansionExists ((Expansion) it.next ()))
{
return false;
}
}
return true;
}
else
if (exp instanceof TryBlock)
{
return emptyExpansionExists (((TryBlock) exp).exp);
}
else
{
return false; // This should be dead code.
}
}
// Updates prod.leftExpansions based on a walk of exp.
static private void addLeftMost (final NormalProduction prod, final Expansion exp)
{
if (exp instanceof NonTerminal)
{
for (int i = 0; i < prod.leIndex; i++)
{
if (prod.getLeftExpansions ()[i] == ((NonTerminal) exp).getProd ())
{
return;
}
}
if (prod.leIndex == prod.getLeftExpansions ().length)
{
final NormalProduction [] newle = new NormalProduction [prod.leIndex * 2];
System.arraycopy (prod.getLeftExpansions (), 0, newle, 0, prod.leIndex);
prod.setLeftExpansions (newle);
}
prod.getLeftExpansions ()[prod.leIndex++] = ((NonTerminal) exp).getProd ();
}
else
if (exp instanceof OneOrMore)
{
addLeftMost (prod, ((OneOrMore) exp).expansion);
}
else
if (exp instanceof ZeroOrMore)
{
addLeftMost (prod, ((ZeroOrMore) exp).expansion);
}
else
if (exp instanceof ZeroOrOne)
{
addLeftMost (prod, ((ZeroOrOne) exp).expansion);
}
else
if (exp instanceof Choice)
{
for (final Iterator it = ((Choice) exp).getChoices ().iterator (); it.hasNext ();)
{
addLeftMost (prod, (Expansion) it.next ());
}
}
else
if (exp instanceof Sequence)
{
for (final Iterator it = ((Sequence) exp).units.iterator (); it.hasNext ();)
{
final Expansion e = (Expansion) it.next ();
addLeftMost (prod, e);
if (!emptyExpansionExists (e))
{
break;
}
}
}
else
if (exp instanceof TryBlock)
{
addLeftMost (prod, ((TryBlock) exp).exp);
}
}
// The string in which the following methods store information.
static private String loopString;
// Returns true to indicate an unraveling of a detected left recursion loop,
// and returns false otherwise.
static private boolean prodWalk (final NormalProduction prod)
{
prod.setWalkStatus (-1);
for (int i = 0; i < prod.leIndex; i++)
{
if (prod.getLeftExpansions ()[i].getWalkStatus () == -1)
{
prod.getLeftExpansions ()[i].setWalkStatus (-2);
loopString = prod.getLhs () + "... --> " + prod.getLeftExpansions ()[i].getLhs () + "...";
if (prod.getWalkStatus () == -2)
{
prod.setWalkStatus (1);
JavaCCErrors.semantic_error (prod, "Left recursion detected: \"" + loopString + "\"");
return false;
}
else
{
prod.setWalkStatus (1);
return true;
}
}
else
if (prod.getLeftExpansions ()[i].getWalkStatus () == 0)
{
if (prodWalk (prod.getLeftExpansions ()[i]))
{
loopString = prod.getLhs () + "... --> " + loopString;
if (prod.getWalkStatus () == -2)
{
prod.setWalkStatus (1);
JavaCCErrors.semantic_error (prod, "Left recursion detected: \"" + loopString + "\"");
return false;
}
else
{
prod.setWalkStatus (1);
return true;
}
}
}
}
prod.setWalkStatus (1);
return false;
}
// Returns true to indicate an unraveling of a detected loop,
// and returns false otherwise.
static private boolean rexpWalk (final RegularExpression rexp)
{
if (rexp instanceof RJustName)
{
final RJustName jn = (RJustName) rexp;
if (jn.regexpr.walkStatus == -1)
{
jn.regexpr.walkStatus = -2;
loopString = "..." + jn.regexpr.label + "...";
// Note: Only the regexpr's of RJustName nodes and the top leve
// regexpr's can have labels. Hence it is only in these cases that
// the labels are checked for to be added to the loopString.
return true;
}
else
if (jn.regexpr.walkStatus == 0)
{
jn.regexpr.walkStatus = -1;
if (rexpWalk (jn.regexpr))
{
loopString = "..." + jn.regexpr.label + "... --> " + loopString;
if (jn.regexpr.walkStatus == -2)
{
jn.regexpr.walkStatus = 1;
JavaCCErrors.semantic_error (jn.regexpr, "Loop in regular expression detected: \"" + loopString + "\"");
return false;
}
else
{
jn.regexpr.walkStatus = 1;
return true;
}
}
else
{
jn.regexpr.walkStatus = 1;
return false;
}
}
}
else
if (rexp instanceof RChoice)
{
for (final Iterator it = ((RChoice) rexp).getChoices ().iterator (); it.hasNext ();)
{
if (rexpWalk ((RegularExpression) it.next ()))
{
return true;
}
}
return false;
}
else
if (rexp instanceof RSequence)
{
for (final Iterator it = ((RSequence) rexp).units.iterator (); it.hasNext ();)
{
if (rexpWalk ((RegularExpression) it.next ()))
{
return true;
}
}
return false;
}
else
if (rexp instanceof ROneOrMore)
{
return rexpWalk (((ROneOrMore) rexp).regexpr);
}
else
if (rexp instanceof RZeroOrMore)
{
return rexpWalk (((RZeroOrMore) rexp).regexpr);
}
else
if (rexp instanceof RZeroOrOne)
{
return rexpWalk (((RZeroOrOne) rexp).regexpr);
}
else
if (rexp instanceof RRepetitionRange)
{
return rexpWalk (((RRepetitionRange) rexp).regexpr);
}
return false;
}
/**
* Objects of this class are created from class Semanticize to work on
* references to regular expressions from RJustName's.
*/
static class FixRJustNames extends JavaCCGlobals implements TreeWalkerOp
{
public RegularExpression root;
public boolean goDeeper (final Expansion e)
{
return true;
}
public void action (final Expansion e)
{
if (e instanceof RJustName)
{
final RJustName jn = (RJustName) e;
final RegularExpression rexp = (RegularExpression) named_tokens_table.get (jn.label);
if (rexp == null)
{
JavaCCErrors.semantic_error (e, "Undefined lexical token name \"" + jn.label + "\".");
}
else
if (jn == root && !jn.tpContext.isExplicit && rexp.private_rexp)
{
JavaCCErrors.semantic_error (e, "Token name \"" +
jn.label +
"\" refers to a private " +
"(with a #) regular expression.");
}
else
if (jn == root && !jn.tpContext.isExplicit && rexp.tpContext.kind != TokenProduction.TOKEN)
{
JavaCCErrors.semantic_error (e, "Token name \"" +
jn.label +
"\" refers to a non-token " +
"(SKIP, MORE, IGNORE_IN_BNF) regular expression.");
}
else
{
jn.ordinal = rexp.ordinal;
jn.regexpr = rexp;
}
}
}
}
static class LookaheadFixer extends JavaCCGlobals implements TreeWalkerOp
{
public boolean goDeeper (final Expansion e)
{
if (e instanceof RegularExpression)
{
return false;
}
else
{
return true;
}
}
public void action (final Expansion e)
{
if (e instanceof Sequence)
{
if (e.parent instanceof Choice ||
e.parent instanceof ZeroOrMore ||
e.parent instanceof OneOrMore ||
e.parent instanceof ZeroOrOne)
{
return;
}
final Sequence seq = (Sequence) e;
final Lookahead la = (Lookahead) (seq.units.get (0));
if (!la.isExplicit ())
{
return;
}
// Create a singleton choice with an empty action.
final Choice ch = new Choice ();
ch.setLine (la.getLine ());
ch.setColumn (la.getColumn ());
ch.parent = seq;
final Sequence seq1 = new Sequence ();
seq1.setLine (la.getLine ());
seq1.setColumn (la.getColumn ());
seq1.parent = ch;
seq1.units.add (la);
la.parent = seq1;
final Action act = new Action ();
act.setLine (la.getLine ());
act.setColumn (la.getColumn ());
act.parent = seq1;
seq1.units.add (act);
ch.getChoices ().add (seq1);
if (la.getAmount () != 0)
{
if (la.getActionTokens ().size () != 0)
{
JavaCCErrors.warning (la, "Encountered LOOKAHEAD(...) at a non-choice location. "
+ "Only semantic lookahead will be considered here.");
}
else
{
JavaCCErrors.warning (la, "Encountered LOOKAHEAD(...) at a non-choice location. This will be ignored.");
}
}
// Now we have moved the lookahead into the singleton choice. Now create
// a new dummy lookahead node to replace this one at its original
// location.
final Lookahead la1 = new Lookahead ();
la1.setExplicit (false);
la1.setLine (la.getLine ());
la1.setColumn (la.getColumn ());
la1.parent = seq;
// Now set the la_expansion field of la and la1 with a dummy expansion
// (we use EOF).
la.setLaExpansion (new REndOfFile ());
la1.setLaExpansion (new REndOfFile ());
seq.units.set (0, la1);
seq.units.add (1, ch);
}
}
}
static class ProductionDefinedChecker extends JavaCCGlobals implements TreeWalkerOp
{
public boolean goDeeper (final Expansion e)
{
if (e instanceof RegularExpression)
{
return false;
}
else
{
return true;
}
}
public void action (final Expansion e)
{
if (e instanceof NonTerminal)
{
final NonTerminal nt = (NonTerminal) e;
if ((nt.setProd ((NormalProduction) production_table.get (nt.getName ()))) == null)
{
JavaCCErrors.semantic_error (e, "Non-terminal " + nt.getName () + " has not been defined.");
}
else
{
nt.getProd ().getParents ().add (nt);
}
}
}
}
static class EmptyChecker extends JavaCCGlobals implements TreeWalkerOp
{
public boolean goDeeper (final Expansion e)
{
if (e instanceof RegularExpression)
{
return false;
}
else
{
return true;
}
}
public void action (final Expansion e)
{
if (e instanceof OneOrMore)
{
if (Semanticize.emptyExpansionExists (((OneOrMore) e).expansion))
{
JavaCCErrors.semantic_error (e, "Expansion within \"(...)+\" can be matched by empty string.");
}
}
else
if (e instanceof ZeroOrMore)
{
if (Semanticize.emptyExpansionExists (((ZeroOrMore) e).expansion))
{
JavaCCErrors.semantic_error (e, "Expansion within \"(...)*\" can be matched by empty string.");
}
}
else
if (e instanceof ZeroOrOne)
{
if (Semanticize.emptyExpansionExists (((ZeroOrOne) e).expansion))
{
JavaCCErrors.semantic_error (e, "Expansion within \"(...)?\" can be matched by empty string.");
}
}
}
}
static class LookaheadChecker extends JavaCCGlobals implements TreeWalkerOp
{
public boolean goDeeper (final Expansion e)
{
if (e instanceof RegularExpression)
{
return false;
}
else
if (e instanceof Lookahead)
{
return false;
}
else
{
return true;
}
}
public void action (final Expansion e)
{
if (e instanceof Choice)
{
if (Options.getLookahead () == 1 || Options.getForceLaCheck ())
{
LookaheadCalc.choiceCalc ((Choice) e);
}
}
else
if (e instanceof OneOrMore)
{
final OneOrMore exp = (OneOrMore) e;
if (Options.getForceLaCheck () || (implicitLA (exp.expansion) && Options.getLookahead () == 1))
{
LookaheadCalc.ebnfCalc (exp, exp.expansion);
}
}
else
if (e instanceof ZeroOrMore)
{
final ZeroOrMore exp = (ZeroOrMore) e;
if (Options.getForceLaCheck () || (implicitLA (exp.expansion) && Options.getLookahead () == 1))
{
LookaheadCalc.ebnfCalc (exp, exp.expansion);
}
}
else
if (e instanceof ZeroOrOne)
{
final ZeroOrOne exp = (ZeroOrOne) e;
if (Options.getForceLaCheck () || (implicitLA (exp.expansion) && Options.getLookahead () == 1))
{
LookaheadCalc.ebnfCalc (exp, exp.expansion);
}
}
}
static boolean implicitLA (final Expansion exp)
{
if (!(exp instanceof Sequence))
{
return true;
}
final Sequence seq = (Sequence) exp;
final Object obj = seq.units.get (0);
if (!(obj instanceof Lookahead))
{
return true;
}
final Lookahead la = (Lookahead) obj;
return !la.isExplicit ();
}
}
public static void reInit ()
{
removeList = new ArrayList ();
itemList = new ArrayList ();
other = null;
loopString = null;
}
}