You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

339 lines
10 KiB
Kotlin

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)
}
@Test
fun `valid code with function declaration`() {
val code = """
fun add(a, b) {
return 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 Function)
val function = stmt as Function
assertEquals("add",function.name.lexeme)
assertEquals(2,function.params.size)
assertEquals(1,function.body.size)
}
@Test
fun `valid code with function call`() {
val code = """
fun add(a, b) {
return a + b;
}
print add(1, 2);
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(2,statements.size)
val stmt = statements[1]
assertTrue(stmt is Print)
val printStmt = stmt as Print
assertTrue(printStmt.expression is Call)
}
@Test
fun `valid code with class declaration`() {
val code = """
class DevonshireCream {
serveOn() {
return "Scones";
}
}
print DevonshireCream;
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(2,statements.size)
val stmt = statements[0]
assertTrue(stmt is ClassStmt)
}
@Test
fun `valid code with class instantiation`() {
val code = """
class DevonshireCream {}
var cream = DevonshireCream();
print cream;
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(3,statements.size)
val stmt = statements[1]
assertTrue(stmt is Var)
}
@Test
fun `valid code with properties`() {
val code = """
class DevonshireCream {
}
var cream = DevonshireCream();
cream.color = "White";
print cream.color;
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(4,statements.size)
assertTrue(statements[0] is ClassStmt)
assertTrue(statements[1] is Var)
assertTrue(statements[2] is Expression)
assertTrue(statements[3] is Print)
}
@Test
fun `valid code with methods`() {
val code = """
class Bacon {
eat() {
print "Crunch crunch crunch!";
}
}
Bacon().eat();
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(2,statements.size)
assertTrue(statements[0] is ClassStmt)
assertTrue(statements[1] is Expression)
}
@Test
fun `valid code with this`() {
val code = """
class Cake {
taste() {
var adjective = "delicious";
print "The " + this.flavor + " cake is " + adjective + "!";
}
}
var cake = Cake();
cake.flavor = "German chocolate";
cake.taste();
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(4,statements.size)
assertTrue(statements[0] is ClassStmt)
assertTrue(statements[1] is Var)
assertTrue(statements[2] is Expression)
assertTrue(statements[3] is Expression)
}
}