From 58e96a05db6fd99238026bdef65f9b0217901e3c Mon Sep 17 00:00:00 2001 From: "Sidra S.Ali" Date: Fri, 29 Aug 2025 23:29:00 +0200 Subject: [PATCH] feat: Implement compound statements + update tests #29 --- src/jsMain/kotlin/export/ASTExport.kt | 44 +- src/jsMain/kotlin/lexer/Lexer.kt | 4 +- src/jsMain/kotlin/parser/BlockItems.kt | 12 + src/jsMain/kotlin/parser/Functions.kt | 2 +- src/jsMain/kotlin/parser/LabelAnalysis.kt | 22 +- src/jsMain/kotlin/parser/LoopLabeling.kt | 12 +- src/jsMain/kotlin/parser/Parser.kt | 20 +- .../kotlin/parser/VariableResolution.kt | 28 +- src/jsMain/kotlin/parser/Visitor.kt | 4 + src/jsMain/kotlin/tacky/TackyGenVisitor.kt | 14 +- .../kotlin/integration/InvalidTestCases.kt | 7 +- .../kotlin/integration/ValidTestCases.kt | 489 +++++++++--------- src/jsTest/kotlin/parser/LabelAnalysisTest.kt | 102 ++-- .../kotlin/parser/VariableResolutionTest.kt | 58 ++- 14 files changed, 489 insertions(+), 329 deletions(-) diff --git a/src/jsMain/kotlin/export/ASTExport.kt b/src/jsMain/kotlin/export/ASTExport.kt index f1798cb..75eb2b0 100644 --- a/src/jsMain/kotlin/export/ASTExport.kt +++ b/src/jsMain/kotlin/export/ASTExport.kt @@ -7,7 +7,9 @@ import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import parser.AssignmentExpression import parser.BinaryExpression +import parser.Block import parser.BreakStatement +import parser.CompoundStatement import parser.ConditionalExpression import parser.ContinueStatement import parser.D @@ -228,7 +230,7 @@ class ASTExport : Visitor { JsonObject( mapOf( "name" to JsonPrimitive(node.name), - "body" to JsonArray(node.body.map { Json.decodeFromString(it.accept(this)) }) + "body" to JsonPrimitive(node.body.accept(this)) ) ) @@ -475,4 +477,44 @@ class ASTExport : Visitor { return Json.encodeToString(jsonNode) } + + override fun visit(node: Block): String { + val children = + JsonObject( + mapOf( + "block" to JsonPrimitive(node.block.map { it.accept(this) }.joinToString(",")) + ) + ) + + val jsonNode = + JsonObject( + mapOf( + "type" to JsonPrimitive("Block"), + "label" to JsonPrimitive("block items"), + "children" to children + ) + ) + + return Json.encodeToString(jsonNode) + } + + override fun visit(node: CompoundStatement): String { + val children = + JsonObject( + mapOf( + "block" to JsonPrimitive(node.block.accept(this)) + ) + ) + + val jsonNode = + JsonObject( + mapOf( + "type" to JsonPrimitive("CompoundStatement"), + "label" to JsonPrimitive("compound statement"), + "children" to children + ) + ) + + return Json.encodeToString(jsonNode) + } } diff --git a/src/jsMain/kotlin/lexer/Lexer.kt b/src/jsMain/kotlin/lexer/Lexer.kt index afd1884..79b9392 100644 --- a/src/jsMain/kotlin/lexer/Lexer.kt +++ b/src/jsMain/kotlin/lexer/Lexer.kt @@ -97,7 +97,9 @@ data class Token( val lexeme: String, val line: Int, val column: Int -) +) { + override fun equals(other: Any?): Boolean = other is Token && other.type == this.type && other.lexeme == this.lexeme +} class Lexer( val source: String diff --git a/src/jsMain/kotlin/parser/BlockItems.kt b/src/jsMain/kotlin/parser/BlockItems.kt index e80d625..6bfe94c 100644 --- a/src/jsMain/kotlin/parser/BlockItems.kt +++ b/src/jsMain/kotlin/parser/BlockItems.kt @@ -115,3 +115,15 @@ data class D( ) : BlockItem() { override fun accept(visitor: Visitor): T = visitor.visit(this) } + +data class CompoundStatement( + val block: Block +) : Statement() { + override fun accept(visitor: Visitor): T = visitor.visit(this) +} + +data class Block( + val block: List +) : ASTNode() { + override fun accept(visitor: Visitor): T = visitor.visit(this) +} diff --git a/src/jsMain/kotlin/parser/Functions.kt b/src/jsMain/kotlin/parser/Functions.kt index 4c0d734..17bcea2 100644 --- a/src/jsMain/kotlin/parser/Functions.kt +++ b/src/jsMain/kotlin/parser/Functions.kt @@ -4,7 +4,7 @@ sealed class FunctionDefinition : ASTNode() data class Function( val name: String, - val body: List + val body: Block ) : FunctionDefinition() { override fun accept(visitor: Visitor): T = visitor.visit(this) } diff --git a/src/jsMain/kotlin/parser/LabelAnalysis.kt b/src/jsMain/kotlin/parser/LabelAnalysis.kt index 3b22768..ed84c3b 100644 --- a/src/jsMain/kotlin/parser/LabelAnalysis.kt +++ b/src/jsMain/kotlin/parser/LabelAnalysis.kt @@ -5,7 +5,9 @@ import exceptions.UndeclaredLabelException import parser.ASTNode import parser.AssignmentExpression import parser.BinaryExpression +import parser.Block import parser.BreakStatement +import parser.CompoundStatement import parser.ConditionalExpression import parser.ContinueStatement import parser.D @@ -50,7 +52,7 @@ class LabelCollector : Visitor { } override fun visit(node: Function) { - node.body.forEach { it.accept(this) } + node.body.accept(this) } override fun visit(node: VariableExpression) { @@ -119,6 +121,14 @@ class LabelCollector : Visitor { } override fun visit(node: GotoStatement) {} + + override fun visit(node: Block) { + node.block.forEach { it.accept(this) } + } + + override fun visit(node: CompoundStatement) { + node.block.accept(this) + } } private class GotoValidator( @@ -136,7 +146,7 @@ private class GotoValidator( } override fun visit(node: Function) { - node.body.forEach { it.accept(this) } + node.body.accept(this) } override fun visit(node: VariableExpression) { @@ -213,6 +223,14 @@ private class GotoValidator( override fun visit(node: InitExpression) { node.expression?.accept(this) } + + override fun visit(node: Block) { + node.block.forEach { it.accept(this) } + } + + override fun visit(node: CompoundStatement) { + node.block.accept(this) + } } class LabelAnalysis { diff --git a/src/jsMain/kotlin/parser/LoopLabeling.kt b/src/jsMain/kotlin/parser/LoopLabeling.kt index 3ee8786..2aefe67 100644 --- a/src/jsMain/kotlin/parser/LoopLabeling.kt +++ b/src/jsMain/kotlin/parser/LoopLabeling.kt @@ -3,7 +3,9 @@ package compiler.parser import exceptions.InvalidStatementException import parser.AssignmentExpression import parser.BinaryExpression +import parser.Block import parser.BreakStatement +import parser.CompoundStatement import parser.ConditionalExpression import parser.ContinueStatement import parser.D @@ -98,7 +100,7 @@ class LoopLabeling : Visitor { } override fun visit(node: Function) { - node.body.forEach { it.accept(this) } + node.body.accept(this) } override fun visit(node: VariableExpression) { @@ -151,4 +153,12 @@ class LoopLabeling : Visitor { override fun visit(node: D) { node.declaration.accept(this) } + + override fun visit(node: Block) { + node.block.forEach { it.accept(this) } + } + + override fun visit(node: CompoundStatement) { + node.block.accept(this) + } } diff --git a/src/jsMain/kotlin/parser/Parser.kt b/src/jsMain/kotlin/parser/Parser.kt index bf96630..572a190 100644 --- a/src/jsMain/kotlin/parser/Parser.kt +++ b/src/jsMain/kotlin/parser/Parser.kt @@ -53,11 +53,7 @@ class Parser { expect(TokenType.KEYWORD_VOID, tokens) expect(TokenType.RIGHT_PAREN, tokens) expect(TokenType.LEFT_BRACK, tokens) - val body = mutableListOf() - while (!tokens.isEmpty() && tokens.first().type != TokenType.RIGHT_BRACK) { - val next = parseBlockItem(tokens) - body.add(next) - } + val body = parseBlock(tokens) expect(TokenType.RIGHT_BRACK, tokens) return Function( @@ -66,6 +62,14 @@ class Parser { ) } + private fun parseBlock(tokens: MutableList): Block { + val body = mutableListOf() + while (tokens.firstOrNull()?.type != TokenType.RIGHT_BRACK) { + body.add(parseBlockItem(tokens)) + } + return Block(body) + } + private fun parseBlockItem(tokens: MutableList): BlockItem = if (tokens.firstOrNull()?.type == TokenType.KEYWORD_INT) { D(parseDeclaration(tokens)) @@ -210,6 +214,12 @@ class Parser { body = body ) } + TokenType.LEFT_BRACK -> { + tokens.removeFirst() + val body = parseBlock(tokens) + expect(TokenType.RIGHT_BRACK, tokens) + return CompoundStatement(body) + } else -> { val expression = parseOptionalExpression(tokens = tokens, followedByType = TokenType.SEMICOLON) return if (expression != null) ExpressionStatement(expression) else NullStatement() diff --git a/src/jsMain/kotlin/parser/VariableResolution.kt b/src/jsMain/kotlin/parser/VariableResolution.kt index 58dad20..8e07077 100644 --- a/src/jsMain/kotlin/parser/VariableResolution.kt +++ b/src/jsMain/kotlin/parser/VariableResolution.kt @@ -5,8 +5,10 @@ import exceptions.UndeclaredVariableException import parser.ASTNode import parser.AssignmentExpression import parser.BinaryExpression +import parser.Block import parser.BlockItem import parser.BreakStatement +import parser.CompoundStatement import parser.ConditionalExpression import parser.ContinueStatement import parser.D @@ -38,7 +40,12 @@ class VariableResolution : Visitor { private fun newTemporary(name: String): String = "$name.${tempCounter++}" - private val variableMap = mutableMapOf() + private val variableMap = mutableMapOf() + + data class VariableMapEntry( + val name: String, + val fromCurrentBlock: Boolean = false + ) override fun visit(node: SimpleProgram): ASTNode { // Reset state for each program to avoid leaking declarations across compilations @@ -100,12 +107,12 @@ class VariableResolution : Visitor { } override fun visit(node: Function): ASTNode { - val body = node.body.map { it.accept(this) as BlockItem } + val body = node.body.accept(this) as Block return Function(node.name, body) } override fun visit(node: VariableExpression): ASTNode = - VariableExpression(variableMap[node.name] ?: throw UndeclaredVariableException()) + VariableExpression(variableMap[node.name]?.name ?: throw UndeclaredVariableException()) override fun visit(node: UnaryExpression): ASTNode { val exp = node.expression.accept(this) as Expression @@ -148,11 +155,11 @@ class VariableResolution : Visitor { } override fun visit(node: Declaration): ASTNode { - if (node.name in variableMap) { + if (node.name in variableMap && variableMap[node.name]!!.fromCurrentBlock) { throw DuplicateVariableDeclaration() } val uniqueName = newTemporary(node.name) - variableMap[node.name] = uniqueName + variableMap[node.name] = VariableMapEntry(uniqueName, true) val init = node.init?.accept(this) return Declaration(uniqueName, init as Expression?) } @@ -166,4 +173,15 @@ class VariableResolution : Visitor { val declaration = node.declaration.accept(this) as Declaration return D(declaration) } + + override fun visit(node: Block): ASTNode = Block(node.block.map { it.accept(this) as BlockItem }) + + override fun visit(node: CompoundStatement): ASTNode { + val saved = variableMap.toMutableMap() + variableMap.map { (name, entry) -> name to VariableMapEntry(entry.name) }.toMap(variableMap) + val block = node.block.accept(this) as Block + variableMap.clear() + variableMap.putAll(saved) + return CompoundStatement(block) + } } diff --git a/src/jsMain/kotlin/parser/Visitor.kt b/src/jsMain/kotlin/parser/Visitor.kt index 57b9ddb..477c247 100644 --- a/src/jsMain/kotlin/parser/Visitor.kt +++ b/src/jsMain/kotlin/parser/Visitor.kt @@ -48,4 +48,8 @@ interface Visitor { fun visit(node: S): T fun visit(node: D): T + + fun visit(node: Block): T + + fun visit(node: CompoundStatement): T } diff --git a/src/jsMain/kotlin/tacky/TackyGenVisitor.kt b/src/jsMain/kotlin/tacky/TackyGenVisitor.kt index 54f4c0b..47a1a51 100644 --- a/src/jsMain/kotlin/tacky/TackyGenVisitor.kt +++ b/src/jsMain/kotlin/tacky/TackyGenVisitor.kt @@ -4,7 +4,9 @@ import exceptions.TackyException import lexer.TokenType import parser.AssignmentExpression import parser.BinaryExpression +import parser.Block import parser.BreakStatement +import parser.CompoundStatement import parser.ConditionalExpression import parser.ContinueStatement import parser.D @@ -169,7 +171,7 @@ class TackyGenVisitor : Visitor { val functionName = node.name currentInstructions.clear() - node.body.forEach { it.accept(this) } + node.body.accept(this) // Return 0 to guarantee successful termination currentInstructions.add(TackyRet(TackyConstant(0))) val instructionList = currentInstructions.toList() @@ -313,4 +315,14 @@ class TackyGenVisitor : Visitor { node.declaration.accept(this) return null } + + override fun visit(node: Block): TackyConstruct? { + node.block.forEach { it.accept(this) } + return null + } + + override fun visit(node: CompoundStatement): TackyConstruct? { + node.block.accept(this) + return null + } } diff --git a/src/jsTest/kotlin/integration/InvalidTestCases.kt b/src/jsTest/kotlin/integration/InvalidTestCases.kt index 99e70cf..33cdc65 100644 --- a/src/jsTest/kotlin/integration/InvalidTestCases.kt +++ b/src/jsTest/kotlin/integration/InvalidTestCases.kt @@ -93,7 +93,7 @@ object InvalidTestCases { expectedException = UnexpectedTokenException::class ), InvalidTestCase( - code = "int main(void) { return || 0; }", + code = "int main(void) { return || 0;", failingStage = CompilerStage.PARSER, expectedException = UnexpectedTokenException::class ), @@ -169,6 +169,11 @@ object InvalidTestCases { code = "int main(void) { continue; }", failingStage = CompilerStage.PARSER, expectedException = InvalidStatementException::class + ), + InvalidTestCase( + code = "int main(void) { int a = 10; { int a = 5; int a = 10; } }", + failingStage = CompilerStage.PARSER, + expectedException = DuplicateVariableDeclaration::class ) ) } diff --git a/src/jsTest/kotlin/integration/ValidTestCases.kt b/src/jsTest/kotlin/integration/ValidTestCases.kt index 67665c1..de8b8a3 100644 --- a/src/jsTest/kotlin/integration/ValidTestCases.kt +++ b/src/jsTest/kotlin/integration/ValidTestCases.kt @@ -25,6 +25,8 @@ import lexer.TokenType import parser.ASTNode import parser.AssignmentExpression import parser.BinaryExpression +import parser.Block +import parser.CompoundStatement import parser.D import parser.Declaration import parser.ExpressionStatement @@ -102,47 +104,49 @@ object ValidTestCases { Function( name = "main", body = - listOf( - S( - ReturnStatement( - expression = - BinaryExpression( - left = + Block( + listOf( + S( + ReturnStatement( + expression = BinaryExpression( left = BinaryExpression( - left = IntExpression(5), - operator = Token(TokenType.NEGATION, "-", 2, 14), - right = IntExpression(3) + left = + BinaryExpression( + left = IntExpression(5), + operator = Token(TokenType.NEGATION, "-", 2, 14), + right = IntExpression(3) + ), + operator = Token(TokenType.MULTIPLY, "*", 2, 19), + right = IntExpression(4) ), - operator = Token(TokenType.MULTIPLY, "*", 2, 19), - right = IntExpression(4) - ), - operator = Token(TokenType.PLUS, "+", 2, 23), - right = - BinaryExpression( - left = + operator = Token(TokenType.PLUS, "+", 2, 23), + right = BinaryExpression( left = - UnaryExpression( - operator = Token(TokenType.TILDE, "~", 2, 25), - expression = + BinaryExpression( + left = UnaryExpression( - operator = - Token( - TokenType.NEGATION, - "-", - 2, - 27 - ), - expression = IntExpression(5) - ) + operator = Token(TokenType.TILDE, "~", 2, 25), + expression = + UnaryExpression( + operator = + Token( + TokenType.NEGATION, + "-", + 2, + 27 + ), + expression = IntExpression(5) + ) + ), + operator = Token(TokenType.DIVIDE, "/", 2, 31), + right = IntExpression(6) ), - operator = Token(TokenType.DIVIDE, "/", 2, 31), - right = IntExpression(6) - ), - operator = Token(TokenType.REMAINDER, "%", 2, 35), - right = IntExpression(3) + operator = Token(TokenType.REMAINDER, "%", 2, 35), + right = IntExpression(3) + ) ) ) ) @@ -388,42 +392,44 @@ object ValidTestCases { Function( name = "main", body = - listOf( - D(Declaration(name = "b.0", init = null)), - D( - Declaration( - name = "a.1", - init = - BinaryExpression( - left = IntExpression(10), - operator = Token(TokenType.PLUS, "+", 3, 12), - right = IntExpression(1) - ) - ) - ), - S( - ExpressionStatement( - AssignmentExpression( - lvalue = VariableExpression("b.0"), - rvalue = + Block( + listOf( + D(Declaration(name = "b.0", init = null)), + D( + Declaration( + name = "a.1", + init = BinaryExpression( - left = - AssignmentExpression( - lvalue = VariableExpression("a.1"), - rvalue = IntExpression(2) - ), - operator = Token(TokenType.MULTIPLY, "*", 4, 11), - right = IntExpression(2) + left = IntExpression(10), + operator = Token(TokenType.PLUS, "+", 3, 12), + right = IntExpression(1) ) ) - ) - ), - S( - ReturnStatement( - expression = VariableExpression("b.0") - ) - ), - S(NullStatement()) + ), + S( + ExpressionStatement( + AssignmentExpression( + lvalue = VariableExpression("b.0"), + rvalue = + BinaryExpression( + left = + AssignmentExpression( + lvalue = VariableExpression("a.1"), + rvalue = IntExpression(2) + ), + operator = Token(TokenType.MULTIPLY, "*", 4, 11), + right = IntExpression(2) + ) + ) + ) + ), + S( + ReturnStatement( + expression = VariableExpression("b.0") + ) + ), + S(NullStatement()) + ) ) ) ), @@ -593,12 +599,14 @@ object ValidTestCases { ) ), ValidTestCase( - title = "Testing loop statements", + title = "Testing loop statements with blocks", code = """int main(void) { |int a = 10; - |while(a > 0) + |while(a > 0) { + | int a = 2; | a = a - 1; + |} |for(int i = 0; i < 10; i = i + 1) | a = a + 1; |for(;a > 0;) @@ -630,47 +638,38 @@ object ValidTestCases { Token(TokenType.GREATER, ">", 3, 9), Token(TokenType.INT_LITERAL, "0", 3, 11), Token(TokenType.RIGHT_PAREN, ")", 3, 12), - // a = a - 1; - Token(TokenType.IDENTIFIER, "a", 4, 4), - Token(TokenType.ASSIGN, "=", 4, 6), + // { int a = 2; a = a - 1; } + Token(TokenType.LEFT_BRACK, "{", 3, 14), + Token(TokenType.KEYWORD_INT, "int", 4, 4), Token(TokenType.IDENTIFIER, "a", 4, 8), - Token(TokenType.NEGATION, "-", 4, 10), - Token(TokenType.INT_LITERAL, "1", 4, 12), + Token(TokenType.ASSIGN, "=", 4, 10), + Token(TokenType.INT_LITERAL, "2", 4, 12), Token(TokenType.SEMICOLON, ";", 4, 13), + Token(TokenType.IDENTIFIER, "a", 5, 4), + Token(TokenType.ASSIGN, "=", 5, 6), + Token(TokenType.IDENTIFIER, "a", 5, 8), + Token(TokenType.NEGATION, "-", 5, 10), + Token(TokenType.INT_LITERAL, "1", 5, 12), + Token(TokenType.SEMICOLON, ";", 5, 13), + Token(TokenType.RIGHT_BRACK, "}", 6, 1), // for(int i = 0; i < 10; i = i + 1) - Token(TokenType.KEYWORD_FOR, "for", 5, 1), - Token(TokenType.LEFT_PAREN, "(", 5, 4), - Token(TokenType.KEYWORD_INT, "int", 5, 5), - Token(TokenType.IDENTIFIER, "i", 5, 9), - Token(TokenType.ASSIGN, "=", 5, 11), - Token(TokenType.INT_LITERAL, "0", 5, 13), - Token(TokenType.SEMICOLON, ";", 5, 14), - Token(TokenType.IDENTIFIER, "i", 5, 16), - Token(TokenType.LESS, "<", 5, 18), - Token(TokenType.INT_LITERAL, "10", 5, 20), - Token(TokenType.SEMICOLON, ";", 5, 22), - Token(TokenType.IDENTIFIER, "i", 5, 24), - Token(TokenType.ASSIGN, "=", 5, 26), - Token(TokenType.IDENTIFIER, "i", 5, 28), - Token(TokenType.PLUS, "+", 5, 30), - Token(TokenType.INT_LITERAL, "1", 5, 32), - Token(TokenType.RIGHT_PAREN, ")", 5, 33), - // a = a + 1; - Token(TokenType.IDENTIFIER, "a", 6, 4), - Token(TokenType.ASSIGN, "=", 6, 6), - Token(TokenType.IDENTIFIER, "a", 6, 8), - Token(TokenType.PLUS, "+", 6, 10), - Token(TokenType.INT_LITERAL, "1", 6, 12), - Token(TokenType.SEMICOLON, ";", 6, 13), - // for(;a > 0;) Token(TokenType.KEYWORD_FOR, "for", 7, 1), Token(TokenType.LEFT_PAREN, "(", 7, 4), - Token(TokenType.SEMICOLON, ";", 7, 5), - Token(TokenType.IDENTIFIER, "a", 7, 6), - Token(TokenType.GREATER, ">", 7, 8), - Token(TokenType.INT_LITERAL, "0", 7, 10), - Token(TokenType.SEMICOLON, ";", 7, 11), - Token(TokenType.RIGHT_PAREN, ")", 7, 12), + Token(TokenType.KEYWORD_INT, "int", 7, 5), + Token(TokenType.IDENTIFIER, "i", 7, 9), + Token(TokenType.ASSIGN, "=", 7, 11), + Token(TokenType.INT_LITERAL, "0", 7, 13), + Token(TokenType.SEMICOLON, ";", 7, 14), + Token(TokenType.IDENTIFIER, "i", 7, 16), + Token(TokenType.LESS, "<", 7, 18), + Token(TokenType.INT_LITERAL, "10", 7, 20), + Token(TokenType.SEMICOLON, ";", 7, 22), + Token(TokenType.IDENTIFIER, "i", 7, 24), + Token(TokenType.ASSIGN, "=", 7, 26), + Token(TokenType.IDENTIFIER, "i", 7, 28), + Token(TokenType.PLUS, "+", 7, 30), + Token(TokenType.INT_LITERAL, "1", 7, 32), + Token(TokenType.RIGHT_PAREN, ")", 7, 33), // a = a + 1; Token(TokenType.IDENTIFIER, "a", 8, 4), Token(TokenType.ASSIGN, "=", 8, 6), @@ -678,8 +677,15 @@ object ValidTestCases { Token(TokenType.PLUS, "+", 8, 10), Token(TokenType.INT_LITERAL, "1", 8, 12), Token(TokenType.SEMICOLON, ";", 8, 13), - // do - Token(TokenType.KEYWORD_DO, "do", 9, 1), + // for(;a > 0;) + Token(TokenType.KEYWORD_FOR, "for", 9, 1), + Token(TokenType.LEFT_PAREN, "(", 9, 4), + Token(TokenType.SEMICOLON, ";", 9, 5), + Token(TokenType.IDENTIFIER, "a", 9, 6), + Token(TokenType.GREATER, ">", 9, 8), + Token(TokenType.INT_LITERAL, "0", 9, 10), + Token(TokenType.SEMICOLON, ";", 9, 11), + Token(TokenType.RIGHT_PAREN, ")", 9, 12), // a = a + 1; Token(TokenType.IDENTIFIER, "a", 10, 4), Token(TokenType.ASSIGN, "=", 10, 6), @@ -687,20 +693,29 @@ object ValidTestCases { Token(TokenType.PLUS, "+", 10, 10), Token(TokenType.INT_LITERAL, "1", 10, 12), Token(TokenType.SEMICOLON, ";", 10, 13), + // do + Token(TokenType.KEYWORD_DO, "do", 11, 1), + // a = a + 1; + Token(TokenType.IDENTIFIER, "a", 12, 4), + Token(TokenType.ASSIGN, "=", 12, 6), + Token(TokenType.IDENTIFIER, "a", 12, 8), + Token(TokenType.PLUS, "+", 12, 10), + Token(TokenType.INT_LITERAL, "1", 12, 12), + Token(TokenType.SEMICOLON, ";", 12, 13), // while(a > 0); - Token(TokenType.KEYWORD_WHILE, "while", 11, 1), - Token(TokenType.LEFT_PAREN, "(", 11, 6), - Token(TokenType.IDENTIFIER, "a", 11, 7), - Token(TokenType.GREATER, ">", 11, 9), - Token(TokenType.INT_LITERAL, "0", 11, 11), - Token(TokenType.RIGHT_PAREN, ")", 11, 12), - Token(TokenType.SEMICOLON, ";", 11, 13), + Token(TokenType.KEYWORD_WHILE, "while", 13, 1), + Token(TokenType.LEFT_PAREN, "(", 13, 6), + Token(TokenType.IDENTIFIER, "a", 13, 7), + Token(TokenType.GREATER, ">", 13, 9), + Token(TokenType.INT_LITERAL, "0", 13, 11), + Token(TokenType.RIGHT_PAREN, ")", 13, 12), + Token(TokenType.SEMICOLON, ";", 13, 13), // return 0; - Token(TokenType.KEYWORD_RETURN, "return", 12, 1), - Token(TokenType.INT_LITERAL, "0", 12, 8), - Token(TokenType.SEMICOLON, ";", 12, 9), - Token(TokenType.RIGHT_BRACK, "}", 13, 1), - Token(TokenType.EOF, "", 13, 2) + Token(TokenType.KEYWORD_RETURN, "return", 14, 1), + Token(TokenType.INT_LITERAL, "0", 14, 8), + Token(TokenType.SEMICOLON, ";", 14, 9), + Token(TokenType.RIGHT_BRACK, "}", 15, 1), + Token(TokenType.EOF, "", 15, 2) ), expectedAst = SimpleProgram( @@ -708,120 +723,131 @@ object ValidTestCases { Function( name = "main", body = - listOf( - D(Declaration(name = "a.0", init = IntExpression(10))), - S( - parser.WhileStatement( - condition = - BinaryExpression( - left = VariableExpression("a.0"), - operator = Token(TokenType.GREATER, ">", 3, 9), - right = IntExpression(0) - ), - body = - ExpressionStatement( - AssignmentExpression( - lvalue = VariableExpression("a.0"), - rvalue = - BinaryExpression( - left = VariableExpression("a.0"), - operator = Token(TokenType.NEGATION, "-", 4, 10), - right = IntExpression(1) + Block( + listOf( + D(Declaration(name = "a.0", init = IntExpression(10))), + S( + parser.WhileStatement( + condition = + BinaryExpression( + left = VariableExpression("a.0"), + operator = Token(TokenType.GREATER, ">", 3, 9), + right = IntExpression(0) + ), + body = + CompoundStatement( + Block( + listOf( + D(Declaration(name = "a.1", init = IntExpression(2))), + S( + ExpressionStatement( + AssignmentExpression( + lvalue = VariableExpression("a.1"), + rvalue = + BinaryExpression( + left = VariableExpression("a.1"), + operator = Token(TokenType.NEGATION, "-", 5, 10), + right = IntExpression(1) + ) + ) + ) + ) + ) ) - ) - ), - label = "loop.0" - ) - ), - S( - parser.ForStatement( - init = - parser.InitDeclaration( - Declaration(name = "i.1", init = IntExpression(0)) - ), - condition = - BinaryExpression( - left = VariableExpression("i.1"), - operator = Token(TokenType.LESS, "<", 5, 18), - right = IntExpression(10) - ), - post = - AssignmentExpression( - lvalue = VariableExpression("i.1"), - rvalue = + ), + label = "loop.0" + ) + ), + S( + parser.ForStatement( + init = + parser.InitDeclaration( + Declaration(name = "i.2", init = IntExpression(0)) + ), + condition = BinaryExpression( - left = VariableExpression("i.1"), - operator = Token(TokenType.PLUS, "+", 5, 30), - right = IntExpression(1) - ) - ), - body = - ExpressionStatement( + left = VariableExpression("i.2"), + operator = Token(TokenType.LESS, "<", 5, 18), + right = IntExpression(10) + ), + post = AssignmentExpression( - lvalue = VariableExpression("a.0"), + lvalue = VariableExpression("i.2"), rvalue = BinaryExpression( - left = VariableExpression("a.0"), - operator = Token(TokenType.PLUS, "+", 6, 10), + left = VariableExpression("i.2"), + operator = Token(TokenType.PLUS, "+", 5, 30), right = IntExpression(1) ) - ) - ), - label = "loop.1" - ) - ), - S( - parser.ForStatement( - init = - parser.InitExpression( - expression = null - ), - condition = - BinaryExpression( - left = VariableExpression("a.0"), - operator = Token(TokenType.GREATER, ">", 7, 8), - right = IntExpression(0) - ), - post = null, - body = - ExpressionStatement( - AssignmentExpression( - lvalue = VariableExpression("a.0"), - rvalue = - BinaryExpression( - left = VariableExpression("a.0"), - operator = Token(TokenType.PLUS, "+", 8, 10), - right = IntExpression(1) + ), + body = + ExpressionStatement( + AssignmentExpression( + lvalue = VariableExpression("a.0"), + rvalue = + BinaryExpression( + left = VariableExpression("a.0"), + operator = Token(TokenType.PLUS, "+", 6, 10), + right = IntExpression(1) + ) ) - ) - ), - label = "loop.2" - ) - ), - S( - parser.DoWhileStatement( - condition = - BinaryExpression( - left = VariableExpression("a.0"), - operator = Token(TokenType.GREATER, ">", 11, 9), - right = IntExpression(0) - ), - body = - ExpressionStatement( - AssignmentExpression( - lvalue = VariableExpression("a.0"), - rvalue = - BinaryExpression( - left = VariableExpression("a.0"), - operator = Token(TokenType.PLUS, "+", 10, 10), - right = IntExpression(1) + ), + label = "loop.1" + ) + ), + S( + parser.ForStatement( + init = + parser.InitExpression( + expression = null + ), + condition = + BinaryExpression( + left = VariableExpression("a.0"), + operator = Token(TokenType.GREATER, ">", 7, 8), + right = IntExpression(0) + ), + post = null, + body = + ExpressionStatement( + AssignmentExpression( + lvalue = VariableExpression("a.0"), + rvalue = + BinaryExpression( + left = VariableExpression("a.0"), + operator = Token(TokenType.PLUS, "+", 8, 10), + right = IntExpression(1) + ) ) - ) - ), - label = "loop.3" - ) - ), - S(ReturnStatement(expression = IntExpression(0))) + ), + label = "loop.2" + ) + ), + S( + parser.DoWhileStatement( + condition = + BinaryExpression( + left = VariableExpression("a.0"), + operator = Token(TokenType.GREATER, ">", 11, 9), + right = IntExpression(0) + ), + body = + ExpressionStatement( + AssignmentExpression( + lvalue = VariableExpression("a.0"), + rvalue = + BinaryExpression( + left = VariableExpression("a.0"), + operator = Token(TokenType.PLUS, "+", 10, 10), + right = IntExpression(1) + ) + ) + ), + label = "loop.3" + ) + ), + S(ReturnStatement(expression = IntExpression(0))) + ) ) ) ), @@ -837,23 +863,24 @@ object ValidTestCases { TackyLabel("continue_loop.0"), TackyBinary(TackyBinaryOP.GREATER, TackyVar("a.0"), TackyConstant(0), TackyVar("tmp.0")), JumpIfZero(TackyVar("tmp.0"), TackyLabel("break_loop.0")), + TackyCopy(src = TackyConstant(value = 2), dest = TackyVar(name = "a.1")), // a.0 = a.0 - 1 - TackyBinary(TackyBinaryOP.SUBTRACT, TackyVar("a.0"), TackyConstant(1), TackyVar("tmp.1")), - TackyCopy(TackyVar("tmp.1"), TackyVar("a.0")), + TackyBinary(TackyBinaryOP.SUBTRACT, TackyVar("a.1"), TackyConstant(1), TackyVar("tmp.1")), + TackyCopy(TackyVar("tmp.1"), TackyVar("a.1")), TackyJump(TackyLabel("continue_loop.0")), TackyLabel("break_loop.0"), - // for (int i.1 = 0; i.1 < 10; i.1 = i.1 + 1) ... label loop.1 - TackyCopy(TackyConstant(0), TackyVar("i.1")), + // for (int i.2 = 0; i.2 < 10; i.2 = i.2 + 1) ... label loop.1 + TackyCopy(TackyConstant(0), TackyVar("i.2")), TackyLabel("start_loop.1"), - TackyBinary(TackyBinaryOP.LESS, TackyVar("i.1"), TackyConstant(10), TackyVar("tmp.2")), + TackyBinary(TackyBinaryOP.LESS, TackyVar("i.2"), TackyConstant(10), TackyVar("tmp.2")), JumpIfZero(TackyVar("tmp.2"), TackyLabel("break_loop.1")), // body: a.0 = a.0 + 1 TackyBinary(TackyBinaryOP.ADD, TackyVar("a.0"), TackyConstant(1), TackyVar("tmp.3")), TackyCopy(TackyVar("tmp.3"), TackyVar("a.0")), TackyLabel("continue_loop.1"), - // post: i.1 = i.1 + 1 - TackyBinary(TackyBinaryOP.ADD, TackyVar("i.1"), TackyConstant(1), TackyVar("tmp.4")), - TackyCopy(TackyVar("tmp.4"), TackyVar("i.1")), + // post: i.2 = i.2 + 1 + TackyBinary(TackyBinaryOP.ADD, TackyVar("i.2"), TackyConstant(1), TackyVar("tmp.4")), + TackyCopy(TackyVar("tmp.4"), TackyVar("i.2")), TackyJump(TackyLabel("start_loop.1")), TackyLabel("break_loop.1"), // for (; a.0 > 0;) ... label loop.2 diff --git a/src/jsTest/kotlin/parser/LabelAnalysisTest.kt b/src/jsTest/kotlin/parser/LabelAnalysisTest.kt index 5df7426..a47a2bd 100644 --- a/src/jsTest/kotlin/parser/LabelAnalysisTest.kt +++ b/src/jsTest/kotlin/parser/LabelAnalysisTest.kt @@ -11,27 +11,28 @@ class LabelAnalysisTest { private val labelAnalysis = LabelAnalysis() @Test - fun `test valid labels and gotos`() { + fun `test valid labels and gotos complete successfully`() { // Arrange: A program with a forward jump and a backward jump. val ast: ASTNode = SimpleProgram( functionDefinition = Function( name = "main", - body = - listOf( - S(GotoStatement("end")), - S( - LabeledStatement( - label = "start", - statement = ExpressionStatement(IntExpression(1)) - ) - ), - S(GotoStatement("start")), - S( - LabeledStatement( - label = "end", - statement = ReturnStatement(IntExpression(0)) + body = Block( + listOf( + S(GotoStatement("end")), + S( + LabeledStatement( + label = "start", + statement = ExpressionStatement(IntExpression(1)) + ) + ), + S(GotoStatement("start")), + S( + LabeledStatement( + label = "end", + statement = ReturnStatement(IntExpression(0)) + ) ) ) ) @@ -52,18 +53,19 @@ class LabelAnalysisTest { functionDefinition = Function( name = "main", - body = - listOf( - S( - LabeledStatement( - label = "my_label", - statement = NullStatement() - ) - ), - S( - LabeledStatement( - label = "my_label", - statement = ReturnStatement(IntExpression(0)) + body = Block( + listOf( + S( + LabeledStatement( + label = "my_label", + statement = NullStatement() + ) + ), + S( + LabeledStatement( + label = "my_label", + statement = ReturnStatement(IntExpression(0)) + ) ) ) ) @@ -84,10 +86,11 @@ class LabelAnalysisTest { functionDefinition = Function( name = "main", - body = - listOf( - S(GotoStatement("missing_label")), - S(ReturnStatement(IntExpression(0))) + body = Block( + listOf( + S(GotoStatement("missing_label")), + S(ReturnStatement(IntExpression(0))) + ) ) ) ) @@ -106,36 +109,25 @@ class LabelAnalysisTest { functionDefinition = Function( name = "main", - body = - listOf( - S( - IfStatement( - condition = IntExpression(1), - then = ( - LabeledStatement( - label = "then_branch", - statement = GotoStatement("end") - ) + body = Block( + listOf( + S( + IfStatement( + condition = IntExpression(1), + then = LabeledStatement( + label = "nested_label", + statement = ReturnStatement(IntExpression(1)) ), - _else = ( - LabeledStatement( - label = "else_branch", - statement = GotoStatement("end") - ) - ) - ) - ), - S( - LabeledStatement( - label = "end", - statement = ReturnStatement(IntExpression(0)) - ) + _else = null + ) + ), + S(GotoStatement("nested_label")) ) ) ) ) - // Act & Assert: This should complete successfully. + // Act & Assert: This should complete successfully without throwing an exception. labelAnalysis.analyze(ast) assertTrue(true, "Analysis of nested labels should complete successfully.") } diff --git a/src/jsTest/kotlin/parser/VariableResolutionTest.kt b/src/jsTest/kotlin/parser/VariableResolutionTest.kt index dd1b67c..f807958 100644 --- a/src/jsTest/kotlin/parser/VariableResolutionTest.kt +++ b/src/jsTest/kotlin/parser/VariableResolutionTest.kt @@ -16,17 +16,19 @@ class VariableResolutionTest { Function( name = "main", body = - listOf( - D(Declaration(name = "a", init = null)), - S( - ExpressionStatement( - AssignmentExpression( - lvalue = VariableExpression("a"), - rvalue = IntExpression(1) + Block( + listOf( + D(Declaration(name = "a", init = null)), + S( + ExpressionStatement( + AssignmentExpression( + lvalue = VariableExpression("a"), + rvalue = IntExpression(1) + ) ) - ) - ), - S(ReturnStatement(expression = VariableExpression("a"))) + ), + S(ReturnStatement(expression = VariableExpression("a"))) + ) ) ) ) @@ -39,17 +41,19 @@ class VariableResolutionTest { Function( name = "main", body = - listOf( - D(Declaration(name = "a.0", init = null)), - S( - ExpressionStatement( - AssignmentExpression( - lvalue = VariableExpression("a.0"), - rvalue = IntExpression(1) + Block( + listOf( + D(Declaration(name = "a.0", init = null)), + S( + ExpressionStatement( + AssignmentExpression( + lvalue = VariableExpression("a.0"), + rvalue = IntExpression(1) + ) ) - ) - ), - S(ReturnStatement(expression = VariableExpression("a.0"))) + ), + S(ReturnStatement(expression = VariableExpression("a.0"))) + ) ) ) ) @@ -65,9 +69,11 @@ class VariableResolutionTest { Function( name = "main", body = - listOf( - D(Declaration(name = "a", init = null)), - D(Declaration(name = "a", init = null)) + Block( + listOf( + D(Declaration(name = "a", init = null)), + D(Declaration(name = "a", init = null)) + ) ) ) ) @@ -85,8 +91,10 @@ class VariableResolutionTest { Function( name = "main", body = - listOf( - S(ReturnStatement(expression = VariableExpression("x"))) + Block( + listOf( + S(ReturnStatement(expression = VariableExpression("x"))) + ) ) ) )