public void TestNonLalr1()
        {
            //S->aEa | bEb | aFb | bFa
            //E->e
            //F->e

            var s      = new NonTerminal("S");
            var e      = new NonTerminal("E");
            var f      = new NonTerminal("F");
            var a      = new Token("a");
            var b      = new Token("b");
            var eToken = new Token("e");

            var rules = new[]
            {
                new Rule(Start, s),
                new Rule(s, a, e, a),
                new Rule(s, b, e, b),
                new Rule(s, a, f, b),
                new Rule(s, b, f, a),
                new Rule(e, eToken),
                new Rule(f, eToken),
            };
            var nodes  = ParserBuilder.CreateParser(rules);
            var parser = new ParserNodeParser(nodes, Start, this.output.WriteLine);

            var listener = new TreeListener();

            parser.Parse(new[] { a, eToken, a }, listener);

            this.output.WriteLine(listener.Root.Flatten().ToString());
            listener.Root.Flatten().ToString()
            .ShouldEqual("Start(S(a, E(e), a))");
        }
        public void TestTrueGenericsAmbiguity()
        {
            var name                      = new NonTerminal("Name");
            var argList                   = new NonTerminal("List<Exp>");
            var genericParameters         = new NonTerminal("Gen");
            var optionalGenericParameters = new NonTerminal("Opt<Gen>");
            var cmp = new NonTerminal("Cmp");

            var rules = new[]
            {
                new Rule(Start, Exp),

                new Rule(Exp, ID),
                new Rule(Exp, OPEN_PAREN, Exp, CLOSE_PAREN),
                new Rule(Exp, ID, cmp, Exp),
                new Rule(Exp, name, OPEN_PAREN, argList, CLOSE_PAREN),

                new Rule(cmp, LT),
                new Rule(cmp, GT),

                new Rule(argList, Exp),
                new Rule(argList, Exp, COMMA, argList),

                new Rule(name, ID, optionalGenericParameters),

                new Rule(optionalGenericParameters),
                new Rule(optionalGenericParameters, genericParameters),

                new Rule(genericParameters, LT, ID, GT)
            };

            // currently this fails due to duplicate prefixes
            var ex = Assert.Throws <NotSupportedException>(() => ParserBuilder.CreateParser(rules));

            this.output.WriteLine(ex.Message);

            var nodes = ParserBuilder.CreateParser(
                rules,
                new Dictionary <IReadOnlyList <Symbol>, Rule>
            {
                {
                    // id<id>(id could be call(id<id>, id) or compare(id, compare(id, id))
                    new[] { ID, LT, ID, GT, OPEN_PAREN, ID },
                    rules.Single(r => r.Symbols.SequenceEqual(new Symbol[] { name, OPEN_PAREN, argList, CLOSE_PAREN }))
                },
                {
                    // id<id>(( could be call(id<id>, (...)) or compare(id, compare(id, (...)))
                    new[] { ID, LT, ID, GT, OPEN_PAREN, OPEN_PAREN },
                    rules.Single(r => r.Symbols.SequenceEqual(new Symbol[] { name, OPEN_PAREN, argList, CLOSE_PAREN }))
                },
            }
                );
            var parser   = new ParserNodeParser(nodes, Start, this.output.WriteLine);
            var listener = new TreeListener();

            // compares: id < id > (id) vs. call: id<id>(id) => our resolution says call
            parser.Parse(new[] { ID, LT, ID, GT, OPEN_PAREN, ID, CLOSE_PAREN }, listener);
            this.output.WriteLine(listener.Root.Flatten().ToString());
            listener.Root.Flatten().ToString().ShouldEqual("Start(Exp(Name(ID, Opt<Gen>(Gen(<, ID, >))), (, List<Exp>(Exp(ID)), )))");
        }
        public void TestAliases()
        {
            var binop = new NonTerminal("Binop");
            var rules = new[]
            {
                new Rule(Start, Exp),

                new Rule(Exp, ID),
                new Rule(Exp, MINUS, Exp),
                new Rule(Exp, binop),

                new Rule(binop, Exp, TIMES, Exp),
                new Rule(binop, Exp, MINUS, Exp),
            };

            var rewritten = LeftRecursionRewriter.Rewrite(rules, rightAssociativeRules: ImmutableHashSet.Create(rules[1]));

            this.output.WriteLine(ToString(rewritten));

            var nodes    = ParserBuilder.CreateParser(rewritten.Keys);
            var listener = new TreeListener(rewritten);
            var parser   = new ParserNodeParser(nodes, Start);

            parser.Parse(new[] { ID, TIMES, MINUS, ID, MINUS, ID }, listener);
            this.output.WriteLine(ToGroupedTokenString(listener.Root));
            ToGroupedTokenString(listener.Root)
            .ShouldEqual("(ID * (- ID)) - ID");
        }
