//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\//\\ // 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(" -----------------------------------------------"); } } } }