From 5540918e7c48eb0ae3b4b44fb05d91d1a39cab9a Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Tue, 21 Nov 2023 04:04:44 -0800 Subject: [PATCH] vm: a functional virtual machine, mostly --- .../gay/pizza/pork/bytecode/CompiledWorld.kt | 3 +- .../gay/pizza/pork/bytecode/Constant.kt | 20 ++++++- .../gay/pizza/pork/bytecode/ConstantPool.kt | 2 +- .../gay/pizza/pork/bytecode/ConstantTag.kt | 8 +++ .../pork/bytecode/MutableConstantPool.kt | 8 +-- .../gay/pizza/pork/bytecode/OpAnnotation.kt | 6 +++ .../kotlin/gay/pizza/pork/bytecode/Opcode.kt | 20 +++---- .../gay/pizza/pork/bytecode/SymbolTable.kt | 11 +++- .../gay/pizza/pork/compiler/CodeBuilder.kt | 7 ++- .../pizza/pork/compiler/CompilableSymbol.kt | 4 +- .../pork/compiler/CompiledSymbolResult.kt | 3 ++ .../pork/compiler/CompiledWorldLayout.kt | 27 +++++++--- .../gay/pizza/pork/compiler/Compiler.kt | 2 +- .../gay/pizza/pork/compiler/LocalState.kt | 7 +++ .../pizza/pork/compiler/StubOpAnnotation.kt | 3 ++ .../gay/pizza/pork/compiler/StubOpEmitter.kt | 52 +++++++++++++++---- .../kotlin/gay/pizza/pork/compiler/StubVar.kt | 2 +- ....kt => NativeFunctionDescriptorElement.kt} | 2 +- .../pork/idea/psi/gen/PorkElementFactory.kt | 2 +- .../gay/pizza/pork/tool/CompileCommand.kt | 7 ++- .../pizza/pork/tool/ScopeAnalysisCommand.kt | 2 +- .../gay/pizza/pork/vm/InternalMachine.kt | 44 +++++++++++++--- .../gay/pizza/pork/vm/VirtualMachine.kt | 17 ++++-- .../gay/pizza/pork/vm/ops/AddOpHandler.kt | 9 +--- .../gay/pizza/pork/vm/ops/AndOpHandler.kt | 14 +++++ .../gay/pizza/pork/vm/ops/CallOpHandler.kt | 1 + .../pork/vm/ops/CompareEqualOpHandler.kt | 2 +- .../vm/ops/CompareGreaterEqualOpHandler.kt | 14 +++++ .../vm/ops/CompareLesserEqualOpHandler.kt | 9 +--- .../gay/pizza/pork/vm/ops/EndOpHandler.kt | 12 +++++ .../gay/pizza/pork/vm/ops/IndexOpHandler.kt | 14 +++++ .../gay/pizza/pork/vm/ops/JumpIfOpHandler.kt | 6 +-- ...{ListOpHandler.kt => ListMakeOpHandler.kt} | 4 +- .../pizza/pork/vm/ops/ListSizeOpHandler.kt | 13 +++++ .../gay/pizza/pork/vm/ops/NativeOpHandler.kt | 2 +- .../gay/pizza/pork/vm/ops/NotOpHandler.kt | 12 +++++ .../gay/pizza/pork/vm/ops/OrOpHandler.kt | 14 +++++ .../pork/vm/ops/ReturnAddressOpHandler.kt | 12 +++++ .../{RetOpHandler.kt => ReturnOpHandler.kt} | 10 ++-- 39 files changed, 323 insertions(+), 84 deletions(-) create mode 100644 bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantTag.kt create mode 100644 bytecode/src/main/kotlin/gay/pizza/pork/bytecode/OpAnnotation.kt create mode 100644 compiler/src/main/kotlin/gay/pizza/pork/compiler/CompiledSymbolResult.kt create mode 100644 compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpAnnotation.kt rename support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/{NativeElement.kt => NativeFunctionDescriptorElement.kt} (84%) create mode 100644 vm/src/main/kotlin/gay/pizza/pork/vm/ops/AndOpHandler.kt create mode 100644 vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareGreaterEqualOpHandler.kt create mode 100644 vm/src/main/kotlin/gay/pizza/pork/vm/ops/EndOpHandler.kt create mode 100644 vm/src/main/kotlin/gay/pizza/pork/vm/ops/IndexOpHandler.kt rename vm/src/main/kotlin/gay/pizza/pork/vm/ops/{ListOpHandler.kt => ListMakeOpHandler.kt} (80%) create mode 100644 vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListSizeOpHandler.kt create mode 100644 vm/src/main/kotlin/gay/pizza/pork/vm/ops/NotOpHandler.kt create mode 100644 vm/src/main/kotlin/gay/pizza/pork/vm/ops/OrOpHandler.kt create mode 100644 vm/src/main/kotlin/gay/pizza/pork/vm/ops/ReturnAddressOpHandler.kt rename vm/src/main/kotlin/gay/pizza/pork/vm/ops/{RetOpHandler.kt => ReturnOpHandler.kt} (55%) diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/CompiledWorld.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/CompiledWorld.kt index 18221a6..38fc26c 100644 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/CompiledWorld.kt +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/CompiledWorld.kt @@ -6,5 +6,6 @@ import kotlinx.serialization.Serializable data class CompiledWorld( val constantPool: ConstantPool, val symbolTable: SymbolTable, - val code: List + val code: List, + val annotations: List ) diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Constant.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Constant.kt index 1bcfc13..b566e28 100644 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Constant.kt +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Constant.kt @@ -1,3 +1,21 @@ package gay.pizza.pork.bytecode -class Constant(val id: UInt, val value: ByteArray) +import kotlinx.serialization.Serializable + +@Serializable +data class Constant(val id: UInt, val tag: ConstantTag, val value: ByteArray) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + other as Constant + + if (id != other.id) return false + if (!value.contentEquals(other.value)) return false + return true + } + + override fun hashCode(): Int { + var result = id.hashCode() + result = 31 * result + value.contentHashCode() + return result + } +} diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantPool.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantPool.kt index 09c69a2..152e2ad 100644 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantPool.kt +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantPool.kt @@ -3,4 +3,4 @@ package gay.pizza.pork.bytecode import kotlinx.serialization.Serializable @Serializable -data class ConstantPool(val constants: List) +data class ConstantPool(val constants: List) diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantTag.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantTag.kt new file mode 100644 index 0000000..12889af --- /dev/null +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/ConstantTag.kt @@ -0,0 +1,8 @@ +package gay.pizza.pork.bytecode + +import kotlinx.serialization.Serializable + +@Serializable +enum class ConstantTag { + String +} diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/MutableConstantPool.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/MutableConstantPool.kt index 80c7d2f..dda725f 100644 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/MutableConstantPool.kt +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/MutableConstantPool.kt @@ -3,16 +3,16 @@ package gay.pizza.pork.bytecode class MutableConstantPool { private val pool = mutableListOf() - fun assign(content: ByteArray): UInt { + fun assign(tag: ConstantTag, content: ByteArray): UInt { for (constant in pool) { - if (constant.value.contentEquals(content)) { + if (constant.value.contentEquals(content) && tag == constant.tag) { return constant.id } } val id = pool.size.toUInt() - pool.add(Constant(id, content)) + pool.add(Constant(id = id, tag = tag, value = content)) return id } - fun all(): List = pool + fun build(): ConstantPool = ConstantPool(pool) } diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/OpAnnotation.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/OpAnnotation.kt new file mode 100644 index 0000000..44f6bac --- /dev/null +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/OpAnnotation.kt @@ -0,0 +1,6 @@ +package gay.pizza.pork.bytecode + +import kotlinx.serialization.Serializable + +@Serializable +data class OpAnnotation(val inst: UInt, val text: String) diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Opcode.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Opcode.kt index f466186..c5e634b 100644 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Opcode.kt +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Opcode.kt @@ -30,14 +30,16 @@ enum class Opcode(val id: UByte) { BinaryAnd(28u), BinaryOr(29u), BinaryXor(30u), - List(31u), - Integer(32u), - Double(33u), - Call(34u), - EuclideanModulo(35u), - Remainder(36u), - Index(37u), - ScopeIn(38u), - ScopeOut(39u), + ListMake(31u), + ListSize(32u), + Integer(33u), + Double(34u), + Call(35u), + EuclideanModulo(36u), + Remainder(37u), + Index(38u), + ScopeIn(39u), + ScopeOut(40u), + ReturnAddress(41u), End(255u), } diff --git a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/SymbolTable.kt b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/SymbolTable.kt index ba54398..3808aff 100644 --- a/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/SymbolTable.kt +++ b/bytecode/src/main/kotlin/gay/pizza/pork/bytecode/SymbolTable.kt @@ -5,4 +5,13 @@ import kotlinx.serialization.Serializable @Serializable data class SymbolTable( val symbols: List -) +) { + fun lookup(inst: UInt): Pair? { + val symbol = symbols.firstOrNull { + (inst >= it.offset) && inst < (it.offset + it.size) + } ?: return null + + val rel = inst - symbol.offset + return symbol to rel + } +} diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CodeBuilder.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CodeBuilder.kt index b84bcfa..8463c0e 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CodeBuilder.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CodeBuilder.kt @@ -6,6 +6,7 @@ import gay.pizza.pork.bytecode.Opcode class CodeBuilder(val symbol: CompilableSymbol) { private val ops = mutableListOf() + private val annotations = mutableListOf() val localState: LocalState = LocalState(symbol) @@ -39,5 +40,9 @@ class CodeBuilder(val symbol: CompilableSymbol) { ops.add(PatchSymOp(Op(code, arguments), patches)) } - fun build(): List = ops.toList() + fun annotate(text: String) { + annotations.add(StubOpAnnotation(symbol, nextOpInst(), text)) + } + + fun build(): CompiledSymbolResult = CompiledSymbolResult(ops.toList(), annotations.toList()) } 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 37d3e23..a03f085 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSymbol.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompilableSymbol.kt @@ -6,12 +6,12 @@ import gay.pizza.pork.ast.gen.visit import gay.pizza.pork.frontend.scope.ScopeSymbol class CompilableSymbol(val compilableSlab: CompilableSlab, val scopeSymbol: ScopeSymbol) { - val compiledStubOps: List by lazy { compile() } + val compiledStubOps: CompiledSymbolResult by lazy { compile() } val usedSymbols: List get() = scopeSymbol.scope.usedSymbols - private fun compile(): List { + private fun compile(): CompiledSymbolResult { val emitter = StubOpEmitter(compilableSlab.compiler, this) emitter.enter() val what = if (scopeSymbol.definition is FunctionDefinition) { diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompiledSymbolResult.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompiledSymbolResult.kt new file mode 100644 index 0000000..ab5acab --- /dev/null +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompiledSymbolResult.kt @@ -0,0 +1,3 @@ +package gay.pizza.pork.compiler + +class CompiledSymbolResult(val ops: List, val annotations: List) diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompiledWorldLayout.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompiledWorldLayout.kt index b3a7760..ec713e8 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompiledWorldLayout.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/CompiledWorldLayout.kt @@ -4,13 +4,16 @@ import gay.pizza.pork.bytecode.* class CompiledWorldLayout(val compiler: Compiler) : StubResolutionContext { private val allStubOps = mutableListOf() + private val allStubAnnotations = mutableListOf() private val symbolTable = mutableMapOf() fun add(symbol: CompilableSymbol) { val start = allStubOps.size - val stubOps = symbol.compiledStubOps + val result = symbol.compiledStubOps + val stubOps = result.ops symbolTable[symbol] = SymbolInfo(symbol.id, start.toUInt(), stubOps.size.toUInt()) allStubOps.addAll(stubOps) + allStubAnnotations.addAll(result.annotations) } private fun patch(): List { @@ -23,16 +26,24 @@ class CompiledWorldLayout(val compiler: Compiler) : StubResolutionContext { return ops } + private fun patchAnnotations(): List { + val annotations = mutableListOf() + for (stub in allStubAnnotations) { + val actual = symbolTable[stub.symbol]!!.offset + stub.rel + annotations.add(OpAnnotation(actual, stub.text)) + } + return annotations + } + override fun resolveJumpTarget(symbol: CompilableSymbol): UInt { return symbolTable[symbol]?.offset ?: throw RuntimeException("Unable to resolve jump target: ${symbol.scopeSymbol.symbol.id}") } - fun layoutCompiledWorld(): CompiledWorld { - val constantPool = mutableListOf() - for (item in compiler.constantPool.all()) { - constantPool.add(item.value) - } - return CompiledWorld(ConstantPool(constantPool), SymbolTable(symbolTable.values.toList()), patch()) - } + fun build(): CompiledWorld = CompiledWorld( + constantPool = compiler.constantPool.build(), + symbolTable = SymbolTable(symbolTable.values.toList()), + code = patch(), + annotations = patchAnnotations() + ) } 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 5d94427..4527b2d 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/Compiler.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/Compiler.kt @@ -42,6 +42,6 @@ class Compiler { for (used in usedSymbolSet) { layout.add(used) } - return layout.layoutCompiledWorld() + return layout.build() } } 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 56014db..4bf8651 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/LocalState.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/LocalState.kt @@ -31,6 +31,13 @@ class LocalState(val symbol: CompilableSymbol) { return variable } + fun createAnonymousLocal(): StubVar { + val scope = variables.last() + val variable = StubVar(localVarIndex++) + scope.add(variable) + return variable + } + fun pushScope() { variables.add(mutableListOf()) } diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpAnnotation.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpAnnotation.kt new file mode 100644 index 0000000..c160d40 --- /dev/null +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpAnnotation.kt @@ -0,0 +1,3 @@ +package gay.pizza.pork.compiler + +data class StubOpAnnotation(val symbol: CompilableSymbol, val rel: UInt, val text: String) diff --git a/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpEmitter.kt b/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpEmitter.kt index 2a09b8b..78279b5 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpEmitter.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubOpEmitter.kt @@ -2,11 +2,12 @@ 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(symbol) + val code: CodeBuilder = CodeBuilder(symbol) fun allocateOuterScope(definition: FunctionDefinition) { val allNormalArguments = definition.arguments.takeWhile { !it.multiple } @@ -55,7 +56,35 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func } override fun visitForIn(node: ForIn) { - TODO("ForIn is currently unsupported") + 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) { @@ -64,10 +93,12 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func val targetSymbol = compiler.resolve(targetScopeSymbol) val functionDefinition = targetSymbol.scopeSymbol.definition as FunctionDefinition val retRel = MutableRel(0u) - code.patch(Opcode.Integer, listOf(0u), 0, symbol, retRel) - 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) { @@ -83,14 +114,15 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func for (item in variableArguments.reversed()) { item.visit(this) } - code.emit(Opcode.List, listOf(variableArguments.size.toUInt())) + code.emit(Opcode.ListMake, listOf(variableArguments.size.toUInt())) } for (item in normalArguments.reversed()) { visit(item) } - retRel.rel = code.nextOpInst() + 1u + retRel.rel = code.nextOpInst() + 2u + code.patch(Opcode.ReturnAddress, listOf(0u), 0, symbol, retRel) code.patch(Opcode.Call, listOf(0u), mapOf(0 to targetSymbol)) } @@ -154,7 +186,7 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func for (item in node.items) { item.visit(this) } - code.emit(Opcode.List, listOf(count.toUInt())) + code.emit(Opcode.ListMake, listOf(count.toUInt())) } override fun visitLongLiteral(node: LongLiteral) { @@ -190,7 +222,7 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func override fun visitStringLiteral(node: StringLiteral) { val bytes = node.text.toByteArray() - val constant = compiler.constantPool.assign(bytes) + val constant = compiler.constantPool.assign(ConstantTag.String, bytes) code.emit(Opcode.Constant, listOf(constant)) } @@ -243,10 +275,10 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor) { for (def in node.definitions) { - val defConstant = compiler.constantPool.assign(def.text.toByteArray()) + val defConstant = compiler.constantPool.assign(ConstantTag.String, def.text.toByteArray()) code.emit(Opcode.Constant, listOf(defConstant)) } - val formConstant = compiler.constantPool.assign(node.form.id.toByteArray()) + val formConstant = compiler.constantPool.assign(ConstantTag.String, node.form.id.toByteArray()) code.emit(Opcode.Native, listOf(formConstant, node.definitions.size.toUInt())) } 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 1209be7..050f4b2 100644 --- a/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubVar.kt +++ b/compiler/src/main/kotlin/gay/pizza/pork/compiler/StubVar.kt @@ -4,5 +4,5 @@ import gay.pizza.pork.ast.gen.Symbol class StubVar( val index: UInt, - val symbol: Symbol + val symbol: Symbol? = null ) diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/NativeElement.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/NativeFunctionDescriptorElement.kt similarity index 84% rename from support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/NativeElement.kt rename to support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/NativeFunctionDescriptorElement.kt index 8ea92c5..d6f002b 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/NativeElement.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/NativeFunctionDescriptorElement.kt @@ -6,7 +6,7 @@ import com.intellij.navigation.ItemPresentation import gay.pizza.pork.idea.psi.PorkElementHelpers import javax.swing.Icon -class NativeElement(node: ASTNode) : PorkElement(node) { +class NativeFunctionDescriptorElement(node: ASTNode) : PorkElement(node) { override fun getIcon(flags: Int): Icon? = PorkElementHelpers.iconOf(this) diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt index f529c74..c9206f7 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt @@ -40,7 +40,7 @@ object PorkElementFactory { NodeType.Break -> BreakElement(node) NodeType.Continue -> ContinueElement(node) NodeType.NoneLiteral -> NoneLiteralElement(node) - NodeType.Native -> NativeElement(node) + NodeType.NativeFunctionDescriptor -> NativeFunctionDescriptorElement(node) NodeType.IndexedBy -> IndexedByElement(node) else -> ASTWrapperPsiElement(node) } 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 150cb92..af9725d 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt @@ -24,7 +24,12 @@ class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "c val code = compiledWorld.code.subList(symbol.offset.toInt(), (symbol.offset + symbol.size).toInt()) println(symbol.id) for ((index, op) in code.withIndex()) { - println(" ${symbol.offset + index.toUInt()} ${op.code.name} ${op.args.joinToString(" ")}") + var annotation = "" + val annotations = compiledWorld.annotations.filter { it.inst == (symbol.offset + index.toUInt()) } + if (annotations.isNotEmpty()) { + annotation = " ; ${annotations.joinToString(", ") { it.text}}" + } + println(" ${symbol.offset + index.toUInt()} ${op.code.name} ${op.args.joinToString(" ")}${annotation}") } } val vm = VirtualMachine(compiledWorld) diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/ScopeAnalysisCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/ScopeAnalysisCommand.kt index d85d2a6..f0ad523 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/ScopeAnalysisCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/ScopeAnalysisCommand.kt @@ -20,7 +20,7 @@ class ScopeAnalysisCommand : CliktCommand(help = "Run Scope Analysis", name = "s "symbol ${visibleScopeSymbol.scopeSymbol.symbol.id} " + "type=${visibleScopeSymbol.scopeSymbol.definition.type.name} " + "internal=${visibleScopeSymbol.isInternalSymbol} " + - "slab=${visibleScopeSymbol.scopeSymbol.slab.location.commonFriendlyName}" + "slab=${visibleScopeSymbol.scopeSymbol.slabScope.slab.location.commonFriendlyName}" ) } } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt index 4d7817b..6b160ad 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt @@ -1,6 +1,7 @@ package gay.pizza.pork.vm import gay.pizza.pork.bytecode.CompiledWorld +import gay.pizza.pork.bytecode.ConstantTag class InternalMachine(val world: CompiledWorld, val handlers: List) { private val inlined = world.code.map { op -> @@ -10,10 +11,12 @@ class InternalMachine(val world: CompiledWorld, val handlers: List) { } private var inst: UInt = 0u - private val stack = mutableListOf(EndOfCode) + private val stack = mutableListOf() private val locals = mutableListOf>( mutableMapOf() ) + private val callStack = mutableListOf(0u) + private val returnAddressStack = mutableListOf() private var autoNextInst = true private var exitFlag = false @@ -36,7 +39,11 @@ class InternalMachine(val world: CompiledWorld, val handlers: List) { } fun loadConstant(id: UInt) { - push(world.constantPool.constants[id.toInt()]) + val constant = world.constantPool.constants[id.toInt()] + when (constant.tag) { + ConstantTag.String -> push(String(constant.value)) + else -> throw VirtualMachineException("Unknown Constant Tag: ${constant.tag.name}") + } } fun loadLocal(id: UInt) { @@ -48,7 +55,7 @@ class InternalMachine(val world: CompiledWorld, val handlers: List) { fun storeLocal(id: UInt) { val localSet = locals.last() - val value = pop() + val value = popAnyValue() localSet[id] = value } @@ -57,24 +64,47 @@ class InternalMachine(val world: CompiledWorld, val handlers: List) { autoNextInst = false } + fun pushReturnAddress(value: UInt) { + returnAddressStack.add(value) + } + + fun pushCallStack(value: UInt) { + callStack.add(value) + } + + fun popCallStack() { + callStack.removeLast() + } + + fun armReturnAddressIfSet() { + val returnAddress = returnAddressStack.removeLastOrNull() + if (returnAddress != null) { + setNextInst(returnAddress) + } else { + exit() + } + } + fun push(item: Any) { stack.add(item) } - fun pop(): Any = stack.removeLast() + fun popAnyValue(): Any = stack.removeLast() + + inline fun pop(): T = popAnyValue() as T + fun exit() { exitFlag = true } fun reset() { stack.clear() - stack.add(EndOfCode) + callStack.clear() + callStack.add(0u) locals.clear() locals.add(mutableMapOf()) inst = 0u exitFlag = false autoNextInst = true } - - data object EndOfCode } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt index 966a07e..d762c64 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt @@ -12,10 +12,18 @@ class VirtualMachine(world: CompiledWorld) : ExecutionContext { TrueOpHandler, FalseOpHandler, - ListOpHandler, + ListMakeOpHandler, + ListSizeOpHandler, + + IndexOpHandler, + + AndOpHandler, + OrOpHandler, + NotOpHandler, CompareEqualOpHandler, CompareLesserEqualOpHandler, + CompareGreaterEqualOpHandler, AddOpHandler, @@ -25,13 +33,16 @@ class VirtualMachine(world: CompiledWorld) : ExecutionContext { LoadLocalOpHandler, StoreLocalOpHandler, + ReturnAddressOpHandler, CallOpHandler, - RetOpHandler, + ReturnOpHandler, NativeOpHandler, ScopeInOpHandler, - ScopeOutOpHandler + ScopeOutOpHandler, + + EndOpHandler )) override fun execute() { diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/AddOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/AddOpHandler.kt index 9029273..3b7ad68 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/AddOpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/AddOpHandler.kt @@ -4,16 +4,11 @@ import gay.pizza.pork.bytecode.Op import gay.pizza.pork.bytecode.Opcode import gay.pizza.pork.vm.InternalMachine import gay.pizza.pork.vm.OpHandler -import gay.pizza.pork.vm.VirtualMachineException object AddOpHandler : OpHandler(Opcode.Add) { override fun handle(machine: InternalMachine, op: Op) { - val left = machine.pop() - val right = machine.pop() - - if (left !is Int || right !is Int) { - throw VirtualMachineException("Bad types.") - } + val left = machine.pop() + val right = machine.pop() machine.push(left + right) } } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/AndOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/AndOpHandler.kt new file mode 100644 index 0000000..afc1e01 --- /dev/null +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/AndOpHandler.kt @@ -0,0 +1,14 @@ +package gay.pizza.pork.vm.ops + +import gay.pizza.pork.bytecode.Op +import gay.pizza.pork.bytecode.Opcode +import gay.pizza.pork.vm.InternalMachine +import gay.pizza.pork.vm.OpHandler + +object AndOpHandler : OpHandler(Opcode.And) { + override fun handle(machine: InternalMachine, op: Op) { + val left = machine.pop() + val right = machine.pop() + machine.push(left && right) + } +} diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CallOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CallOpHandler.kt index b7c699c..bf0183c 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CallOpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CallOpHandler.kt @@ -8,6 +8,7 @@ import gay.pizza.pork.vm.OpHandler object CallOpHandler : OpHandler(Opcode.Call) { override fun handle(machine: InternalMachine, op: Op) { machine.setNextInst(op.args[0]) + machine.pushCallStack(op.args[0]) machine.pushScope() } } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareEqualOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareEqualOpHandler.kt index a221dce..a5e101a 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareEqualOpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareEqualOpHandler.kt @@ -7,6 +7,6 @@ import gay.pizza.pork.vm.OpHandler object CompareEqualOpHandler : OpHandler(Opcode.CompareEqual) { override fun handle(machine: InternalMachine, op: Op) { - machine.push(machine.pop() == machine.pop()) + machine.push(machine.popAnyValue() == machine.popAnyValue()) } } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareGreaterEqualOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareGreaterEqualOpHandler.kt new file mode 100644 index 0000000..76dcc0a --- /dev/null +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareGreaterEqualOpHandler.kt @@ -0,0 +1,14 @@ +package gay.pizza.pork.vm.ops + +import gay.pizza.pork.bytecode.Op +import gay.pizza.pork.bytecode.Opcode +import gay.pizza.pork.vm.InternalMachine +import gay.pizza.pork.vm.OpHandler + +object CompareGreaterEqualOpHandler : OpHandler(Opcode.CompareGreaterEqual) { + override fun handle(machine: InternalMachine, op: Op) { + val right = machine.pop() + val left = machine.pop() + machine.push(left >= right) + } +} diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareLesserEqualOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareLesserEqualOpHandler.kt index b60f326..c52ccba 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareLesserEqualOpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/CompareLesserEqualOpHandler.kt @@ -4,16 +4,11 @@ import gay.pizza.pork.bytecode.Op import gay.pizza.pork.bytecode.Opcode import gay.pizza.pork.vm.InternalMachine import gay.pizza.pork.vm.OpHandler -import gay.pizza.pork.vm.VirtualMachineException object CompareLesserEqualOpHandler : OpHandler(Opcode.CompareLesserEqual) { override fun handle(machine: InternalMachine, op: Op) { - val right = machine.pop() - val left = machine.pop() - - if (left !is Int || right !is Int) { - throw VirtualMachineException("Bad types.") - } + val right = machine.pop() + val left = machine.pop() machine.push(left <= right) } } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/EndOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/EndOpHandler.kt new file mode 100644 index 0000000..4e4e62c --- /dev/null +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/EndOpHandler.kt @@ -0,0 +1,12 @@ +package gay.pizza.pork.vm.ops + +import gay.pizza.pork.bytecode.Op +import gay.pizza.pork.bytecode.Opcode +import gay.pizza.pork.vm.InternalMachine +import gay.pizza.pork.vm.OpHandler + +object EndOpHandler : OpHandler(Opcode.End) { + override fun handle(machine: InternalMachine, op: Op) { + machine.exit() + } +} diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/IndexOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/IndexOpHandler.kt new file mode 100644 index 0000000..4fdd9f3 --- /dev/null +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/IndexOpHandler.kt @@ -0,0 +1,14 @@ +package gay.pizza.pork.vm.ops + +import gay.pizza.pork.bytecode.Op +import gay.pizza.pork.bytecode.Opcode +import gay.pizza.pork.vm.InternalMachine +import gay.pizza.pork.vm.OpHandler + +object IndexOpHandler : OpHandler(Opcode.Index) { + override fun handle(machine: InternalMachine, op: Op) { + val list = machine.pop>() + val index = machine.pop().toInt() + machine.push(list[index] as Any) + } +} diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/JumpIfOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/JumpIfOpHandler.kt index 197b4b6..ee0559b 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/JumpIfOpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/JumpIfOpHandler.kt @@ -8,11 +8,7 @@ import gay.pizza.pork.vm.VirtualMachineException object JumpIfOpHandler : OpHandler(Opcode.JumpIf) { override fun handle(machine: InternalMachine, op: Op) { - val value = machine.pop() - if (value !is Boolean) { - throw VirtualMachineException("JumpIf expects a boolean value on the stack.") - } - + val value = machine.pop() if (value) { machine.setNextInst(op.args[0]) } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListMakeOpHandler.kt similarity index 80% rename from vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListOpHandler.kt rename to vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListMakeOpHandler.kt index 98ea28e..0983429 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListOpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListMakeOpHandler.kt @@ -5,12 +5,12 @@ import gay.pizza.pork.bytecode.Opcode import gay.pizza.pork.vm.InternalMachine import gay.pizza.pork.vm.OpHandler -object ListOpHandler : OpHandler(Opcode.List) { +object ListMakeOpHandler : OpHandler(Opcode.ListMake) { override fun handle(machine: InternalMachine, op: Op) { val count = op.args[0] val list = mutableListOf() for (i in 1u..count) { - val item = machine.pop() + val item = machine.popAnyValue() list.add(item) } machine.push(list.reversed()) diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListSizeOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListSizeOpHandler.kt new file mode 100644 index 0000000..3068a3c --- /dev/null +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListSizeOpHandler.kt @@ -0,0 +1,13 @@ +package gay.pizza.pork.vm.ops + +import gay.pizza.pork.bytecode.Op +import gay.pizza.pork.bytecode.Opcode +import gay.pizza.pork.vm.InternalMachine +import gay.pizza.pork.vm.OpHandler + +object ListSizeOpHandler : OpHandler(Opcode.ListSize) { + override fun handle(machine: InternalMachine, op: Op) { + val list = machine.pop>() + machine.push(list.size) + } +} diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt index 18ae662..f3d3062 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt @@ -10,7 +10,7 @@ object NativeOpHandler : OpHandler(Opcode.Native) { val countOfNativeDefs = op.args[1].toInt() val defs = mutableListOf() for (i in 0 until countOfNativeDefs) { - defs.add(String(machine.pop() as ByteArray)) + defs.add(machine.pop() as String) } } } diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NotOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NotOpHandler.kt new file mode 100644 index 0000000..ab5bd5e --- /dev/null +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/NotOpHandler.kt @@ -0,0 +1,12 @@ +package gay.pizza.pork.vm.ops + +import gay.pizza.pork.bytecode.Op +import gay.pizza.pork.bytecode.Opcode +import gay.pizza.pork.vm.InternalMachine +import gay.pizza.pork.vm.OpHandler + +object NotOpHandler : OpHandler(Opcode.Not) { + override fun handle(machine: InternalMachine, op: Op) { + machine.push(!machine.pop()) + } +} diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/OrOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/OrOpHandler.kt new file mode 100644 index 0000000..48c348e --- /dev/null +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/OrOpHandler.kt @@ -0,0 +1,14 @@ +package gay.pizza.pork.vm.ops + +import gay.pizza.pork.bytecode.Op +import gay.pizza.pork.bytecode.Opcode +import gay.pizza.pork.vm.InternalMachine +import gay.pizza.pork.vm.OpHandler + +object OrOpHandler : OpHandler(Opcode.Or) { + override fun handle(machine: InternalMachine, op: Op) { + val left = machine.pop() + val right = machine.pop() + machine.push(left || right) + } +} diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ReturnAddressOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ReturnAddressOpHandler.kt new file mode 100644 index 0000000..cb5f526 --- /dev/null +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ReturnAddressOpHandler.kt @@ -0,0 +1,12 @@ +package gay.pizza.pork.vm.ops + +import gay.pizza.pork.bytecode.Op +import gay.pizza.pork.bytecode.Opcode +import gay.pizza.pork.vm.InternalMachine +import gay.pizza.pork.vm.OpHandler + +object ReturnAddressOpHandler : OpHandler(Opcode.ReturnAddress) { + override fun handle(machine: InternalMachine, op: Op) { + machine.pushReturnAddress(op.args[0]) + } +} diff --git a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/RetOpHandler.kt b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ReturnOpHandler.kt similarity index 55% rename from vm/src/main/kotlin/gay/pizza/pork/vm/ops/RetOpHandler.kt rename to vm/src/main/kotlin/gay/pizza/pork/vm/ops/ReturnOpHandler.kt index 62becc6..e685dee 100644 --- a/vm/src/main/kotlin/gay/pizza/pork/vm/ops/RetOpHandler.kt +++ b/vm/src/main/kotlin/gay/pizza/pork/vm/ops/ReturnOpHandler.kt @@ -5,14 +5,10 @@ import gay.pizza.pork.bytecode.Opcode import gay.pizza.pork.vm.InternalMachine import gay.pizza.pork.vm.OpHandler -object RetOpHandler : OpHandler(Opcode.Return) { +object ReturnOpHandler : OpHandler(Opcode.Return) { override fun handle(machine: InternalMachine, op: Op) { - val last = machine.pop() - if (last == InternalMachine.EndOfCode) { - machine.exit() - return - } machine.popScope() - machine.setNextInst((last as Int).toUInt()) + machine.armReturnAddressIfSet() + machine.popCallStack() } }