Example #1
0
        public void BoolInfixExpressionsParseTest()
        {
            var infixTests = new BoolInfixTest[]
            {
                new() { Input = "true == true", LeftValue = true, RightValue = true, Operator = "==" },
                new() { Input = "true != false", LeftValue = true, RightValue = false, Operator = "!=" },
                new() { Input = "false == false", LeftValue = false, RightValue = false, Operator = "==" }
            };

            foreach (var infixTest in infixTests)
            {
                var lexer   = new Lexer(infixTest.Input);
                var parser  = new Parser(lexer);
                var program = parser.ParseCode();
                StaticTests.CheckParserErrors(parser);

                Assert.AreEqual(1, program.Statements.Count,
                                $"program should have 1 statement, got={program.Statements.Count}");

                var stmt = program.Statements.First() as ExpressionStatement;

                Assert.AreEqual("ExpressionStatement", stmt.GetType().Name,
                                $"first statement not of type 'ExpressionStatement', got={stmt.GetType().Name}");

                var exp = stmt.Expression as InfixExpression;

                StaticTests.TestInfixExpression(stmt.Expression, exp.Left, exp.Operator, exp.Right);
            }
        }
    }
Example #2
0
        public void IndexExpressionParsingTest()
        {
            var input = "someList[1 + 1]";

            var lexer   = new Lexer(input);
            var parser  = new Parser(lexer);
            var program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);

            Assert.AreEqual(nameof(ExpressionStatement), program.Statements.First().GetType().Name);

            var stmt = (ExpressionStatement)program.Statements.First();

            Assert.AreEqual(nameof(IndexExpression), stmt.Expression.GetType().Name);

            var indexExp = (IndexExpression)stmt.Expression;

            StaticTests.TestIdentifier(indexExp.Left, "someList");

            var int1 = new IntegerLiteral {
                Token = new Token {
                    Type = Token.Int, Literal = "1"
                }, Value = 1
            };

            StaticTests.TestInfixExpression(indexExp.Index, int1, "+", int1);
        }
Example #3
0
        public void IfExpressionTest()
        {
            const string input   = "if (x < y) { x }";
            var          lexer   = new Lexer(input);
            var          parser  = new Parser(lexer);
            var          program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);

            Assert.AreEqual(1, program.Statements.Count,
                            $"program should have 1 statement, got={program.Statements.Count}");

            var stmt = program.Statements.First() as ExpressionStatement;

            Assert.AreEqual("ExpressionStatement", stmt.GetType().Name,
                            $"first statement not of type 'ExpressionStatement', got={stmt.GetType().Name}");

            Assert.AreEqual("IfExpression", stmt.Expression.GetType().Name);

            var exp = stmt.Expression as IfExpression;

            Assert.AreEqual("(x < y)", exp.Condition.Str());

            Assert.AreEqual(1, exp.Consequence.Statements.Count);

            Assert.AreEqual("ExpressionStatement", exp.Consequence.Statements.First().GetType().Name);

            var consequence = exp.Consequence.Statements.First() as ExpressionStatement;

            StaticTests.TestIdentifier(consequence.Expression, "x");

            Assert.AreEqual(null, exp.Alternative);
        }
Example #4
0
        public void IntegerLiteralTest()
        {
            const string input   = "100500;";
            var          lexer   = new Lexer(input);
            var          parser  = new Parser(lexer);
            var          program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);
            Assert.AreEqual(1, program.Statements.Count,
                            $"program should have 1 statement, got={program.Statements.Count}");

            var stmt = program.Statements.First() as ExpressionStatement;

            Assert.AreEqual("ExpressionStatement", stmt.GetType().Name,
                            $"first statement not of type 'ExpressionStatement', got={stmt.GetType().Name}");

            Assert.AreEqual("IntegerLiteral", stmt.Expression.GetType().Name,
                            $"expression not of type 'IntegerLiteral', got={stmt.Expression.GetType().Name}");

            var int64Literal = stmt.Expression as IntegerLiteral;

            Assert.AreEqual(100500, int64Literal.Value, $"literal not 100500, got={int64Literal.Value}");

            Assert.AreEqual("100500", int64Literal.TokenLiteral(),
                            $"literals token literal not 100500, got={int64Literal.TokenLiteral()}");
        }
