mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 13:11:32 +00:00
vm: a functional virtual machine, mostly
This commit is contained in:
@ -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
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
14
vm/src/main/kotlin/gay/pizza/pork/vm/ops/AndOpHandler.kt
Normal file
14
vm/src/main/kotlin/gay/pizza/pork/vm/ops/AndOpHandler.kt
Normal 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)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/EndOpHandler.kt
Normal file
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/EndOpHandler.kt
Normal 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()
|
||||
}
|
||||
}
|
14
vm/src/main/kotlin/gay/pizza/pork/vm/ops/IndexOpHandler.kt
Normal file
14
vm/src/main/kotlin/gay/pizza/pork/vm/ops/IndexOpHandler.kt
Normal 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)
|
||||
}
|
||||
}
|
@ -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])
|
||||
}
|
||||
|
@ -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())
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/NotOpHandler.kt
Normal file
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/NotOpHandler.kt
Normal 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>())
|
||||
}
|
||||
}
|
14
vm/src/main/kotlin/gay/pizza/pork/vm/ops/OrOpHandler.kt
Normal file
14
vm/src/main/kotlin/gay/pizza/pork/vm/ops/OrOpHandler.kt
Normal 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)
|
||||
}
|
||||
}
|
@ -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])
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user