vm: a functional virtual machine, mostly

This commit is contained in:
Alex Zenla 2023-11-21 04:04:44 -08:00
parent 4c50d48e1e
commit 5540918e7c
Signed by: alex
GPG Key ID: C0780728420EBFE5
39 changed files with 323 additions and 84 deletions

View File

@ -6,5 +6,6 @@ import kotlinx.serialization.Serializable
data class CompiledWorld( data class CompiledWorld(
val constantPool: ConstantPool, val constantPool: ConstantPool,
val symbolTable: SymbolTable, val symbolTable: SymbolTable,
val code: List<Op> val code: List<Op>,
val annotations: List<OpAnnotation>
) )

View File

@ -1,3 +1,21 @@
package gay.pizza.pork.bytecode 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
}
}

View File

@ -3,4 +3,4 @@ package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
data class ConstantPool(val constants: List<ByteArray>) data class ConstantPool(val constants: List<Constant>)

View File

@ -0,0 +1,8 @@
package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable
@Serializable
enum class ConstantTag {
String
}

View File

@ -3,16 +3,16 @@ package gay.pizza.pork.bytecode
class MutableConstantPool { class MutableConstantPool {
private val pool = mutableListOf<Constant>() private val pool = mutableListOf<Constant>()
fun assign(content: ByteArray): UInt { fun assign(tag: ConstantTag, content: ByteArray): UInt {
for (constant in pool) { for (constant in pool) {
if (constant.value.contentEquals(content)) { if (constant.value.contentEquals(content) && tag == constant.tag) {
return constant.id return constant.id
} }
} }
val id = pool.size.toUInt() val id = pool.size.toUInt()
pool.add(Constant(id, content)) pool.add(Constant(id = id, tag = tag, value = content))
return id return id
} }
fun all(): List<Constant> = pool fun build(): ConstantPool = ConstantPool(pool)
} }

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable
@Serializable
data class OpAnnotation(val inst: UInt, val text: String)

View File

@ -30,14 +30,16 @@ enum class Opcode(val id: UByte) {
BinaryAnd(28u), BinaryAnd(28u),
BinaryOr(29u), BinaryOr(29u),
BinaryXor(30u), BinaryXor(30u),
List(31u), ListMake(31u),
Integer(32u), ListSize(32u),
Double(33u), Integer(33u),
Call(34u), Double(34u),
EuclideanModulo(35u), Call(35u),
Remainder(36u), EuclideanModulo(36u),
Index(37u), Remainder(37u),
ScopeIn(38u), Index(38u),
ScopeOut(39u), ScopeIn(39u),
ScopeOut(40u),
ReturnAddress(41u),
End(255u), End(255u),
} }

View File

@ -5,4 +5,13 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class SymbolTable( data class SymbolTable(
val symbols: List<SymbolInfo> val symbols: List<SymbolInfo>
) ) {
fun lookup(inst: UInt): Pair<SymbolInfo, UInt>? {
val symbol = symbols.firstOrNull {
(inst >= it.offset) && inst < (it.offset + it.size)
} ?: return null
val rel = inst - symbol.offset
return symbol to rel
}
}

View File

@ -6,6 +6,7 @@ import gay.pizza.pork.bytecode.Opcode
class CodeBuilder(val symbol: CompilableSymbol) { class CodeBuilder(val symbol: CompilableSymbol) {
private val ops = mutableListOf<StubOp>() private val ops = mutableListOf<StubOp>()
private val annotations = mutableListOf<StubOpAnnotation>()
val localState: LocalState = LocalState(symbol) val localState: LocalState = LocalState(symbol)
@ -39,5 +40,9 @@ class CodeBuilder(val symbol: CompilableSymbol) {
ops.add(PatchSymOp(Op(code, arguments), patches)) ops.add(PatchSymOp(Op(code, arguments), patches))
} }
fun build(): List<StubOp> = ops.toList() fun annotate(text: String) {
annotations.add(StubOpAnnotation(symbol, nextOpInst(), text))
}
fun build(): CompiledSymbolResult = CompiledSymbolResult(ops.toList(), annotations.toList())
} }

View File

