From 8951c3cd60863d95a07e2f687341290f97895d90 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Thu, 23 Nov 2023 21:48:10 -0800 Subject: [PATCH] compiler: full support for IR based compilation --- .../main/kotlin/gay/pizza/pork/bir/IrCall.kt | 4 +- .../kotlin/gay/pizza/pork/bir/IrCodeBlock.kt | 6 +- .../kotlin/gay/pizza/pork/bir/IrContinue.kt | 2 +- .../kotlin/gay/pizza/pork/bir/IrDefinition.kt | 6 +- .../gay/pizza/pork/bir/IrDefinitionType.kt | 3 +- .../gay/pizza/pork/bir/IrFunctionArgument.kt | 5 + .../main/kotlin/gay/pizza/pork/bir/IrLoad.kt | 4 +- .../main/kotlin/gay/pizza/pork/bir/IrLoop.kt | 6 +- .../gay/pizza/pork/bir/IrNativeDefinition.kt | 5 + .../main/kotlin/gay/pizza/pork/bir/IrStore.kt | 2 +- .../kotlin/gay/pizza/pork/bir/IrSuffix.kt | 7 + .../kotlin/gay/pizza/pork/bir/IrSuffixOp.kt | 6 + .../gay/pizza/pork/bir/IrSymbolGraph.kt | 40 +++ .../gay/pizza/pork/bir/IrSymbolOwner.kt | 5 + .../kotlin/gay/pizza/pork/bir/IrSymbolUser.kt | 5 + .../kotlin/gay/pizza/pork/bir/IrVisitor.kt | 59 ++++ .../pork/bir/{IrAccess.kt => IrWorld.kt} | 4 +- .../gay/pizza/pork/bytecode/MutableRel.kt | 2 +- .../kotlin/gay/pizza/pork/bytecode/Ops.kt | 3 - .../gay/pizza/pork/compiler/CompilableSlab.kt | 14 + .../pizza/pork/compiler/CompilableSymbol.kt | 45 ++- .../gay/pizza/pork/compiler/Compiler.kt | 4 + .../gay/pizza/pork/compiler/IrCodeEmitter.kt | 57 +++- .../gay/pizza/pork/compiler/IrCodeVisitor.kt | 33 ++ .../pizza/pork/compiler/IrStubOpEmitter.kt | 222 +++++++++++++ .../gay/pizza/pork/compiler/IrSymbolWorld.kt | 9 +- .../gay/pizza/pork/compiler/LocalState.kt | 73 ++--- .../gay/pizza/pork/compiler/LoopState.kt | 3 +- .../gay/pizza/pork/compiler/StubOpEmitter.kt | 301 ------------------ .../kotlin/gay/pizza/pork/compiler/StubVar.kt | 4 +- .../pizza/pork/frontend/scope/ScopeSymbol.kt | 2 + .../gay/pizza/pork/tool/CompileCommand.kt | 13 - 32 files changed, 554 insertions(+), 400 deletions(-) create mode 100644 bir/src/main/kotlin/gay/pizza/pork/bir/IrFunctionArgument.kt create mode 100644 bir/src/main/kotlin/gay/pizza/pork/bir/IrNativeDefinition.kt create mode 100644 bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffix.kt create mode 100644 bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffixOp.kt create mode 100644 bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolGraph.kt create mode 100644 bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolOwner.kt create mode 100644 bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolUser.kt create mode 100644 bir/src/main/kotlin/gay/pizza/pork/bir/IrVisitor.kt rename bir/src/main/kotlin/gay/pizza/pork/bir/{IrAccess.kt => IrWorld.kt} (50%) delete mode 100644 bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Ops.kt create mode 100644 compiler/src/main/kotlin/gay/pizza/pork/compiler/IrCodeVisitor.kt create mode 100644 compiler/src/main/kotlin/gay/pizza/pork/compiler/IrStubOpEmitter.kt delete mode 100644 compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpEmitter.kt diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrCall.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrCall.kt index 8f3b23b..f355022 100644 --- a/bir/src/main/kotlin/gay/pizza/pork/bir/IrCall.kt +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrCall.kt @@ -1,10 +1,10 @@ package gay.pizza.pork.bir data class IrCall( - val target: IrSymbol, + override val target: IrSymbol, val arguments: List, val variableArguments: List? -) : IrCodeElement { +) : IrCodeElement, IrSymbolUser { override fun crawl(block: (IrElement) -> Unit) { block(target) arguments.forEach(block) diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrCodeBlock.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrCodeBlock.kt index 6d98c25..a6f3bbf 100644 --- a/bir/src/main/kotlin/gay/pizza/pork/bir/IrCodeBlock.kt +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrCodeBlock.kt @@ -1,3 +1,7 @@ package gay.pizza.pork.bir -data class IrCodeBlock(val items: List) : IrCodeElement +data class IrCodeBlock(val items: List) : IrCodeElement { + override fun crawl(block: (IrElement) -> Unit) { + items.forEach(block) + } +} diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrContinue.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrContinue.kt index f9fdd6a..113f3d2 100644 --- a/bir/src/main/kotlin/gay/pizza/pork/bir/IrContinue.kt +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrContinue.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.bir -data class IrContinue(val target: IrSymbol) : IrCodeElement { +data class IrContinue(override val target: IrSymbol) : IrCodeElement, IrSymbolUser { override fun crawl(block: (IrElement) -> Unit) { block(target) } diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrDefinition.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrDefinition.kt index d6d62bb..af300a9 100644 --- a/bir/src/main/kotlin/gay/pizza/pork/bir/IrDefinition.kt +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrDefinition.kt @@ -1,12 +1,14 @@ package gay.pizza.pork.bir data class IrDefinition( - val symbol: IrSymbol, + override val symbol: IrSymbol, val type: IrDefinitionType, + val arguments: List, val code: IrCodeBlock -) : IrElement { +) : IrElement, IrSymbolOwner { override fun crawl(block: (IrElement) -> Unit) { block(symbol) + arguments.forEach(block) block(code) } } diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrDefinitionType.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrDefinitionType.kt index 46acaf7..34c3867 100644 --- a/bir/src/main/kotlin/gay/pizza/pork/bir/IrDefinitionType.kt +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrDefinitionType.kt @@ -2,5 +2,6 @@ package gay.pizza.pork.bir enum class IrDefinitionType { Variable, - Function + CodeFunction, + NativeFunction } diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrFunctionArgument.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrFunctionArgument.kt new file mode 100644 index 0000000..e317109 --- /dev/null +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrFunctionArgument.kt @@ -0,0 +1,5 @@ +package gay.pizza.pork.bir + +data class IrFunctionArgument(override val symbol: IrSymbol) : IrSymbolOwner { + override fun crawl(block: (IrElement) -> Unit) {} +} diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrLoad.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrLoad.kt index 59361a6..41c53fc 100644 --- a/bir/src/main/kotlin/gay/pizza/pork/bir/IrLoad.kt +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrLoad.kt @@ -1,7 +1,7 @@ package gay.pizza.pork.bir -data class IrLoad(val symbol: IrSymbol) : IrCodeElement { +data class IrLoad(override val target: IrSymbol) : IrCodeElement, IrSymbolUser { override fun crawl(block: (IrElement) -> Unit) { - block(symbol) + block(target) } } diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrLoop.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrLoop.kt index 8d43193..2463931 100644 --- a/bir/src/main/kotlin/gay/pizza/pork/bir/IrLoop.kt +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrLoop.kt @@ -1,6 +1,10 @@ package gay.pizza.pork.bir -data class IrLoop(val symbol: IrSymbol, val condition: IrCodeElement, val inner: IrCodeElement) : IrCodeElement { +data class IrLoop( + override val symbol: IrSymbol, + val condition: IrCodeElement, + val inner: IrCodeElement +) : IrCodeElement, IrSymbolOwner { override fun crawl(block: (IrElement) -> Unit) { block(symbol) block(condition) diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrNativeDefinition.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrNativeDefinition.kt new file mode 100644 index 0000000..45e91db --- /dev/null +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrNativeDefinition.kt @@ -0,0 +1,5 @@ +package gay.pizza.pork.bir + +data class IrNativeDefinition(val form: String, val definitions: List) : IrCodeElement { + override fun crawl(block: (IrElement) -> Unit) {} +} diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrStore.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrStore.kt index acc7f9f..51225fe 100644 --- a/bir/src/main/kotlin/gay/pizza/pork/bir/IrStore.kt +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrStore.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.bir -data class IrStore(val symbol: IrSymbol, val value: IrCodeElement) : IrCodeElement { +data class IrStore(override val target: IrSymbol, val value: IrCodeElement) : IrCodeElement, IrSymbolUser { override fun crawl(block: (IrElement) -> Unit) { value.crawl(block) } diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffix.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffix.kt new file mode 100644 index 0000000..abad0e1 --- /dev/null +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffix.kt @@ -0,0 +1,7 @@ +package gay.pizza.pork.bir + +data class IrSuffix(val op: IrSuffixOp, override val target: IrSymbol) : IrCodeElement, IrSymbolUser { + override fun crawl(block: (IrElement) -> Unit) { + block(target) + } +} diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffixOp.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffixOp.kt new file mode 100644 index 0000000..0d4d270 --- /dev/null +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSuffixOp.kt @@ -0,0 +1,6 @@ +package gay.pizza.pork.bir + +enum class IrSuffixOp { + Increment, + Decrement +} diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolGraph.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolGraph.kt new file mode 100644 index 0000000..0d89130 --- /dev/null +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolGraph.kt @@ -0,0 +1,40 @@ +package gay.pizza.pork.bir + +class IrSymbolGraph { + private val edges = mutableSetOf>() + + private fun crawlForKnown(known: MutableMap, root: IrElement) { + if (root is IrSymbolOwner) { + known[root.symbol] = root + } + + root.crawl { item -> + crawlForKnown(known, item) + } + } + + private fun crawlForAssociations(known: Map, root: IrElement) { + if (root is IrSymbolUser) { + val what = known[root.target] + if (what != null) { + edges.add(root to what) + } + } + + root.crawl { item -> + crawlForAssociations(known, item) + } + } + + fun crawl(root: IrElement) { + val known = mutableMapOf() + crawlForKnown(known, root) + crawlForAssociations(known, root) + } + + fun forEachEdge(block: (IrSymbolUser, IrSymbolOwner) -> Unit) { + for ((from, to) in edges) { + block(from, to) + } + } +} diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolOwner.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolOwner.kt new file mode 100644 index 0000000..f52ae66 --- /dev/null +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolOwner.kt @@ -0,0 +1,5 @@ +package gay.pizza.pork.bir + +sealed interface IrSymbolOwner : IrElement { + val symbol: IrSymbol +} diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolUser.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolUser.kt new file mode 100644 index 0000000..c888ce1 --- /dev/null +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrSymbolUser.kt @@ -0,0 +1,5 @@ +package gay.pizza.pork.bir + +sealed interface IrSymbolUser : IrElement { + val target: IrSymbol +} diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrVisitor.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrVisitor.kt new file mode 100644 index 0000000..2cfd3be --- /dev/null +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrVisitor.kt @@ -0,0 +1,59 @@ +package gay.pizza.pork.bir + +interface IrVisitor { + fun visitIrSlab(ir: IrSlab): T + fun visitIrSlabLocation(ir: IrSlabLocation): T + fun visitIrDefinition(ir: IrDefinition): T + fun visitIrSymbol(ir: IrSymbol): T + fun visitIrBeak(ir: IrBreak): T + fun visitIrCall(ir: IrCall): T + fun visitIrCodeBlock(ir: IrCodeBlock): T + fun visitIrConditional(ir: IrConditional): T + fun visitIrBooleanConstant(ir: IrBooleanConstant): T + fun visitIrIntegerConstant(ir: IrIntegerConstant): T + fun visitIrLongConstant(ir: IrLongConstant): T + fun visitIrDoubleConstant(ir: IrDoubleConstant): T + fun visitIrStringConstant(ir: IrStringConstant): T + fun visitIrNoneConstant(ir: IrNoneConstant): T + fun visitIrContinue(ir: IrContinue): T + fun visitIrInfix(ir: IrInfix): T + fun visitIrList(ir: IrList): T + fun visitIrLoad(ir: IrLoad): T + fun visitIrLoop(ir: IrLoop): T + fun visitIrPrefix(ir: IrPrefix): T + fun visitIrReturn(ir: IrReturn): T + fun visitIrStore(ir: IrStore): T + fun visitIrSuffix(ir: IrSuffix): T + fun visitIrWorld(ir: IrWorld): T + fun visitIrNativeDefinition(ir: IrNativeDefinition): T + fun visitIrFunctionArgument(ir: IrFunctionArgument): T + + fun visit(ir: IrElement): T = when (ir) { + is IrBreak -> visitIrBeak(ir) + is IrCall -> visitIrCall(ir) + is IrCodeBlock -> visitIrCodeBlock(ir) + is IrConditional -> visitIrConditional(ir) + is IrBooleanConstant -> visitIrBooleanConstant(ir) + is IrDoubleConstant -> visitIrDoubleConstant(ir) + is IrIntegerConstant -> visitIrIntegerConstant(ir) + is IrLongConstant -> visitIrLongConstant(ir) + is IrNoneConstant -> visitIrNoneConstant(ir) + is IrStringConstant -> visitIrStringConstant(ir) + is IrContinue -> visitIrContinue(ir) + is IrInfix -> visitIrInfix(ir) + is IrList -> visitIrList(ir) + is IrLoad -> visitIrLoad(ir) + is IrLoop -> visitIrLoop(ir) + is IrPrefix -> visitIrPrefix(ir) + is IrReturn -> visitIrReturn(ir) + is IrStore -> visitIrStore(ir) + is IrSuffix -> visitIrSuffix(ir) + is IrDefinition -> visitIrDefinition(ir) + is IrSlab -> visitIrSlab(ir) + is IrSlabLocation -> visitIrSlabLocation(ir) + is IrSymbol -> visitIrSymbol(ir) + is IrWorld -> visitIrWorld(ir) + is IrNativeDefinition -> visitIrNativeDefinition(ir) + is IrFunctionArgument -> visitIrFunctionArgument(ir) + } +} diff --git a/bir/src/main/kotlin/gay/pizza/pork/bir/IrAccess.kt b/bir/src/main/kotlin/gay/pizza/pork/bir/IrWorld.kt similarity index 50% rename from bir/src/main/kotlin/gay/pizza/pork/bir/IrAccess.kt rename to bir/src/main/kotlin/gay/pizza/pork/bir/IrWorld.kt index 9787b4b..32726df 100644 --- a/bir/src/main/kotlin/gay/pizza/pork/bir/IrAccess.kt +++ b/bir/src/main/kotlin/gay/pizza/pork/bir/IrWorld.kt @@ -1,7 +1,7 @@ package gay.pizza.pork.bir -data class IrAccess(val target: IrSymbol) : IrCodeElement { +data class IrWorld(val slabs: List) : IrElement { override fun crawl(block: (IrElement) -> Unit) { - block(target) + slabs.forEach(block) } } diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/MutableRel.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/MutableRel.kt index 758c58b..48149bb 100644 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/MutableRel.kt +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/MutableRel.kt @@ -1,3 +1,3 @@ package gay.pizza.pork.bytecode -class MutableRel(var rel: UInt) +data class MutableRel(var rel: UInt) diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Ops.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Ops.kt deleted file mode 100644 index 0ba222b..0000000 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Ops.kt +++ /dev/null @@ -1,3 +0,0 @@ -package gay.pizza.pork.bytecode - -class Ops(val ops: List) diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSlab.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSlab.kt index 875fdb1..fac74e7 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSlab.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSlab.kt @@ -1,9 +1,14 @@ package gay.pizza.pork.compiler import gay.pizza.pork.ast.gen.Symbol +import gay.pizza.pork.bir.IrDefinition +import gay.pizza.pork.bir.IrSlab +import gay.pizza.pork.bir.IrSlabLocation import gay.pizza.pork.frontend.Slab class CompilableSlab(val compiler: Compiler, val slab: Slab) { + val compiledIrSlab: IrSlab by lazy { compileIrSlab() } + val compilableSymbols: List by lazy { slab.scope.internalSymbols.map { symbol -> CompilableSymbol(this, symbol) @@ -18,4 +23,13 @@ class CompilableSlab(val compiler: Compiler, val slab: Slab) { val scopeSymbol = slab.scope.resolve(symbol) ?: return null return compiler.resolveOrNull(scopeSymbol) } + + private fun compileIrSlab(): IrSlab { + val definitions = mutableListOf() + for (compilableSymbol in compilableSymbols) { + definitions.add(compilableSymbol.compiledIrDefinition) + } + val irSlabLocation = IrSlabLocation(slab.location.form, slab.location.filePath) + return IrSlab(irSlabLocation, definitions) + } } diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSymbol.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSymbol.kt index 3ee381b..1089f39 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSymbol.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSymbol.kt @@ -2,29 +2,62 @@ package gay.pizza.pork.compiler import gay.pizza.pork.ast.gen.FunctionDefinition import gay.pizza.pork.ast.gen.LetDefinition +import gay.pizza.pork.ast.gen.NativeFunctionDescriptor import gay.pizza.pork.ast.gen.visit +import gay.pizza.pork.bir.IrCodeBlock +import gay.pizza.pork.bir.IrDefinition +import gay.pizza.pork.bir.IrDefinitionType +import gay.pizza.pork.bir.IrSymbolTag import gay.pizza.pork.frontend.scope.ScopeSymbol class CompilableSymbol(val compilableSlab: CompilableSlab, val scopeSymbol: ScopeSymbol) { + val compiledIrDefinition: IrDefinition by lazy { compileIrDefinition() } val compiledStubOps: CompiledSymbolResult by lazy { compile() } val usedSymbols: List get() = scopeSymbol.scope.usedSymbols private fun compile(): CompiledSymbolResult { - val emitter = StubOpEmitter(compilableSlab.compiler, this) - emitter.enter() + val code = CodeBuilder(this) + val ir = compiledIrDefinition + val emitter = IrStubOpEmitter(ir, code) + emitter.visit(ir.code) + emitter.final() + return emitter.code.build() + } + + private fun compileIrDefinition(): IrDefinition { + val compiler = compilableSlab.compiler + val functionSymbol = compiler.irSymbolWorld.create(scopeSymbol, IrSymbolTag.Function) + val irCodeEmitter = IrCodeEmitter( + self = functionSymbol, + irSymbolWorld = compiler.irSymbolWorld, + irSymbolAssignment = compiler.irSymbolAssignment, + scope = compilableSlab.slab.scope + ) + irCodeEmitter.enterLocalScope() val what = if (scopeSymbol.definition is FunctionDefinition) { val functionDefinition = scopeSymbol.definition as FunctionDefinition - emitter.allocateOuterScope(functionDefinition) + irCodeEmitter.createFunctionArguments(functionDefinition) functionDefinition.block ?: functionDefinition.nativeFunctionDescriptor!! } else { val letDefinition = scopeSymbol.definition as LetDefinition letDefinition.value } - emitter.visit(what) - emitter.exit() - return emitter.code.build() + val type = if (what is NativeFunctionDescriptor) { + IrDefinitionType.NativeFunction + } else IrDefinitionType.CodeFunction + val irCodeElement = irCodeEmitter.visit(what) + val irCodeBlock = if (irCodeElement is IrCodeBlock) { + irCodeElement + } else IrCodeBlock(listOf(irCodeElement)) + irCodeEmitter.exitLocalScope() + return IrDefinition( + symbol = functionSymbol, + type = type, + arguments = irCodeEmitter.functionArguments, + code = irCodeBlock + ) } val id: String diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/Compiler.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/Compiler.kt index 88dd8c6..9c796e3 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/Compiler.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/Compiler.kt @@ -1,5 +1,6 @@ package gay.pizza.pork.compiler +import gay.pizza.pork.bir.IrSymbolAssignment import gay.pizza.pork.bytecode.CompiledWorld import gay.pizza.pork.bytecode.MutableConstantPool import gay.pizza.pork.frontend.Slab @@ -11,6 +12,9 @@ class Compiler { CompilableSlab(this, slab) } + val irSymbolAssignment: IrSymbolAssignment = IrSymbolAssignment() + val irSymbolWorld: IrSymbolWorld = IrSymbolWorld(irSymbolAssignment) + fun resolveOrNull(scopeSymbol: ScopeSymbol): CompilableSymbol? { val compiledSlab = compilableSlabs.of(scopeSymbol.slabScope.slab) return compiledSlab.resolve(scopeSymbol.symbol) diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrCodeEmitter.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrCodeEmitter.kt index 1bc88ea..2d9cd2b 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrCodeEmitter.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrCodeEmitter.kt @@ -8,12 +8,23 @@ import gay.pizza.pork.frontend.scope.SlabScope class IrCodeEmitter( val self: IrSymbol, - val irSymbolWorld: IrSymbolWorld, + val irSymbolWorld: IrSymbolWorld, val irSymbolAssignment: IrSymbolAssignment, val scope: SlabScope ) : FunctionLevelVisitor() { private val loopSymbols = mutableListOf() - private val localVariables = mutableListOf>() + private val localVariables = mutableListOf>() + + var functionArguments: List = emptyList() + + fun createFunctionArguments(functionDefinition: FunctionDefinition) { + val functionSymbols = mutableListOf() + for (arg in functionDefinition.arguments) { + val symbol = createLocalVariable(arg.symbol) + functionSymbols.add(IrFunctionArgument(symbol)) + } + functionArguments = functionSymbols + } private fun startLoop(): IrSymbol { val symbol = irSymbolAssignment.next(IrSymbolTag.Loop) @@ -34,19 +45,24 @@ class IrCodeEmitter( } } - private fun enterBlockScope() { - val locals = mutableListOf() + fun enterLocalScope() { + val locals = mutableMapOf() localVariables.add(locals) } - private fun exitBlockScope() { + fun exitLocalScope() { localVariables.removeLast() } private fun createLocalVariable(name: Symbol): IrSymbol { val symbol = irSymbolAssignment.next(IrSymbolTag.Local) val variable = LocalVariable(symbol, name) - localVariables.last().add(variable) + val variables = localVariables.last() + val existing = variables[name.id] + if (existing != null) { + throw CompileError("Unable to define local variable '${name.id}' within this scope, it already exists", name) + } + variables[name.id] = variable return symbol } @@ -58,10 +74,10 @@ class IrCodeEmitter( } private fun lookupLocalVariable(name: Symbol): IrSymbol? { - for (i in 0..localVariables.size) { - val b = localVariables.size - i - 1 + for (i in 1..localVariables.size) { + val b = localVariables.size - i val scope = localVariables[b] - val found = scope.firstOrNull { it.name == name } + val found = scope[name.id] if (found != null) { return found.symbol } @@ -76,20 +92,20 @@ class IrCodeEmitter( } val scoped = scope.resolve(name) if (scoped != null) { - return irSymbolWorld.lookup(scoped, scopeSymbolToTag(scoped)) + return irSymbolWorld.create(scoped, scopeSymbolToTag(scoped)) } return null } private fun lookupFunction(name: Symbol): Pair? { val scoped = scope.resolve(name) ?: return null - return scoped to irSymbolWorld.lookup(scoped, scopeSymbolToTag(scoped)) + return scoped to irSymbolWorld.create(scoped, scopeSymbolToTag(scoped)) } override fun visitBlock(node: Block): IrCodeBlock { - enterBlockScope() + enterLocalScope() val block = IrCodeBlock(node.expressions.map { it.visit(this) }) - exitBlockScope() + exitLocalScope() return block } @@ -149,7 +165,7 @@ class IrCodeEmitter( } } - if (functionDefinition.arguments.any { it.multiple }) { + if (variableArguments == null && functionDefinition.arguments.any { it.multiple }) { variableArguments = mutableListOf() } @@ -234,7 +250,13 @@ class IrCodeEmitter( IrStringConstant(node.text) override fun visitSuffixOperation(node: SuffixOperation): IrCodeElement { - TODO("Not yet implemented") + val op = when (node.op) { + SuffixOperator.Increment -> IrSuffixOp.Increment + SuffixOperator.Decrement -> IrSuffixOp.Decrement + } + val symbol = lookup(node.reference.symbol) ?: throw CompileError( + "Unable to find symbol for suffix operation '${node.reference.symbol.id}'", node) + return IrSuffix(op, symbol) } override fun visitSymbolReference(node: SymbolReference): IrCodeElement { @@ -255,4 +277,9 @@ class IrCodeEmitter( inner = node.block.visit(this) ) } + + override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor): IrCodeElement = IrNativeDefinition( + form = node.form.id, + definitions = node.definitions.map { it.text } + ) } diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrCodeVisitor.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrCodeVisitor.kt new file mode 100644 index 0000000..00eb1a8 --- /dev/null +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrCodeVisitor.kt @@ -0,0 +1,33 @@ +package gay.pizza.pork.compiler + +import gay.pizza.pork.bir.* + +interface IrCodeVisitor : IrVisitor { + override fun visitIrDefinition(ir: IrDefinition): T { + codeOnlyError("IrDefinition") + } + + override fun visitIrSlab(ir: IrSlab): T { + codeOnlyError("IrSlab") + } + + override fun visitIrSlabLocation(ir: IrSlabLocation): T { + codeOnlyError("IrSlabLocation") + } + + override fun visitIrWorld(ir: IrWorld): T { + codeOnlyError("IrWorld") + } + + override fun visitIrSymbol(ir: IrSymbol): T { + codeOnlyError("IrSymbol") + } + + override fun visitIrFunctionArgument(ir: IrFunctionArgument): T { + codeOnlyError("IrFunctionArgument") + } + + private fun codeOnlyError(type: String): Nothing { + throw RuntimeException("This visitor targets only code, and $type is not a code element.") + } +} diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrStubOpEmitter.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrStubOpEmitter.kt new file mode 100644 index 0000000..dbf91c9 --- /dev/null +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrStubOpEmitter.kt @@ -0,0 +1,222 @@ +package gay.pizza.pork.compiler + +import gay.pizza.pork.bir.* +import gay.pizza.pork.bytecode.ConstantTag +import gay.pizza.pork.bytecode.MutableRel +import gay.pizza.pork.bytecode.Opcode + +class IrStubOpEmitter(val irDefinition: IrDefinition, val code: CodeBuilder) : IrCodeVisitor { + private val symbol = code.symbol + private val functionArgumentCount = irDefinition.arguments.size + + init { + for (argument in irDefinition.arguments.reversed()) { + val stubVar = code.localState.createOrFindLocal(argument.symbol) + code.emit(Opcode.StoreLocal, listOf(stubVar.index)) + } + } + + fun final() { + if (irDefinition.type == IrDefinitionType.CodeFunction) { + code.emit(Opcode.None) + } + code.emit(Opcode.Return) + code.emit(Opcode.End) + } + + private fun resolve(symbol: IrSymbol): Loadable = code.localState.resolve(symbol) + + private fun load(callOrStubVar: Loadable) { + if (callOrStubVar.stubVar != null) { + code.emit(Opcode.LoadLocal, listOf(callOrStubVar.stubVar.index)) + } else { + code.emit(Opcode.Integer, listOf(code.nextOpInst() + 2u)) + code.patch(Opcode.Call, listOf(0u), mapOf(0 to callOrStubVar.call!!)) + } + } + + private fun store(stubVar: StubVar) { + code.emit(Opcode.StoreLocal, listOf(stubVar.index)) + } + + override fun visitIrBeak(ir: IrBreak) { + val loop = code.localState.findLoopState(ir.target) + code.patch(Opcode.Jump, listOf(0u), 0, symbol, loop.exitJumpTarget) + } + + override fun visitIrCall(ir: IrCall) { + val target = resolve(ir.target) + val targetSymbol = target.call!! + val retRel = MutableRel(0u) + for (argument in ir.arguments) { + visit(argument) + } + val variableArguments = ir.variableArguments + if (variableArguments != null) { + for (argument in variableArguments) { + visit(argument) + } + code.emit(Opcode.ListMake, listOf(variableArguments.size.toUInt())) + } + retRel.rel = code.nextOpInst() + 2u + code.patch(Opcode.ReturnAddress, listOf(0u), 0, symbol, retRel) + code.patch(Opcode.Call, listOf(0u), mapOf(0 to targetSymbol)) + } + + override fun visitIrCodeBlock(ir: IrCodeBlock) { + for (item in ir.items) { + visit(item) + } + } + + override fun visitIrConditional(ir: IrConditional) { + val thenRel = MutableRel(0u) + val endRel = MutableRel(0u) + visit(ir.conditional) + code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, thenRel) + visit(ir.ifTrue) + code.patch(Opcode.Jump, listOf(0u), 0, symbol, endRel) + thenRel.rel = code.nextOpInst() + visit(ir.ifFalse) + endRel.rel = code.nextOpInst() + } + + override fun visitIrBooleanConstant(ir: IrBooleanConstant) { + code.emit(if (ir.value) Opcode.True else Opcode.False) + } + + override fun visitIrIntegerConstant(ir: IrIntegerConstant) { + code.emit(Opcode.Integer, listOf(ir.value.toUInt())) + } + + override fun visitIrLongConstant(ir: IrLongConstant) { + code.emit(Opcode.Integer, listOf(ir.value.toUInt())) + } + + override fun visitIrDoubleConstant(ir: IrDoubleConstant) { + code.emit(Opcode.Integer, listOf(ir.value.toUInt())) + } + + override fun visitIrStringConstant(ir: IrStringConstant) { + val bytes = ir.value.toByteArray() + val constant = symbol.compilableSlab.compiler.constantPool.assign(ConstantTag.String, bytes) + code.emit(Opcode.Constant, listOf(constant)) + } + + override fun visitIrNoneConstant(ir: IrNoneConstant) { + code.emit(Opcode.None) + } + + override fun visitIrContinue(ir: IrContinue) { + val loop = code.localState.findLoopState(ir.target) + code.patch(Opcode.Jump, listOf(0u), 0, symbol, loop.exitJumpTarget) + } + + override fun visitIrInfix(ir: IrInfix) { + visit(ir.left) + visit(ir.right) + when (ir.op) { + IrInfixOp.Add -> code.emit(Opcode.Add) + IrInfixOp.Subtract -> code.emit(Opcode.Subtract) + IrInfixOp.Multiply -> code.emit(Opcode.Multiply) + IrInfixOp.Divide -> code.emit(Opcode.Divide) + IrInfixOp.Equals -> code.emit(Opcode.CompareEqual) + IrInfixOp.NotEquals -> { + code.emit(Opcode.CompareEqual) + code.emit(Opcode.Not) + } + IrInfixOp.EuclideanModulo -> code.emit(Opcode.EuclideanModulo) + IrInfixOp.Remainder -> code.emit(Opcode.Remainder) + IrInfixOp.Lesser -> code.emit(Opcode.CompareLesser) + IrInfixOp.Greater -> code.emit(Opcode.CompareGreater) + IrInfixOp.GreaterEqual -> code.emit(Opcode.CompareGreaterEqual) + IrInfixOp.LesserEqual -> code.emit(Opcode.CompareLesserEqual) + IrInfixOp.BooleanAnd -> code.emit(Opcode.And) + IrInfixOp.BooleanOr -> code.emit(Opcode.Or) + IrInfixOp.BinaryAnd -> code.emit(Opcode.BinaryAnd) + IrInfixOp.BinaryOr -> code.emit(Opcode.BinaryOr) + IrInfixOp.BinaryExclusiveOr -> code.emit(Opcode.BinaryXor) + } + } + + override fun visitIrList(ir: IrList) { + val count = ir.items.size + for (item in ir.items) { + visit(item) + } + code.emit(Opcode.ListMake, listOf(count.toUInt())) + } + + override fun visitIrLoad(ir: IrLoad) { + val loadable = resolve(ir.target) + load(loadable) + } + + override fun visitIrLoop(ir: IrLoop) { + val startOfBody = MutableRel(0u) + val startOfLoop = MutableRel(0u) + val endOfLoop = MutableRel(0u) + code.localState.startLoop(ir.symbol, code.nextOpInst(), endOfLoop) + startOfLoop.rel = code.nextOpInst() + visit(ir.condition) + code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, startOfBody) + code.patch(Opcode.Jump, listOf(0u), 0, symbol, endOfLoop) + startOfBody.rel = code.nextOpInst() + visit(ir.inner) + code.patch(Opcode.Jump, listOf(0u), 0, symbol, startOfLoop) + endOfLoop.rel = code.nextOpInst() + code.localState.endLoop(ir.symbol) + } + + override fun visitIrPrefix(ir: IrPrefix) { + visit(ir.value) + when (ir.op) { + IrPrefixOp.BooleanNot -> code.emit(Opcode.Not) + IrPrefixOp.UnaryPlus -> code.emit(Opcode.UnaryPlus) + IrPrefixOp.UnaryMinus -> code.emit(Opcode.UnaryMinus) + IrPrefixOp.BinaryNot -> code.emit(Opcode.BinaryNot) + } + } + + override fun visitIrReturn(ir: IrReturn) { + visit(ir.value) + code.emit(Opcode.Return) + } + + override fun visitIrStore(ir: IrStore) { + visit(ir.value) + val variable = code.localState.createOrFindLocal(ir.target) + store(variable) + } + + override fun visitIrSuffix(ir: IrSuffix) { + val loadable = code.localState.resolve(ir.target) + load(loadable) + when (ir.op) { + IrSuffixOp.Increment -> { + code.emit(Opcode.Integer, listOf(1u)) + code.emit(Opcode.Add, emptyList()) + } + IrSuffixOp.Decrement-> { + code.emit(Opcode.Integer, listOf(1u)) + code.emit(Opcode.Subtract, emptyList()) + } + } + store(loadable.stubVar!!) + } + + override fun visitIrNativeDefinition(ir: IrNativeDefinition) { + for (def in ir.definitions.reversed()) { + val defConstant = symbol.compilableSlab.compiler.constantPool.assign( + ConstantTag.String, + def.encodeToByteArray() + ) + code.emit(Opcode.Constant, listOf(defConstant)) + } + val formConstant = symbol.compilableSlab.compiler.constantPool.assign( + ConstantTag.String, + ir.form.encodeToByteArray() + ) + code.emit(Opcode.Native, listOf(formConstant, ir.definitions.size.toUInt(), functionArgumentCount.toUInt())) + } +} diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrSymbolWorld.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrSymbolWorld.kt index 5e5170a..00989f9 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrSymbolWorld.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/IrSymbolWorld.kt @@ -4,10 +4,13 @@ import gay.pizza.pork.bir.IrSymbol import gay.pizza.pork.bir.IrSymbolAssignment import gay.pizza.pork.bir.IrSymbolTag -class IrSymbolWorld(val irSymbolAssignment: IrSymbolAssignment) { - private val symbols = mutableMapOf() +class IrSymbolWorld(val irSymbolAssignment: IrSymbolAssignment) { + private val symbols = mutableMapOf() - fun lookup(value: Any, tag: IrSymbolTag): IrSymbol = symbols.getOrPut(value) { + fun create(value: T, tag: IrSymbolTag): IrSymbol = symbols.getOrPut(value) { irSymbolAssignment.next(tag) } + + fun resolve(value: T): IrSymbol? = symbols[value] + fun resolve(symbol: IrSymbol): T? = symbols.entries.firstOrNull { it.value == symbol }?.key } diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/LocalState.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/LocalState.kt index 69b11d7..98ac485 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/LocalState.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/LocalState.kt @@ -1,62 +1,55 @@ package gay.pizza.pork.compiler -import gay.pizza.pork.ast.gen.Symbol +import gay.pizza.pork.bir.IrSymbol import gay.pizza.pork.bytecode.MutableRel +import gay.pizza.pork.frontend.scope.ScopeSymbol class LocalState(val symbol: CompilableSymbol) { private var internalLoopState: LoopState? = null - val loopState: LoopState? - get() = internalLoopState private var localVarIndex: UInt = 0u - private val variables = mutableListOf>() + private val stubVariables = mutableMapOf() + private val loops = mutableMapOf() - fun startLoop(startOfLoop: UInt, exitJumpTarget: MutableRel) { - internalLoopState = LoopState( + fun startLoop(symbol: IrSymbol, startOfLoop: UInt, exitJumpTarget: MutableRel) { + val existing = loops[symbol] + if (existing != null) { + throw CompileError("Starting loop that already is started") + } + val loopState = LoopState( startOfLoop = startOfLoop, exitJumpTarget = exitJumpTarget, - scopeDepth = (internalLoopState?.scopeDepth ?: 0u) + 1u, - enclosing = internalLoopState + scopeDepth = (internalLoopState?.scopeDepth ?: 0u) + 1u ) + loops[symbol] = loopState } - fun endLoop() { - internalLoopState = internalLoopState?.enclosing + fun findLoopState(symbol: IrSymbol): LoopState = + loops[symbol] ?: throw CompileError("Unable to find target loop") + + fun endLoop(symbol: IrSymbol) { + loops.remove(symbol) ?: throw CompileError("End of loop target not found") } - fun createLocal(symbol: Symbol): StubVar { - val scope = variables.last() - val variable = StubVar(localVarIndex++, symbol) - scope.add(variable) + fun createOrFindLocal(symbol: IrSymbol): StubVar { + val existing = stubVariables[symbol] + if (existing != null) { + return existing + } + val variable = StubVar(localVarIndex++, symbol.id) + stubVariables[symbol] = variable return variable } - fun createAnonymousLocal(): StubVar { - val scope = variables.last() - val variable = StubVar(localVarIndex++) - scope.add(variable) - return variable - } - - fun pushScope() { - variables.add(mutableListOf()) - } - - fun popScope() { - variables.removeLast() - } - - fun resolve(symbol: Symbol): Loadable { - for (scope in variables.reversed()) { - val found = scope.firstOrNull { it.symbol == symbol } - if (found != null) { - return Loadable(stubVar = found) - } + fun resolve(symbol: IrSymbol): Loadable { + val localStubVar = stubVariables[symbol] + if (localStubVar != null) { + return Loadable(stubVar = localStubVar) } - val found = this.symbol.compilableSlab.resolveVisible(symbol) - if (found != null) { - return Loadable(call = found) - } - throw RuntimeException("Unable to resolve symbol: ${symbol.id}") + val value = this.symbol.compilableSlab.compiler.irSymbolWorld.resolve(symbol) ?: + throw RuntimeException("Unable to resolve symbol: ${symbol.id} ${symbol.tag}") + val scopeSymbol = value as ScopeSymbol + val call = this.symbol.compilableSlab.compiler.resolve(scopeSymbol) + return Loadable(call = call) } } diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/LoopState.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/LoopState.kt index 6ffecf9..8b04af5 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/LoopState.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/LoopState.kt @@ -5,6 +5,5 @@ import gay.pizza.pork.bytecode.MutableRel class LoopState( val startOfLoop: UInt, val exitJumpTarget: MutableRel, - val scopeDepth: UInt, - val enclosing: LoopState? = null + val scopeDepth: UInt ) diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpEmitter.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpEmitter.kt deleted file mode 100644 index 21ef47a..0000000 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpEmitter.kt +++ /dev/null @@ -1,301 +0,0 @@ -package gay.pizza.pork.compiler - -import gay.pizza.pork.ast.FunctionLevelVisitor -import gay.pizza.pork.ast.gen.* -import gay.pizza.pork.bytecode.ConstantTag -import gay.pizza.pork.bytecode.MutableRel -import gay.pizza.pork.bytecode.Opcode - -class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : FunctionLevelVisitor() { - val code: CodeBuilder = CodeBuilder(symbol) - - fun allocateOuterScope(definition: FunctionDefinition) { - val allNormalArguments = definition.arguments.takeWhile { !it.multiple } - val varArgument = definition.arguments.firstOrNull { it.multiple } - - for (arg in allNormalArguments.reversed()) { - val functionLocal = code.localState.createLocal(arg.symbol) - code.emit(Opcode.StoreLocal, listOf(functionLocal.index)) - } - - if (varArgument != null) { - val functionLocal = code.localState.createLocal(varArgument.symbol) - code.emit(Opcode.StoreLocal, listOf(functionLocal.index)) - } - } - - fun enter() { - code.localState.pushScope() - } - - fun exit() { - code.localState.popScope() - code.emit(Opcode.None) - code.emit(Opcode.Return) - } - - override fun visitBlock(node: Block) { - code.localState.pushScope() - node.visitChildren(this) - code.localState.popScope() - } - - override fun visitBooleanLiteral(node: BooleanLiteral) { - code.emit(if (node.value) Opcode.True else Opcode.False) - } - - override fun visitBreak(node: Break) { - code.patch(Opcode.Jump, listOf(0u), 0, symbol, code.localState.loopState!!.exitJumpTarget) - } - - override fun visitContinue(node: Continue) { - code.patch(Opcode.Jump, listOf(0u), 0, symbol, code.localState.loopState!!.startOfLoop) - } - - override fun visitDoubleLiteral(node: DoubleLiteral) { - code.emit(Opcode.Integer, listOf(node.value.toUInt())) - } - - override fun visitForIn(node: ForIn) { - val listLocalVar = code.localState.createAnonymousLocal() - val sizeLocalVar = code.localState.createAnonymousLocal() - val currentIndexVar = code.localState.createAnonymousLocal() - val currentValueVar = code.localState.createLocal(node.item.symbol) - node.expression.visit(this) - code.emit(Opcode.StoreLocal, listOf(listLocalVar.index)) - load(Loadable(stubVar = listLocalVar)) - code.emit(Opcode.ListSize) - code.emit(Opcode.StoreLocal, listOf(sizeLocalVar.index)) - code.emit(Opcode.Integer, listOf(0u)) - code.emit(Opcode.StoreLocal, listOf(currentIndexVar.index)) - val endOfLoop = MutableRel(0u) - val startOfLoop = code.nextOpInst() - code.localState.startLoop(startOfLoop, endOfLoop) - load(Loadable(stubVar = currentIndexVar)) - load(Loadable(stubVar = sizeLocalVar)) - code.emit(Opcode.CompareGreaterEqual) - code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, endOfLoop) - load(Loadable(stubVar = currentIndexVar)) - load(Loadable(stubVar = listLocalVar)) - code.emit(Opcode.Index) - code.emit(Opcode.StoreLocal, listOf(currentValueVar.index)) - node.block.visit(this) - code.emit(Opcode.LoadLocal, listOf(currentIndexVar.index)) - code.emit(Opcode.Integer, listOf(1u)) - code.emit(Opcode.Add) - code.emit(Opcode.StoreLocal, listOf(currentIndexVar.index)) - code.patch(Opcode.Jump, listOf(0u), 0, symbol, startOfLoop) - endOfLoop.rel = code.nextOpInst() - } - - override fun visitFunctionCall(node: FunctionCall) { - val targetScopeSymbol = symbol.scopeSymbol.scope.resolve(node.symbol) ?: - throw RuntimeException("Unable to resolve symbol: ${node.symbol.id}") - val targetSymbol = compiler.resolve(targetScopeSymbol) - val functionDefinition = targetSymbol.scopeSymbol.definition as FunctionDefinition - val retRel = MutableRel(0u) - val normalArguments = mutableListOf() - var variableArguments: List? = null - if (functionDefinition.arguments.any { it.multiple }) { - variableArguments = emptyList() - } - - for ((index, item) in functionDefinition.arguments.zip(node.arguments).withIndex()) { - val (spec, value) = item - if (spec.multiple) { - val remaining = node.arguments.drop(index) - variableArguments = remaining - break - } else { - normalArguments.add(value) - } - } - - if (variableArguments != null) { - for (item in variableArguments.reversed()) { - item.visit(this) - } - code.emit(Opcode.ListMake, listOf(variableArguments.size.toUInt())) - } - - for (item in normalArguments.reversed()) { - visit(item) - } - - retRel.rel = code.nextOpInst() + 2u - code.patch(Opcode.ReturnAddress, listOf(0u), 0, symbol, retRel) - code.patch(Opcode.Call, listOf(0u), mapOf(0 to targetSymbol)) - } - - override fun visitIf(node: If) { - val thenRel = MutableRel(0u) - val endRel = MutableRel(0u) - node.condition.visit(this) - code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, thenRel) - node.elseBlock?.visit(this) - code.patch(Opcode.Jump, listOf(0u), 0, symbol, endRel) - thenRel.rel = code.nextOpInst() - node.thenBlock.visit(this) - endRel.rel = code.nextOpInst() - } - - override fun visitIndexedBy(node: IndexedBy) { - node.expression.visit(this) - node.index.visit(this) - code.emit(Opcode.Index) - } - - override fun visitInfixOperation(node: InfixOperation) { - node.left.visit(this) - node.right.visit(this) - when (node.op) { - InfixOperator.Plus -> code.emit(Opcode.Add) - InfixOperator.Minus -> code.emit(Opcode.Subtract) - InfixOperator.Multiply -> code.emit(Opcode.Multiply) - InfixOperator.Divide -> code.emit(Opcode.Divide) - InfixOperator.Equals -> code.emit(Opcode.CompareEqual) - InfixOperator.NotEquals -> { - code.emit(Opcode.CompareEqual) - code.emit(Opcode.Not) - } - InfixOperator.EuclideanModulo -> code.emit(Opcode.EuclideanModulo) - InfixOperator.Remainder -> code.emit(Opcode.Remainder) - InfixOperator.Lesser -> code.emit(Opcode.CompareLesser) - InfixOperator.Greater -> code.emit(Opcode.CompareGreater) - InfixOperator.GreaterEqual -> code.emit(Opcode.CompareGreaterEqual) - InfixOperator.LesserEqual -> code.emit(Opcode.CompareLesserEqual) - InfixOperator.BooleanAnd -> code.emit(Opcode.And) - InfixOperator.BooleanOr -> code.emit(Opcode.Or) - InfixOperator.BinaryAnd -> code.emit(Opcode.BinaryAnd) - InfixOperator.BinaryOr -> code.emit(Opcode.BinaryOr) - InfixOperator.BinaryExclusiveOr -> code.emit(Opcode.BinaryXor) - } - } - - override fun visitIntegerLiteral(node: IntegerLiteral) { - code.emit(Opcode.Integer, listOf(node.value.toUInt())) - } - - override fun visitLetAssignment(node: LetAssignment) { - val variable = code.localState.createLocal(node.symbol) - node.value.visit(this) - code.emit(Opcode.StoreLocal, listOf(variable.index)) - } - - override fun visitListLiteral(node: ListLiteral) { - val count = node.items.size - for (item in node.items) { - item.visit(this) - } - code.emit(Opcode.ListMake, listOf(count.toUInt())) - } - - override fun visitLongLiteral(node: LongLiteral) { - code.emit(Opcode.Integer, listOf(node.value.toUInt())) - } - - override fun visitNoneLiteral(node: NoneLiteral) { - code.emit(Opcode.None) - } - - override fun visitParentheses(node: Parentheses) { - node.expression.visit(this) - } - - override fun visitPrefixOperation(node: PrefixOperation) { - node.expression.visit(this) - when (node.op) { - PrefixOperator.BooleanNot -> code.emit(Opcode.Not) - PrefixOperator.UnaryPlus -> code.emit(Opcode.UnaryPlus) - PrefixOperator.UnaryMinus -> code.emit(Opcode.UnaryMinus) - PrefixOperator.BinaryNot -> code.emit(Opcode.BinaryNot) - } - } - - override fun visitReturn(node: Return) { - node.value.visit(this) - code.emit(Opcode.Return) - } - - override fun visitSetAssignment(node: SetAssignment) { - val stubVarOrCall = code.localState.resolve(node.symbol) - if (stubVarOrCall.stubVar == null) { - throw RuntimeException("Invalid set assignment.") - } - node.value.visit(this) - code.emit(Opcode.StoreLocal, listOf(stubVarOrCall.stubVar.index)) - } - - override fun visitStringLiteral(node: StringLiteral) { - val bytes = node.text.toByteArray() - val constant = compiler.constantPool.assign(ConstantTag.String, bytes) - code.emit(Opcode.Constant, listOf(constant)) - } - - override fun visitSuffixOperation(node: SuffixOperation) { - val stubVarOrCall = code.localState.resolve(node.reference.symbol) - if (stubVarOrCall.stubVar == null) { - throw RuntimeException("Invalid suffix operation.") - } - load(stubVarOrCall) - when (node.op) { - SuffixOperator.Increment -> { - code.emit(Opcode.Integer, listOf(1u)) - code.emit(Opcode.Add, emptyList()) - code.emit(Opcode.StoreLocal, listOf(stubVarOrCall.stubVar.index)) - } - SuffixOperator.Decrement -> { - code.emit(Opcode.Integer, listOf(1u)) - code.emit(Opcode.Subtract, emptyList()) - code.emit(Opcode.StoreLocal, listOf(stubVarOrCall.stubVar.index)) - } - } - } - - override fun visitSymbolReference(node: SymbolReference) { - val variable = code.localState.resolve(node.symbol) - load(variable) - } - - override fun visitVarAssignment(node: VarAssignment) { - val variable = code.localState.createLocal(node.symbol) - node.value.visit(this) - code.emit(Opcode.StoreLocal, listOf(variable.index)) - } - - override fun visitWhile(node: While) { - val startOfBody = MutableRel(0u) - val startOfLoop = MutableRel(0u) - val endOfLoop = MutableRel(0u) - code.localState.startLoop(code.nextOpInst(), endOfLoop) - startOfLoop.rel = code.nextOpInst() - node.condition.visit(this) - code.patch(Opcode.JumpIf, listOf(0u), 0, symbol, startOfBody) - code.patch(Opcode.Jump, listOf(0u), 0, symbol, endOfLoop) - startOfBody.rel = code.nextOpInst() - node.block.visit(this) - code.patch(Opcode.Jump, listOf(0u), 0, symbol, startOfLoop) - endOfLoop.rel = code.nextOpInst() - code.localState.endLoop() - } - - override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor) { - for (def in node.definitions) { - val defConstant = compiler.constantPool.assign(ConstantTag.String, def.text.toByteArray()) - code.emit(Opcode.Constant, listOf(defConstant)) - } - val formConstant = compiler.constantPool.assign(ConstantTag.String, node.form.id.toByteArray()) - val functionDefinition = symbol.scopeSymbol.definition as FunctionDefinition - val functionArgumentCount = functionDefinition.arguments.size - code.emit(Opcode.Native, listOf(formConstant, node.definitions.size.toUInt(), functionArgumentCount.toUInt())) - } - - private fun load(callOrStubVar: Loadable) { - if (callOrStubVar.stubVar != null) { - code.emit(Opcode.LoadLocal, listOf(callOrStubVar.stubVar.index)) - } else { - code.emit(Opcode.Integer, listOf(code.nextOpInst() + 2u)) - code.patch(Opcode.Call, listOf(0u), mapOf(0 to callOrStubVar.call!!)) - } - } -} diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubVar.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubVar.kt index 050f4b2..6f41830 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubVar.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubVar.kt @@ -1,8 +1,6 @@ package gay.pizza.pork.compiler -import gay.pizza.pork.ast.gen.Symbol - class StubVar( val index: UInt, - val symbol: Symbol? = null + val id: UInt ) diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/ScopeSymbol.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/ScopeSymbol.kt index 049c194..1ed2c7d 100644 --- a/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/ScopeSymbol.kt +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/ScopeSymbol.kt @@ -18,4 +18,6 @@ class ScopeSymbol(val slabScope: SlabScope, val definition: Definition) { result = 31 * result + symbol.hashCode() return result } + + override fun toString(): String = "ScopeSymbol(${symbol.id})" } diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt index ac46313..4b4cf5f 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt @@ -3,14 +3,8 @@ package gay.pizza.pork.tool import com.github.ajalt.clikt.core.CliktCommand import com.github.ajalt.clikt.parameters.arguments.argument import gay.pizza.dough.fs.PlatformFsProvider -import gay.pizza.pork.ast.gen.FunctionDefinition import gay.pizza.pork.ast.gen.Symbol -import gay.pizza.pork.ast.gen.visit -import gay.pizza.pork.bir.IrSymbolAssignment -import gay.pizza.pork.bir.IrSymbolTag import gay.pizza.pork.compiler.Compiler -import gay.pizza.pork.compiler.IrCodeEmitter -import gay.pizza.pork.compiler.IrSymbolWorld import gay.pizza.pork.minimal.FileTool class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "compile") { @@ -37,12 +31,5 @@ class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "c println(" ${symbol.offset + index.toUInt()} ${op}${annotation}") } } - - val irSymbolAssignment = IrSymbolAssignment() - val irSymbolWorld = IrSymbolWorld(irSymbolAssignment) - val self = irSymbolAssignment.next(IrSymbolTag.Function) - val irCodeEmitter = IrCodeEmitter(self, irSymbolWorld, irSymbolAssignment, compiledSlab.slab.scope) - val ir = irCodeEmitter.visit((compiledMain.scopeSymbol.definition as FunctionDefinition).block!!) - println(ir) } }