Example #5
0
        public void LetStatementsTest()
        {
            const string input  = @"let x = 5; 
let y = 10; 
let foo = 123456789; ";
            var          lexer  = new Lexer(input);
            var          parser = new Parser(lexer);

            var program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);
            Assert.IsNotNull(program, "ParseProgram returned null");
            Assert.AreEqual(program.Statements.Count, 3,
                            $"program.Statements does not contain 3 statements, got={program.Statements.Count}");
            var tests = new Identifier[]
            {
                new() { Value = "x" },
                new() { Value = "y" },
                new() { Value = "foo" }
            };

            for (var i = 0; i < tests.Length; i++)
            {
                var stmt = program.Statements[i];
                var tt   = tests[i];
                StaticTests.TestLetStatement(stmt as LetStatement, tt.Value);
            }
        }
Example #6
0
        public void CallExpressionTest()
        {
            const string input   = "sub(1, 2 * 3, 4 + 5)";
            var          lexer   = new Lexer(input);
            var          parser  = new Parser(lexer);
            var          program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);

            Assert.AreEqual(1, program.Statements.Count,
                            $"program should have 1 statement, got={program.Statements.Count}");

            Assert.AreEqual("ExpressionStatement", program.Statements.First().GetType().Name,
                            $"first statement not of type 'ExpressionStatement', got={program.Statements.First().GetType().Name}");

            var stmt = program.Statements.First() as ExpressionStatement;

            Assert.AreEqual("CallExpression", stmt.Expression.GetType().Name,
                            $"first statement not of type 'CallExpression', got={stmt.Expression.GetType().Name}");

            var exp = stmt.Expression as CallExpression;

            StaticTests.TestIdentifier(exp.Function, "sub");

            Assert.AreEqual(3, exp.Arguments.Count);

            Assert.AreEqual("1", exp.Arguments.First().Str());
            Assert.AreEqual("(2 * 3)", exp.Arguments[1].Str());
            Assert.AreEqual("(4 + 5)", exp.Arguments.Last().Str());
        }
Example #7
0
        public void PrefixExpressionsParseTest()
        {
            var prefixTests = new PrefixTest[]
            {
                new() { Input = "!5", Operator = "!", IntegerValue = 5 },
                new() { Input = "-50", Operator = "-", IntegerValue = 50 }
            };

            foreach (var prefixTest in prefixTests)
            {
                var lexer   = new Lexer(prefixTest.Input);
                var parser  = new Parser(lexer);
                var program = parser.ParseCode();
                StaticTests.CheckParserErrors(parser);

                Assert.AreEqual(1, program.Statements.Count,
                                $"program should have 1 statement, got={program.Statements.Count}");

                var stmt = program.Statements.First() as ExpressionStatement;

                Assert.AreEqual("ExpressionStatement", stmt.GetType().Name,
                                $"first statement not of type 'ExpressionStatement', got={stmt.GetType().Name}");

                Assert.AreEqual("PrefixExpression", stmt.Expression.GetType().Name,
                                $"expression not of type 'PrefixExpression', got={stmt.Expression.GetType().Name}");

                var exp = stmt.Expression as PrefixExpression;

                Assert.AreEqual(prefixTest.Operator, exp.Operator,
                                $"operator is not {prefixTest.Operator}, got={exp.Operator}");

                StaticTests.TestIntegerLiteral(exp.Right, prefixTest.IntegerValue);
            }
        }
    }