@ -6,12 +6,12 @@ import gay.pizza.pork.ast.gen.visit
import gay.pizza.pork.frontend.scope.ScopeSymbol import gay.pizza.pork.frontend.scope.ScopeSymbol
class CompilableSymbol(val compilableSlab: CompilableSlab, val scopeSymbol: ScopeSymbol) { class CompilableSymbol(val compilableSlab: CompilableSlab, val scopeSymbol: ScopeSymbol) {
val compiledStubOps: List<StubOp> by lazy { compile() } val compiledStubOps: CompiledSymbolResult by lazy { compile() }
val usedSymbols: List<ScopeSymbol> val usedSymbols: List<ScopeSymbol>
get() = scopeSymbol.scope.usedSymbols get() = scopeSymbol.scope.usedSymbols
private fun compile(): List<StubOp> { private fun compile(): CompiledSymbolResult {
val emitter = StubOpEmitter(compilableSlab.compiler, this) val emitter = StubOpEmitter(compilableSlab.compiler, this)
emitter.enter() emitter.enter()
val what = if (scopeSymbol.definition is FunctionDefinition) { val what = if (scopeSymbol.definition is FunctionDefinition) {

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.compiler
class CompiledSymbolResult(val ops: List<StubOp>, val annotations: List<StubOpAnnotation>)

View File

@ -4,13 +4,16 @@ import gay.pizza.pork.bytecode.*
class CompiledWorldLayout(val compiler: Compiler) : StubResolutionContext { class CompiledWorldLayout(val compiler: Compiler) : StubResolutionContext {
private val allStubOps = mutableListOf<StubOp>() private val allStubOps = mutableListOf<StubOp>()
private val allStubAnnotations = mutableListOf<StubOpAnnotation>()
private val symbolTable = mutableMapOf<CompilableSymbol, SymbolInfo>() private val symbolTable = mutableMapOf<CompilableSymbol, SymbolInfo>()
fun add(symbol: CompilableSymbol) { fun add(symbol: CompilableSymbol) {
val start = allStubOps.size 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()) symbolTable[symbol] = SymbolInfo(symbol.id, start.toUInt(), stubOps.size.toUInt())
allStubOps.addAll(stubOps) allStubOps.addAll(stubOps)
allStubAnnotations.addAll(result.annotations)
} }
private fun patch(): List<Op> { private fun patch(): List<Op> {
@ -23,16 +26,24 @@ class CompiledWorldLayout(val compiler: Compiler) : StubResolutionContext {
return ops return ops
} }
private fun patchAnnotations(): List<OpAnnotation> {
val annotations = mutableListOf<OpAnnotation>()
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 { override fun resolveJumpTarget(symbol: CompilableSymbol): UInt {
return symbolTable[symbol]?.offset ?: return symbolTable[symbol]?.offset ?:
throw RuntimeException("Unable to resolve jump target: ${symbol.scopeSymbol.symbol.id}") throw RuntimeException("Unable to resolve jump target: ${symbol.scopeSymbol.symbol.id}")
} }
fun layoutCompiledWorld(): CompiledWorld { fun build(): CompiledWorld = CompiledWorld(
val constantPool = mutableListOf<ByteArray>() constantPool = compiler.constantPool.build(),
for (item in compiler.constantPool.all()) { symbolTable = SymbolTable(symbolTable.values.toList()),
constantPool.add(item.value) code = patch(),
} annotations = patchAnnotations()
return CompiledWorld(ConstantPool(constantPool), SymbolTable(symbolTable.values.toList()), patch()) )
}
} }

View File

@ -42,6 +42,6 @@ class Compiler {
for (used in usedSymbolSet) { for (used in usedSymbolSet) {
layout.add(used) layout.add(used)
} }
return layout.layoutCompiledWorld() return layout.build()
} }
} }

View File

@ -31,6 +31,13 @@ class LocalState(val symbol: CompilableSymbol) {
return variable return variable
} }
fun createAnonymousLocal(): StubVar {
val scope = variables.last()
val variable = StubVar(localVarIndex++)
scope.add(variable)
return variable
}
fun pushScope() { fun pushScope() {
variables.add(mutableListOf()) variables.add(mutableListOf())
} }

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.compiler
data class StubOpAnnotation(val symbol: CompilableSymbol, val rel: UInt, val text: String)

View File

@ -2,11 +2,12 @@ package gay.pizza.pork.compiler
import gay.pizza.pork.ast.FunctionLevelVisitor import gay.pizza.pork.ast.FunctionLevelVisitor
import gay.pizza.pork.ast.gen.* import gay.pizza.pork.ast.gen.*
import gay.pizza.pork.bytecode.ConstantTag
import gay.pizza.pork.bytecode.MutableRel import gay.pizza.pork.bytecode.MutableRel
import gay.pizza.pork.bytecode.Opcode import gay.pizza.pork.bytecode.Opcode
class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : FunctionLevelVisitor<Unit>() { class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : FunctionLevelVisitor<Unit>() {
val code = CodeBuilder(symbol) val code: CodeBuilder = CodeBuilder(symbol)
fun allocateOuterScope(definition: FunctionDefinition) { fun allocateOuterScope(definition: FunctionDefinition) {
val allNormalArguments = definition.arguments.takeWhile { !it.multiple } 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) { 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) { override fun visitFunctionCall(node: FunctionCall) {
@ -64,10 +93,12 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func
val targetSymbol = compiler.resolve(targetScopeSymbol) val targetSymbol = compiler.resolve(targetScopeSymbol)
val functionDefinition = targetSymbol.scopeSymbol.definition as FunctionDefinition val functionDefinition = targetSymbol.scopeSymbol.definition as FunctionDefinition
val retRel = MutableRel(0u) val retRel = MutableRel(0u)
code.patch(Opcode.Integer, listOf(0u), 0, symbol, retRel)
val normalArguments = mutableListOf<Expression>() val normalArguments = mutableListOf<Expression>()
var variableArguments: List<Expression>? = null var variableArguments: List<Expression>? = null
if (functionDefinition.arguments.any { it.multiple }) {
variableArguments = emptyList()
}
for ((index, item) in functionDefinition.arguments.zip(node.arguments).withIndex()) { for ((index, item) in functionDefinition.arguments.zip(node.arguments).withIndex()) {
val (spec, value) = item val (spec, value) = item
if (spec.multiple) { if (spec.multiple) {
@ -83,14 +114,15 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func
for (item in variableArguments.reversed()) { for (item in variableArguments.reversed()) {
item.visit(this) item.visit(this)
} }
code.emit(Opcode.List, listOf(variableArguments.size.toUInt())) code.emit(Opcode.ListMake, listOf(variableArguments.size.toUInt()))
} }
for (item in normalArguments.reversed()) { for (item in normalArguments.reversed()) {
visit(item) 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)) 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) { for (item in node.items) {
item.visit(this) item.visit(this)
} }
code.emit(Opcode.List, listOf(count.toUInt())) code.emit(Opcode.ListMake, listOf(count.toUInt()))
} }
override fun visitLongLiteral(node: LongLiteral) { override fun visitLongLiteral(node: LongLiteral) {
@ -190,7 +222,7 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func
override fun visitStringLiteral(node: StringLiteral) { override fun visitStringLiteral(node: StringLiteral) {
val bytes = node.text.toByteArray() 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)) code.emit(Opcode.Constant, listOf(constant))
} }
@ -243,10 +275,10 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func
override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor) { override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor) {
for (def in node.definitions) { 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)) 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())) code.emit(Opcode.Native, listOf(formConstant, node.definitions.size.toUInt()))
} }

View File

@ -4,5 +4,5 @@ import gay.pizza.pork.ast.gen.Symbol
class StubVar( class StubVar(
val index: UInt, val index: UInt,
val symbol: Symbol val symbol: Symbol? = null
) )

View File

@ -6,7 +6,7 @@ import com.intellij.navigation.ItemPresentation
import gay.pizza.pork.idea.psi.PorkElementHelpers import gay.pizza.pork.idea.psi.PorkElementHelpers
import javax.swing.Icon import javax.swing.Icon
class NativeElement(node: ASTNode) : PorkElement(node) { class NativeFunctionDescriptorElement(node: ASTNode) : PorkElement(node) {
override fun getIcon(flags: Int): Icon? = override fun getIcon(flags: Int): Icon? =
PorkElementHelpers.iconOf(this) PorkElementHelpers.iconOf(this)

View File

@ -40,7 +40,7 @@ object PorkElementFactory {
NodeType.Break -> BreakElement(node) NodeType.Break -> BreakElement(node)
NodeType.Continue -> ContinueElement(node) NodeType.Continue -> ContinueElement(node)
NodeType.NoneLiteral -> NoneLiteralElement(node) NodeType.NoneLiteral -> NoneLiteralElement(node)
NodeType.Native -> NativeElement(node) NodeType.NativeFunctionDescriptor -> NativeFunctionDescriptorElement(node)
NodeType.IndexedBy -> IndexedByElement(node) NodeType.IndexedBy -> IndexedByElement(node)
else -> ASTWrapperPsiElement(node) else -> ASTWrapperPsiElement(node)
} }

View File

@ -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()) val code = compiledWorld.code.subList(symbol.offset.toInt(), (symbol.offset + symbol.size).toInt())
println(symbol.id) println(symbol.id)
for ((index, op) in code.withIndex()) { 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) val vm = VirtualMachine(compiledWorld)

View File

@ -20,7 +20,7 @@ class ScopeAnalysisCommand : CliktCommand(help = "Run Scope Analysis", name = "s
"symbol ${visibleScopeSymbol.scopeSymbol.symbol.id} " + "symbol ${visibleScopeSymbol.scopeSymbol.symbol.id} " +
"type=${visibleScopeSymbol.scopeSymbol.definition.type.name} " + "type=${visibleScopeSymbol.scopeSymbol.definition.type.name} " +
"internal=${visibleScopeSymbol.isInternalSymbol} " + "internal=${visibleScopeSymbol.isInternalSymbol} " +
"slab=${visibleScopeSymbol.scopeSymbol.slab.location.commonFriendlyName}" "slab=${visibleScopeSymbol.scopeSymbol.slabScope.slab.location.commonFriendlyName}"
) )
} }
} }