Ejemplo n.º 4
0
        public void TestAsyncAwaitWithAmbiguity()
        {
            var beginMethod   = new NonTerminal("BeginMethod");
            var endMethod     = new NonTerminal("EndMethod");
            var asyncOption   = new NonTerminal($"Option<{ASYNC.Name}>");
            var call          = new NonTerminal("Call");
            var arguments     = new NonTerminal("Arguments");
            var argumentsTail = new NonTerminal("ArgumentsTail");

            var awaitRule = new Rule(Exp, new Symbol[] { AWAIT, Exp }, requiredParserVariable: "asyncMethod");
            var rules     = new[]
            {
                new Rule(Start, Method),
                new Rule(Method, beginMethod, asyncOption, ID, OPEN_BRACE, StmtList, CLOSE_BRACE, endMethod),
                new Rule(beginMethod, new Symbol[0], action: new RuleAction("asyncMethod", RuleActionKind.Push)),
                new Rule(endMethod, new Symbol[0], action: new RuleAction("asyncMethod", RuleActionKind.Pop)),

                new Rule(asyncOption, new Symbol[] { ASYNC }, action: new RuleAction("asyncMethod", RuleActionKind.Set)),
                new Rule(asyncOption),

                new Rule(StmtList, Stmt, StmtList),
                new Rule(StmtList),

                new Rule(Stmt, Exp, SEMICOLON),

                new Rule(Exp, Identifier),
                new Rule(Exp, OPEN_PAREN, Exp, CLOSE_PAREN),
                new Rule(Exp, call),
                awaitRule,

                new Rule(call, Identifier, OPEN_PAREN, arguments, CLOSE_PAREN),

                new Rule(arguments),
                new Rule(arguments, Exp, argumentsTail),
                new Rule(argumentsTail),
                new Rule(argumentsTail, COMMA, Exp, argumentsTail),

                new Rule(Identifier, ID),
                new Rule(Identifier, AWAIT),
            };

            var nodes  = ParserBuilder.CreateParser(rules);
            var parser = new ParserNodeParser(nodes, Start, this.output.WriteLine);

            var listener1 = new TreeListener();

            parser.Parse(new[] { ASYNC, ID, OPEN_BRACE, AWAIT, OPEN_PAREN, ID, CLOSE_PAREN, SEMICOLON, CLOSE_BRACE }, listener1);
            this.output.WriteLine(listener1.Root.Flatten().ToString());
            listener1.Root.Flatten().ToString().ShouldEqual("Start(Method(BeginMethod(), Option<async>(async), ID, {, List<Stmt>(Stmt(Exp(await, Exp((, Exp(Id(ID)), ))), ;)), }, EndMethod()))");

            var listener2 = new TreeListener();

            parser.Parse(new[] { ID, OPEN_BRACE, AWAIT, OPEN_PAREN, ID, CLOSE_PAREN, SEMICOLON, CLOSE_BRACE }, listener2);
            this.output.WriteLine(listener2.Root.Flatten().ToString());
            listener2.Root.Flatten().ToString().ShouldEqual("Start(Method(BeginMethod(), Option<async>(), ID, {, List<Stmt>(Stmt(Exp(Call(Id(await), (, Arguments(Exp(Id(ID)), ArgumentsTail()), ))), ;)), }, EndMethod()))");
        }
        public void TestAwaitParensAmbiguity()
        {
            var call          = new NonTerminal("Call");
            var arguments     = new NonTerminal("Arguments");
            var argumentsTail = new NonTerminal("ArgumentsTail");
            var identifier    = new NonTerminal("Identifier");
            var await         = new Token("await");

            var awaitRule = new Rule(Exp, new Symbol[] { await, Exp });
            var rules     = new[]
            {
                new Rule(Start, StmtList),

                new Rule(StmtList, Stmt, StmtList),
                new Rule(StmtList),

                new Rule(Stmt, Exp, SEMICOLON),

                new Rule(Exp, identifier),
                new Rule(Exp, OPEN_PAREN, Exp, CLOSE_PAREN),
                new Rule(Exp, call),
                awaitRule,

                new Rule(call, identifier, OPEN_PAREN, arguments, CLOSE_PAREN),

                new Rule(arguments),
                new Rule(arguments, Exp, argumentsTail),
                new Rule(argumentsTail),
                new Rule(argumentsTail, COMMA, Exp, argumentsTail),

                new Rule(identifier, ID),
                new Rule(identifier, await),
            };

            var nodes = ParserBuilder.CreateParser(
                rules,
                new Dictionary <IReadOnlyList <Symbol>, Rule>
            {
                { new Symbol[] { await, OPEN_PAREN, ID, CLOSE_PAREN }, awaitRule },
                { new Symbol[] { await, OPEN_PAREN, OPEN_PAREN, Exp, CLOSE_PAREN, CLOSE_PAREN }, awaitRule },
                { new Symbol[] { await, OPEN_PAREN, ID, OPEN_PAREN, arguments, CLOSE_PAREN, CLOSE_PAREN }, awaitRule },
                { new Symbol[] { await, OPEN_PAREN, await, CLOSE_PAREN }, awaitRule },
                { new Symbol[] { await, OPEN_PAREN, await, Exp, CLOSE_PAREN }, awaitRule },
                { new Symbol[] { await, OPEN_PAREN, await, OPEN_PAREN, arguments, CLOSE_PAREN, CLOSE_PAREN }, awaitRule },
                { new Symbol[] { await, OPEN_PAREN, await, OPEN_PAREN, Exp, CLOSE_PAREN, CLOSE_PAREN }, awaitRule },
            }
                );
            var parser = new ParserNodeParser(nodes, Start, this.output.WriteLine);

            var listener = new TreeListener();

            parser.Parse(new[] { await, OPEN_PAREN, ID, CLOSE_PAREN, SEMICOLON, await, OPEN_PAREN, ID, COMMA, ID, CLOSE_PAREN, SEMICOLON }, listener);
            this.output.WriteLine(listener.Root.Flatten().ToString());
            listener.Root.Flatten().ToString().ShouldEqual("Start(List<Stmt>(Stmt(Exp(await, Exp((, Exp(Identifier(ID)), ))), ;), Stmt(Exp(Call(Identifier(await), (, Arguments(Exp(Identifier(ID)), ArgumentsTail(,, Exp(Identifier(ID)), ArgumentsTail())), ))), ;)))");
        }
        public void TestGenericsAmbiguity()
        {
            var name                      = new NonTerminal("Name");
            var nameListOption            = new NonTerminal("Opt<List<Name>>");
            var nameList                  = new NonTerminal("List<Name>");
            var genericParameters         = new NonTerminal("Gen");
            var optionalGenericParameters = new NonTerminal("Opt<Gen>");

            var rules = new[]
            {
                new Rule(Exp, name),
                new Rule(name, ID, optionalGenericParameters),
                new Rule(optionalGenericParameters),
                new Rule(optionalGenericParameters, genericParameters),
                new Rule(genericParameters, LT, nameListOption, GT),
                new Rule(nameListOption),
                new Rule(nameListOption, nameList),
                new Rule(nameList, name),
                new Rule(nameList, name, COMMA, nameList)
            };

            var nodes1    = ParserBuilder.CreateParser(rules);
            var parser1   = new ParserNodeParser(nodes1, Exp, this.output.WriteLine);
            var listener1 = new TreeListener();

            parser1.Parse(new[] { ID, LT, ID, COMMA, ID, GT }, listener1);
            this.output.WriteLine(listener1.Root.Flatten().ToString());
            listener1.Root.Flatten().ToString().ShouldEqual("Exp(Name(ID, Opt<Gen>(Gen(<, Opt<List<Name>>(List<Name>(Name(ID, Opt<Gen>()), ,, Name(ID, Opt<Gen>()))), >))))");

            var cmp            = new NonTerminal("Cmp");
            var ambiguousRules = rules.Concat(new[]
            {
                new Rule(cmp, LT),
                new Rule(cmp, GT),
                new Rule(Exp, ID, cmp, Exp),
            })
                                 .ToArray();

            this.output.WriteLine("*********** MORE AMBIGUOUS CASE ***********");
            var nodes2    = ParserBuilder.CreateParser(ambiguousRules);
            var parser2   = new ParserNodeParser(nodes2, Exp, this.output.WriteLine);
            var listener2 = new TreeListener();

            // id < id<id<id>>
            parser2.Parse(new[] { ID, LT, ID, LT, ID, LT, ID, GT, GT }, listener2);
            this.output.WriteLine(listener2.Root.Flatten().ToString());
            listener2.Root.Flatten()
            .ToString()
            .ShouldEqual("Exp(ID, Cmp(<), Exp(Name(ID, Opt<Gen>(Gen(<, Opt<List<Name>>(List<Name>(Name(ID, Opt<Gen>(Gen(<, Opt<List<Name>>(List<Name>(Name(ID, Opt<Gen>()))), >))))), >)))))");
        }
        public void TestCastAmbiguity()
        {
            var cast  = new NonTerminal("Cast");
            var term  = new NonTerminal("Term");
            var minus = new Token("-");

            var rules = new[]
            {
                new Rule(Start, Exp),

                new Rule(Exp, term),
                new Rule(Exp, term, minus, Exp),

                new Rule(term, ID),
                new Rule(term, OPEN_PAREN, Exp, CLOSE_PAREN),
                new Rule(term, cast),

                new Rule(cast, OPEN_PAREN, ID, CLOSE_PAREN, Exp),
            };

            var ex = Assert.Throws <NotSupportedException>(() => ParserBuilder.CreateParser(rules));

            this.output.WriteLine(ex.Message);

            var nodes = ParserBuilder.CreateParser(
                rules,
                new Dictionary <IReadOnlyList <Symbol>, Rule>
            {
                {
                    new Symbol[] { term, minus },
                    rules.Single(r => r.Symbols.SequenceEqual(new Symbol[] { term, minus, Exp }))
                },
            }
                );
            var parser   = new ParserNodeParser(nodes, Start, this.output.WriteLine);
            var listener = new TreeListener();

            // (id)((id) - id)
            parser.Parse(new[] { OPEN_PAREN, ID, CLOSE_PAREN, OPEN_PAREN, OPEN_PAREN, ID, CLOSE_PAREN, minus, ID, CLOSE_PAREN }, listener);
            this.output.WriteLine(listener.Root.Flatten().ToString());
            listener.Root.Flatten().ToString()
            .ShouldEqual("Start(Exp(Term(Cast((, ID, ), Exp(Term((, Exp(Term((, Exp(Term(ID)), )), -, Exp(Term(ID))), )))))))");
        }
