public static IList <CfgMessage> EliminateFirstFirstConflicts(this CfgDocument cfg) { var result = new List <CfgMessage>(); foreach (var nt in new List <string>(cfg.EnumNonTerminals())) { var rules = cfg.FillNonTerminalRules(nt); var rights = new List <IList <string> >(); foreach (var rule in rules) { rights.Add(rule.Right); } while (true) { var pfx = rights.GetLongestCommonPrefix(); if (pfx.IsNullOrEmpty()) { break; } // obv first first conflict var nnt = _GetLeftFactorId(cfg, nt); var suffixes = new List <IList <string> >(); foreach (var rule in rules) { if (rule.Right.StartsWith(pfx)) { rights.Remove(rule.Right); suffixes.Add(new List <string>(rule.Right.Range(pfx.Count))); cfg.Rules.Remove(rule); result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Format("Removed rule {0} because it is part of a first-first conflict.", rule), rule.Line, rule.Column, rule.Position)); } } var newRule = new CfgRule(nt); newRule.Right.AddRange(pfx); newRule.Right.Add(nnt); if (!cfg.Rules.Contains(newRule)) { cfg.Rules.Add(newRule); } result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Format("Added rule {0} to resolve first-first conflict.", newRule), 0, 0, 0)); foreach (var suffix in suffixes) { newRule = new CfgRule(nnt); newRule.Right.AddRange(suffix); if (!cfg.Rules.Contains(newRule)) { cfg.Rules.Add(newRule); } result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Format("Added rule {0} to resolve first-first conflict.", newRule), 0, 0, 0)); } _SetAttribute(cfg, nnt, "collapsed", true); } } return(result); }
static Lrfa _ToLrfa(CfgDocument cfg, IProgress <CfgLalr1Progress> progress) { if (null != progress) { progress.Report(new CfgLalr1Progress(CfgLalr1Status.ComputingStates, 0)); } // TODO: this takes a long time sometimes var map = new Dictionary <ICollection <LRItem>, Lrfa>(_LRItemSetComparer.Default); // create an augmented grammar - add rule {start} -> [[StartId]] var start = new CfgRule(cfg.GetAugmentedStartId(cfg.StartSymbol), new string[] { cfg.StartSymbol }); var cl = new HashSet <LRItem>(); cl.Add(new LRItem(start, 0)); _FillLRClosureInPlace(cfg, progress, cl); var lrfa = new Lrfa(true, cl); var items = cl.Count; map.Add(cl, lrfa); var done = false; var oc = 0; while (!done) { done = true; var arr = map.Keys.ToArray(); for (var i = 0; i < arr.Length; ++i) { var itemSet = arr[i]; foreach (var item in itemSet) { var next = item.RightIndex < item.Rule.Right.Count ? item.Rule.Right[item.RightIndex] : null; if (item.RightIndex < item.Rule.Right.Count) { var n = _FillLRMove(cfg, itemSet, next, progress); if (!_ContainsItemSet(map.Keys, n)) { done = false; var npda = new Lrfa(true, n); map.Add(n, npda); items += n.Count; if (null != progress) { progress.Report(new CfgLalr1Progress(CfgLalr1Status.ComputingConfigurations, items)); } } map[itemSet].Transitions[next] = map[n]; } } } if (!done) { oc = map.Count; if (null != progress) { progress.Report(new CfgLalr1Progress(CfgLalr1Status.ComputingStates, oc)); } } } return(lrfa); }
public CfgLL1Conflict(CfgLL1ConflictKind kind, CfgRule rule1, CfgRule rule2, string symbol) { Kind = kind; Rule1 = rule1 ?? throw new ArgumentNullException("rule1"); Rule2 = rule2 ?? throw new ArgumentNullException("rule2"); Symbol = symbol; }
public static IList <CfgMessage> EliminateFirstFollowsConflicts(this CfgDocument cfg) { var result = new List <CfgMessage>(); var conflicts = cfg.FillLL1Conflicts(); for (int ic = conflicts.Count, i = 0; i < ic; ++i) { var conflict = conflicts[i]; if (CfgLL1ConflictKind.FirstFollows == conflict.Kind) { if (conflict.Rule1.IsNil || conflict.Rule2.IsNil) { var rule = conflict.Rule1.IsNil ? conflict.Rule1 : conflict.Rule2; // we might be able to do something about this. var refs = cfg.FillReferencesToSymbol(rule.Left); var ntr = cfg.FillNonTerminalRules(rule.Left); for (int jc = refs.Count, j = 0; j < jc; ++j) { for (int kc = ntr.Count, k = 0; k < kc; ++k) { var ntrr = ntr[k]; var r = refs[j]; var rr = new CfgRule(r.Left, r.Right.Replace(rule.Left, ntrr.Right)); if (!cfg.Rules.Contains(rr)) { cfg.Rules.Add(rr); } result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Concat("Added rule ", rr.ToString(), " to resolve first-follows conflict."), rr.Line, rr.Column, rr.Position)); } result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Concat("Removed rule ", refs[j].ToString(), " to resolve first-follows conflict."), refs[j].Line, refs[j].Column, refs[j].Position)); cfg.Rules.Remove(refs[j]); } for (int jc = ntr.Count, j = 0; j < jc; ++j) { cfg.Rules.Remove(ntr[j]); result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Concat("Removed rule ", ntr[j].ToString(), " to resolve first-follows conflict."), ntr[j].Line, ntr[j].Column, ntr[j].Position)); } } } } return(result); }
public static IList <CfgMessage> TryToLalr1ParseTable(this CfgDocument cfg, IProgress <CfgLalr1Progress> progress, out CfgLalr1ParseTable parseTable) { var result = new List <CfgMessage>(); var start = cfg.GetAugmentedStartId(cfg.StartSymbol); var lrfa = _ToLrfa(cfg, progress); var trnsCfg = _ToLRTransitionGrammar(cfg, lrfa, progress); trnsCfg.RebuildCache(); var closure = new List <Lrfa>(); parseTable = new CfgLalr1ParseTable(); var itemSets = new List <ICollection <LRItem> >(); lrfa.FillClosure(closure); var i = 0; foreach (var p in closure) { itemSets.Add(p.AcceptSymbol); parseTable.Add(new Dictionary <string, (int RuleOrStateId, string Left, string[] Right)>()); ++i; } i = 0; foreach (var p in closure) { foreach (var trn in p.Transitions) { var idx = closure.IndexOf(trn.Value); parseTable[i].Add( trn.Key, (idx, null, null) ); } foreach (var item in p.AcceptSymbol) { if (Equals(item.Rule.Left, start) && item.RightIndex == item.Rule.Right.Count) { parseTable[i].Add( "#EOS", (-1, null, null)); break; } } ++i; } var follows = trnsCfg.FillFollows(); // work on our reductions now var map = new Dictionary <CfgRule, ICollection <string> >(_TransitionMergeRuleComparer.Default); foreach (var rule in trnsCfg.Rules) { ICollection <string> f; if (!map.TryGetValue(rule, out f)) { map.Add(rule, follows[rule.Left]); } else { foreach (var o in follows[rule.Left]) { if (!f.Contains(o)) { f.Add(o); } } } } var j = 0; foreach (var mapEntry in map) { if (null != progress) { progress.Report(new CfgLalr1Progress(CfgLalr1Status.ComputingReductions, j)); } var rule = mapEntry.Key; var lr = _LrtSymbol.Parse(rule.Right[rule.Right.Count - 1]); var left = _LrtSymbol.Parse(rule.Left).Id; var right = new List <string>(); foreach (var s in rule.Right) { right.Add(_LrtSymbol.Parse(s).Id); } var newRule = new CfgRule(left, right); if (!Equals(left, start)) { foreach (var f in mapEntry.Value) { // build the rule data var rr = new string[newRule.Right.Count]; for (var ri = 0; ri < rr.Length; ri++) { rr[ri] = newRule.Right[ri]; } var iid = _LrtSymbol.Parse(f).Id; (int RuleOrStateId, string Left, string[] Right)tuple; var rid = cfg.Rules.IndexOf(newRule); var newTuple = (RuleOrStateId : rid, Left : newRule.Left, Right : rr); // this gets rid of duplicate entries which crop up in the table if (!parseTable[lr.To].TryGetValue(iid, out tuple)) { parseTable[lr.To].Add(_LrtSymbol.Parse(f).Id, newTuple); } else { // TODO: Verify this - may need the dragon book if (null == tuple.Right) { var nr = cfg.Rules[rid]; var msg = new CfgMessage(CfgErrorLevel.Warning, -1, string.Format("Shift-Reduce conflict on rule {0}, token {1}", nr, iid), nr.Line, nr.Column, nr.Position); if (!result.Contains(msg)) { result.Add(msg); } } else { if (rid != newTuple.RuleOrStateId) { var nr = cfg.Rules[rid]; var msg = new CfgMessage(CfgErrorLevel.Error, -1, string.Format("Reduce-Reduce conflict on rule {0}, token {1}", nr, iid), nr.Line, nr.Column, nr.Position); if (!result.Contains(msg)) { result.Add(msg); } } } } } } ++j; } return(result); }
public LRItem(CfgRule rule, int rightIndex) { Rule = rule; RightIndex = rightIndex; }
public static IList <CfgMessage> EliminateLeftRecursion(this CfgDocument cfg) { var result = new List <CfgMessage>(); var done = false; while (!done) { done = true; var ic = cfg.Rules.Count; for (var i = 0; i < ic; ++i) { var rule = cfg.Rules[i]; if (rule.IsDirectlyLeftRecursive) { cfg.Rules.Remove(rule); result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Format("Removed rule {0} because it is directly left recursive.", rule), rule.Line, rule.Column, rule.Position)); var newId = _GetRightAssocId(cfg, rule.Left); var col = new List <string>(); var c = rule.Right.Count; for (var j = 1; j < c; ++j) { col.Add(rule.Right[j]); } col.Add(newId); var o = cfg.GetAttribute(rule.Left, "collapsed", false); if (o is bool && (bool)o) { _SetAttribute(cfg, newId, "collapsed", true); } else { _SetAttribute(cfg, newId, "substitute", rule.Left); } var newRule = new CfgRule(newId); for (int jc = col.Count, j = 0; j < jc; ++j) { newRule.Right.Add(col[j]); } if (!cfg.Rules.Contains(newRule)) { cfg.Rules.Add(newRule); } result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Format("Added rule {1} to replace rule {0}", rule, newRule), rule.Line, rule.Column, rule.Position)); var rr = new CfgRule(newId); if (!cfg.Rules.Contains(rr)) { cfg.Rules.Add(rr); } result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Format("Added rule {1} to replace rule {0}", rule, rr), rule.Line, rule.Column, rule.Position)); foreach (var r in cfg.Rules) { if (Equals(r.Left, rule.Left)) { if (!r.IsDirectlyLeftRecursive) { r.Right.Add(newId); } } } } /*else if (_IsIndirectlyLeftRecursive(rule)) * { * result.Add(new CfgMessage(CfgErrorLevel.Message, -1, string.Concat("Rule ", rule, " modified because it was indirectly left recursive."))); * Rules.Remove(rule); * var jc = rule.Right.Count; * var append = new List<string>(jc - 1); * for (var j = 1; j < jc; ++j) * append.Add(rule.Right[j]); * // do indirect left recursion elimination. * // first make it directly left recursive. * var dstRules = FillNonTerminalRules(rule.Right[0]); * foreach (var drule in dstRules) * { * var newRule = new CfgRule(rule.Left); * // now add the stuff from the dst rule; * newRule.Right.AddRange(drule.Right); * newRule.Right.AddRange(append); * if (!Rules.Contains(newRule)) * Rules.Add(newRule); * done = false; * var nt = GetTransformId(rule.Left); * var allRules = FillNonTerminalRules(rule.Left); * foreach (var ar in allRules) * { * // Section 2.3, 3.2 * // TODO: This needs lots more testing * * if (ar.IsNil || !Equals(ar.Right[0], rule.Left)) * { * var nar = new CfgRule(rule.Left); * nar.Right.AddRange(ar.Right); * nar.Right.Add(nt); * if (!Rules.Contains(nar)) * Rules.Add(nar); * Rules.Remove(ar); * } * else * { * ar.Right.RemoveAt(0); * ar.Left = nt; * ar.Right.Add(nt); * var nr2 = new CfgRule(nt); * if (!Rules.Contains(nr2)) * Rules.Add(nr2); * } * //} * * } * * result.AddRange(EliminateUnderivableRules()); * * break; * * } * if (!done) * break; * }*/ } } return(result); }
public static IList <CfgMessage> TryPrepareLL1(this CfgDocument cfg, IProgress <CfgLL1Progress> progress = null) { const int repeat = 2; var result = new List <CfgMessage>(); CfgDocument old = cfg; for (int j = 0; j < repeat; ++j) { if (null != progress) { progress.Report(new CfgLL1Progress(CfgLL1Status.Factoring, j)); } // if 20 times doesn't sort out this grammar it's not LL(1) // the math is such that we don't know unless we try // and the tries can go on forever. for (int i = 0; i < 20; ++i) { if (cfg.IsDirectlyLeftRecursive) { result.AddRange(EliminateLeftRecursion(cfg)); } var cc = FillLL1Conflicts(cfg); if (_HasFirstFollowsConflicts(cc)) { result.AddRange(EliminateFirstFollowsConflicts(cfg)); } cc = FillLL1Conflicts(cfg); if (_HasFirstFirstConflicts(cc)) { result.AddRange(EliminateFirstFirstConflicts(cfg)); } //result.AddRange(EliminateUnderivableRules()); cc = cfg.FillLL1Conflicts(); if (0 == cc.Count && !cfg.IsDirectlyLeftRecursive) { break; } if (old.Equals(cfg)) { break; } old = cfg.Clone(); } } if (cfg.IsDirectlyLeftRecursive) { // search for a directly recursive rule so we can report location info CfgRule r = null; for (int ic = cfg.Rules.Count, i = 0; i < ic; ++i) { var rr = cfg.Rules[i]; if (rr.IsDirectlyLeftRecursive) { r = rr; break; } } result.Add(new CfgMessage(CfgErrorLevel.Error, -1, "Grammar is unresolvably and directly left recursive and cannot be parsed with an LL parser.", r.Line, r.Column, r.Position)); } var fc = cfg.FillLL1Conflicts(); foreach (var f in fc) { result.Add(new CfgMessage(CfgErrorLevel.Error, -1, string.Format("Grammar has unresolvable first-{0} conflict between {1} and {2} on symbol {3}", f.Kind == CfgLL1ConflictKind.FirstFirst ? "first" : "follows", f.Rule1, f.Rule2, f.Symbol), f.Rule2.Line, f.Rule2.Column, f.Rule2.Position)); } cfg.TryValidateLL1(result); return(result); }
public CfgLL1ParseTableEntry(CfgRule rule) { Rule = rule; }