View File

@ -1,6 +1,7 @@
package gay.pizza.pork.vm package gay.pizza.pork.vm
import gay.pizza.pork.bytecode.CompiledWorld import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.bytecode.ConstantTag
class InternalMachine(val world: CompiledWorld, val handlers: List<OpHandler>) { class InternalMachine(val world: CompiledWorld, val handlers: List<OpHandler>) {
private val inlined = world.code.map { op -> private val inlined = world.code.map { op ->
@ -10,10 +11,12 @@ class InternalMachine(val world: CompiledWorld, val handlers: List<OpHandler>) {
} }
private var inst: UInt = 0u private var inst: UInt = 0u
private val stack = mutableListOf<Any>(EndOfCode) private val stack = mutableListOf<Any>()
private val locals = mutableListOf<MutableMap<UInt, Any>>( private val locals = mutableListOf<MutableMap<UInt, Any>>(
mutableMapOf() mutableMapOf()
) )
private val callStack = mutableListOf(0u)
private val returnAddressStack = mutableListOf<UInt>()
private var autoNextInst = true private var autoNextInst = true
private var exitFlag = false private var exitFlag = false
@ -36,7 +39,11 @@ class InternalMachine(val world: CompiledWorld, val handlers: List<OpHandler>) {
} }
fun loadConstant(id: UInt) { 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) { fun loadLocal(id: UInt) {
@ -48,7 +55,7 @@ class InternalMachine(val world: CompiledWorld, val handlers: List<OpHandler>) {
fun storeLocal(id: UInt) { fun storeLocal(id: UInt) {
val localSet = locals.last() val localSet = locals.last()
val value = pop() val value = popAnyValue()
localSet[id] = value localSet[id] = value
} }
@ -57,24 +64,47 @@ class InternalMachine(val world: CompiledWorld, val handlers: List<OpHandler>) {
autoNextInst = false 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) { fun push(item: Any) {
stack.add(item) stack.add(item)
} }
fun pop(): Any = stack.removeLast() fun popAnyValue(): Any = stack.removeLast()
inline fun <reified T> pop(): T = popAnyValue() as T
fun exit() { fun exit() {
exitFlag = true exitFlag = true
} }
fun reset() { fun reset() {
stack.clear() stack.clear()
stack.add(EndOfCode) callStack.clear()
callStack.add(0u)
locals.clear() locals.clear()
locals.add(mutableMapOf()) locals.add(mutableMapOf())
inst = 0u inst = 0u
exitFlag = false exitFlag = false
autoNextInst = true autoNextInst = true
} }
data object EndOfCode
} }

View File

@ -12,10 +12,18 @@ class VirtualMachine(world: CompiledWorld) : ExecutionContext {
TrueOpHandler, TrueOpHandler,
FalseOpHandler, FalseOpHandler,
ListOpHandler, ListMakeOpHandler,
ListSizeOpHandler,
IndexOpHandler,
AndOpHandler,
OrOpHandler,
NotOpHandler,
CompareEqualOpHandler, CompareEqualOpHandler,
CompareLesserEqualOpHandler, CompareLesserEqualOpHandler,
CompareGreaterEqualOpHandler,
AddOpHandler, AddOpHandler,
@ -25,13 +33,16 @@ class VirtualMachine(world: CompiledWorld) : ExecutionContext {
LoadLocalOpHandler, LoadLocalOpHandler,
StoreLocalOpHandler, StoreLocalOpHandler,
ReturnAddressOpHandler,
CallOpHandler, CallOpHandler,
RetOpHandler, ReturnOpHandler,
NativeOpHandler, NativeOpHandler,
ScopeInOpHandler, ScopeInOpHandler,
ScopeOutOpHandler ScopeOutOpHandler,
EndOpHandler
)) ))
override fun execute() { override fun execute() {

View File

@ -4,16 +4,11 @@ import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler import gay.pizza.pork.vm.OpHandler
import gay.pizza.pork.vm.VirtualMachineException
object AddOpHandler : OpHandler(Opcode.Add) { object AddOpHandler : OpHandler(Opcode.Add) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
val left = machine.pop() val left = machine.pop<Int>()
val right = machine.pop() val right = machine.pop<Int>()
if (left !is Int || right !is Int) {
throw VirtualMachineException("Bad types.")
}
machine.push(left + right) machine.push(left + right)
} }
} }

