} //method #endregion #region Creating parser states private void CreateInitialAndFinalStates() { //there is always just one initial production "Root' -> .Root", and we're interested in LR item at 0 index LR0ItemList itemList = new LR0ItemList(); NtData rootData = (NtData)Data.AugmentedRoot.ParserData; itemList.Add(rootData.Productions[0].LR0Items[0]); Data.InitialState = FindOrCreateState(itemList); //it is actually create Data.InitialState.Items[0].NewLookaheads.Add(Grammar.Eof.Key); #region comment about FinalState //Create final state - because of the way states construction works, it doesn't create the final state automatically. // We need to create it explicitly and assign it to _data.FinalState property // The final executed reduction is "Root' -> Root.". This jump is executed as follows: // 1. parser creates Root' node // 2. Parser pops the state from stack - that would be initial state // 3. Finally, parser tries to find the transition in state.Actions table by the key of [Root'] element. // We must create the final state, and create the entry in transition table // The final state is based on the same initial production, but different LRItem - the one with dot AFTER the root nonterminal. // it is item at index 1. #endregion itemList.Clear(); itemList.Add(rootData.Productions[0].LR0Items[1]); Data.FinalState = FindOrCreateState(itemList); //it is actually create //Create shift transition from initial to final state string rootKey = Data.AugmentedRoot.Key; Data.InitialState.Actions[rootKey] = new ActionRecord(rootKey, ParserActionType.Shift, Data.FinalState, null); }
private void InitNonTerminalData() { foreach (NonTerminal nt in Data.NonTerminals) { NtData.GetOrCreate(nt); } }
private bool CalculateNullability(NonTerminal nonTerminal, NonTerminalList undecided) { NtData nonTerminalInfo = (NtData)nonTerminal.ParserData; foreach (Production prod in nonTerminalInfo.Productions) { //If production has terminals, it is not nullable and cannot contribute to nullability if (prod.IsSet(ProductionFlags.HasTerminals)) { continue; } if (prod.IsSet(ProductionFlags.IsEmpty)) { nonTerminalInfo.Nullable = true; return(true); //Nullable }//if //Go thru all elements of production and check nullability bool allNullable = true; foreach (BnfTerm term in prod.RValues) { NtData ntd = term.ParserData as NtData; if (ntd != null) { allNullable &= ntd.Nullable; } }//foreach nt if (allNullable) { nonTerminalInfo.Nullable = true; return(true); } }//foreach prod return(false); //cannot decide }
private void CreateProductions() { Data.Productions.Clear(); //each LR0Item gets its unique ID, last assigned (max) Id is kept in static field LR0Item._maxID = 0; foreach (NonTerminal nt in Data.NonTerminals) { NtData ntInfo = NtData.GetOrCreate(nt); ntInfo.Productions.Clear(); //Get data (sequences) from both Rule and ErrorRule BnfExpressionData allData = new BnfExpressionData(); allData.AddRange(nt.Rule.Data); if (nt.ErrorRule != null) { allData.AddRange(nt.ErrorRule.Data); } //actually create productions for each sequence foreach (BnfTermList prodOperands in allData) { Production prod = CreateProduction(nt, prodOperands); //Add the production to non-terminal's list and to global list ntInfo.Productions.Add(prod); Data.Productions.Add(prod); }//foreach prodOperands } }
private void CalculateFirsts() { //1. Calculate PropagateTo lists and put initial terminals into Firsts lists foreach (Production prod in Data.Productions) { NtData lvData = prod.LValue.ParserData as NtData; foreach (BnfTerm term in prod.RValues) { if (term is Terminal) //it is terminal, so add it to Firsts and that's all with this production { lvData.Firsts.Add(term.Key); // Add terminal to Firsts (note: Add ignores repetitions) break; //from foreach term }//if NtData ntInfo = term.ParserData as NtData; if (!ntInfo.PropagateFirstsTo.Contains(prod.LValue)) { ntInfo.PropagateFirstsTo.Add(prod.LValue); //ignores repetitions } if (!ntInfo.Nullable) { break; //if not nullable we're done } }//foreach oper }//foreach prod //2. Propagate all firsts thru all dependencies NonTerminalList workList = Data.NonTerminals; while (workList.Count > 0) { NonTerminalList newList = new NonTerminalList(); foreach (NonTerminal nt in workList) { NtData ntInfo = (NtData)nt.ParserData; foreach (NonTerminal toNt in ntInfo.PropagateFirstsTo) { NtData toInfo = (NtData)toNt.ParserData; foreach (string symbolKey in ntInfo.Firsts) { if (!toInfo.Firsts.Contains(symbolKey)) { toInfo.Firsts.Add(symbolKey); if (!newList.Contains(toNt)) { newList.Add(toNt); } } //if } //foreach symbolKey } //foreach toNt } //foreach nt in workList workList = newList; } //while } //method
//Creates closure items with "spontaneously generated" lookaheads private bool AddClosureItems(ParserState state) { bool result = false; //note that we change collection while we iterate thru it, so we have to use "for i" loop for (int i = 0; i < state.Items.Count; i++) { LRItem item = state.Items[i]; BnfTerm currTerm = item.Core.Current; if (currTerm == null || !(currTerm is NonTerminal)) { continue; } //1. Add normal closure items NtData currInfo = (NtData)currTerm.ParserData; foreach (Production prod in currInfo.Productions) { LR0Item core = prod.LR0Items[0]; //item at zero index is the one that starts with dot LRItem newItem = TryFindItem(state, core); if (newItem == null) { newItem = new LRItem(state, core); state.Items.Add(newItem); result = true; } #region Comments on lookaheads processing // The general idea of generating ("spontaneously") the lookaheads is the following: // Let's the original item be in the form // [A -> alpha . B beta , lset] // where <B> is a non-terminal and <lset> is a set of lookaheads, // <beta> is some string (B's tail in our terminology) // Then the closure item on non-teminal B is an item // [B -> x, firsts(beta + lset)] // (the lookahead set is expression after the comma). // To generate lookaheads on a closure item, we simply take "firsts" // from the tail <beta> of the NonTerminal <B>. // Normally if tail <beta> is nullable we would add ("propagate") // the <lset> lookaheads from <A> to <B>. // We dont' do it right here - we simply add a propagation link. // We propagate all lookaheads later in a separate process. #endregion newItem.NewLookaheads.AddRange(item.Core.TailFirsts); if (item.Core.TailIsNullable && !item.PropagateTargets.Contains(newItem)) { item.PropagateTargets.Add(newItem); } } //foreach prod } //for i (LRItem) return(result); }
} //method #endregion #region Calculating Tail Firsts private void CalculateTailFirsts() { foreach (Production prod in Data.Productions) { StringSet accumulatedFirsts = new StringSet(); bool allNullable = true; //We are going backwards in LR0Items list for (int i = prod.LR0Items.Count - 1; i >= 0; i--) { LR0Item item = prod.LR0Items[i]; if (i >= prod.LR0Items.Count - 2) { //Last and before last items have empty tails item.TailIsNullable = true; item.TailFirsts.Clear(); continue; } BnfTerm nextTerm = prod.RValues[i + 1]; //Element after-after-dot; remember we're going in reverse direction NtData nextData = (NtData)nextTerm.ParserData; //if (ntElem == null) continue; //it is not NonTerminal bool notNullable = nextTerm is Terminal || nextData != null && !nextData.Nullable; if (notNullable) //next term is not nullable (a terminal or non-nullable NonTerminal) //term is not nullable, so we clear all old firsts and add this term { accumulatedFirsts.Clear(); allNullable = false; item.TailIsNullable = false; if (nextTerm is Terminal) { item.TailFirsts.Add(nextTerm.Key);//term is terminal so add its key accumulatedFirsts.Add(nextTerm.Key); } else if (nextData != null) //it is NonTerminal { item.TailFirsts.AddRange(nextData.Firsts); //nonterminal accumulatedFirsts.AddRange(nextData.Firsts); } continue; } //if we are here, then ntElem is a nullable NonTerminal. We add accumulatedFirsts.AddRange(nextData.Firsts); item.TailFirsts.AddRange(accumulatedFirsts); item.TailIsNullable = allNullable; } //for i } //foreach prod } //method