Example #8
0
        public void ArrayLiteralParsingTest()
        {
            const string input = "[1, 2 * 2, 3 + 3]";

            var lexer   = new Lexer(input);
            var parser  = new Parser(lexer);
            var program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);

            Assert.AreEqual(nameof(ExpressionStatement), program.Statements.First().GetType().Name);

            var stmt = (ExpressionStatement)program.Statements.First();

            Assert.AreEqual(nameof(ArrayLiteral), stmt.Expression.GetType().Name);

            var array = (ArrayLiteral)stmt.Expression;

            Assert.AreEqual(3, array.Elements.Count);

            StaticTests.TestIntegerLiteral(array.Elements.First(), 1);
            IntegerLiteral two   = 2;
            IntegerLiteral three = 3;

            StaticTests.TestInfixExpression(array.Elements[1], two, "*", two);
            StaticTests.TestInfixExpression(array.Elements[2], three, "+", three);
        }
Example #9
0
        public void StringLiteralExpressionTest1()
        {
            var tests = new[]
            {
                "'hello'", "\"world\""
            };


            foreach (var test in tests)
            {
                var lexer   = new Lexer(test);
                var parser  = new Parser(lexer);
                var program = parser.ParseCode();
                StaticTests.CheckParserErrors(parser);

                Assert.AreEqual(nameof(ExpressionStatement), program.Statements.First().GetType().Name);

                var stmt = (ExpressionStatement)program.Statements.First();

                Assert.AreEqual(nameof(StringLiteral), stmt.Expression.GetType().Name);

                var literal = (StringLiteral)stmt.Expression;

                Assert.AreEqual(test.Replace("'", "").Replace("\"", ""), literal.Value);
            }
        }
Example #10
0
        public void InfixExpressionsParseTest()
        {
            var infixTests = new InfixTest[]
            {
                new() { Input = "5 + 5", LeftValue = 5, RightValue = 5, Operator = "+" },
                new() { Input = "5 - 5", LeftValue = 5, RightValue = 5, Operator = "-" },
                new() { Input = "5 * 5", LeftValue = 5, RightValue = 5, Operator = "*" },
                new() { Input = "5 / 5", LeftValue = 5, RightValue = 5, Operator = "/" },
                new() { Input = "5 > 5", LeftValue = 5, RightValue = 5, Operator = ">" },
                new() { Input = "5 < 5", LeftValue = 5, RightValue = 5, Operator = "<" },
                new() { Input = "5 == 5", LeftValue = 5, RightValue = 5, Operator = "==" },
                new() { Input = "5 != 5", LeftValue = 5, RightValue = 5, Operator = "!=" }
            };

            foreach (var infixTest in infixTests)
            {
                var lexer   = new Lexer(infixTest.Input);
                var parser  = new Parser(lexer);
                var program = parser.ParseCode();
                StaticTests.CheckParserErrors(parser);

                Assert.AreEqual(1, program.Statements.Count,
                                $"program should have 1 statement, got={program.Statements.Count}");

                var stmt = program.Statements.First() as ExpressionStatement;

                Assert.AreEqual("ExpressionStatement", stmt.GetType().Name,
                                $"first statement not of type 'ExpressionStatement', got={stmt.GetType().Name}");

                var exp = stmt.Expression as InfixExpression;

                StaticTests.TestInfixExpression(stmt.Expression, exp.Left, exp.Operator, exp.Right);
            }
        }