View File

@ -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<Boolean>()
val right = machine.pop<Boolean>()
machine.push(left && right)
}
}

View File

@ -8,6 +8,7 @@ import gay.pizza.pork.vm.OpHandler
object CallOpHandler : OpHandler(Opcode.Call) { object CallOpHandler : OpHandler(Opcode.Call) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
machine.setNextInst(op.args[0]) machine.setNextInst(op.args[0])
machine.pushCallStack(op.args[0])
machine.pushScope() machine.pushScope()
} }
} }

View File

@ -7,6 +7,6 @@ import gay.pizza.pork.vm.OpHandler
object CompareEqualOpHandler : OpHandler(Opcode.CompareEqual) { object CompareEqualOpHandler : OpHandler(Opcode.CompareEqual) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
machine.push(machine.pop() == machine.pop()) machine.push(machine.popAnyValue() == machine.popAnyValue())
} }
} }

View File

@ -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<Int>()
val left = machine.pop<Int>()
machine.push(left >= right)
}
}

View File

@ -4,16 +4,11 @@ import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler import gay.pizza.pork.vm.OpHandler
import gay.pizza.pork.vm.VirtualMachineException
object CompareLesserEqualOpHandler : OpHandler(Opcode.CompareLesserEqual) { object CompareLesserEqualOpHandler : OpHandler(Opcode.CompareLesserEqual) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
val right = machine.pop() val right = machine.pop<Int>()
val left = machine.pop() val left = machine.pop<Int>()
if (left !is Int || right !is Int) {
throw VirtualMachineException("Bad types.")
}
machine.push(left <= right) machine.push(left <= right)
} }
} }

