예제 #1
0
        /// <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);
        }
예제 #2
0
        /// <summary>
        /// Time- and space-optimized goto which returns gotos for all relevant symbols, storing kernels only.
        /// For this function, the input state must have its closure production heads computed (with LR0ComputeClosureNonterminals).
        /// The output states do not have closure productions.
        /// </summary>
        private Dictionary <SymbolId, LrItemSet> Lr0GotoKernels(LrItemSet itemSet)
        {
            if (itemSet.ClosureProductions == null)
            {
                throw new InvalidOperationException();
            }
            // Create new items by advancing the marker for the input kernels
            var kernelItems = itemSet
                              .Kernels
                              .Where(k => k.Marker < k.Length)
                              .Select(k => new KeyValuePair <SymbolId, LrItem>(k.Rule.RuleSymbolIds[k.Marker], new LrItem(k.Rule, k.Marker + 1, true)));
            // Create new items from the nonkernels (using closure production heads)
            // Since goto is to be computed for ALL symbols, advance the marker for all nonkernels
            // The first item in the rule is the goto symbol, and the marker is advanced past the first item
            var nonKernelItems = itemSet
                                 .ClosureProductions
                                 .SelectMany(n => this.ProductionRulesBySymbol[n])
                                 .Where(r => r.RuleSymbolIds.Count > 0)
                                 .Select(r => new KeyValuePair <SymbolId, LrItem>(r.RuleSymbolIds[0], new LrItem(r, 1, true)));

            // The dictionary will contain item sets for all possible goto symbols X, Y, and Z for the given input state
            return(kernelItems
                   .Concat(nonKernelItems)
                   .GroupBy(t => t.Key, t => t.Value)
                   .ToDictionary(g => g.Key, g => new LrItemSet(g)));
        }
예제 #3
0
        private void Lr0ComputeClosureNonterminals(LrItemSet itemSet)
        {
            if (itemSet.ClosureProductions != null)
            {
                return;
            }
            // Initialize the set with the next symbol at each marker
            var nonterminalSet = new HashSet <SymbolId>(MarkedSymbols(itemSet).Where(this.IsNonterminal));
            var queue          = new Queue <SymbolId>(nonterminalSet);

            while (queue.Count > 0)
            {
                var current = queue.Dequeue();
                // Get the first nonterminal from rules starting with these
                foreach (var nonterminal in FirstSymbols(this.ProductionRulesBySymbol[current]).Where(this.IsNonterminal).Distinct())
                {
                    if (nonterminalSet.Add(nonterminal))
                    {
                        // New production, add it to the closure
                        queue.Enqueue(nonterminal);
                    }
                }
            }
            itemSet.ClosureProductions = nonterminalSet;
        }
예제 #4
0
        private void ComputeLr0ItemSetKernelsAndGotoLookup(out LrItemSetCollection itemSets, out Dictionary <int, Dictionary <SymbolId, int> > gotos, out int acceptIndex)
        {
            itemSets = new LrItemSetCollection();
            gotos    = new Dictionary <int, Dictionary <SymbolId, int> >();
            var itemSetByKernels = new Dictionary <IEnumerable <LrItem>, LrItemSet>(SetEqualityComparer <LrItem> .Default);
            var queue            = new Queue <LrItemSet>();
            var startItem        = new LrItemSet(new LrItem(this.ProductionRulesBySymbol[this.Init].Single(), 0, true));

            this.Lr0ComputeClosureNonterminals(startItem);
            itemSets.StartState = startItem;
            acceptIndex         = this.ProductionRulesBySymbol[this.Init].Single().Index;
            startItem.Index     = itemSets.Count;
            itemSets.Add(startItem);
            itemSetByKernels.Add(startItem.Kernels.ToArray(), startItem);
            queue.Enqueue(startItem);
            while (queue.Count > 0)
            {
                var itemSet       = queue.Dequeue();
                var gotoLookup    = this.Lr0GotoKernels(itemSet);
                var gotosForState = new Dictionary <SymbolId, int>();
                gotos.Add(itemSet.Index, gotosForState);
                foreach (var symbol in gotoLookup.Keys)
                {
                    if (!itemSetByKernels.TryGetValue(gotoLookup[symbol].Kernels, out var gotoState))
                    {
                        gotoState = gotoLookup[symbol];
                        this.Lr0ComputeClosureNonterminals(gotoState);
                        gotoState.Index = itemSets.Count;
                        itemSets.Add(gotoState);
                        itemSetByKernels.Add(gotoState.Kernels.ToArray(), gotoState);
                        queue.Enqueue(gotoState);
                    }
                    gotosForState.Add(symbol, gotoState.Index);
                }
            }
        }
예제 #5
0
        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);
        }