Example #11
0
        public void OperatorPrecedenceParseTest()
        {
            var tests = new OperatorTest[]
            {
                new() { Input = "-a * b", Expected = "((-a) * b)" },
                new() { Input = "!-a", Expected = "(!(-a))" },
                new() { Input = "a + b + c", Expected = "((a + b) + c)" },
                new() { Input = "a * b * c", Expected = "((a * b) * c)" },
                new() { Input = "a + b - c", Expected = "((a + b) - c)" },
                new() { Input = "a * b / c", Expected = "((a * b) / c)" },
                new() { Input = "a + b / c", Expected = "(a + (b / c))" },
                new() { Input = "a + b * c + d / e - f", Expected = "(((a + (b * c)) + (d / e)) - f)" },
                new() { Input = "a > b == c < b", Expected = "((a > b) == (c < b))" },
                new() { Input = "a + b; -c * c", Expected = "(a + b)((-c) * c)" },
                new() { Input = "a < b != c > b", Expected = "((a < b) != (c > b))" },
                new()
                {
                    Input    = "3 + 4 * 5 == 3 * 1 + 4 * 5",
                    Expected = "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))"
                },
                new() { Input = "true", Expected = "true" },
                new() { Input = "false", Expected = "false" },
                new() { Input = "3 > 5 == false", Expected = "((3 > 5) == false)" },
                new() { Input = "3 < 5 == true", Expected = "((3 < 5) == true)" },
                new() { Input = "!(true == true)", Expected = "(!(true == true))" },
                new() { Input = "a + add(b * c) + d", Expected = "((a + add((b * c))) + d)" },
                new()
                {
                    Input    = "add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))",
                    Expected = "add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))"
                },
                new()
                {
                    Input    = "add(a + b + c * d / f + g)",
                    Expected = "add((((a + b) + ((c * d) / f)) + g))"
                },
                new()
                {
                    Input    = "a * [1, 2, 3, 4][b * c] * d",
                    Expected = "((a * ([1, 2, 3, 4][(b * c)])) * d)"
                },
                new()
                {
                    Input    = "add(a * b[2], b[1], 2 * [1,2][1])",
                    Expected = "add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))"
                }
            };

            foreach (var operatorTest in tests)
            {
                var lexer   = new Lexer(operatorTest.Input);
                var parser  = new Parser(lexer);
                var program = parser.ParseCode();
                StaticTests.CheckParserErrors(parser);
                var actual = program.Str();
                Assert.AreEqual(operatorTest.Expected, actual);
            }
        }
    }
Example #12
0
        public void HashLiteralWithExpressionParsingTest()
        {
            const string input   = "{'one': 0 + 1, 'two': 228 - 226, 'three': 45 / 15}";
            var          lexer   = new Lexer(input);
            var          parser  = new Parser(lexer);
            var          program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);

            Assert.AreEqual(nameof(ExpressionStatement), program.Statements.First().GetType().Name);

            var stmt = (ExpressionStatement)program.Statements.First();

            Assert.AreEqual(nameof(HashLiteral), stmt.Expression.GetType().Name);

            var hash = (HashLiteral)stmt.Expression;

            Assert.AreEqual(3, hash.Pairs.Count);

            var tests = new Dictionary <string, ExpressionTestDelegate>
            {
                {
                    "one",
                    expression =>
                    StaticTests.TestInfixExpression(expression, (IntegerLiteral)0, "+", (IntegerLiteral)1)
                },
                {
                    "two",
                    expression =>
                    StaticTests.TestInfixExpression(expression, (IntegerLiteral)228, "-", (IntegerLiteral)226)
                },
                {
                    "three",
                    expression =>
                    StaticTests.TestInfixExpression(expression, (IntegerLiteral)45, "/", (IntegerLiteral)15)
                }
            };

            foreach (var pairsKey in hash.Pairs.Keys)
            {
                var value = hash.Pairs[pairsKey];
                Assert.AreEqual(nameof(StringLiteral), pairsKey.GetType().Name);
                var literal = (StringLiteral)pairsKey;

                var keyExists = tests.TryGetValue(literal.Str(), out var testFunc);

                Assert.True(keyExists);

                testFunc(value);
            }
        }
