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,6 +6,7 @@ import gay.pizza.pork.bytecode.Opcode
|
||||
|
||||
class CodeBuilder(val symbol: CompilableSymbol) {
|
||||
private val ops = mutableListOf<StubOp>()
|
||||
private val annotations = mutableListOf<StubOpAnnotation>()
|
||||
|
||||
val localState: LocalState = LocalState(symbol)
|
||||
|
||||
@ -39,5 +40,9 @@ class CodeBuilder(val symbol: CompilableSymbol) {
|
||||
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
|
||||
|
||||
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>
|
||||
get() = scopeSymbol.scope.usedSymbols
|
||||
|
||||
private fun compile(): List<StubOp> {
|
||||
private fun compile(): CompiledSymbolResult {
|
||||
val emitter = StubOpEmitter(compilableSlab.compiler, this)
|
||||
emitter.enter()
|
||||
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 {
|
||||
private val allStubOps = mutableListOf<StubOp>()
|
||||
private val allStubAnnotations = mutableListOf<StubOpAnnotation>()
|
||||
private val symbolTable = mutableMapOf<CompilableSymbol, SymbolInfo>()
|
||||
|
||||
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<Op> {
|
||||
@ -23,16 +26,24 @@ class CompiledWorldLayout(val compiler: Compiler) : StubResolutionContext {
|
||||
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 {
|
||||
return symbolTable[symbol]?.offset ?:
|
||||
throw RuntimeException("Unable to resolve jump target: ${symbol.scopeSymbol.symbol.id}")
|
||||
}
|
||||
|
||||
fun layoutCompiledWorld(): CompiledWorld {
|
||||
val constantPool = mutableListOf<ByteArray>()
|
||||
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()
|
||||
)
|
||||
}
|
||||
|
@ -42,6 +42,6 @@ class Compiler {
|
||||
for (used in usedSymbolSet) {
|
||||
layout.add(used)
|
||||
}
|
||||
return layout.layoutCompiledWorld()
|
||||
return layout.build()
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
|
@ -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.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<Unit>() {
|
||||
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<Expression>()
|
||||
var variableArguments: List<Expression>? = 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()))
|
||||
}
|
||||
|
||||
|
@ -4,5 +4,5 @@ import gay.pizza.pork.ast.gen.Symbol
|
||||
|
||||
class StubVar(
|
||||
val index: UInt,
|
||||
val symbol: Symbol
|
||||
val symbol: Symbol? = null
|
||||
)
|
||||
|
Reference in New Issue
Block a user