Ejemplo n.º 8
0
        public void TestAsyncAwait()
        {
            var beginMethod = new NonTerminal("BeginMethod");
            var endMethod   = new NonTerminal("EndMethod");
            var asyncOption = new NonTerminal($"Option<{ASYNC.Name}>");

            var rules = new[]
            {
                new Rule(Start, Method),
                new Rule(Method, beginMethod, asyncOption, ID, OPEN_BRACE, StmtList, CLOSE_BRACE, endMethod),
                new Rule(beginMethod, new Symbol[0], action: new RuleAction("asyncMethod", RuleActionKind.Push)),
                new Rule(endMethod, new Symbol[0], action: new RuleAction("asyncMethod", RuleActionKind.Pop)),

                new Rule(asyncOption, new Symbol[] { ASYNC }, action: new RuleAction("asyncMethod", RuleActionKind.Set)),
                new Rule(asyncOption),

                new Rule(StmtList, Stmt, StmtList),
                new Rule(StmtList),

                new Rule(Stmt, Exp, SEMICOLON),

                new Rule(Exp, Identifier),
                new Rule(Exp, new Symbol[] { AWAIT, Exp }, requiredParserVariable: "asyncMethod"),

                new Rule(Identifier, ID),
                new Rule(Identifier, AWAIT),
            };

            var nodes  = ParserBuilder.CreateParser(rules);
            var parser = new ParserNodeParser(nodes, Start, this.output.WriteLine);

            var listener1 = new TreeListener();

            parser.Parse(new[] { ASYNC, ID, OPEN_BRACE, AWAIT, ID, SEMICOLON, AWAIT, SEMICOLON, CLOSE_BRACE }, listener1);
            this.output.WriteLine(listener1.Root.Flatten().ToString());
            listener1.Root.Flatten().ToString().ShouldEqual("Start(Method(BeginMethod(), Option<async>(async), ID, {, List<Stmt>(Stmt(Exp(await, Exp(Id(ID))), ;), Stmt(Exp(Id(await)), ;)), }, EndMethod()))");

            var ex = Assert.Throws <InvalidOperationException>(() => parser.Parse(new[] { ID, OPEN_BRACE, AWAIT, ID, SEMICOLON, AWAIT, SEMICOLON, CLOSE_BRACE }, new TreeListener()));

            this.output.WriteLine(ex.ToString());
            ex.Message.ShouldEqual("Cannot parse Exp -> await Exp { REQUIRE asyncMethod } without variable asyncMethod");
        }
        public void TestTernaryRewrite()
        {
            var rules = new Rule[]
            {
                new Rule(Start, Exp),
                new Rule(Exp, Exp, QUESTION_MARK, Exp, COLON, Exp),
                new Rule(Exp, ID)
            };

            var rewritten = LeftRecursionRewriter.Rewrite(rules, rightAssociativeRules: ImmutableHashSet.Create(rules[1]));

            this.output.WriteLine(ToString(rewritten));

            var nodes    = ParserBuilder.CreateParser(rewritten.Keys);
            var listener = new TreeListener(rewritten);
            var parser   = new ParserNodeParser(nodes, Start);

            parser.Parse(new[] { ID, QUESTION_MARK, ID, QUESTION_MARK, ID, COLON, ID, COLON, ID, QUESTION_MARK, ID, COLON, ID }, listener);
            this.output.WriteLine(ToGroupedTokenString(listener.Root));
            ToGroupedTokenString(listener.Root)
            .ShouldEqual("ID ? (ID ? ID : ID) : (ID ? ID : ID)");
        }
        public void TestLL1()
        {
            var rules = new[]
            {
                new Rule(Start, StmtList),
                new Rule(StmtList),
                new Rule(StmtList, Stmt, StmtList),
                new Rule(Stmt, Exp, SEMICOLON),
                new Rule(Exp, ID),
                new Rule(Exp, OPEN_BRACKET, ExpList, CLOSE_BRACKET),
                new Rule(ExpList),
                new Rule(ExpList, Exp, ExpList)
            };
            var nodes  = ParserBuilder.CreateParser(rules);
            var parser = new ParserNodeParser(nodes, Start, this.output.WriteLine);

            var listener = new TreeListener();

            parser.Parse(new[] { ID, SEMICOLON, OPEN_BRACKET, ID, OPEN_BRACKET, ID, ID, CLOSE_BRACKET, CLOSE_BRACKET, SEMICOLON }, listener);

            this.output.WriteLine(listener.Root.Flatten().ToString());
            listener.Root.Flatten().ToString()
            .ShouldEqual("Start(List<Stmt>(Stmt(Exp(ID), ;), Stmt(Exp([, List<Exp>(Exp(ID), Exp([, List<Exp>(Exp(ID), Exp(ID)), ])), ]), ;)))");
        }
        public void TestExpressionVsStatementListConflict()
        {
            var rules = new[]
            {
                new Rule(Start, Stmt),
                new Rule(Stmt, Exp, SEMICOLON),
                new Rule(Exp, ID),
                new Rule(Exp, OPEN_BRACKET, ExpList, CLOSE_BRACKET),
                new Rule(Exp, OPEN_BRACKET, Stmt, StmtList, CLOSE_BRACKET),
                new Rule(ExpList),
                new Rule(ExpList, Exp, ExpList),
                new Rule(StmtList),
                new Rule(StmtList, Stmt, StmtList)
            };

            var nodes = ParserBuilder.CreateParser(rules);
            //var parser = new ParserGenerator(rules).Create();

            var parser1   = new ParserNodeParser(nodes, Start);
            var listener1 = new TreeListener();

            // [];
            parser1.Parse(new[] { OPEN_BRACKET, CLOSE_BRACKET, SEMICOLON }, listener1);
            this.output.WriteLine(listener1.Root.Flatten().ToString());
            listener1.Root.Flatten().ToString().ShouldEqual("Start(Stmt(Exp([, List<Exp>(), ]), ;))");

            this.output.WriteLine(Environment.NewLine + "///////////////// CASE 2 /////////////////" + Environment.NewLine);

            var parser2   = new ParserNodeParser(nodes, Start, this.output.WriteLine);
            var listener2 = new TreeListener();

            // [ [ id; ] [ [] id ] ];
            parser2.Parse(new[] { OPEN_BRACKET, OPEN_BRACKET, ID, SEMICOLON, CLOSE_BRACKET, OPEN_BRACKET, OPEN_BRACKET, CLOSE_BRACKET, ID, CLOSE_BRACKET, CLOSE_BRACKET, SEMICOLON }, listener2);
            this.output.WriteLine(listener2.Root.Flatten().ToString());
            listener2.Root.Flatten().ToString().ShouldEqual("Start(Stmt(Exp([, List<Exp>(Exp([, Stmt(Exp(ID), ;), List<Stmt>(), ]), Exp([, List<Exp>(Exp([, List<Exp>(), ]), Exp(ID)), ])), ]), ;))");
        }
        public void TestUnaryRewrite()
        {
            var rules = new Rule[]
            {
                new Rule(Start, Exp),
                new Rule(Exp, MINUS, Exp),
                new Rule(Exp, Exp, MINUS, Exp),
                new Rule(Exp, AWAIT, Exp), // making this lower-priority than e - e, although in C# it isn't
                new Rule(Exp, ID)
            };

            var rewritten = LeftRecursionRewriter.Rewrite(rules, rightAssociativeRules: ImmutableHashSet.Create(rules[1]));

            this.output.WriteLine(ToString(rewritten));

            var nodes    = ParserBuilder.CreateParser(rewritten.Keys);
            var listener = new TreeListener(rewritten);
            var parser   = new ParserNodeParser(nodes, Start);

            parser.Parse(new[] { AWAIT, ID, MINUS, MINUS, ID }, listener);
            this.output.WriteLine(ToGroupedTokenString(listener.Root));
            ToGroupedTokenString(listener.Root)
            .ShouldEqual("await (ID - (- ID))");
        }
        public void TestAdditionAndMultiplicationRewrite()
        {
            var rules = new[]
            {
                new Rule(Start, Exp),

                new Rule(Exp, Exp, TIMES, Exp),
                new Rule(Exp, Exp, PLUS, Exp),
                new Rule(Exp, ID)
            };

            var rewritten = LeftRecursionRewriter.Rewrite(rules, ImmutableHashSet <Rule> .Empty);

            this.output.WriteLine(ToString(rewritten));

            var nodes    = ParserBuilder.CreateParser(rewritten.Keys);
            var listener = new TreeListener(rewritten);
            var parser   = new ParserNodeParser(nodes, Start);

            parser.Parse(new[] { ID, TIMES, ID, TIMES, ID, PLUS, ID, PLUS, ID, TIMES, ID }, listener);
            this.output.WriteLine(ToGroupedTokenString(listener.Root));
            ToGroupedTokenString(listener.Root)
            .ShouldEqual("(((ID * ID) * ID) + ID) + (ID * ID)");
        }