Example #13
0
        public void IdentifierExpressionTest()
        {
            const string input   = "fooBar;";
            var          lexer   = new Lexer(input);
            var          parser  = new Parser(lexer);
            var          program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);
            Assert.AreEqual(1, program.Statements.Count,
                            $"program should have 1 statement, got={program.Statements.Count}");

            var stmt = program.Statements.First() as ExpressionStatement;

            Assert.AreEqual("ExpressionStatement", stmt.GetType().Name,
                            $"first statement not of type 'ExpressionStatement', got={stmt.GetType().Name}");

            StaticTests.TestIdentifier(stmt.Expression, "fooBar");
        }
Example #14
0
        public void EmptyHashLiteralParsingTest()
        {
            const string input   = "{}";
            var          lexer   = new Lexer(input);
            var          parser  = new Parser(lexer);
            var          program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);

            Assert.AreEqual(nameof(ExpressionStatement), program.Statements.First().GetType().Name);

            var stmt = (ExpressionStatement)program.Statements.First();

            Assert.AreEqual(nameof(HashLiteral), stmt.Expression.GetType().Name);

            var hash = (HashLiteral)stmt.Expression;

            Assert.AreEqual(0, hash.Pairs.Count);
        }
Example #15
0
        public void FunctionParameterParsingTest()
        {
            var tests = new FunctionTests[]
            {
                new() { Input = "fun() {}", ExpectedParams = new List <string>() },
                new() { Input = "fun(x) {}", ExpectedParams = new List <string> {
                            "x"
                        } },
                new() { Input = "fun(x, y, z) {}", ExpectedParams = new List <string> {
                            "x", "y", "z"
                        } }
            };

            foreach (var functionTests in tests)
            {
                var lexer   = new Lexer(functionTests.Input);
                var parser  = new Parser(lexer);
                var program = parser.ParseCode();
                StaticTests.CheckParserErrors(parser);

                Assert.AreEqual("ExpressionStatement", program.Statements.First().GetType().Name,
                                $"first statement not of type 'ExpressionStatement', got={program.Statements.First().GetType().Name}");

                var stmt = program.Statements.First() as ExpressionStatement;

                Assert.AreEqual("FunctionLiteral", stmt.Expression.GetType().Name,
                                $"first statement not of type 'FunctionLiteral', got={stmt.Expression.GetType().Name}");

                var function = stmt.Expression as FunctionLiteral;

                Assert.AreEqual(functionTests.ExpectedParams.Count, function.Parameters.Count);
                for (var i = 0; i < functionTests.ExpectedParams.Count; i++)
                {
                    var functionTestsExpectedParam = functionTests.ExpectedParams[i];
                    Assert.AreEqual(functionTestsExpectedParam, function.Parameters[i].Value);
                }
            }
        }
    }
Example #16
0
        public void BooleanLiteralTest()
        {
            var tests = new BooleanTest[]
            {
                new() { Input = "true", Value = true },
                new() { Input = "false", Value = false }
            };

            foreach (var booleanTest in tests)
            {
                var lexer   = new Lexer(booleanTest.Input);
                var parser  = new Parser(lexer);
                var program = parser.ParseCode();
                StaticTests.CheckParserErrors(parser);

                Assert.AreEqual(1, program.Statements.Count,
                                $"program should have 1 statement, got={program.Statements.Count}");


                var stmt = program.Statements.First() as ExpressionStatement;

                Assert.AreEqual("ExpressionStatement", stmt.GetType().Name,
                                $"first statement not of type 'ExpressionStatement', got={stmt.GetType().Name}");

                Assert.AreEqual("BooleanLiteral", stmt.Expression.GetType().Name,
                                $"expression not of type 'BooleanLiteral', got={stmt.Expression.GetType().Name}");

                var booleanLiteral = stmt.Expression as BooleanLiteral;

                Assert.AreEqual(booleanTest.Value, booleanLiteral.Value,
                                $"literal not {booleanTest.Value}, got={booleanLiteral.Value}");

                Assert.AreEqual(booleanTest.Input, booleanLiteral.TokenLiteral(),
                                $"literals token literal not {booleanTest.Input}, got={booleanLiteral.TokenLiteral()}");
            }
        }
    }
