//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// PLEASE DO NOT EDIT WITHOUT THE EXPLICIT CONSENT OF THE AUTHOR! \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
using System.Collections;
using System.Text;
using Ilog.Language.Util;
using Ilog.Language.Tools;
namespace Ilog.Language.Parsing
{
using Stack = Ilog.Language.Util.Stack;
/**
* This is the parser class inherited by all parser classes generated
* by Syntax.ParserGenerator for a grammar using dynamic features.
*
* @see Syntax.ParserGenerator
* @see GenericParser
* @see StaticParser
*
* @version Last modified on Tue May 31 13:47:53 2005 by hak
* @author Hassan Aït-Kaci
* @copyright © 2000 ILOG, S.A.
*/
public abstract class DynamicParser : GenericParser
{
/**
* Returns the precedence of the current handle (corrresponding
* to the given rule).
*/
protected int Precedence (Rule r)
{
if (r.Precedence != -1)
return r.Precedence;
return Node(r,r.TagPosition).Precedence;
}
/**
* Returns the associativity of the current handle (corrresponding
* to the given rule).
*/
protected int Associativity (Rule r)
{
if (r.Associativity != -1)
return r.Associativity;
return Node(r,r.TagPosition).Associativity;
}
/**
* Returns true iff the current handle (determined from
* the given rule) has a terminal symbol tag corresponding to the
* given ParseNode.
*/
protected bool HasTag (Rule r, ParseNode node)
{
if (r.TagPosition != -1)
{
ParseNode tag = Node(r,r.TagPosition);
if (tag.IsTerminal)
{
if (tag.IsOperator && node.IsOperator)
return (tag.Operator == node.Operator);
if (!tag.IsOperator && !node.IsOperator)
return (tag.Symbol == node.Symbol);
}
}
return false;
}
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// PARSER'S PARAMETERS \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
/**
* The following items must be supplied by the generated parser subclass.
*/
/**
* The undo semantic action method.
*/
protected abstract void UndoSemanticAction (Rule r, ParseNode n); // throws IOException
/**
* The parser operators.
*/
public ArrayList operators;
/**
* The table associating identifiers to operators.
*/
protected Hashtable operatorTable = new Hashtable();
/**
* Storage tables for dynamic operators. They are two hash tables.
* The first associates an operator category's name to a set of
* all Operator objects in this category. The second
* associates a specific operator's name to a set containing all
* Operator objects with this name for all categories.
*/
protected Hashtable operatorCategoryTable = new Hashtable();
protected Hashtable operatorNameTable = new Hashtable();
/**
* The generic method for defining dynamic operators.
*/
protected void DefineOperator (string category, string name,
string specifier, int precedence)
// throws NonFatalParseErrorException
{
int prec = Syntax.Grammar.CheckPrecedenceLevel
(Syntax.Grammar.PrologPrecedence(precedence));
ArrayList ops = OperatorsInCategory(category);
NonTerminal cat = Nonterminal(category);
Operator op = new Operator(this,name,cat,prec,specifier);
int i = ops.IndexOf(op);
if (i == -1)
{
op.Add();
ops.Add(op);
ops = Operators(name); // NB: this is another ops!
if (ops == null)
operatorNameTable[name] = (ops = new ArrayList(3));
ops.Add(op);
}
else
((Operator)operators[i]).Redefine(prec,specifier);
}
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// INITIALIZATION METHODS \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
/**
* The following are conveniences initializing some of the above.
* They are used only by the generated parser to set up its
* parameters.
*/
/**
* Defines a noew operator with the specified name, index of
* nonterminal category, precedence, associativity, and fixity.
*/
protected void NewOperator (string name, int category,
int precedence, int associativity, int fixity)
{
Operator op = new Operator(this,name,Nonterminals[category],
precedence,associativity,fixity);
operatorTable[name] = op;
op.Add();
ArrayList ops = OperatorsInCategory(Nonterminals[category].Name);
if (ops == null)
operatorCategoryTable[Nonterminals[category].Name]
= (ops = new ArrayList(5));
ops.Add(op);
ops = Operators(name); // NB: this is another ops!
if (ops == null)
operatorNameTable[name] = (ops = new ArrayList(5));
ops.Add(op);
}
/**
* Allocates the dynamic action table for the state at the
* specified index to be of specified size.
*/
protected static void NewDynamicActionTable (int state, int size)
{
states[state].DynamicActions = new Action[size][];
}
/**
* Allocates the dynamic action array for the state at the
* specified index at the specified index to have the specified
* size.
*/
protected static void NewDynamicActions (int state, int index, int size)
{
states[state].DynamicActions[index] = new Action[size];
}
/**
* Sets the dynamic action of the state of specified index at the
* specified index and position in the state's dynamic actions to
* be the action of specified index.
*/
protected static void SetDynamicAction(int state, int index,
int position, int action)
{
states[state].DynamicActions[index][position] = actions[action];
}
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// PARSING METHODS \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
/**
* Returns the set of operators with this name; or null
* in there are none.
*/
protected internal ArrayList Operators (string name)
{
return (ArrayList)operatorNameTable[name];
}
/**
* Returns the set of operators in this category; or null
* in there are none.
*/
protected ArrayList OperatorsInCategory (string category)
{
return (ArrayList)operatorCategoryTable[category];
}
/**
* The following are local utilities...
*/
/**
* A time stamp manager for stamping the choices so as to know how
* far to undo upon backtracking.
*/
private TimeStampManager tsm = new TimeStampManager();
/**
* Stamps the specified object.
*/
private void Stamp (TimeStamped obj)
{
tsm.SetTimeStamp(obj);
}
/**
* This is where the are "unread" to upon backtracking, and read
* from anew by the NextToken() method when parsing is resumed at
* a certain time in the past.
*/
private Stack readStack = new Stack();
/**
* This is the stack of choice points listing alternatives paths
* to explore upon failure of the chosen one. A choice happens
* upon reading a non-deterministic token such as a dynamic
* operator or having to perform a non-deterministic action
* generated for an ambiguous grammar. It is initialized by the
* generated parser's constructor. This is a finite stack - which
* means that events beyong its capacity are forgotten. Its size
* can be redefined as an option to the parser generator.
*/
protected FiniteStack choiceStack;
/**
* This is the stack for recording effects that must be undone
* upon backtracking over a failed choice. It is initialized by
* the generated parser's constructor. This is a finite stack -
* which means that events beyong its capacity are forgotten. Its
* size can be redefined as an option to the parser generator.
*/
protected FiniteStack trailStack;
/**
* This is a flag to enable resolving R/R conflicts using rule
* precedence. It set to false by default, but may be changed
* using an option of the parser generator. Setting it to true is
* a dangerous thing to do if you rely on implicit rule
* precedences.
*/
protected bool resolveRRsWithPrecedence;
/**
* Resets the parser to a virginal configuration.
*/
public override void ResetParser ()
{
base.ResetParser();
readStack.Clear();
choiceStack.Clear();
trailStack.Clear();
}
/**
* Returns an error token. Overrides the default method.
*/
public override ParseNode Error ()
{
return new DynamicToken(base.Error());
}
/**
* This is true iff this parser admits operators. This is true by
* default, but will be set to false by the generated parser if
* the grammar allows only non-deterministic actions.
*/
protected static bool admitsOperators = true;
/**
* Overrides the implementation in GenericParser so that
* it does not return an error token if the symbol is not a known
* terminal symbol and the parser admits operators, in which case
* it is returned as a literal token anyway that will be resolved
* as a potential dynamic operator by the ReadToken()
* method.
*/
public static new ParseNode LiteralToken (string symbol)
{
Terminal term = Terminal(symbol);
return term == null ? (admitsOperators ? new ParseNode(string.Intern(symbol))
: Error(symbol))
: new ParseNode(term);
}
/**
* Returns the token that was the last one actually read off the
* input stream (as opposed to the read stack); i.e., it is
* the bottom of the read stack if it is not empty, otherwise the
* current token.
*/
protected override ParseNode LatestToken () // throws IOException
{
if (readStack.IsEmpty)
return ((DynamicToken)TokenNode()).Original;
return (ParseNode)readStack[0];
}
/**
* Reads the next token into tokenNode. If the read stack is
* not empty, the token is read from it; otherwise, it is read from
* the input stream. In either case, the new token is checked for
* potential ambiguities. Because a dynamic token may be a symbol
* unknown at parser generation time, it may mutate into one or
* several dynamic operators if the symbol has been declared a a
* dynamic operator of a category expected in the current state.
* Therefore, the newly read token is systematically wrapped inside a
* time-stamped DynamicToken. If one or more candidates are
* found, this token it is transformed into the first of them. Thus,
* it must be restored as originally read and to be time-stamped anew
* when read again from the read stack if backtracking takes the parser
* to an earlier stage.
*
* @see DynamicToken
*/
protected override void ReadToken () // throws IOException
{
if (readStack.IsEmpty)
{
Stamp((TimeStamped)(tokenNode = new DynamicToken(NextToken())));
if (trace)
Err.WriteLine("*** Read token: "+tokenNode+" from input.");
}
else
{
Stamp((TimeStamped)(tokenNode = new DynamicToken((ParseNode)readStack.Pop())));
if (trace)
Err.WriteLine("*** Read token: "+tokenNode+" from read stack.");
}
if (tokenNode.IsError)
CutAll();
else
{
Choice choice = new Choice();
if (tokenNode.HasAlternatives)
TallyAlternatives(choice);
if (admitsOperators)
TallyOperators(choice);
if (!choice.IsEmpty)
PushChoice(choice);
}
readTokenFlag = false;
}
/**
* Adds to given choice point all alternative forms of the current
* token.
*/
private void TallyAlternatives (Choice choice)
{
foreach (ParseNode node in tokenNode.Alternatives)
{
DynamicToken token = new DynamicToken(node);
token.Original = tokenNode;
Stamp(token);
choice.AddOption(token);
}
}
/**
* Pushes the specified choice point on the choice stack, with
* possible amnesia if this creates a spill. Note that a spill in
* the choice stack must trigger corresponding amnesia in the
* trail stack.
*/
private void PushChoice (Choice choice)
{
Stamp(choice);
if ((choice = (Choice)choiceStack.Push(choice)) != null)
while (!trailStack.IsEmpty
&& ((TrailEntry)trailStack.Oldest).TimeStamp < choice.TimeStamp)
trailStack.Drop();
}
/**
* Determines whether the current token is a potential dynamic
* operator, and if so, adds it to given choice point as
* appropriate for potential backtracking.
*/
private void TallyOperators (Choice choice)
{
Stack ops = AdmissibleOperators();
if (!tokenNode.IsKnown)
if (ops != null)
{
tokenNode = (DynamicToken)ops.Pop();
if (ops.IsEmpty)
ops = null;
}
else
tokenNode = Error(tokenNode);
if (ops != null)
choice.AddOptions(ops);
}
/**
* Returns a stack of operator tokens found by looking up
* operatorNameTable for all operators symbols that could
* stand for tokenNode in the current state. Returns
* null if no admissible operator is found.
*/
private Stack AdmissibleOperators ()
{
string name = tokenNode.SValue == null ? tokenNode.Symbol.Name
: string.Intern(tokenNode.SValue);
ArrayList ops = Operators(name);
if (ops == null)
return null;
Stack admissibles = new Stack();
foreach (Operator op in ops)
{
if (SymbolIsHandled(op.SubCategory))
{
if (!tokenNode.IsKnown && op.SubCategory == tokenNode.Symbol)
{
((DynamicToken)tokenNode).MakeOperator(op);
continue;
}
DynamicToken token = new DynamicToken(op,
((DynamicToken)tokenNode).Original);
Stamp(token);
admissibles.Push(token);
}
}
if (admissibles.IsEmpty)
return null;
return admissibles;
}
/**
* Pushes the current state and the given node on the parser
* stack, and marks this new stack element with a time stamp.
*/
protected override void Push (ParseNode node)
{
base.Push(node);
Stamp((TimeStamped)stack.Peek());
}
/**
* A switch to inhibit resetting the parse action from the current
* state upon backtracking over a CHOICE action. When true
* the parse action is reset to be the one from the current state.
*/
private bool parseActionFlag = true;
/**
* Sets the appropriate parser action for the current token in the
* current state. If none exists, the parser action is set to the
* canonical error action.
*/
protected override void GetParseAction () // throws IOException
{
if (parseActionFlag)
{
parseAction = parseState.GetAction((Terminal)TokenNode().Symbol);
if (parseAction == null || NonassociativeUnaryOperator())
parseAction = ErrorAction;
}
parseActionFlag = true;
}
/**
* Returns true iff the current token is a non
* associative unary operator composed with itself.
*/
private bool NonassociativeUnaryOperator () // throws IOException
{
ParseNode node = TokenNode();
return (node.Associativity == Syntax.Grammar.NON_ASSOCIATIVE
&& (node.Fixity == Syntax.OperatorSymbol.POSTFIX
&& parseAction.Type == Syntax.Action.REDUCE
&& HasTag(rules[parseAction.Info],node)
||
node.Fixity == Syntax.OperatorSymbol.PREFIX
&& PreviousTokenIsSameOperator()));
}
/**
* Returns true iff the given node's operator is the same
* as the last token's on the stack.
*/
private bool PreviousTokenIsSameOperator () // throws IOException
{
foreach (StackElement elt in stack)
{
ParseNode node = elt.Node;
if (node.IsTerminal)
return node.IsOperator && node.Operator.Equals(TokenNode().Operator);
}
return false;
}
/**
* Return a string description of the specified handle.
*/
private string StringForm (StackElement[] handle)
{
StringBuilder buf = new StringBuilder("[");
string sep = "";
for (int i=0; in latest elements on the parser stack, where n
* is the length of the current rule's RHS. This may also build a parse
* tree as specified by parseTreeType.
*/
protected override void PopHandle ()
{
base.PopHandle();
if (!choiceStack.IsEmpty)
{
TrailEntry entry = new TrailEntry(parseHandle,parseRule);
Stamp(entry);
entry = (TrailEntry)trailStack.Push(entry);
if (entry != null)
{
while (!choiceStack.IsEmpty
&& ((Choice)choiceStack.Oldest).TimeStamp < entry.TimeStamp)
choiceStack.Drop();
if (choiceStack.IsEmpty)
trailStack.Clear();
}
}
}
/**
* Restores the parser's state from the choice point and trail
* stacks to the most recent recoverable configuration.
*/
private void Backtrack () // throws IOException
{
if (trace)
Err.WriteLine("Backtracking... ");
// The following choice is guaranteed non null thanks to a test
// before Backtrack() is called in PerforemParseAction()
Choice choice = (Choice)choiceStack.Peek();
Undo(choice);
object nextChoice = choice.Options.Pop();
if (choice.Options.IsEmpty)
choiceStack.Pop();
if (choice.IsTokenChoice)
{
tokenNode = (DynamicToken)nextChoice;
readTokenFlag = false;
parseActionFlag = true;
}
else
{
parseAction = (Action)nextChoice;
readTokenFlag = true;
parseActionFlag = false;
}
if (choiceStack.IsEmpty)
trailStack.Clear();
if (trace)
ShowDynamicState();
}
/**
* Undoes all work done after the specified choice point.
*/
private void Undo (Choice choice) // throws IOException
{
long stamp = choice.TimeStamp;
DynamicToken token = (DynamicToken)tokenNode;
if (trace)
Err.WriteLine("Undoing stamp: "+stamp);
if (!choice.IsTokenChoice || token.TimeStamp > stamp)
{
readStack.Push(token.Original);
if (trace)
{
Err.WriteLine("Unreading token: "+token);
Err.WriteLine(Misc.View(readStack, " read stack",0,80));
}
}
StackElement element = (StackElement)stack.Peek();
while (element.TimeStamp > stamp)
{
if (trace)
Err.WriteLine("Popping parser stack element: "+element);
stack.Pop();
if (element.Node.IsTerminal)
{
token = (DynamicToken)element.Node;
if (!choice.IsTokenChoice || token.TimeStamp > stamp)
{
readStack.Push(token.Original);
if (trace)
{
Err.WriteLine("Unreading token: "+token);
Err.WriteLine(Misc.View(readStack," read stack",0,80));
}
}
}
else
{
TrailEntry trail = (TrailEntry)trailStack.Pop();
for (int i=0; iACCEPT action.
*/
protected override bool PerformParseAction () // throws IOException
{
switch (parseAction.Type)
{
case Syntax.Action.ACCEPT:
return false;
case Syntax.Action.DYNAMIC:
ResolveDynamicAction();
return PerformParseAction();
case Syntax.Action.CHOICE:
ResolveChoiceAction();
return PerformParseAction();
case Syntax.Action.SHIFT:
Shift();
break;
case Syntax.Action.REDUCE:
Reduce();
if (parseState != null)
break;
goto case Syntax.Action.ERROR;
case Syntax.Action.ERROR:
if (!choiceStack.IsEmpty)
Backtrack();
else
RecoverFromError();
break;
}
return true;
}
/**
* Looks up the current state's dynamic actions array and chooses
* the most appropriate action according to the current parser
* stack and token node.
*/
private void ResolveDynamicAction () // throws IOException
{
Action[] actions = CurrentState.DynamicActions[parseAction.Info];
parseAction = actions[0];
for (int i=1; i a2_prec) ? a1 : a2;
}
// a2 is a shift:
Rule r = rules[a1.Info];
if (Precedence(r) > TokenNode().Precedence)
return a1; // favor reduction
if (Precedence(r) < TokenNode().Precedence)
return a2; // favor shifting
if (Associativity(r) == Syntax.Grammar.LEFT_ASSOCIATIVE)
return a1; // favor reduction
if (TokenNode().Associativity == Syntax.Grammar.NON_ASSOCIATIVE
&& HasTag(r,TokenNode()))
return ErrorAction; // bad operator composition
return a2; // otherwise, favor shifting
}
return ChooseAction(a2,a1);
}
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
// DISPLAYING METHODS \\
//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\
/**
* The following is for showing items and debugging purposes...
*/
protected override void Show () // throws IOException
{
ShowParseState();
ShowDynamicState();
}
protected void ShowDynamicState ()
{
if (!readStack.IsEmpty)
Err.WriteLine(Misc.View(readStack," read stack",0,80));
if (!trailStack.IsEmpty)
Err.WriteLine(Misc.View(trailStack," trail stack",0,80));
if (!choiceStack.IsEmpty)
Err.WriteLine("choices ==> "+choiceStack);
}
public void ShowOperators ()
{
if (operators.Count != 0)
{
Err.WriteLine("\nDYNAMIC OPERATORS:\n");
Err.WriteLine(" -----------------------------------------------");
Err.WriteLine("\tCATEGORY PRECEDENCE SPECIFIER OPERATOR");
Err.WriteLine(" -----------------------------------------------");
foreach (Operator o in operators)
{
Err.WriteLine(" [" + o.Index
+ "]\t " + o.Category.Name
+ "\t " + Syntax.Grammar.PrologPrecedence(o.Precedence)
+ "\t\t" + o.Specifier
+ "\t " + o.Name
);
}
Err.WriteLine(" -----------------------------------------------");
}
}
}
}