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");
        }
        public void TestFindProblematicLeftRecursion()
        {
            var nullable  = new NonTerminal("N");
            var nullable2 = new NonTerminal("N2");
            var rules     = new[]
            {
                new Rule(Exp, ID),
                new Rule(Exp, Exp, PLUS, Exp),
                new Rule(Exp, nullable, Exp),
                new Rule(nullable, nullable2),
                new Rule(nullable, MINUS),
                new Rule(nullable2, TIMES),
                new Rule(nullable2),
            };

            var ex = Assert.Throws <InvalidOperationException>(() => LeftRecursionRewriter.Rewrite(rules, ImmutableHashSet <Rule> .Empty));

            ex.Message.ShouldEqual("Found Hidden left recursion for Exp: Exp -> N Exp");
        }
        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 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)");
        }
        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);
        }