Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion src/jsMain/kotlin/export/ASTExport.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -228,7 +230,7 @@ class ASTExport : Visitor<String> {
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))
)
)

Expand Down Expand Up @@ -475,4 +477,44 @@ class ASTExport : Visitor<String> {

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)
}
}
4 changes: 3 additions & 1 deletion src/jsMain/kotlin/lexer/Lexer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 12 additions & 0 deletions src/jsMain/kotlin/parser/BlockItems.kt
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,15 @@ data class D(
) : BlockItem() {
override fun <T> accept(visitor: Visitor<T>): T = visitor.visit(this)
}

data class CompoundStatement(
val block: Block
) : Statement() {
override fun <T> accept(visitor: Visitor<T>): T = visitor.visit(this)
}

data class Block(
val block: List<BlockItem>
) : ASTNode() {
override fun <T> accept(visitor: Visitor<T>): T = visitor.visit(this)
}
2 changes: 1 addition & 1 deletion src/jsMain/kotlin/parser/Functions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ sealed class FunctionDefinition : ASTNode()

data class Function(
val name: String,
val body: List<BlockItem>
val body: Block
) : FunctionDefinition() {
override fun <T> accept(visitor: Visitor<T>): T = visitor.visit(this)
}
22 changes: 20 additions & 2 deletions src/jsMain/kotlin/parser/LabelAnalysis.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -50,7 +52,7 @@ class LabelCollector : Visitor<Unit> {
}

override fun visit(node: Function) {
node.body.forEach { it.accept(this) }
node.body.accept(this)
}

override fun visit(node: VariableExpression) {
Expand Down Expand Up @@ -119,6 +121,14 @@ class LabelCollector : Visitor<Unit> {
}

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(
Expand All @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down
12 changes: 11 additions & 1 deletion src/jsMain/kotlin/parser/LoopLabeling.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -98,7 +100,7 @@ class LoopLabeling : Visitor<Unit> {
}

override fun visit(node: Function) {
node.body.forEach { it.accept(this) }
node.body.accept(this)
}

override fun visit(node: VariableExpression) {
Expand Down Expand Up @@ -151,4 +153,12 @@ class LoopLabeling : Visitor<Unit> {
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)
}
}
20 changes: 15 additions & 5 deletions src/jsMain/kotlin/parser/Parser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,7 @@ class Parser {
expect(TokenType.KEYWORD_VOID, tokens)
expect(TokenType.RIGHT_PAREN, tokens)
expect(TokenType.LEFT_BRACK, tokens)
val body = mutableListOf<BlockItem>()
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(
Expand All @@ -66,6 +62,14 @@ class Parser {
)
}

private fun parseBlock(tokens: MutableList<Token>): Block {
val body = mutableListOf<BlockItem>()
while (tokens.firstOrNull()?.type != TokenType.RIGHT_BRACK) {
body.add(parseBlockItem(tokens))
}
return Block(body)
}

private fun parseBlockItem(tokens: MutableList<Token>): BlockItem =
if (tokens.firstOrNull()?.type == TokenType.KEYWORD_INT) {
D(parseDeclaration(tokens))
Expand Down Expand Up @@ -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()
Expand Down
28 changes: 23 additions & 5 deletions src/jsMain/kotlin/parser/VariableResolution.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -38,7 +40,12 @@ class VariableResolution : Visitor<ASTNode> {

private fun newTemporary(name: String): String = "$name.${tempCounter++}"

private val variableMap = mutableMapOf<String, String>()
private val variableMap = mutableMapOf<String, VariableMapEntry>()

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
Expand Down Expand Up @@ -100,12 +107,12 @@ class VariableResolution : Visitor<ASTNode> {
}

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
Expand Down Expand Up @@ -148,11 +155,11 @@ class VariableResolution : Visitor<ASTNode> {
}

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?)
}
Expand All @@ -166,4 +173,15 @@ class VariableResolution : Visitor<ASTNode> {
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)
}
}
4 changes: 4 additions & 0 deletions src/jsMain/kotlin/parser/Visitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,8 @@ interface Visitor<T> {
fun visit(node: S): T

fun visit(node: D): T

fun visit(node: Block): T

fun visit(node: CompoundStatement): T
}
14 changes: 13 additions & 1 deletion src/jsMain/kotlin/tacky/TackyGenVisitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -169,7 +171,7 @@ class TackyGenVisitor : Visitor<TackyConstruct?> {
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()
Expand Down Expand Up @@ -313,4 +315,14 @@ class TackyGenVisitor : Visitor<TackyConstruct?> {
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
}
}
7 changes: 6 additions & 1 deletion src/jsTest/kotlin/integration/InvalidTestCases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
),
Expand Down Expand Up @@ -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
)
)
}
Loading