mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 21:21:33 +00:00
vm: very basic virtual machine
This commit is contained in:
11
compiler/build.gradle.kts
Normal file
11
compiler/build.gradle.kts
Normal file
@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id("gay.pizza.pork.module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":ast"))
|
||||
api(project(":bytecode"))
|
||||
api(project(":parser"))
|
||||
api(project(":frontend"))
|
||||
implementation(project(":common"))
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.gen.Symbol
|
||||
import gay.pizza.pork.frontend.Slab
|
||||
|
||||
class CompilableSlab(val compiler: Compiler, val slab: Slab) {
|
||||
val compilableSymbols: List<CompilableSymbol> by lazy {
|
||||
slab.scope.internalSymbols.map { symbol ->
|
||||
CompilableSymbol(this, symbol)
|
||||
}
|
||||
}
|
||||
|
||||
fun compilableSymbolOf(symbol: Symbol): CompilableSymbol? = compilableSymbols.firstOrNull {
|
||||
it.scopeSymbol.symbol == symbol
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.gen.FunctionDefinition
|
||||
import gay.pizza.pork.ast.gen.LetDefinition
|
||||
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 usedSymbols: List<ScopeSymbol>
|
||||
get() = scopeSymbol.scope.usedSymbols
|
||||
|
||||
private fun compile(): List<StubOp> {
|
||||
val emitter = StubOpEmitter(compilableSlab.compiler, this)
|
||||
emitter.enter()
|
||||
val what = if (scopeSymbol.definition is FunctionDefinition) {
|
||||
val functionDefinition = scopeSymbol.definition as FunctionDefinition
|
||||
emitter.allocateOuterScope(functionDefinition)
|
||||
functionDefinition.block ?: functionDefinition.nativeFunctionDescriptor!!
|
||||
} else {
|
||||
val letDefinition = scopeSymbol.definition as LetDefinition
|
||||
letDefinition.value
|
||||
}
|
||||
emitter.visit(what)
|
||||
emitter.exit()
|
||||
return emitter.ops()
|
||||
}
|
||||
|
||||
val id: String
|
||||
get() = "${compilableSlab.slab.location.commonFriendlyName} ${scopeSymbol.symbol.id}"
|
||||
|
||||
override fun toString(): String = "${compilableSlab.slab.location.commonFriendlyName} ${scopeSymbol.symbol.id}"
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bytecode.*
|
||||
|
||||
class CompiledWorldLayout(val compiler: Compiler) : StubResolutionContext {
|
||||
private val allStubOps = mutableListOf<StubOp>()
|
||||
private val symbolTable = mutableMapOf<CompilableSymbol, SymbolInfo>()
|
||||
|
||||
fun add(symbol: CompilableSymbol) {
|
||||
val start = allStubOps.size
|
||||
val stubOps = symbol.compiledStubOps
|
||||
symbolTable[symbol] = SymbolInfo(symbol.id, start.toUInt(), stubOps.size.toUInt())
|
||||
allStubOps.addAll(stubOps)
|
||||
}
|
||||
|
||||
private fun patch(): List<Op> {
|
||||
val ops = mutableListOf<Op>()
|
||||
for (stub in allStubOps) {
|
||||
val actualArguments = stub.op.args.toMutableList()
|
||||
stub.patch(this, actualArguments)
|
||||
ops.add(Op(stub.op.code, actualArguments))
|
||||
}
|
||||
return ops
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
}
|
47
compiler/src/main/kotlin/gay/pizza/pork/compiler/Compiler.kt
Normal file
47
compiler/src/main/kotlin/gay/pizza/pork/compiler/Compiler.kt
Normal file
@ -0,0 +1,47 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bytecode.CompiledWorld
|
||||
import gay.pizza.pork.bytecode.MutableConstantPool
|
||||
import gay.pizza.pork.frontend.Slab
|
||||
import gay.pizza.pork.frontend.scope.ScopeSymbol
|
||||
|
||||
class Compiler {
|
||||
val constantPool: MutableConstantPool = MutableConstantPool()
|
||||
val compilableSlabs: ComputableState<Slab, CompilableSlab> = ComputableState { slab ->
|
||||
CompilableSlab(this, slab)
|
||||
}
|
||||
|
||||
fun resolveOrNull(scopeSymbol: ScopeSymbol): CompilableSymbol? {
|
||||
val compiledSlab = compilableSlabs.of(scopeSymbol.slabScope.slab)
|
||||
return compiledSlab.compilableSymbolOf(scopeSymbol.symbol)
|
||||
}
|
||||
|
||||
fun resolve(scopeSymbol: ScopeSymbol): CompilableSymbol = resolveOrNull(scopeSymbol) ?:
|
||||
throw RuntimeException(
|
||||
"Unable to resolve scope symbol: " +
|
||||
"${scopeSymbol.slabScope.slab.location.commonFriendlyName} ${scopeSymbol.symbol.id}")
|
||||
|
||||
fun contributeCompiledSymbols(
|
||||
into: MutableSet<CompilableSymbol>,
|
||||
symbol: ScopeSymbol,
|
||||
resolved: CompilableSymbol = resolve(symbol)
|
||||
) {
|
||||
if (!into.add(resolved)) {
|
||||
return
|
||||
}
|
||||
|
||||
for (used in resolved.usedSymbols) {
|
||||
contributeCompiledSymbols(into, used)
|
||||
}
|
||||
}
|
||||
|
||||
fun compile(entryPointSymbol: CompilableSymbol): CompiledWorld {
|
||||
val usedSymbolSet = mutableSetOf<CompilableSymbol>()
|
||||
contributeCompiledSymbols(usedSymbolSet, entryPointSymbol.scopeSymbol, entryPointSymbol)
|
||||
val layout = CompiledWorldLayout(this)
|
||||
for (used in usedSymbolSet) {
|
||||
layout.add(used)
|
||||
}
|
||||
return layout.layoutCompiledWorld()
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
class ComputableState<X, T>(val computation: (X) -> T) {
|
||||
private val state = StoredState<X, T>()
|
||||
|
||||
fun of(key: X): T = state.computeIfAbsent(key, computation)
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bytecode.MutableRel
|
||||
|
||||
class LoopState(
|
||||
val startOfLoop: UInt,
|
||||
val exitJumpTarget: MutableRel,
|
||||
val body: MutableRel,
|
||||
val scopeDepth: Int,
|
||||
val enclosing: LoopState? = null
|
||||
)
|
@ -0,0 +1,12 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bytecode.MutableRel
|
||||
import gay.pizza.pork.bytecode.Op
|
||||
|
||||
class PatchRelOp(op: Op, val index: Int, val symbol: CompilableSymbol, val rel: MutableRel) : StubOp(op) {
|
||||
override fun patch(context: StubResolutionContext, arguments: MutableList<UInt>) {
|
||||
arguments[index] = context.resolveJumpTarget(symbol) + rel.rel
|
||||
}
|
||||
|
||||
override fun toString(): String = "PatchRelOp(${op}, ${index}, ${symbol}, ${rel})"
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bytecode.Op
|
||||
|
||||
class PatchSymOp(op: Op, val patches: Map<Int, CompilableSymbol>) : StubOp(op) {
|
||||
override fun patch(context: StubResolutionContext, arguments: MutableList<UInt>) {
|
||||
for ((index, symbol) in patches) {
|
||||
arguments[index] = context.resolveJumpTarget(symbol)
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String = "PatchSymOp(${op}, ${patches})"
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bytecode.Op
|
||||
|
||||
class StaticOp(op: Op) : StubOp(op) {
|
||||
override fun toString(): String = "StaticOp(${op})"
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
class StoredState<X, T> {
|
||||
private val state = mutableMapOf<X, T>()
|
||||
|
||||
fun computeIfAbsent(key: X, computation: (X) -> T): T = state.computeIfAbsent(key, computation)
|
||||
fun of(key: X): T? = state[key]
|
||||
fun put(key: X, value: T) {
|
||||
state[key] = value
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.bytecode.Op
|
||||
|
||||
abstract class StubOp(val op: Op) {
|
||||
open fun patch(context: StubResolutionContext, arguments: MutableList<UInt>) {}
|
||||
}
|
@ -0,0 +1,336 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.FunctionLevelVisitor
|
||||
import gay.pizza.pork.ast.gen.*
|
||||
import gay.pizza.pork.bytecode.MutableRel
|
||||
import gay.pizza.pork.bytecode.Op
|
||||
import gay.pizza.pork.bytecode.Opcode
|
||||
|
||||
class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : FunctionLevelVisitor<Unit>() {
|
||||
private val ops = mutableListOf<StubOp>()
|
||||
|
||||
private var loopState: LoopState? = null
|
||||
private val localVariables = mutableListOf<MutableList<StubVar>>()
|
||||
private var localVarIndex = 0u
|
||||
|
||||
private val requiredLoopState: LoopState
|
||||
get() {
|
||||
if (loopState != null) {
|
||||
return loopState!!
|
||||
}
|
||||
throw RuntimeException("loopState expected but was not found.")
|
||||
}
|
||||
|
||||
private fun allocateLocalVariable(symbol: Symbol): StubVar {
|
||||
val scope = localVariables.last()
|
||||
val variable = StubVar(localVarIndex++, symbol)
|
||||
scope.add(variable)
|
||||
return variable
|
||||
}
|
||||
|
||||
private fun resolveSymbol(symbol: Symbol): CallOrStubVar {
|
||||
for (scope in localVariables.reversed()) {
|
||||
val found = scope.firstOrNull { it.symbol == symbol }
|
||||
if (found != null) {
|
||||
return CallOrStubVar(stubVar = found)
|
||||
}
|
||||
}
|
||||
val found = this.symbol.compilableSlab.compilableSymbolOf(symbol)
|
||||
if (found != null) {
|
||||
return CallOrStubVar(call = found)
|
||||
}
|
||||
throw RuntimeException("Unable to resolve symbol: ${symbol.id}")
|
||||
}
|
||||
|
||||
private fun pushScope() {
|
||||
emit(Opcode.ScopeIn)
|
||||
localVariables.add(mutableListOf())
|
||||
}
|
||||
|
||||
private fun popScope() {
|
||||
emit(Opcode.ScopeOut)
|
||||
localVariables.removeLast()
|
||||
}
|
||||
|
||||
fun enter() {
|
||||
pushScope()
|
||||
}
|
||||
|
||||
fun allocateOuterScope(definition: FunctionDefinition) {
|
||||
val allNormalArguments = definition.arguments.takeWhile { !it.multiple }
|
||||
val varArgument = definition.arguments.firstOrNull { it.multiple }
|
||||
|
||||
for (arg in allNormalArguments.reversed()) {
|
||||
val functionLocal = allocateLocalVariable(arg.symbol)
|
||||
emit(Opcode.StoreLocal, listOf(functionLocal.index))
|
||||
}
|
||||
|
||||
if (varArgument != null) {
|
||||
val functionLocal = allocateLocalVariable(varArgument.symbol)
|
||||
emit(Opcode.StoreLocal, listOf(functionLocal.index))
|
||||
}
|
||||
}
|
||||
|
||||
fun exit() {
|
||||
popScope()
|
||||
emit(Opcode.Return)
|
||||
}
|
||||
|
||||
override fun visitBlock(node: Block) {
|
||||
pushScope()
|
||||
node.visitChildren(this)
|
||||
popScope()
|
||||
}
|
||||
|
||||
override fun visitBooleanLiteral(node: BooleanLiteral) {
|
||||
emit(if (node.value) Opcode.True else Opcode.False)
|
||||
}
|
||||
|
||||
override fun visitBreak(node: Break) {
|
||||
patch(Opcode.Jump, listOf(0u), 0, symbol, requiredLoopState.exitJumpTarget)
|
||||
}
|
||||
|
||||
override fun visitContinue(node: Continue) {
|
||||
patch(Opcode.Jump, listOf(0u), 0, symbol, requiredLoopState.startOfLoop)
|
||||
}
|
||||
|
||||
override fun visitDoubleLiteral(node: DoubleLiteral) {
|
||||
emit(Opcode.Integer, listOf(node.value.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitForIn(node: ForIn) {
|
||||
TODO("ForIn is currently unsupported")
|
||||
}
|
||||
|
||||
override fun visitFunctionCall(node: FunctionCall) {
|
||||
val targetScopeSymbol = symbol.scopeSymbol.scope.resolve(node.symbol) ?:
|
||||
throw RuntimeException("Unable to resolve symbol: ${node.symbol.id}")
|
||||
val targetSymbol = compiler.resolve(targetScopeSymbol)
|
||||
val functionDefinition = targetSymbol.scopeSymbol.definition as FunctionDefinition
|
||||
val retRel = MutableRel(0u)
|
||||
patch(Opcode.Integer, listOf(0u), 0, symbol, retRel)
|
||||
|
||||
val normalArguments = mutableListOf<Expression>()
|
||||
var variableArguments: List<Expression>? = null
|
||||
for ((index, item) in functionDefinition.arguments.zip(node.arguments).withIndex()) {
|
||||
val (spec, value) = item
|
||||
if (spec.multiple) {
|
||||
val remaining = node.arguments.drop(index)
|
||||
variableArguments = remaining
|
||||
break
|
||||
} else {
|
||||
normalArguments.add(value)
|
||||
}
|
||||
}
|
||||
|
||||
if (variableArguments != null) {
|
||||
for (item in variableArguments.reversed()) {
|
||||
item.visit(this)
|
||||
}
|
||||
emit(Opcode.List, listOf(variableArguments.size.toUInt()))
|
||||
}
|
||||
|
||||
for (item in normalArguments.reversed()) {
|
||||
visit(item)
|
||||
}
|
||||
|
||||
retRel.rel = (ops.size + 1).toUInt()
|
||||
patch(Opcode.Call, listOf(0u), mapOf(0 to targetSymbol))
|
||||
}
|
||||
|
||||
override fun visitIf(node: If) {
|
||||
val thenRel = MutableRel(0u)
|
||||
val endRel = MutableRel(0u)
|
||||
node.condition.visit(this)
|
||||
patch(Opcode.JumpIf, listOf(0u), 0, symbol, thenRel)
|
||||
node.elseBlock?.visit(this)
|
||||
patch(Opcode.Jump, listOf(0u), 0, symbol, endRel)
|
||||
thenRel.rel = ops.size.toUInt()
|
||||
node.thenBlock.visit(this)
|
||||
endRel.rel = ops.size.toUInt()
|
||||
}
|
||||
|
||||
override fun visitIndexedBy(node: IndexedBy) {
|
||||
node.expression.visit(this)
|
||||
node.index.visit(this)
|
||||
emit(Opcode.Index)
|
||||
}
|
||||
|
||||
override fun visitInfixOperation(node: InfixOperation) {
|
||||
node.left.visit(this)
|
||||
node.right.visit(this)
|
||||
when (node.op) {
|
||||
InfixOperator.Plus -> emit(Opcode.Add)
|
||||
InfixOperator.Minus -> emit(Opcode.Subtract)
|
||||
InfixOperator.Multiply -> emit(Opcode.Multiply)
|
||||
InfixOperator.Divide -> emit(Opcode.Divide)
|
||||
InfixOperator.Equals -> emit(Opcode.CompareEqual)
|
||||
InfixOperator.NotEquals -> {
|
||||
emit(Opcode.CompareEqual)
|
||||
emit(Opcode.Not)
|
||||
}
|
||||
InfixOperator.EuclideanModulo -> emit(Opcode.EuclideanModulo)
|
||||
InfixOperator.Remainder -> emit(Opcode.Remainder)
|
||||
InfixOperator.Lesser -> emit(Opcode.CompareLesser)
|
||||
InfixOperator.Greater -> emit(Opcode.CompareGreater)
|
||||
InfixOperator.GreaterEqual -> emit(Opcode.CompareGreaterEqual)
|
||||
InfixOperator.LesserEqual -> emit(Opcode.CompareLesserEqual)
|
||||
InfixOperator.BooleanAnd -> emit(Opcode.And)
|
||||
InfixOperator.BooleanOr -> emit(Opcode.Or)
|
||||
InfixOperator.BinaryAnd -> emit(Opcode.BinaryAnd)
|
||||
InfixOperator.BinaryOr -> emit(Opcode.BinaryOr)
|
||||
InfixOperator.BinaryExclusiveOr -> emit(Opcode.BinaryXor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitIntegerLiteral(node: IntegerLiteral) {
|
||||
emit(Opcode.Integer, listOf(node.value.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitLetAssignment(node: LetAssignment) {
|
||||
val variable = allocateLocalVariable(node.symbol)
|
||||
node.value.visit(this)
|
||||
emit(Opcode.StoreLocal, listOf(variable.index))
|
||||
}
|
||||
|
||||
override fun visitListLiteral(node: ListLiteral) {
|
||||
val count = node.items.size
|
||||
for (item in node.items) {
|
||||
item.visit(this)
|
||||
}
|
||||
emit(Opcode.List, listOf(count.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitLongLiteral(node: LongLiteral) {
|
||||
emit(Opcode.Integer, listOf(node.value.toUInt()))
|
||||
}
|
||||
|
||||
override fun visitNoneLiteral(node: NoneLiteral) {
|
||||
emit(Opcode.None)
|
||||
}
|
||||
|
||||
override fun visitParentheses(node: Parentheses) {
|
||||
node.expression.visit(this)
|
||||
}
|
||||
|
||||
override fun visitPrefixOperation(node: PrefixOperation) {
|
||||
node.expression.visit(this)
|
||||
when (node.op) {
|
||||
PrefixOperator.BooleanNot -> emit(Opcode.Not)
|
||||
PrefixOperator.UnaryPlus -> emit(Opcode.UnaryPlus)
|
||||
PrefixOperator.UnaryMinus -> emit(Opcode.UnaryMinus)
|
||||
PrefixOperator.BinaryNot -> emit(Opcode.BinaryNot)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSetAssignment(node: SetAssignment) {
|
||||
val stubVarOrCall = resolveSymbol(node.symbol)
|
||||
if (stubVarOrCall.stubVar == null) {
|
||||
throw RuntimeException("Invalid set assignment.")
|
||||
}
|
||||
node.value.visit(this)
|
||||
emit(Opcode.StoreLocal, listOf(stubVarOrCall.stubVar.index))
|
||||
}
|
||||
|
||||
override fun visitStringLiteral(node: StringLiteral) {
|
||||
val bytes = node.text.toByteArray()
|
||||
val constant = compiler.constantPool.assign(bytes)
|
||||
emit(Opcode.Constant, listOf(constant))
|
||||
}
|
||||
|
||||
override fun visitSuffixOperation(node: SuffixOperation) {
|
||||
val stubVarOrCall = resolveSymbol(node.reference.symbol)
|
||||
if (stubVarOrCall.stubVar == null) {
|
||||
throw RuntimeException("Invalid suffix operation.")
|
||||
}
|
||||
load(stubVarOrCall)
|
||||
when (node.op) {
|
||||
SuffixOperator.Increment -> {
|
||||
emit(Opcode.Integer, listOf(1u))
|
||||
emit(Opcode.Add, emptyList())
|
||||
emit(Opcode.StoreLocal, listOf(stubVarOrCall.stubVar.index))
|
||||
}
|
||||
SuffixOperator.Decrement -> {
|
||||
emit(Opcode.Integer, listOf(1u))
|
||||
emit(Opcode.Subtract, emptyList())
|
||||
emit(Opcode.StoreLocal, listOf(stubVarOrCall.stubVar.index))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSymbolReference(node: SymbolReference) {
|
||||
val variable = resolveSymbol(node.symbol)
|
||||
load(variable)
|
||||
}
|
||||
|
||||
override fun visitVarAssignment(node: VarAssignment) {
|
||||
val variable = allocateLocalVariable(node.symbol)
|
||||
node.value.visit(this)
|
||||
emit(Opcode.StoreLocal, listOf(variable.index))
|
||||
}
|
||||
|
||||
override fun visitWhile(node: While) {
|
||||
val startOfBody = MutableRel(0u)
|
||||
val endOfLoop = MutableRel(0u)
|
||||
val currentLoopState = LoopState(
|
||||
startOfLoop = ops.size.toUInt(),
|
||||
exitJumpTarget = endOfLoop,
|
||||
body = startOfBody,
|
||||
scopeDepth = (loopState?.scopeDepth ?: 0) + 1,
|
||||
enclosing = loopState
|
||||
)
|
||||
loopState = currentLoopState
|
||||
node.condition.visit(this)
|
||||
patch(Opcode.JumpIf, listOf(0u), 0, symbol, startOfBody)
|
||||
patch(Opcode.Jump, listOf(0u), 0, symbol, endOfLoop)
|
||||
startOfBody.rel = ops.size.toUInt()
|
||||
node.block.visit(this)
|
||||
patch(Opcode.Jump, listOf(0u), 0, symbol, currentLoopState.startOfLoop)
|
||||
endOfLoop.rel = ops.size.toUInt()
|
||||
}
|
||||
|
||||
override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor) {
|
||||
for (def in node.definitions) {
|
||||
val defConstant = compiler.constantPool.assign(def.text.toByteArray())
|
||||
emit(Opcode.Constant, listOf(defConstant))
|
||||
}
|
||||
val formConstant = compiler.constantPool.assign(node.form.id.toByteArray())
|
||||
emit(Opcode.Native, listOf(formConstant, node.definitions.size.toUInt()))
|
||||
}
|
||||
|
||||
private fun emit(code: Opcode) {
|
||||
emit(code, emptyList())
|
||||
}
|
||||
|
||||
private fun emit(code: Opcode, arguments: List<UInt>) {
|
||||
ops.add(StaticOp(Op(code, arguments)))
|
||||
}
|
||||
|
||||
private fun patch(code: Opcode, arguments: List<UInt>, index: Int, symbol: CompilableSymbol, rel: MutableRel) {
|
||||
ops.add(PatchRelOp(Op(code, arguments), index, symbol, rel))
|
||||
}
|
||||
|
||||
private fun patch(code: Opcode, arguments: List<UInt>, index: Int, symbol: CompilableSymbol, rel: UInt) {
|
||||
ops.add(PatchRelOp(Op(code, arguments), index, symbol, MutableRel(rel)))
|
||||
}
|
||||
|
||||
private fun patch(code: Opcode, arguments: List<UInt>, patches: Map<Int, CompilableSymbol>) {
|
||||
ops.add(PatchSymOp(Op(code, arguments), patches))
|
||||
}
|
||||
|
||||
fun ops(): List<StubOp> = ops
|
||||
|
||||
private fun load(callOrStubVar: CallOrStubVar) {
|
||||
if (callOrStubVar.stubVar != null) {
|
||||
emit(Opcode.LoadLocal, listOf(callOrStubVar.stubVar.index))
|
||||
} else {
|
||||
emit(Opcode.Integer, listOf(ops.size.toUInt() + 2u))
|
||||
patch(Opcode.Call, listOf(0u), mapOf(0 to callOrStubVar.call!!))
|
||||
}
|
||||
}
|
||||
|
||||
private class CallOrStubVar(
|
||||
val call: CompilableSymbol? = null,
|
||||
val stubVar: StubVar? = null
|
||||
)
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
interface StubResolutionContext {
|
||||
fun resolveJumpTarget(symbol: CompilableSymbol): UInt
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package gay.pizza.pork.compiler
|
||||
|
||||
import gay.pizza.pork.ast.gen.Symbol
|
||||
|
||||
class StubVar(
|
||||
val index: UInt,
|
||||
val symbol: Symbol
|
||||
)
|
Reference in New Issue
Block a user