public void IsNullableTest()
        {
            var grammar = new Grammar();

            var S = grammar.Symbols.Add("S");
            var a = grammar.Symbols.Add("a");
            var b = grammar.Symbols.Add("b");
            var A = grammar.Symbols.Add("A");
            var B = grammar.Symbols.Add("B");

            grammar.Start = S;
            grammar.Productions.Define(S, new[] { b, A });
            grammar.Productions.Define(A, new[] { a, A, B });
            grammar.Productions.Define(A, new Symbol[0]);
            grammar.Productions.Define(B, new Symbol[0]);

            var target = new RuntimeGrammar(grammar);

            Assert.IsTrue(target.IsNullable(A.Index));
            Assert.IsTrue(target.IsNullable(B.Index));

            Assert.IsFalse(target.IsNullable(a.Index));
            Assert.IsFalse(target.IsNullable(b.Index));
            Assert.IsFalse(target.IsNullable(S.Index));
        }
        private void BuildCache()
        {
            int tokenCount = grammar.SymbolCount;

            tokenCache = new SppfNode[tokenCount];

            for (int token = 0; token != tokenCount; ++token)
            {
                if (grammar.IsNullable(token))
                {
                    tokenCache[token] = new SppfNode(token, null, Loc.Unknown, HLoc.Unknown);
                }
            }

            ruleOffsetInCache    = new int[grammar.Productions.Count];
            ruleEndOffsetInCache = new int[grammar.Productions.Count];

            int nullableCount = 0;

            foreach (var rule in grammar.Productions)
            {
                int i = rule.PatternTokens.Length;
                while (i != 0)
                {
                    int token = rule.PatternTokens[--i];
                    if (tokenCache[token] == null)
                    {
                        break;
                    }

                    ++nullableCount;
                }

                ruleEndOffsetInCache[rule.Index] = nullableCount;
                ruleOffsetInCache[rule.Index]    = nullableCount - rule.PatternTokens.Length;
            }

            this.ruleCache = new SppfNode[nullableCount];

            foreach (var rule in grammar.Productions)
            {
                int endOffset = ruleOffsetInCache[rule.Index] + rule.PatternTokens.Length;
                int i         = rule.PatternTokens.Length;
                while (i != 0)
                {
                    int token = rule.PatternTokens[--i];
                    if (tokenCache[token] == null)
                    {
                        break;
                    }

                    ruleCache[--endOffset] = tokenCache[token];
                }
            }
        }
        private Msg InternalGetNullable(int nonTerm, IStackLookback <Msg> stackLookback)
        {
            Debug.Assert(grammar.IsNullable(nonTerm));

            var production =
                (from r in grammar.GetProductions(nonTerm)
                 where r.PatternTokens.All(grammar.IsNullable)
                 orderby r.PatternTokens.Length ascending
                 select r)
                .First();

            var args = new Msg[production.PatternTokens.Length];

            for (int i = 0; i != args.Length; ++i)
            {
                args[i] = InternalGetNullable(production.PatternTokens[i], stackLookback);
            }

            var value = productionAction(production.Index, args, 0, context, stackLookback);

            return(new Msg(nonTerm, value, Loc.Unknown));
        }