mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 21:21:33 +00:00
vm: a functional virtual machine, mostly
This commit is contained in:
@ -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>
|
||||||
)
|
)
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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>)
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
package gay.pizza.pork.bytecode
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
enum class ConstantTag {
|
||||||
|
String
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package gay.pizza.pork.bytecode
|
||||||
|
|
||||||
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class OpAnnotation(val inst: UInt, val text: String)
|
@ -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),
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
package gay.pizza.pork.compiler
|
||||||
|
|
||||||
|
class CompiledSymbolResult(val ops: List<StubOp>, val annotations: List<StubOpAnnotation>)
|
@ -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())
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
package gay.pizza.pork.compiler
|
||||||
|
|
||||||
|
data class StubOpAnnotation(val symbol: CompilableSymbol, val rel: UInt, val text: String)
|
@ -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()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
)
|
)
|
||||||
|
@ -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)
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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}"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
}
|
||||||
|
@ -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() {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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) {
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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.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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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) {
|
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])
|
||||||
}
|
}
|
||||||
|
@ -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())
|
@ -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 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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.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()
|
||||||
}
|
}
|
||||||
}
|
}
|
Reference in New Issue
Block a user