Add IF ELSE control flow

main
Olivier Abrivard 1 year ago
parent 16e28c07b5
commit 066c613c50

4
.gitignore vendored

@ -1,3 +1,7 @@
### Kotlin ###
.kotlin/
### Gradle ###
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar

@ -52,6 +52,7 @@ defineAst("Expr", exprTypes)
val stmtTypes = listOf(
"Block : List<Stmt?> statements",
"Expression : Expr expression",
"If : Expr condition, Stmt thenBranch, Stmt? elseBranch",
"Print : Expr expression",
"Var : Token name, Expr? initializer"
)

@ -44,6 +44,14 @@ class Interpreter: ExprVisitor<Any?>, StmtVisitor<Unit>{
evaluate(stmt.expression)
}
override fun visitIf(stmt: If) {
if (isTruthy(evaluate(stmt.condition))) {
execute(stmt.thenBranch)
} else {
stmt.elseBranch?.let { execute(it) }
}
}
override fun visitPrint(stmt: Print) {
val value = evaluate(stmt.expression)
println(stringify(value))

@ -46,12 +46,28 @@ class Parser(private val tokens: List<Token>) {
private fun statement(): Stmt {
return when {
match(IF) -> ifStatement()
match(PRINT) -> printStatement()
match(LEFT_BRACE) -> Block(blockStatement())
else -> expressionStatement()
}
}
private fun ifStatement(): Stmt {
consume(LEFT_PAREN, "Expect '(' after 'if'.")
val condition = expression()
consume(RIGHT_PAREN, "Expect ')' after if condition.")
val thenBranch = statement()
var elseBranch: Stmt? = null
if (match(ELSE)) {
elseBranch = statement()
}
return If(condition, thenBranch, elseBranch)
}
private fun printStatement(): Stmt {
val value = expression()
consume(SEMICOLON, "Expect ';' after value.")

@ -6,6 +6,7 @@ package fr.celticinfo.lox
interface StmtVisitor<R> {
fun visitBlock(stmt: Block): R
fun visitExpression(stmt: Expression): R
fun visitIf(stmt: If): R
fun visitPrint(stmt: Print): R
fun visitVar(stmt: Var): R
}
@ -33,6 +34,15 @@ data class Expression(
}
}
data class If(
val condition: Expr,
val thenBranch: Stmt,
val elseBranch: Stmt?
) : Stmt() {
override fun <R> accept(visitor: StmtVisitor<R>): R {
return visitor.visitIf(this)
}
}
data class Print(
val expression: Expr
) : Stmt() {

@ -201,4 +201,108 @@ print c;
System.setOut(standardOut)
}
}
@Test
fun `If statement should work`() {
val standardOut = System.out
val outputStreamCaptor = ByteArrayOutputStream()
System.setOut(PrintStream(outputStreamCaptor))
try {
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)
Interpreter().interpret(statements)
val output = outputStreamCaptor.toString().trim()
assertEquals("true", output)
} finally {
System.setOut(standardOut)
}
}
@Test
fun `If statement with else should work`() {
val standardOut = System.out
val outputStreamCaptor = ByteArrayOutputStream()
System.setOut(PrintStream(outputStreamCaptor))
try {
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)
Interpreter().interpret(statements)
val output = outputStreamCaptor.toString().trim()
assertEquals("false", output)
} finally {
System.setOut(standardOut)
}
}
@Test
fun `If statement with else if should work`() {
val standardOut = System.out
val outputStreamCaptor = ByteArrayOutputStream()
System.setOut(PrintStream(outputStreamCaptor))
try {
val code = """
if (1 > 2) print "true";
else if (1 < 2) print "false";
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(1, statements.size)
Interpreter().interpret(statements)
val output = outputStreamCaptor.toString().trim()
assertEquals("false", output)
} finally {
System.setOut(standardOut)
}
}
@Test
fun `If statement with else if and else should work`() {
val standardOut = System.out
val outputStreamCaptor = ByteArrayOutputStream()
System.setOut(PrintStream(outputStreamCaptor))
try {
val code = """
if (1 > 2) print "true";
else if (1 < 2) print "false";
else print "else";
""".trimIndent()
val scanner = Scanner(code)
val tokens = scanner.scanTokens()
val parser = Parser(tokens)
val statements = parser.parse()
assertEquals(1, statements.size)
Interpreter().interpret(statements)
val output = outputStreamCaptor.toString().trim()
assertEquals("false", output)
} finally {
System.setOut(standardOut)
}
}
}

@ -126,4 +126,39 @@ class ParserTest {
{ 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)
}
}
Loading…
Cancel
Save