package fr.celticinfo.lox import fr.celticinfo.loxext.RpnPrinter import org.junit.jupiter.api.assertAll import kotlin.test.* import kotlin.test.Test class ParserTest { @Test fun `validate parser`() { val code = """ 1 + 2 * 3 - 4 / 5; """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(1,statements.size) val stmt = statements.first() assertTrue(stmt is Expression) val expr = stmt.expression assertEquals("1.0 2.0 3.0 * + 4.0 5.0 / -", RpnPrinter().print(expr)) } @Test fun `invalid code generates parsing error`() { val code = """ 1 + (2 """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(1,statements.size) val stmt = statements.first() assertNull(stmt) } @Test fun `invalid expression generates parsing error`() { val code = """ 1(2 """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(1,statements.size) val stmt = statements.first() assertNull(stmt) } @Test fun `valid code with multiple statements`() { val code = """ var a = 1; var b = 2; print a + b; """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(3,statements.size) assertAll( { assertTrue(statements[0] is Var) }, { assertTrue(statements[1] is Var) }, { assertTrue(statements[2] is Print) } ) } @Test fun `valid code with block`() { val code = """ { var a = 1; var b = 2; print a + b; } """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(1,statements.size) val stmt = statements.first() assertTrue(stmt is Block) val block = stmt.statements assertEquals(3,block.size) assertAll( { assertTrue(block[0] is Var) }, { assertTrue(block[1] is Var) }, { assertTrue(block[2] is Print) } ) } @Test fun `valid code with block and nested block`() { val code = """ { var a = 1; { var b = 2; print a + b; } } """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(1,statements.size) val stmt = statements.first() assertTrue(stmt is Block) val block = stmt.statements assertEquals(2,block.size) assertAll( { assertTrue(block[0] is Var) }, { assertTrue(block[1] is Block) } ) val nestedBlock = block[1] as Block val nestedStatements = nestedBlock.statements assertEquals(2,nestedStatements.size) assertAll( { assertTrue(nestedStatements[0] is Var) }, { assertTrue(nestedStatements[1] is Print) } ) } @Test fun `valid code with if statement`() { val code = """ if (1 < 2) print "true"; """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(1,statements.size) val stmt = statements.first() assertTrue(stmt is If) val ifStmt = stmt as If assertTrue(ifStmt.condition is Binary) assertTrue(ifStmt.thenBranch is Print) } @Test fun `valid code with if else statement`() { val code = """ if (1 < 2) print "true"; else print "false"; """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(1,statements.size) val stmt = statements.first() assertTrue(stmt is If) val ifStmt = stmt as If assertTrue(ifStmt.condition is Binary) assertTrue(ifStmt.thenBranch is Print) assertTrue(ifStmt.elseBranch is Print) } @Test fun `valid code with while statement`() { val code = """ while (1 < 2) print "true"; """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(1,statements.size) val stmt = statements.first() assertTrue(stmt is While) val whileStmt = stmt as While assertTrue(whileStmt.condition is Binary) assertTrue(whileStmt.body is Print) } @Test fun `valid code with for statement`() { val code = """ for (var i = 0; i < 10; i = i + 1) print i; """.trimIndent() val scanner = Scanner(code) val tokens = scanner.scanTokens() val parser = Parser(tokens) val statements = parser.parse() assertEquals(1,statements.size) val stmt = statements.first() assertTrue(stmt is Block) val block = stmt as Block val blockStatements = block.statements assertEquals(2,blockStatements.size) } }