Ejemplo n.º 14
0
        public void TestLargeGrammar()
        {
            var id         = new Token("ID");
            var plus       = new Token("+");
            var times      = new Token("*");
            var num        = new Token("NUM");
            var openParen  = new Token("(");
            var closeParen = new Token(")");
            var openBrace  = new Token("{");
            var closeBrace = new Token("}");
            var semi       = new Token(";");
            var comma      = new Token(",");
            var colon      = new Token(":");
            var @return    = new Token("return");
            var goesTo     = new Token("=>");
            var var        = new Token("var");
            var assign     = new Token("=");

            var start                  = new NonTerminal("Start");
            var stmt                   = new NonTerminal("Stmt");
            var stmtList               = new NonTerminal("List<Stmt>");
            var exp                    = new NonTerminal("Exp");
            var ident                  = new NonTerminal("Ident");
            var tuple                  = new NonTerminal("Tuple");
            var tupleMemberBinding     = new NonTerminal("TupleMemberBinding");
            var tupleMemberBindingList = new NonTerminal("List<TupleMemberBinding>");
            var expBlock               = new NonTerminal("ExpBlock");
            var lambda                 = new NonTerminal("Lambda");
            var lambdaParameters       = new NonTerminal("LambdaArgs");
            var lambdaParameterList    = new NonTerminal("List<LambdaArg>");
            var assignment             = new NonTerminal("Assignment");
            var call                   = new NonTerminal("Call");
            var argList                = new NonTerminal("List<Arg>");

            var rules = new Rule[]
            {
                new Rule(start, stmtList),

                new Rule(stmtList, stmt, stmtList),
                new Rule(stmtList),

                new Rule(stmt, exp, semi),
                new Rule(stmt, @return, exp, semi),
                new Rule(stmt, assignment),

                new Rule(exp, ident),
                new Rule(exp, num),
                new Rule(exp, openParen, exp, closeParen),
                new Rule(exp, exp, times, exp),
                new Rule(exp, exp, plus, exp),
                new Rule(exp, tuple),
                new Rule(exp, expBlock),
                new Rule(exp, lambda),
                new Rule(exp, call),

                new Rule(ident, id),
                new Rule(ident, var),

                new Rule(tuple, openParen, tupleMemberBindingList, closeParen),

                new Rule(tupleMemberBindingList, tupleMemberBinding, comma, tupleMemberBindingList),
                new Rule(tupleMemberBindingList, tupleMemberBinding),
                new Rule(tupleMemberBindingList),

                new Rule(tupleMemberBinding, ident, colon, exp),

                new Rule(expBlock, openParen, stmt, stmtList, closeParen),

                new Rule(lambda, lambdaParameters, goesTo, exp),

                new Rule(lambdaParameters, ident),
                new Rule(lambdaParameters, openParen, lambdaParameterList, closeParen),

                new Rule(lambdaParameterList),
                new Rule(lambdaParameterList, ident, comma, lambdaParameterList),
                new Rule(lambdaParameterList, ident),

                new Rule(assignment, var, ident, assign, exp, semi),

                new Rule(call, exp, openParen, argList, closeParen),

                new Rule(argList),
                new Rule(argList, exp, comma, argList),
                new Rule(argList, exp),
            };

            var rewritten = LeftRecursionRewriter.Rewrite(rules, rightAssociativeRules: ImmutableHashSet <Rule> .Empty);
            var nodes     = ParserBuilder.CreateParser(rewritten.Keys);
            var parser    = new ParserNodeParser(nodes, start, this.output.WriteLine);

            var          listener = new TreeListener(rewritten);
            const string Code     = @"
                var a = 2;
                var func = i => (var x = i + a; return x;);
                var t = (z: a, y: func);
                func(77);
            ";
            var          tokens   = Lex(Code, rules);

            Record.Exception(() => parser.Parse(tokens, listener))
            .ShouldEqual(null);
        }