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); } } }
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); }
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); }
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()}"); }
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); } }
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()); }
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); } } }
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); }
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); } }
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); } }
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); } } }
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); } }
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"); }
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); }
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); } } } }
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()}"); } } }
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()); } }
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); } }
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()); }