public BnfTermSet GetReduceReduceConflicts() { var result = new BnfTermSet(); result.UnionWith(Conflicts); result.ExceptWith(ShiftTerms); return(result); }
public BnfTermSet GetShiftReduceConflicts() { var result = new BnfTermSet(); result.UnionWith(Conflicts); result.IntersectWith(ShiftTerms); return(result); }
public string ToString(BnfTermSet exceptLookaheads) { string s = Core.ToString(); if (!this.Core.IsFinal) { return(s); } var lkhds = new BnfTermSet(); lkhds.UnionWith(Lookaheads); lkhds.ExceptWith(exceptLookaheads); s += " [" + lkhds.ToString() + "]"; return(s); }
private void ComputeReportedExpectedSet(ParserState state) { //2. Compute reduced expected terms - to be used in error reporting //2.1. Scan Expected terms, add non-terminals with non-empty DisplayName to reduced set, and collect all their firsts var reducedSet = state.ReportedExpectedSet = new BnfTermSet(); var allFirsts = new BnfTermSet(); foreach (var term in state.ExpectedTerms) { var nt = term as NonTerminal; if (nt == null) continue; if (!reducedSet.Contains(nt) && !string.IsNullOrEmpty(nt.DisplayName) && !allFirsts.Contains(nt)) { reducedSet.Add(nt); allFirsts.UnionWith(nt.Firsts); } } //2.2. Now go thru all expected terms and add only those that are NOT in the allFirsts set. foreach (var term in state.ExpectedTerms) { if (!reducedSet.Contains(term) && !allFirsts.Contains(term) && (term is Terminal || !string.IsNullOrEmpty(term.DisplayName))) reducedSet.Add(term); } //Clean-up reduced set, remove pseudo terms if (reducedSet.Contains(_grammar.Eof)) reducedSet.Remove(_grammar.Eof); if (reducedSet.Contains(_grammar.SyntaxError)) reducedSet.Remove(_grammar.SyntaxError); }
//Detect conflicts that cannot be handled by non-canonical NLALR method directly, by may be fixed by grammar transformation private void DetectNlalrFixableConflicts(ParserState state) { var stateData = state.BuilderData; //compute R-R and S-R conflicting lookaheads var reduceLkhds = new BnfTermSet(); var rrConflicts = new BnfTermSet(); var srConflicts = new BnfTermSet(); foreach (var reduceItem in state.BuilderData.ReduceItems) { foreach (var lkh in reduceItem.ReducedLookaheads) { if (stateData.ShiftTerms.Contains(lkh)) { if (!lkh.IsSet(TermOptions.UsePrecedence)) srConflicts.Add(lkh); //S-R conflict } else if (reduceLkhds.Contains(lkh)) rrConflicts.Add(lkh); //R-R conflict reduceLkhds.Add(lkh); }//foreach lkh }//foreach item if (srConflicts.Count == 0 && rrConflicts.Count == 0) return; //Collect all cores to recommend for adding WrapTail hint. var allConflicts = new BnfTermSet(); allConflicts.UnionWith(srConflicts); allConflicts.UnionWith(rrConflicts); foreach (var conflict in allConflicts) { var conflictingShiftItems = state.BuilderData.ShiftItems.SelectByCurrent(conflict); foreach (var item in conflictingShiftItems) if (!item.Core.IsInitial) //only non-initial _coresToAddWrapTailHint.Add(item.Core); foreach (var reduceItem in state.BuilderData.ReduceItems) { var conflictingSources = reduceItem.ReducedLookaheadSources.SelectByCurrent(conflict); foreach (var source in conflictingSources) _coresToAddWrapTailHint.Add(source.Core); } } //still report them as conflicts ReportParseConflicts(state, srConflicts, rrConflicts); //create default actions and remove conflicts from list so we don't deal with them anymore foreach (var conflict in rrConflicts) { var reduceItems = stateData.ReduceItems.SelectByReducedLookahead(conflict); var action = ParserAction.CreateReduce(reduceItems.First().Core.Production); state.Actions[conflict] = action; } //Update ResolvedConflicts and Conflicts sets stateData.ResolvedConflicts.UnionWith(srConflicts); stateData.ResolvedConflicts.UnionWith(rrConflicts); stateData.Conflicts.ExceptWith(stateData.ResolvedConflicts); }//method
}//method #region some explanations //Computes non-canonical lookaheads and jump lookaheads - those that cause jump // to non-canonical state // We are doing it top-down way, starting from most reduced lookaheads - they are not conflicting. // (If there were conflicting reduced lookaheads in a state initially, the grammar transformation algorithm // should have already wrapped them into non-conflicting "tail" non-terminals.) // We want to eliminate reduced lookaheads as much as possible, and replace them with expanded "child" // terms, to have only those non-canonical lookaheads that are absolutely necessary. // So for each reduced lookahead we check if we can replace it with its expanded, "child" terms // (from DirectFirsts set). We do it only if lookaheads child terms are all non-conflicting as lookaheads in // the state. If however, at least one child is conflicting, the reduced parent should stay. // What if we have some children conflicting and some not? We leave the parent reduced lookahead in state, // to cover (hide) the conflicting children, but we also add non-conflicting children as well, to allow // the parser automaton to use them (in canonical state) as soon as they are recognized, without need // to reduce the parent and switch back to canonical state. #endregion private void ComputeStateNonCanonicalLookaheads(ParserState state) { var stateData = state.BuilderData; //rename for shorter code var jumps = stateData.JumpLookaheads; // conflicting lookaheads, that must result in jump to non-canonical state var valids = stateData.NonCanonicalLookaheads; // valid non-canonical lookaheads, non-terminals only jumps.Clear(); valids.Clear(); var alreadyChecked = new BnfTermSet(); var toCheck = new BnfTermSet(); //terms to check for expansion //1. precompute initial set to check foreach (var reduceItem in stateData.ReduceItems) toCheck.UnionWith(reduceItem.ReducedLookaheads); toCheck.RemoveWhere(t => t is Terminal); //we are interested in non-terminals only //2. Try to expand all initial (reduced) lookaheads, and replace original lookaheads with expanded versions while (toCheck.Count > 0) { // do until no terms to check left var lkhInCheck = toCheck.First() as NonTerminal; toCheck.Remove(lkhInCheck); //to prevent repeated checking of mutually recursive terms if (alreadyChecked.Contains(lkhInCheck)) continue; alreadyChecked.Add(lkhInCheck); //Now check children for conflicts; go through all direct firsts of lkhInCheck and check them for conflicts bool hasJumpChild = false; foreach (var lkhChild in lkhInCheck.DirectFirsts) { if (lkhChild == lkhInCheck) continue; if (jumps.Contains(lkhChild)) { hasJumpChild = true; continue; } var ntChild = lkhChild as NonTerminal; if (ntChild != null && valids.Contains(ntChild)) continue; //the child has not been tested yet; check if it is a conflict in current state var occurCount = GetLookaheadOccurenceCount(state, lkhChild); if (occurCount > 1) { //possible conflict, check precedence if (lkhChild.IsSet(TermOptions.UsePrecedence)) { if (ntChild != null) { valids.Add(ntChild); //if it is terminal, it is valid; if (!alreadyChecked.Contains(lkhChild)) toCheck.Add(ntChild); } //if ntChild } else { //conflict! hasJumpChild = true; jumps.Add(lkhChild); //if it is non-terminal, add its Firsts to conflict as well if (ntChild != null) { jumps.UnionWith(ntChild.Firsts); //valids.ExceptWith(ntChild.Firsts); } }//if IsSet... else... } else { //occurCount == 1 //no conflict: if it is non-terminal, add it to toCheck set to check in the future if (ntChild != null && !alreadyChecked.Contains(ntChild)) toCheck.Add(ntChild); //if nonterminal and not checked yet, add it to toCheck for further checking }//if ...else... }//foreach lkhChild //Ok, we finished checking all direct children; if at least one of them has conflict, // then lkhInCheck (parent) must stay as a lookahead - we cannot fully expand it replacing by all children if (hasJumpChild) valids.Add(lkhInCheck); }//while toCheck.Count > 0 //remove conflicts stateData.Conflicts.Clear(); }//method
//computes DirectFirsts, Firsts for non-terminals and productions private static void ComputeFirsts(GrammarData data) { //compute prod direct firsts and initialize NT.Firsts foreach (var nt in data.NonTerminals) { foreach (var prod in nt.Productions) { foreach (var term in prod.RValues) { prod.DirectFirsts.Add(term); nt.DirectFirsts.Add(term); nt.Firsts.Add(term); if (!term.IsSet(TermOptions.IsNullable)) break; //foreach term } } }//foreach nt //propagate NT.Firsts int time = 0; var done = false; var newSet = new BnfTermSet(); while (!done) { done = true; foreach (var nt in data.NonTerminals) { newSet.Clear(); foreach (var first in nt.Firsts) { var ntFirst = first as NonTerminal; if (ntFirst != null && ntFirst._lastChanged >= nt._lastChecked) newSet.UnionWith(ntFirst.Firsts); } nt._lastChecked = time++; var oldCount = nt.Firsts.Count; nt.Firsts.UnionWith(newSet); if (nt.Firsts.Count > oldCount) { done = false; nt._lastChanged = time; } }//foreach nt }//while //compute prod.Firsts foreach (var nt in data.NonTerminals) { foreach (var prod in nt.Productions) { prod.Firsts.UnionWith(prod.DirectFirsts); foreach (var directFirst in prod.DirectFirsts) { var ntDirectFirst = directFirst as NonTerminal; if (ntDirectFirst != null) prod.Firsts.UnionWith(ntDirectFirst.Firsts); }//foreach directFirst }//foreach prod }//foreach nt }
//Detect conflicts that cannot be handled by non-canonical NLALR method directly, by may be fixed by grammar transformation private void DetectNlalrFixableConflicts(ParserState state) { var stateData = state.BuilderData; //compute R-R and S-R conflicting lookaheads var reduceLkhds = new BnfTermSet(); var rrConflicts = new BnfTermSet(); var srConflicts = new BnfTermSet(); foreach (var reduceItem in state.BuilderData.ReduceItems) { foreach (var lkh in reduceItem.ReducedLookaheads) { if (stateData.ShiftTerms.Contains(lkh)) { if (!lkh.IsSet(TermOptions.UsePrecedence)) { srConflicts.Add(lkh); //S-R conflict } } else if (reduceLkhds.Contains(lkh)) { rrConflicts.Add(lkh); //R-R conflict } reduceLkhds.Add(lkh); } //foreach lkh } //foreach item if (srConflicts.Count == 0 && rrConflicts.Count == 0) { return; } //Collect all cores to recommend for adding WrapTail hint. var allConflicts = new BnfTermSet(); allConflicts.UnionWith(srConflicts); allConflicts.UnionWith(rrConflicts); foreach (var conflict in allConflicts) { var conflictingShiftItems = state.BuilderData.ShiftItems.SelectByCurrent(conflict); foreach (var item in conflictingShiftItems) { if (!item.Core.IsInitial) //only non-initial { _coresToAddWrapTailHint.Add(item.Core); } } foreach (var reduceItem in state.BuilderData.ReduceItems) { var conflictingSources = reduceItem.ReducedLookaheadSources.SelectByCurrent(conflict); foreach (var source in conflictingSources) { _coresToAddWrapTailHint.Add(source.Core); } } } //still report them as conflicts ReportParseConflicts(state, srConflicts, rrConflicts); //create default actions and remove conflicts from list so we don't deal with them anymore foreach (var conflict in rrConflicts) { var reduceItems = stateData.ReduceItems.SelectByReducedLookahead(conflict); var action = ParserAction.CreateReduce(reduceItems.First().Core.Production); state.Actions[conflict] = action; } //Update ResolvedConflicts and Conflicts sets stateData.ResolvedConflicts.UnionWith(srConflicts); stateData.ResolvedConflicts.UnionWith(rrConflicts); stateData.Conflicts.ExceptWith(stateData.ResolvedConflicts); }//method
}//method #region some explanations //Computes non-canonical lookaheads and jump lookaheads - those that cause jump // to non-canonical state // We are doing it top-down way, starting from most reduced lookaheads - they are not conflicting. // (If there were conflicting reduced lookaheads in a state initially, the grammar transformation algorithm // should have already wrapped them into non-conflicting "tail" non-terminals.) // We want to eliminate reduced lookaheads as much as possible, and replace them with expanded "child" // terms, to have only those non-canonical lookaheads that are absolutely necessary. // So for each reduced lookahead we check if we can replace it with its expanded, "child" terms // (from DirectFirsts set). We do it only if lookaheads child terms are all non-conflicting as lookaheads in // the state. If however, at least one child is conflicting, the reduced parent should stay. // What if we have some children conflicting and some not? We leave the parent reduced lookahead in state, // to cover (hide) the conflicting children, but we also add non-conflicting children as well, to allow // the parser automaton to use them (in canonical state) as soon as they are recognized, without need // to reduce the parent and switch back to canonical state. #endregion private void ComputeStateNonCanonicalLookaheads(ParserState state) { var stateData = state.BuilderData; //rename for shorter code var jumps = stateData.JumpLookaheads; // conflicting lookaheads, that must result in jump to non-canonical state var valids = stateData.NonCanonicalLookaheads; // valid non-canonical lookaheads, non-terminals only jumps.Clear(); valids.Clear(); var alreadyChecked = new BnfTermSet(); var toCheck = new BnfTermSet(); //terms to check for expansion //1. precompute initial set to check foreach (var reduceItem in stateData.ReduceItems) { toCheck.UnionWith(reduceItem.ReducedLookaheads); } toCheck.RemoveWhere(t => t is Terminal); //we are interested in non-terminals only //2. Try to expand all initial (reduced) lookaheads, and replace original lookaheads with expanded versions while (toCheck.Count > 0) // do until no terms to check left { var lkhInCheck = toCheck.First() as NonTerminal; toCheck.Remove(lkhInCheck); //to prevent repeated checking of mutually recursive terms if (alreadyChecked.Contains(lkhInCheck)) { continue; } alreadyChecked.Add(lkhInCheck); //Now check children for conflicts; go through all direct firsts of lkhInCheck and check them for conflicts bool hasJumpChild = false; foreach (var lkhChild in lkhInCheck.DirectFirsts) { if (lkhChild == lkhInCheck) { continue; } if (jumps.Contains(lkhChild)) { hasJumpChild = true; continue; } var ntChild = lkhChild as NonTerminal; if (ntChild != null && valids.Contains(ntChild)) { continue; } //the child has not been tested yet; check if it is a conflict in current state var occurCount = GetLookaheadOccurenceCount(state, lkhChild); if (occurCount > 1) { //possible conflict, check precedence if (lkhChild.IsSet(TermOptions.UsePrecedence)) { if (ntChild != null) { valids.Add(ntChild); //if it is terminal, it is valid; if (!alreadyChecked.Contains(lkhChild)) { toCheck.Add(ntChild); } } //if ntChild } else { //conflict! hasJumpChild = true; jumps.Add(lkhChild); //if it is non-terminal, add its Firsts to conflict as well if (ntChild != null) { jumps.UnionWith(ntChild.Firsts); //valids.ExceptWith(ntChild.Firsts); } } //if IsSet... else... } else //occurCount == 1 //no conflict: if it is non-terminal, add it to toCheck set to check in the future { if (ntChild != null && !alreadyChecked.Contains(ntChild)) { toCheck.Add(ntChild); //if nonterminal and not checked yet, add it to toCheck for further checking } }//if ...else... }//foreach lkhChild //Ok, we finished checking all direct children; if at least one of them has conflict, // then lkhInCheck (parent) must stay as a lookahead - we cannot fully expand it replacing by all children if (hasJumpChild) { valids.Add(lkhInCheck); } }//while toCheck.Count > 0 //remove conflicts stateData.Conflicts.Clear(); }//method
//computes DirectFirsts, Firsts for non-terminals and productions private static void ComputeFirsts(GrammarData data) { //compute prod direct firsts and initialize NT.Firsts foreach (var nt in data.NonTerminals) { foreach (var prod in nt.Productions) { foreach (var term in prod.RValues) { prod.DirectFirsts.Add(term); nt.DirectFirsts.Add(term); nt.Firsts.Add(term); if (!term.IsSet(TermOptions.IsNullable)) { break; //foreach term } } } }//foreach nt //propagate NT.Firsts int time = 0; var done = false; var newSet = new BnfTermSet(); while (!done) { done = true; foreach (var nt in data.NonTerminals) { newSet.Clear(); foreach (var first in nt.Firsts) { var ntFirst = first as NonTerminal; if (ntFirst != null && ntFirst._lastChanged >= nt._lastChecked) { newSet.UnionWith(ntFirst.Firsts); } } nt._lastChecked = time++; var oldCount = nt.Firsts.Count; nt.Firsts.UnionWith(newSet); if (nt.Firsts.Count > oldCount) { done = false; nt._lastChanged = time; } } //foreach nt } //while //compute prod.Firsts foreach (var nt in data.NonTerminals) { foreach (var prod in nt.Productions) { prod.Firsts.UnionWith(prod.DirectFirsts); foreach (var directFirst in prod.DirectFirsts) { var ntDirectFirst = directFirst as NonTerminal; if (ntDirectFirst != null) { prod.Firsts.UnionWith(ntDirectFirst.Firsts); } } //foreach directFirst } //foreach prod } //foreach nt } //method
//Detect conflicts that cannot be handled by non-canonical NLALR method directly, by may be fixed by grammar transformation private void DetectNlalrFixableConflicts(ParserState state) { var stateData = state.BuilderData; //compute R-R and S-R conflicting lookaheads var reduceLkhds = new BnfTermSet(); var rrConflicts = new BnfTermSet(); var srConflicts = new BnfTermSet(); foreach (var reduceItem in state.BuilderData.ReduceItems) { foreach (var lkh in reduceItem.ReducedLookaheads) { if (stateData.ShiftTerms.Contains(lkh)) { if (!lkh.IsSet(TermOptions.UsePrecedence)) srConflicts.Add(lkh); //S-R conflict } else if (reduceLkhds.Contains(lkh)) rrConflicts.Add(lkh); //R-R conflict reduceLkhds.Add(lkh); }//foreach lkh }//foreach item if (srConflicts.Count == 0 && rrConflicts.Count == 0) return; //Collect all cores to recommend for adding WrapTail hint. var allConflicts = new BnfTermSet(); allConflicts.UnionWith(srConflicts); allConflicts.UnionWith(rrConflicts); foreach (var conflict in allConflicts) { var conflictingShiftItems = state.BuilderData.ShiftItems.SelectByCurrent(conflict); foreach (var item in conflictingShiftItems) if (!item.Core.IsInitial) //only non-initial _coresToAddWrapTailHint.Add(item.Core); foreach (var reduceItem in state.BuilderData.ReduceItems) { var conflictingSources = reduceItem.ReducedLookaheadSources.SelectByCurrent(conflict); foreach (var source in conflictingSources) _coresToAddWrapTailHint.Add(source.Core); } } ReportAndCreateDefaultActionsForConflicts(state, srConflicts, rrConflicts); //Update ResolvedConflicts and Conflicts sets stateData.ResolvedConflicts.UnionWith(srConflicts); stateData.ResolvedConflicts.UnionWith(rrConflicts); stateData.Conflicts.ExceptWith(stateData.ResolvedConflicts); }