/// <summary> /// Compute the LR(1) closure including lookaheads in the item sets (page 261 of Compilers 2nd Ed.) /// </summary> private LrItemSet Lr1Closure(SymbolMetaDictionary symbolMeta, LrItemSet items) { // Initialize the return set to the item set var newset = new LrItemSet(items) { IsClosed = true }; var toAdd = new List <LrItem>(); var newLookaheads = new HashSet <SymbolId>(); do { toAdd.Clear(); // For each item in the set with a marker before a nonterminal foreach (var item in newset.Where(i => (i.Marker < i.Length) && (this.symbols[i.Rule.RuleSymbolIds[i.Marker]] == SymbolKind.Nonterminal))) { var nonterminal = item.Rule.RuleSymbolIds[item.Marker]; // Get all the possible lookaheads past this symbol newLookaheads.Clear(); foreach (var lookahead in item.LookaheadIds) { var followingSymbols = item.Rule.RuleSymbolIds.Skip(item.Marker + 1).Append(lookahead); newLookaheads.UnionWith(symbolMeta.FirstOfAll(followingSymbols)); } if (newLookaheads.Any()) { // For each rule of the production past the marker for this item toAdd.AddRange(this.ProductionRulesBySymbol[nonterminal].Select(rule => new LrItem(rule, 0, false, newLookaheads))); } } } while ((toAdd.Count > 0) && newset.MergeWith(toAdd)); return(newset); }
public LalrTable ComputeTable() { var symbolMeta = new SymbolMetaDictionary(this.symbols); symbolMeta.ComputeFirstFollowsAndNullable(this.Start, this.Eof, this.ProductionRules); var states = this.ComputeItemSets(symbolMeta, out var acceptIndex); var gotoLookup = this.ComputeGotoLookup(symbolMeta, states); var actionTable = new Dictionary <StateKey <SymbolId>, LalrAction>(); foreach (var state in states) { foreach (var sym in this.symbols) { var key = new StateKey <SymbolId>(state.Index, sym.Key); if (sym.Value == SymbolKind.Terminal) { foreach (var item in state) { if ((item.Marker < item.Length) && (item.Rule.RuleSymbolIds[item.Marker] == sym.Key)) { if (gotoLookup.TryGetValue(key, out var gotoState)) { actionTable[key] = new ShiftAction(gotoState); } } else if (item.Length == item.Marker) { if (item.Rule.Index == acceptIndex) { if (sym.Key == this.Eof) { actionTable[key] = new AcceptAction(); } } else if (item.LookaheadIds.Contains(sym.Key) && (item.Rule.ProductionSymbolId != this.Init)) { if (actionTable.TryGetValue(key, out var action)) { // Reduce-reduce conflict - plain LALR parser will fail, GLR parsers will try both rules in parallel actionTable[key] = ((ReduceAction)action).AddProductionRule(item.Rule); } else { actionTable[key] = new ReduceSingleAction(item.Rule); } } } } } else { if (gotoLookup.TryGetValue(key, out var gotoState)) { actionTable[key] = new GotoAction(gotoState); } } } } return(new LalrTable(states.StartState.Index, actionTable, this.ProductionRules)); }
private Dictionary <StateKey <SymbolId>, int> ComputeGotoLookup(SymbolMetaDictionary symbolMeta, LrItemSetCollection itemSets) { var gotos = new Dictionary <StateKey <SymbolId>, int>(); var itemSetByKernels = itemSets.ToDictionary(s => s.Kernels.ToArray(), s => s, SetEqualityComparer <LrItem> .Default); foreach (var itemSet in itemSets) { foreach (var sym in symbolMeta.Keys) { // Compute dynamic GOTO var gotoClosure = this.Lr1Closure(symbolMeta, new LrItemSet(itemSet .Where(item => (item.Marker < item.Length) && item.Rule.RuleSymbolIds[item.Marker].Equals(sym)) .Select(item => new LrItem(item.Rule, item.Marker + 1, true)))); if (!gotoClosure.Any()) { continue; } var key = new StateKey <SymbolId>(itemSet.Index, sym); if (gotos.ContainsKey(key)) { continue; } // Match the dynamic GOTO to an actual state if (itemSetByKernels.TryGetValue(gotoClosure.Kernels, out var existingGoto)) { gotos.Add(key, existingGoto.Index); } } } return(gotos); }
private LrItemSetCollection ComputeItemSets(SymbolMetaDictionary symbolMeta, out int acceptIndex) { this.ComputeLr0ItemSetKernelsAndGotoLookup(out var itemSets, out var gotoSymbol, out acceptIndex); var dummyLookahead = new[] { this.Unknown }; itemSets.StartState.Single().LookaheadIds.Add(this.Eof); var rulePropagations = new Dictionary <StateKey <LrItem>, HashSet <StateKey <LrItem> > >(); foreach (var itemSet in itemSets) { var gotosForState = gotoSymbol[itemSet.Index]; foreach (var kernelItem in itemSet) { var itemKey = new StateKey <LrItem>(itemSet.Index, kernelItem); // Create an item set with a dummy lookahead, based on the current item set var dummyItem = new LrItem(kernelItem.Rule, kernelItem.Marker, kernelItem.Marker > 0, dummyLookahead); var dummyItemSet = new LrItemSet(dummyItem.Yield()); var j = this.Lr1Closure(symbolMeta, dummyItemSet); foreach (var gotoForState in gotosForState) { var gotoItemSet = itemSets[gotoForState.Value]; var gotoItemLookup = gotoItemSet.ToDictionary(g => g, g => g); // Find the items in the dummy set with the marker before this symbol foreach (var b in j.Where(bb => (bb.Marker < bb.Length) && bb.Rule.RuleSymbolIds[bb.Marker].Equals(gotoForState.Key))) { // Get the item corresponding to the goto state with the marker past the current symbol var gotoItem = gotoItemLookup[new LrItem(b.Rule, b.Marker + 1, true)]; if (b.LookaheadIds.Any(l => l == this.Unknown)) { if (!rulePropagations.ContainsKey(itemKey)) { rulePropagations[itemKey] = new HashSet <StateKey <LrItem> >(); } rulePropagations[itemKey].Add(new StateKey <LrItem>(gotoItemSet.Index, gotoItem)); } gotoItem.LookaheadIds.UnionWith(b.LookaheadIds.Where(l => l != this.Unknown)); } } } } bool changed; do { changed = false; foreach (var itemSet in itemSets) { foreach (var item in itemSet) { var itemKey = new StateKey <LrItem>(itemSet.Index, item); if (!rulePropagations.TryGetValue(itemKey, out var propagated)) { continue; } foreach (var key in propagated) { if (key.Value.LookaheadIds.AddRange(item.LookaheadIds)) { changed = true; } } } } } while (changed); // Close all kernels for (var i = 0; i < itemSets.Count; i++) { itemSets[i] = this.Lr1Closure(symbolMeta, itemSets[i]); itemSets[i].Index = i; } return(itemSets); }