View File

@ -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()
}
}

View File

@ -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<List<*>>()
val index = machine.pop<Number>().toInt()
machine.push(list[index] as Any)
}
}

View File

@ -8,11 +8,7 @@ import gay.pizza.pork.vm.VirtualMachineException
object JumpIfOpHandler : OpHandler(Opcode.JumpIf) { object JumpIfOpHandler : OpHandler(Opcode.JumpIf) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
val value = machine.pop() val value = machine.pop<Boolean>()
if (value !is Boolean) {
throw VirtualMachineException("JumpIf expects a boolean value on the stack.")
}
if (value) { if (value) {
machine.setNextInst(op.args[0]) machine.setNextInst(op.args[0])
} }

View File

@ -5,12 +5,12 @@ import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler import gay.pizza.pork.vm.OpHandler
object ListOpHandler : OpHandler(Opcode.List) { object ListMakeOpHandler : OpHandler(Opcode.ListMake) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
val count = op.args[0] val count = op.args[0]
val list = mutableListOf<Any>() val list = mutableListOf<Any>()
for (i in 1u..count) { for (i in 1u..count) {
val item = machine.pop() val item = machine.popAnyValue()
list.add(item) list.add(item)
} }
machine.push(list.reversed()) machine.push(list.reversed())

View File

@ -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<List<*>>()
machine.push(list.size)
}
}

View File

@ -10,7 +10,7 @@ object NativeOpHandler : OpHandler(Opcode.Native) {
val countOfNativeDefs = op.args[1].toInt() val countOfNativeDefs = op.args[1].toInt()
val defs = mutableListOf<Any>() val defs = mutableListOf<Any>()
for (i in 0 until countOfNativeDefs) { for (i in 0 until countOfNativeDefs) {
defs.add(String(machine.pop() as ByteArray)) defs.add(machine.pop() as String)
} }
} }
} }

View File

@ -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<Boolean>())
}
}

View File

@ -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<Boolean>()
val right = machine.pop<Boolean>()
machine.push(left || right)
}
}

View File

@ -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])
}
}

View File

@ -5,14 +5,10 @@ import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler import gay.pizza.pork.vm.OpHandler
object RetOpHandler : OpHandler(Opcode.Return) { object ReturnOpHandler : OpHandler(Opcode.Return) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
val last = machine.pop()
if (last == InternalMachine.EndOfCode) {
machine.exit()
return
}
machine.popScope() machine.popScope()
machine.setNextInst((last as Int).toUInt()) machine.armReturnAddressIfSet()
machine.popCallStack()
} }
} }