Example #17
0
        public void LetStatementsTest1()
        {
            var tests = new[]
            {
                new LetStatementTestCases {
                    Input = "let x = 5;", ExpectedIdentifier = "x"
                }.ExpectedValueSet(5),
                new LetStatementTestCases {
                    Input = "let y = true;", ExpectedIdentifier = "y"
                }
                .ExpectedValueSet(true),
                new LetStatementTestCases {
                    Input = "let foo = y;", ExpectedIdentifier = "foo"
                }
                .ExpectedValueSet("y")
            };

            foreach (var letStatementTestCases in tests)
            {
                var lexer   = new Lexer(letStatementTestCases.Input);
                var parser  = new Parser(lexer);
                var program = parser.ParseCode();

                StaticTests.CheckParserErrors(parser);

                Assert.AreEqual(1, program.Statements.Count);

                var stmt = program.Statements.First() as LetStatement;

                StaticTests.TestLetStatement(stmt, letStatementTestCases.ExpectedIdentifier);

                var value = stmt.Value;

                StaticTests.TestLiteralExpression(value, letStatementTestCases.ExpectedValueGet());
            }
        }
Example #18
0
        public void HashLiteralParsingStringKeysTest()
        {
            const string input   = "{'one': 1, 'two': 2, 'three': 3}";
            var          lexer   = new Lexer(input);
            var          parser  = new Parser(lexer);
            var          program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);

            Assert.AreEqual(nameof(ExpressionStatement), program.Statements.First().GetType().Name);

            var stmt = (ExpressionStatement)program.Statements.First();

            Assert.AreEqual(nameof(HashLiteral), stmt.Expression.GetType().Name);

            var hash = (HashLiteral)stmt.Expression;

            Assert.AreEqual(3, hash.Pairs.Count);

            var expected = new Dictionary <string, long>
            {
                { "one", 1 },
                { "two", 2 },
                { "three", 3 }
            };

            foreach (var pairsKey in hash.Pairs.Keys)
            {
                Assert.AreEqual(nameof(StringLiteral), pairsKey.GetType().Name);
                var literal = (StringLiteral)pairsKey;

                var expectedValue = expected[literal.Str()];

                StaticTests.TestIntegerLiteral(hash.Pairs[pairsKey], expectedValue);
            }
        }
Example #19
0
        public void FunctionLiteralTest()
        {
            const string input   = "fun (x, y) { x + y; }";
            var          lexer   = new Lexer(input);
            var          parser  = new Parser(lexer);
            var          program = parser.ParseCode();

            StaticTests.CheckParserErrors(parser);

            Assert.AreEqual(1, program.Statements.Count,
                            $"program should have 1 statement, got={program.Statements.Count}");

            Assert.AreEqual("ExpressionStatement", program.Statements.First().GetType().Name,
                            $"first statement not of type 'ExpressionStatement', got={program.Statements.First().GetType().Name}");

            var stmt = program.Statements.First() as ExpressionStatement;

            Assert.AreEqual("FunctionLiteral", stmt.Expression.GetType().Name,
                            $"first statement not of type 'FunctionLiteral', got={stmt.Expression.GetType().Name}");

            var function = stmt.Expression as FunctionLiteral;

            Assert.AreEqual(2, function.Parameters.Count);

            Assert.AreEqual("x", function.Parameters.First().Value);
            Assert.AreEqual("y", function.Parameters.Last().Value);

            Assert.AreEqual(1, function.Body.Statements.Count);

            Assert.AreEqual("ExpressionStatement", function.Body.Statements.First().GetType().Name,
                            $"first statement not of type 'ExpressionStatement', got={function.Body.Statements.First().GetType().Name}");

            var bodyStmt = function.Body.Statements.First() as ExpressionStatement;

            Assert.AreEqual("(x + y)", bodyStmt.Str());
        }