diff --git a/src/test/kotlin/lumen/ParserTest.kt b/src/test/kotlin/lumen/ParserTest.kt new file mode 100644 index 0000000..ff8f0a2 --- /dev/null +++ b/src/test/kotlin/lumen/ParserTest.kt @@ -0,0 +1,101 @@ +package lumen + +import lumen.ast.* +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFailsWith +import kotlin.test.assertIs +import kotlin.test.assertTrue + +class ParserTest { + @Test + fun respectsArithmeticPrecedence() { + val program = Lumen.parse("1 + 2 * 3") + val expr = (program.single() as ExprStmt).expr + val add = assertIs(expr) + assertEquals(BinaryOp.Add, add.op) + assertIs(add.left) + val mul = assertIs(add.right) + assertEquals(BinaryOp.Mul, mul.op) + } + + @Test + fun comparisonBindsLooserThanArithmetic() { + val expr = (Lumen.parse("a + b < c").single() as ExprStmt).expr + val comparison = assertIs(expr) + assertEquals(BinaryOp.Less, comparison.op) + assertIs(comparison.left) + assertIs(comparison.right) + } + + @Test + fun parsesLetAndVar() { + val program = Lumen.parse("let a = 1\nvar b = 2") + val letStmt = assertIs(program[0]) + assertEquals("a", letStmt.name) + assertEquals(false, letStmt.mutable) + val varStmt = assertIs(program[1]) + assertEquals(true, varStmt.mutable) + } + + @Test + fun parsesIfElseChain() { + val program = Lumen.parse("if (x) { a } else if (y) { b } else { c }") + val ifStmt = assertIs(program.single()) + assertEquals(1, ifStmt.thenBranch.size) + val elseBranch = ifStmt.elseBranch + assertTrue(elseBranch != null) + assertIs(elseBranch.single()) + } + + @Test + fun parsesFunctionDefinition() { + val program = Lumen.parse("fn add(a, b) { return a + b }") + val fn = assertIs(program.single()) + assertEquals("add", fn.name) + assertEquals(listOf("a", "b"), fn.function.params) + assertIs(fn.function.body.single()) + } + + @Test + fun parsesListAndMapAndIndex() { + val list = (Lumen.parse("[1, 2, 3]").single() as ExprStmt).expr + assertEquals(3, assertIs(list).elements.size) + val map = (Lumen.parse("{\"a\": 1}").single() as ExprStmt).expr + assertEquals(1, assertIs(map).entries.size) + val index = (Lumen.parse("xs[0]").single() as ExprStmt).expr + assertIs(index) + } + + @Test + fun parsesIndexAssignment() { + val expr = (Lumen.parse("xs[0] = 9").single() as ExprStmt).expr + assertIs(expr) + } + + @Test + fun parsesForOverIterable() { + val program = Lumen.parse("for (x in items) { print(x) }") + val forStmt = assertIs(program.single()) + assertEquals("x", forStmt.name) + assertIs(forStmt.iterable) + } + + @Test + fun logicalShortCircuitOperators() { + val expr = (Lumen.parse("a and b or c").single() as ExprStmt).expr + val or = assertIs(expr) + assertEquals(LogicalOp.Or, or.op) + assertEquals(LogicalOp.And, assertIs(or.left).op) + } + + @Test + fun rejectsInvalidAssignmentTarget() { + assertFailsWith { Lumen.parse("1 + 2 = 3") } + } + + @Test + fun rejectsUnterminatedBlock() { + assertFailsWith { Lumen.parse("if (x) { a") } + } +}