vm: a functional virtual machine, mostly

This commit is contained in:
2023-11-21 04:04:44 -08:00
parent 4c50d48e1e
commit 5540918e7c
39 changed files with 323 additions and 84 deletions

View File

@ -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<OpHandler>) {
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 val stack = mutableListOf<Any>(EndOfCode)
private val stack = mutableListOf<Any>()
private val locals = mutableListOf<MutableMap<UInt, Any>>(
mutableMapOf()
)
private val callStack = mutableListOf(0u)
private val returnAddressStack = mutableListOf<UInt>()
private var autoNextInst = true
private var exitFlag = false
@ -36,7 +39,11 @@ class InternalMachine(val world: CompiledWorld, val handlers: List<OpHandler>) {
}
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<OpHandler>) {
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<OpHandler>) {
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 <reified T> 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
}

View File

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

View File

@ -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<Int>()
val right = machine.pop<Int>()
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) {
override fun handle(machine: InternalMachine, op: Op) {
machine.setNextInst(op.args[0])
machine.pushCallStack(op.args[0])
machine.pushScope()
}
}

View File

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

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.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<Int>()
val left = machine.pop<Int>()
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) {
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<Boolean>()
if (value) {
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.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<Any>()
for (i in 1u..count) {
val item = machine.pop()
val item = machine.popAnyValue()
list.add(item)
}
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 defs = mutableListOf<Any>()
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.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()
}
}