vm: very basic virtual machine

This commit is contained in:
Alex Zenla 2023-11-14 23:44:10 -08:00
parent 8c48c93663
commit 041848c14e
Signed by: alex
GPG Key ID: C0780728420EBFE5
92 changed files with 1652 additions and 243 deletions

View File

@ -0,0 +1,42 @@
package gay.pizza.pork.ast
import gay.pizza.pork.ast.gen.*
abstract class FunctionLevelVisitor<T> : NodeVisitor<T> {
override fun visitForInItem(node: ForInItem): T =
throw RuntimeException("Visiting ForInItem is not supported.")
override fun visitSymbol(node: Symbol): T =
throw RuntimeException("Visiting Symbol is not supported.")
override fun visitLetDefinition(node: LetDefinition): T {
topLevelUsedError("LetDefinition")
}
override fun visitArgumentSpec(node: ArgumentSpec): T =
throw RuntimeException("Visiting ArgumentSpec is not supported.")
override fun visitFunctionDefinition(node: FunctionDefinition): T {
topLevelUsedError("FunctionDefinition")
}
override fun visitImportDeclaration(node: ImportDeclaration): T {
topLevelUsedError("ImportDeclaration")
}
override fun visitImportPath(node: ImportPath): T {
topLevelUsedError("ImportPath")
}
override fun visitCompilationUnit(node: CompilationUnit): T {
topLevelUsedError("CompilationUnit")
}
override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor): T {
topLevelUsedError("NativeFunctionDescriptor")
}
private fun topLevelUsedError(name: String): Nothing {
throw RuntimeException("$name cannot be visited in a FunctionVisitor.")
}
}

View File

@ -0,0 +1,7 @@
plugins {
id("gay.pizza.pork.module")
}
dependencies {
implementation(project(":common"))
}

View File

@ -0,0 +1,10 @@
package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable
@Serializable
data class CompiledWorld(
val constantPool: ConstantPool,
val symbolTable: SymbolTable,
val code: List<Op>
)

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.bytecode
class Constant(val id: UInt, val value: ByteArray)

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable
@Serializable
data class ConstantPool(val constants: List<ByteArray>)

View File

@ -0,0 +1,18 @@
package gay.pizza.pork.bytecode
class MutableConstantPool {
private val pool = mutableListOf<Constant>()
fun assign(content: ByteArray): UInt {
for (constant in pool) {
if (constant.value.contentEquals(content)) {
return constant.id
}
}
val id = pool.size.toUInt()
pool.add(Constant(id, content))
return id
}
fun all(): List<Constant> = pool
}

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.bytecode
class MutableRel(var rel: UInt)

View File

@ -0,0 +1,6 @@
package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable
@Serializable
data class Op(val code: Opcode, val args: List<UInt>)

View File

@ -0,0 +1,43 @@
package gay.pizza.pork.bytecode
enum class Opcode(val id: UByte) {
Constant(1u),
None(2u),
False(3u),
True(4u),
Pop(5u),
Jump(6u),
JumpIf(7u),
Not(8u),
UnaryPlus(9u),
UnaryMinus(10u),
BinaryNot(11u),
And(20u),
Native(24u),
Return(10u),
StoreLocal(16u),
LoadLocal(17u),
Add(18u),
Subtract(19u),
Multiply(20u),
Divide(21u),
CompareEqual(22u),
CompareLesser(23u),
CompareGreater(24u),
CompareLesserEqual(25u),
CompareGreaterEqual(26u),
Or(27u),
BinaryAnd(28u),
BinaryOr(29u),
BinaryXor(30u),
List(31u),
Integer(32u),
Double(33u),
Call(34u),
EuclideanModulo(35u),
Remainder(36u),
Index(37u),
ScopeIn(38u),
ScopeOut(39u),
End(255u),
}

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.bytecode
class Ops(val ops: List<Op>)

View File

@ -0,0 +1,10 @@
package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable
@Serializable
data class SymbolInfo(
val id: String,
val offset: UInt,
val size: UInt
)

View File

@ -0,0 +1,8 @@
package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable
@Serializable
data class SymbolTable(
val symbols: List<SymbolInfo>
)

View File

@ -0,0 +1,40 @@
package gay.pizza.pork.common
object ByteRepresentation {
fun encode(value: Int): ByteArray {
val buffer = ByteArray(4)
encode(value, buffer, 0)
return buffer
}
fun encode(value: Long): ByteArray {
val buffer = ByteArray(8)
encode(value, buffer, 0)
return buffer
}
fun encode(value: Double): ByteArray =
encode(value.toRawBits())
fun encode(value: Int, buffer: ByteArray, offset: Int) {
buffer[offset + 0] = (value shr 0).toByte()
buffer[offset + 1] = (value shr 8).toByte()
buffer[offset + 2] = (value shr 16).toByte()
buffer[offset + 3] = (value shr 24).toByte()
}
fun encode(value: Long, buffer: ByteArray, offset: Int) {
buffer[offset + 0] = (value shr 0).toByte()
buffer[offset + 1] = (value shr 8).toByte()
buffer[offset + 2] = (value shr 16).toByte()
buffer[offset + 3] = (value shr 24).toByte()
buffer[offset + 4] = (value shr 32).toByte()
buffer[offset + 5] = (value shr 40).toByte()
buffer[offset + 6] = (value shr 48).toByte()
buffer[offset + 7] = (value shr 56).toByte()
}
fun encode(value: Double, buffer: ByteArray, offset: Int) {
encode(value.toRawBits(), buffer, offset)
}
}

11
compiler/build.gradle.kts Normal file
View 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"))
}

View File

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

View File

@ -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}"
}

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.compiler
interface StubResolutionContext {
fun resolveJumpTarget(symbol: CompilableSymbol): UInt
}

View File

@ -0,0 +1,8 @@
package gay.pizza.pork.compiler
import gay.pizza.pork.ast.gen.Symbol
class StubVar(
val index: UInt,
val symbol: Symbol
)

View File

@ -4,6 +4,7 @@ plugins {
dependencies {
api(project(":ast"))
api(project(":execution"))
api(project(":frontend"))
implementation(project(":common"))

View File

@ -1,78 +0,0 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.*
import gay.pizza.pork.frontend.ImportLocator
class CompilationUnitContext(
val compilationUnit: CompilationUnit,
val evaluator: Evaluator,
rootScope: Scope,
val name: String = "unknown"
) {
val internalScope = rootScope.fork("internal $name")
val externalScope = rootScope.fork("external $name")
private var initialized = false
fun initIfNeeded() {
if (initialized) {
return
}
initialized = true
processAllImports()
processAllDefinitions()
}
private fun processAllDefinitions() {
for (definition in compilationUnit.definitions) {
processDefinition(definition)
}
}
private fun processDefinition(definition: Definition) {
val internalValue = definitionValue(definition)
internalScope.define(definition.symbol.id, internalValue)
if (definition.modifiers.export) {
externalScope.define(definition.symbol.id, internalValue)
}
}
private fun definitionValue(definition: Definition): Any = when (definition) {
is FunctionDefinition -> FunctionContext(this, definition)
is LetDefinition -> {
EvaluationVisitor(internalScope.fork("let ${definition.symbol.id}"), CallStack())
.visit(definition.value)
}
}
private fun processAllImports() {
processPreludeImport()
val imports = compilationUnit.declarations.filterIsInstance<ImportDeclaration>()
for (import in imports) {
processImport(import)
}
}
private fun processImport(import: ImportDeclaration) {
val importPath = import.path.components.joinToString("/") { it.id } + ".pork"
val importLocator = ImportLocator(import.form.id, importPath)
val evaluationContext = evaluator.context(importLocator)
internalScope.inherit(evaluationContext.externalScope)
}
private fun processPreludeImport() {
processImport(preludeImport)
}
companion object {
private val preludeImport = ImportDeclaration(
Symbol("std"),
ImportPath(
listOf(
Symbol("lang"),
Symbol("prelude")
)
)
)
}
}

View File

@ -1,10 +1,11 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.FunctionLevelVisitor
import gay.pizza.pork.ast.gen.*
import kotlin.math.abs
@Suppress("JavaIoSerializableObjectMustHaveReadResolve")
class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
class EvaluationVisitor(root: Scope, val stack: CallStack) : FunctionLevelVisitor<Any>() {
private var currentScope: Scope = root
override fun visitIntegerLiteral(node: IntegerLiteral): Any = node.value
@ -66,10 +67,6 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
return value
}
override fun visitLetDefinition(node: LetDefinition): Any {
topLevelUsedError("LetDefinition", "CompilationUnitContext")
}
override fun visitSymbolReference(node: SymbolReference): Any =
currentScope.value(node.symbol.id)
@ -383,18 +380,6 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
}
}
override fun visitFunctionDefinition(node: FunctionDefinition): Any {
topLevelUsedError("FunctionDefinition", "FunctionContext")
}
override fun visitImportDeclaration(node: ImportDeclaration): Any {
topLevelUsedError("ImportDeclaration", "CompilationUnitContext")
}
override fun visitImportPath(node: ImportPath): Any {
topLevelUsedError("ImportPath", "CompilationUnitContext")
}
override fun visitIndexedBy(node: IndexedBy): Any {
val value = node.expression.visit(this)
val index = node.index.visit(this)
@ -410,14 +395,6 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
throw RuntimeException("Failed to index '${value}' by '${index}': Unsupported types used.")
}
override fun visitCompilationUnit(node: CompilationUnit): Any {
topLevelUsedError("CompilationUnit", "CompilationUnitContext")
}
override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor): Any {
topLevelUsedError("NativeFunctionDescriptor", "FunctionContext")
}
override fun visitNoneLiteral(node: NoneLiteral): Any = None
override fun visitContinue(node: Continue): Any = ContinueMarker
@ -445,13 +422,6 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
throw RuntimeException("Can't perform $operation between floating point types")
}
private fun topLevelUsedError(name: String, alternative: String): Nothing {
throw RuntimeException(
"$name cannot be visited in an EvaluationVisitor. " +
"Utilize an $alternative instead."
)
}
private object BreakMarker : RuntimeException("Break Marker")
private object ContinueMarker: RuntimeException("Continue Marker")
}

View File

@ -1,25 +1,33 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.frontend.ImportLocator
import gay.pizza.pork.frontend.Slab
import gay.pizza.pork.frontend.World
class Evaluator(val world: World, val scope: Scope) {
private val contexts = mutableMapOf<String, CompilationUnitContext>()
class Evaluator(val world: World) : ExecutionContextProvider {
private val scope = Scope.root()
private val contexts = mutableMapOf<Slab, SlabContext>()
private val nativeProviders = mutableMapOf<String, NativeProvider>()
fun evaluate(locator: ImportLocator): Scope =
context(locator).externalScope
fun context(locator: ImportLocator): CompilationUnitContext {
val unit = world.load(locator)
val identity = world.stableIdentity(locator)
val context = contexts.computeIfAbsent(identity) {
CompilationUnitContext(unit, this, scope, name = "${locator.form} ${locator.path}")
}
context.initIfNeeded()
return context
fun evaluate(locator: ImportLocator): Scope {
val slabContext = context(locator)
slabContext.finalizeScope()
return slabContext.externalScope
}
fun context(slab: Slab): SlabContext {
val slabContext = contexts.computeIfAbsent(slab) {
SlabContext(slab, this, scope)
}
slabContext.ensureImportedContextsExist()
return slabContext
}
fun context(locator: ImportLocator): SlabContext = context(world.load(locator))
fun nativeFunctionProvider(form: String): NativeProvider {
return nativeProviders[form] ?:
throw RuntimeException("Unknown native function form: $form")
@ -28,4 +36,10 @@ class Evaluator(val world: World, val scope: Scope) {
fun addNativeProvider(form: String, nativeProvider: NativeProvider) {
nativeProviders[form] = nativeProvider
}
override fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol): ExecutionContext {
val slab = context(importLocator)
slab.finalizeScope()
return EvaluatorExecutionContext(this, slab, entryPointSymbol)
}
}

View File

@ -0,0 +1,18 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.execution.ExecutionContext
class EvaluatorExecutionContext(val evaluator: Evaluator, val slab: SlabContext, val entryPointSymbol: Symbol) : ExecutionContext {
private val function: CallableFunction by lazy {
val value = slab.externalScope.value(entryPointSymbol.id)
if (value !is CallableFunction) {
throw RuntimeException("Symbol '${entryPointSymbol.id}' resolves to a non-function.")
}
value
}
override fun execute() {
function.call(emptyList(), CallStack())
}
}

View File

@ -2,16 +2,16 @@ package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.FunctionDefinition
class FunctionContext(val compilationUnitContext: CompilationUnitContext, val node: FunctionDefinition) : CallableFunction {
val name: String = "${compilationUnitContext.name} ${node.symbol.id}"
class FunctionContext(val slabContext: SlabContext, val node: FunctionDefinition) : CallableFunction {
val name: String by lazy { "${slabContext.slab.location.commonFriendlyName} ${node.symbol.id}" }
private fun resolveMaybeNative(): CallableFunction? = if (node.nativeFunctionDescriptor == null) {
null
} else {
val native = node.nativeFunctionDescriptor!!
val nativeFunctionProvider =
compilationUnitContext.evaluator.nativeFunctionProvider(native.form.id)
nativeFunctionProvider.provideNativeFunction(native.definitions.map { it.text }, node.arguments, compilationUnitContext)
slabContext.evaluator.nativeFunctionProvider(native.form.id)
nativeFunctionProvider.provideNativeFunction(native.definitions.map { it.text }, node.arguments, slabContext)
}
private val nativeCached by lazy { resolveMaybeNative() }
@ -21,7 +21,7 @@ class FunctionContext(val compilationUnitContext: CompilationUnitContext, val no
return nativeCached!!.call(arguments, stack)
}
val scope = compilationUnitContext.internalScope.fork(node.symbol.id)
val scope = slabContext.internalScope.fork(node.symbol.id)
for ((index, spec) in node.arguments.withIndex()) {
if (spec.multiple) {
val list = arguments.subList(index, arguments.size - 1)

View File

@ -18,7 +18,7 @@ class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
override fun provideNativeFunction(
definitions: List<String>,
arguments: List<ArgumentSpec>,
inside: CompilationUnitContext
inside: SlabContext
): CallableFunction {
val definition = definitions[0]
return functions[definition] ?: throw RuntimeException("Unknown Internal Function: $definition")

View File

@ -3,5 +3,5 @@ package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.ArgumentSpec
interface NativeProvider {
fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>, inside: CompilationUnitContext): CallableFunction
fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>, inside: SlabContext): CallableFunction
}

View File

@ -0,0 +1,62 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.Definition
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.Slab
class SlabContext(val slab: Slab, val evaluator: Evaluator, rootScope: Scope) {
val internalScope = rootScope.fork("internal ${slab.location.commonFriendlyName}")
val externalScope = rootScope.fork("external ${slab.location.commonFriendlyName}")
init {
processAllDefinitions()
}
fun ensureImportedContextsExist() {
for (importedSlab in slab.importedSlabs) {
evaluator.context(importedSlab)
}
}
private var initializedFinalScope = false
fun finalizeScope() {
if (initializedFinalScope) {
return
}
initializedFinalScope = true
processFinalImportScopes()
}
private fun processAllDefinitions() {
for (definition in slab.compilationUnit.definitions) {
processDefinition(definition)
}
}
private fun processDefinition(definition: Definition) {
val internalValue = definitionValue(definition)
internalScope.define(definition.symbol.id, internalValue)
if (definition.modifiers.export) {
externalScope.define(definition.symbol.id, internalValue)
}
}
private fun definitionValue(definition: Definition): Any = when (definition) {
is FunctionDefinition -> FunctionContext(this, definition)
is LetDefinition -> {
EvaluationVisitor(internalScope.fork("let ${definition.symbol.id}"), CallStack())
.visit(definition.value)
}
}
private fun processFinalImportScopes() {
for (importedSlab in slab.importedSlabs) {
val importedSlabContext = evaluator.context(importedSlab)
importedSlabContext.processFinalImportScopes()
internalScope.inherit(importedSlabContext.externalScope)
}
}
}

View File

@ -1,9 +1,11 @@
let count = 5
export func main() {
var x = 1
while x <= count {
println(x)
while x <= 5 {
if x == 3 {
println("The value is 3")
} else {
println("The value is not 3")
}
x++
}
}

View File

@ -0,0 +1,8 @@
plugins {
id("gay.pizza.pork.module")
}
dependencies {
api(project(":frontend"))
implementation(project(":common"))
}

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.execution
interface ExecutionContext {
fun execute()
}

View File

@ -0,0 +1,8 @@
package gay.pizza.pork.execution
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.frontend.ImportLocator
interface ExecutionContextProvider {
fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol): ExecutionContext
}

View File

@ -25,7 +25,7 @@ class FfiNativeProvider : NativeProvider {
override fun provideNativeFunction(
definitions: List<String>,
arguments: List<ArgumentSpec>,
inside: CompilationUnitContext
inside: SlabContext
): CallableFunction {
if (definitions[0] == "internal") {
val internal = internalFunctions[definitions[1]] ?:
@ -73,7 +73,7 @@ class FfiNativeProvider : NativeProvider {
}
}
private fun addStructDefs(ffiTypeRegistry: FfiTypeRegistry, types: List<String>, inside: CompilationUnitContext) {
private fun addStructDefs(ffiTypeRegistry: FfiTypeRegistry, types: List<String>, inside: SlabContext) {
for (parameter in types) {
if (!parameter.startsWith("struct ")) {
continue

View File

@ -15,5 +15,5 @@ object JavaAutogenContentSource : ContentSource {
return StringCharSource(content)
}
override fun stableContentIdentity(path: String): String = path
override fun stableContentPath(path: String): String = path
}

View File

@ -2,7 +2,7 @@ package gay.pizza.pork.ffi
import gay.pizza.pork.ast.gen.ArgumentSpec
import gay.pizza.pork.evaluator.CallableFunction
import gay.pizza.pork.evaluator.CompilationUnitContext
import gay.pizza.pork.evaluator.SlabContext
import gay.pizza.pork.evaluator.NativeProvider
import gay.pizza.pork.evaluator.None
import java.lang.invoke.MethodHandles
@ -14,7 +14,7 @@ class JavaNativeProvider : NativeProvider {
override fun provideNativeFunction(
definitions: List<String>,
arguments: List<ArgumentSpec>,
inside: CompilationUnitContext
inside: SlabContext
): CallableFunction {
val functionDefinition = JavaFunctionDefinition.parse(definitions)
val javaClass = lookupClass(functionDefinition.type)

View File

@ -4,5 +4,5 @@ import gay.pizza.pork.tokenizer.CharSource
interface ContentSource {
fun loadAsCharSource(path: String): CharSource
fun stableContentIdentity(path: String): String
fun stableContentPath(path: String): String
}

View File

@ -1,7 +1,7 @@
package gay.pizza.pork.frontend
class DynamicImportSource : ImportSource {
private val providers = mutableMapOf<String,ContentSource>()
private val providers = mutableMapOf<String, ContentSource>()
override fun provideContentSource(form: String): ContentSource {
return providers[form] ?:

View File

@ -10,7 +10,7 @@ class FsContentSource(val root: FsPath) : ContentSource {
override fun loadAsCharSource(path: String): CharSource =
StringCharSource(asFsPath(path).readString())
override fun stableContentIdentity(path: String): String =
override fun stableContentPath(path: String): String =
asFsPath(path).fullPathString
private fun asFsPath(path: String): FsPath {

View File

@ -0,0 +1,12 @@
package gay.pizza.pork.frontend
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.frontend.scope.SlabScope
class Slab(val world: World, val location: SourceLocation, val compilationUnit: CompilationUnit) {
val importedSlabs: List<Slab> by lazy {
world.resolveAllImports(this)
}
val scope: SlabScope by lazy { world.scope.index(this) }
}

View File

@ -0,0 +1,10 @@
package gay.pizza.pork.frontend
import gay.pizza.pork.tokenizer.SourceIndex
data class SourceLocation(val form: String, val filePath: String, val index: SourceIndex? = null) {
val commonFriendlyName: String by lazy { "$form $filePath" }
fun withSourceIndex(index: SourceIndex): SourceLocation =
SourceLocation(form, filePath, index)
}

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.frontend
data class StableSourceKey(val form: String, val path: String) {
fun asSourceLocation(): SourceLocation = SourceLocation(form, path)
}

View File

@ -1,22 +1,25 @@
package gay.pizza.pork.frontend
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.ast.gen.ImportDeclaration
import gay.pizza.pork.frontend.scope.WorldScope
import gay.pizza.pork.parser.DiscardNodeAttribution
import gay.pizza.pork.parser.Parser
import gay.pizza.pork.tokenizer.Tokenizer
class World(val importSource: ImportSource) {
private val internalUnits = mutableMapOf<String, CompilationUnit>()
private val importedUnits = mutableMapOf<CompilationUnit, Set<CompilationUnit>>()
private val preludeImportLocator = ImportLocator("std", "lang/prelude.pork")
val units: List<CompilationUnit>
get() = internalUnits.values.toList()
private val internalSlabs = mutableMapOf<StableSourceKey, Slab>()
private fun loadOneUnit(importLocator: ImportLocator): CompilationUnit {
val slabs: List<Slab>
get() = internalSlabs.values.toList()
val scope: WorldScope by lazy { WorldScope(this) }
private fun loadOneSlab(importLocator: ImportLocator): Slab {
val contentSource = pickContentSource(importLocator.form)
val stableKey = stableIdentity(importLocator, contentSource = contentSource)
val cached = internalUnits[stableKey]
val stableKey = stableSourceKey(importLocator, contentSource = contentSource)
val cached = internalSlabs[stableKey]
if (cached != null) {
return cached
}
@ -24,40 +27,39 @@ class World(val importSource: ImportSource) {
val tokenizer = Tokenizer(charSource)
val parser = Parser(tokenizer, DiscardNodeAttribution)
val unit = parser.parseCompilationUnit()
internalUnits[stableKey] = unit
return unit
val slab = Slab(world = this, location = stableKey.asSourceLocation(), compilationUnit = unit)
internalSlabs[stableKey] = slab
return slab
}
private fun resolveAllImports(unit: CompilationUnit): Set<CompilationUnit> {
val units = mutableSetOf<CompilationUnit>()
for (declaration in unit.declarations.filterIsInstance<ImportDeclaration>()) {
internal fun resolveAllImports(slab: Slab): List<Slab> {
val slabs = mutableListOf<Slab>()
if (slab.location.form != preludeImportLocator.form &&
slab.location.filePath != preludeImportLocator.path) {
slabs.add(loadOneSlab(preludeImportLocator))
}
for (declaration in slab.compilationUnit.declarations.filterIsInstance<ImportDeclaration>()) {
val importPath = declaration.path.components.joinToString("/") { it.id } + ".pork"
val importLocator = ImportLocator(declaration.form.id, importPath)
val importedUnit = loadOneUnit(importLocator)
units.add(importedUnit)
val importedModule = loadOneSlab(importLocator)
slabs.add(importedModule)
}
importedUnits[unit] = units
return units
return slabs
}
fun load(importLocator: ImportLocator): CompilationUnit {
val unit = loadOneUnit(importLocator)
resolveAllImports(unit)
return unit
fun load(importLocator: ImportLocator): Slab {
return loadOneSlab(importLocator)
}
fun importedBy(unit: CompilationUnit): Set<CompilationUnit> =
importedUnits[unit] ?: emptySet()
private fun pickContentSource(form: String): ContentSource =
importSource.provideContentSource(form)
fun stableIdentity(
fun stableSourceKey(
importLocator: ImportLocator,
contentSource: ContentSource = pickContentSource(importLocator.form)
): String {
): StableSourceKey {
val formKey = importLocator.form
val stableIdentity = contentSource.stableContentIdentity(importLocator.path)
return "[${formKey}][${stableIdentity}]"
val stableContentPath = contentSource.stableContentPath(importLocator.path)
return StableSourceKey(formKey, stableContentPath)
}
}

View File

@ -1,36 +0,0 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.ast.gen.Symbol
class CompilationUnitScope(val worldScope: WorldScope, val unit: CompilationUnit) {
val externalSymbols = mutableSetOf<ScopeSymbol>()
val internalSymbols = mutableSetOf<ScopeSymbol>()
fun index() {
for (definition in unit.definitions) {
val scopeSymbol = ScopeSymbol(unit, definition)
if (definition.modifiers.export) {
externalSymbols.add(scopeSymbol)
}
internalSymbols.add(scopeSymbol)
}
}
fun findInternallyVisibleSymbols(): Set<VisibleScopeSymbol> {
val allSymbols = mutableMapOf<Symbol, VisibleScopeSymbol>()
val imports = worldScope.world.importedBy(unit)
for (import in imports) {
val scope = worldScope.index(import)
for (importedSymbol in scope.externalSymbols) {
allSymbols[importedSymbol.symbol] = VisibleScopeSymbol(unit, importedSymbol)
}
}
for (internalSymbol in internalSymbols) {
allSymbols[internalSymbol.symbol] = VisibleScopeSymbol(unit, internalSymbol)
}
return allSymbols.values.toSet()
}
}

View File

@ -0,0 +1,21 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.Definition
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.ast.gen.visit
class DefinitionScope(val slabScope: SlabScope, val definition: Definition) {
val usedSymbols: List<ScopeSymbol> by lazy {
val symbols = mutableListOf<ScopeSymbol>()
val analyzer = ExternalSymbolUsageAnalyzer()
analyzer.visit(definition)
for (symbol in analyzer.usedSymbols) {
val resolved = slabScope.resolve(symbol)
?: throw RuntimeException("Unable to resolve symbol: ${symbol.id}")
symbols.add(resolved)
}
symbols
}
fun resolve(symbol: Symbol): ScopeSymbol? = slabScope.resolve(symbol)
}

View File

@ -0,0 +1,135 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.FunctionLevelVisitor
import gay.pizza.pork.ast.gen.*
class ExternalSymbolUsageAnalyzer : FunctionLevelVisitor<Unit>() {
private val symbols = mutableSetOf<Symbol>()
private val internalSymbols = mutableListOf<MutableSet<Symbol>>()
val usedSymbols: Set<Symbol>
get() = symbols
override fun visitFunctionDefinition(node: FunctionDefinition) {
internalSymbols.add(node.arguments.map { it.symbol }.toMutableSet())
node.block?.visit(this)
internalSymbols.removeLast()
}
override fun visitLetDefinition(node: LetDefinition) {
node.value.visit(this)
}
override fun visitBlock(node: Block) {
internalSymbols.add(mutableSetOf())
node.visitChildren(this)
internalSymbols.removeLast()
}
override fun visitBooleanLiteral(node: BooleanLiteral) {
node.visitChildren(this)
}
override fun visitBreak(node: Break) {
node.visitChildren(this)
}
override fun visitContinue(node: Continue) {
node.visitChildren(this)
}
override fun visitDoubleLiteral(node: DoubleLiteral) {
node.visitChildren(this)
}
override fun visitForIn(node: ForIn) {
node.expression.visit(this)
internalSymbols.add(mutableSetOf(node.item.symbol))
node.block.visit(this)
internalSymbols.removeLast()
}
override fun visitFunctionCall(node: FunctionCall) {
checkAndContribute(node.symbol)
for (argument in node.arguments) {
visit(argument)
}
}
override fun visitIf(node: If) {
node.condition.visit(this)
node.thenBlock.visit(this)
node.elseBlock?.visit(this)
}
override fun visitIndexedBy(node: IndexedBy) {
node.visitChildren(this)
}
override fun visitInfixOperation(node: InfixOperation) {
node.visitChildren(this)
}
override fun visitIntegerLiteral(node: IntegerLiteral) {
node.visitChildren(this)
}
override fun visitLetAssignment(node: LetAssignment) {
internalSymbols.last().add(node.symbol)
node.value.visit(this)
}
override fun visitListLiteral(node: ListLiteral) {
node.visitChildren(this)
}
override fun visitLongLiteral(node: LongLiteral) {
node.visitChildren(this)
}
override fun visitNoneLiteral(node: NoneLiteral) {
node.visitChildren(this)
}
override fun visitParentheses(node: Parentheses) {
node.visitChildren(this)
}
override fun visitPrefixOperation(node: PrefixOperation) {
node.visitChildren(this)
}
override fun visitSetAssignment(node: SetAssignment) {
node.visitChildren(this)
}
override fun visitStringLiteral(node: StringLiteral) {
node.visitChildren(this)
}
override fun visitSuffixOperation(node: SuffixOperation) {
node.visitChildren(this)
}
override fun visitSymbolReference(node: SymbolReference) {
checkAndContribute(node.symbol)
}
override fun visitVarAssignment(node: VarAssignment) {
internalSymbols.last().add(node.symbol)
node.value.visit(this)
}
override fun visitWhile(node: While) {
node.condition.visit(this)
internalSymbols.add(mutableSetOf())
node.block.visit(this)
internalSymbols.removeLast()
}
private fun checkAndContribute(symbol: Symbol) {
if (internalSymbols.none { it.contains(symbol) }) {
symbols.add(symbol)
}
}
}

View File

@ -1,11 +1,9 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.Definition
import gay.pizza.pork.ast.gen.Node
import gay.pizza.pork.ast.gen.Symbol
class ScopeSymbol(
val compilationUnit: Node,
val definition: Definition
) {
val symbol = definition.symbol
class ScopeSymbol(val slabScope: SlabScope, val definition: Definition) {
val symbol: Symbol = definition.symbol
val scope: DefinitionScope by lazy { DefinitionScope(slabScope, definition) }
}

View File

@ -0,0 +1,48 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.frontend.Slab
class SlabScope(val worldScope: WorldScope, val slab: Slab) {
private val externalSymbolsList = mutableSetOf<ScopeSymbol>()
private val internalSymbolsList = mutableSetOf<ScopeSymbol>()
val externalSymbols: Set<ScopeSymbol>
get() = externalSymbolsList
val internalSymbols: Set<ScopeSymbol>
get() = internalSymbolsList
val internallyVisibleSymbols: List<VisibleScopeSymbol> by lazy { findInternallyVisibleSymbols() }
fun index() {
for (definition in slab.compilationUnit.definitions) {
val scopeSymbol = ScopeSymbol(this, definition)
if (definition.modifiers.export) {
externalSymbolsList.add(scopeSymbol)
}
internalSymbolsList.add(scopeSymbol)
}
}
private fun findInternallyVisibleSymbols(): List<VisibleScopeSymbol> {
val allSymbols = mutableMapOf<Symbol, VisibleScopeSymbol>()
val imports = slab.importedSlabs
for (import in imports) {
val scope = worldScope.index(import)
for (importedSymbol in scope.externalSymbols) {
allSymbols[importedSymbol.symbol] = VisibleScopeSymbol(slab, importedSymbol)
}
}
for (internalSymbol in internalSymbols) {
allSymbols[internalSymbol.symbol] = VisibleScopeSymbol(slab, internalSymbol)
}
return allSymbols.values.toList()
}
fun resolve(symbol: Symbol): ScopeSymbol? = internallyVisibleSymbols.firstOrNull {
it.scopeSymbol.symbol == symbol
}?.scopeSymbol
}

View File

@ -1,8 +1,8 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.frontend.Slab
class VisibleScopeSymbol(val visibleToUnit: CompilationUnit, val scopeSymbol: ScopeSymbol) {
class VisibleScopeSymbol(val visibleToSlab: Slab, val scopeSymbol: ScopeSymbol) {
val isInternalSymbol: Boolean
get() = visibleToUnit == scopeSymbol.compilationUnit
get() = visibleToSlab == scopeSymbol.slabScope.slab
}

View File

@ -1,24 +1,24 @@
package gay.pizza.pork.frontend.scope
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.frontend.World
import gay.pizza.pork.frontend.Slab
class WorldScope(val world: World) {
private val compilationUnitScopes = mutableMapOf<CompilationUnit, CompilationUnitScope>()
private val slabScopes = mutableMapOf<Slab, SlabScope>()
fun indexAll() {
for (unit in world.units) {
index(unit)
for (module in world.slabs) {
index(module)
}
}
fun index(unit: CompilationUnit): CompilationUnitScope =
scope(unit).apply {
fun index(slab: Slab): SlabScope =
scope(slab).apply {
index()
}
fun scope(unit: CompilationUnit): CompilationUnitScope =
compilationUnitScopes.computeIfAbsent(unit) {
CompilationUnitScope(this, unit)
fun scope(slab: Slab): SlabScope =
slabScopes.computeIfAbsent(slab) {
SlabScope(this, slab)
}
}

View File

@ -36,16 +36,16 @@ abstract class Tool {
fun <T> visit(visitor: NodeVisitor<T>): T = visitor.visit(parse())
fun loadMainFunction(scope: Scope, setupEvaluator: Evaluator.() -> Unit = {}): CallableFunction {
fun loadMainFunction(setupEvaluator: Evaluator.() -> Unit = {}): CallableFunction {
val world = buildWorld()
val evaluator = Evaluator(world, scope)
val evaluator = Evaluator(world)
setupEvaluator(evaluator)
val resultingScope = evaluator.evaluate(rootImportLocator)
return resultingScope.value("main") as CallableFunction
}
fun loadMainFunctionStandard(scope: Scope, quiet: Boolean = false): CallableFunction =
loadMainFunction(scope, setupEvaluator = {
fun loadMainFunctionStandard(quiet: Boolean = false): CallableFunction =
loadMainFunction(setupEvaluator = {
addNativeProvider("internal", InternalNativeProvider(quiet = quiet))
addNativeProvider("ffi", FfiNativeProvider())
addNativeProvider("java", JavaNativeProvider())
@ -60,8 +60,8 @@ abstract class Tool {
return World(dynamicImportSource)
}
fun run(scope: Scope, quiet: Boolean = false) {
val main = loadMainFunctionStandard(scope, quiet = quiet)
fun run(quiet: Boolean = false) {
val main = loadMainFunctionStandard(quiet = quiet)
main.call(emptyList(), CallStack())
}
}

View File

@ -11,5 +11,5 @@ fun main(args: Array<String>) {
}
val path = PlatformFsProvider.resolve(args[0])
val tool = FileTool(path)
tool.run(Scope.root())
tool.run()
}

View File

@ -6,8 +6,12 @@ include(
":common",
":tokenizer",
":ast",
":bytecode",
":parser",
":frontend",
":compiler",
":execution",
":vm",
":evaluator",
":stdlib",
":ffi",

View File

@ -34,7 +34,7 @@ object PorkStdlib : ContentSource {
return StringCharSource(readPorkFile(path))
}
override fun stableContentIdentity(path: String): String {
override fun stableContentPath(path: String): String {
return path
}
}

View File

@ -1,7 +1,7 @@
export func ffiStructDefine(items...)
native ffi "internal" "ffiStructDefine"
export func ffiStructAllocate(struct)
export func ffiStructAllocate(def)
native ffi "internal" "ffiStructAllocate"
export func ffiStructValue(def, field, value)

View File

@ -76,7 +76,6 @@ enum class TokenType(vararg val properties: TokenTypeProperty) {
))),
BlockComment(CharConsume(MatchedCharConsumer("/*", "*/")), CommentFamily),
LineComment(CharConsume(MatchedCharConsumer("//", "\n", AllowEofTermination)), CommentFamily),
Struct(ManyChars("struct"), KeywordFamily),
EndOfFile;
val promotions: List<Promotion> =

View File

@ -7,6 +7,8 @@ plugins {
dependencies {
api(project(":minimal"))
api(project(":compiler"))
api(project(":vm"))
api("com.github.ajalt.clikt:clikt:4.2.0")
implementation(project(":common"))

View File

@ -0,0 +1,33 @@
package gay.pizza.pork.tool
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.compiler.Compiler
import gay.pizza.pork.minimal.FileTool
import gay.pizza.pork.vm.VirtualMachine
class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "compile") {
val path by argument("file")
override fun run() {
val tool = FileTool(PlatformFsProvider.resolve(path))
val world = tool.buildWorld()
val compiler = Compiler()
val slab = world.load(tool.rootImportLocator)
val compiledSlab = compiler.compilableSlabs.of(slab)
val compiledMain = compiledSlab.compilableSymbolOf(Symbol("main"))
?: throw RuntimeException("'main' function not found.")
val compiledWorld = compiler.compile(compiledMain)
for (symbol in compiledWorld.symbolTable.symbols) {
val code = compiledWorld.code.subList(symbol.offset.toInt(), (symbol.offset + symbol.size).toInt())
println(symbol.id)
for ((index, op) in code.withIndex()) {
println(" ${symbol.offset + index.toUInt()} ${op.code.name} ${op.args.joinToString(" ")}")
}
}
val vm = VirtualMachine(compiledWorld)
vm.execute()
}
}

View File

@ -17,7 +17,8 @@ class RootCommand : CliktCommand(
AstCommand(),
AttributeCommand(),
ScopeAnalysisCommand(),
CopyStdlibCommand()
CopyStdlibCommand(),
CompileCommand()
)
}

View File

@ -6,8 +6,6 @@ import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.evaluator.*
import gay.pizza.pork.ffi.FfiNativeProvider
import gay.pizza.pork.ffi.JavaNativeProvider
import gay.pizza.pork.minimal.FileTool
class RunCommand : CliktCommand(help = "Run Program", name = "run") {
@ -19,12 +17,11 @@ class RunCommand : CliktCommand(help = "Run Program", name = "run") {
override fun run() {
val tool = FileTool(PlatformFsProvider.resolve(path))
val scope = Scope.root()
val main = tool.loadMainFunctionStandard(scope, quiet = quiet)
val main = tool.loadMainFunctionStandard(quiet = quiet)
if (dumpScope) {
val functionContext = main as FunctionContext
val internalScope = functionContext.compilationUnitContext.internalScope
val internalScope = functionContext.slabContext.internalScope
internalScope.crawlScopePath { key, path ->
println("[scope] $key [${path.joinToString(" -> ")}]")
}

View File

@ -15,12 +15,12 @@ class ScopeAnalysisCommand : CliktCommand(help = "Run Scope Analysis", name = "s
val root = world.load(tool.rootImportLocator)
val scope = WorldScope(world).apply { index(root) }
val rootScope = scope.scope(root)
val visibleScopeSymbols = rootScope.findInternallyVisibleSymbols()
for (visibleScopeSymbol in visibleScopeSymbols) {
for (visibleScopeSymbol in rootScope.internallyVisibleSymbols) {
println(
"symbol ${visibleScopeSymbol.scopeSymbol.symbol.id} " +
"type=${visibleScopeSymbol.scopeSymbol.definition.type.name} " +
"internal=${visibleScopeSymbol.isInternalSymbol}"
"internal=${visibleScopeSymbol.isInternalSymbol} " +
"slab=${visibleScopeSymbol.scopeSymbol.slab.location.commonFriendlyName}"
)
}
}

11
vm/build.gradle.kts Normal file
View File

@ -0,0 +1,11 @@
plugins {
id("gay.pizza.pork.module")
}
dependencies {
api(project(":execution"))
api(project(":bytecode"))
api(project(":compiler"))
implementation(project(":common"))
}

View File

@ -0,0 +1,80 @@
package gay.pizza.pork.vm
import gay.pizza.pork.bytecode.CompiledWorld
class InternalMachine(val world: CompiledWorld, val handlers: List<OpHandler>) {
private val inlined = world.code.map { op ->
val handler = handlers.firstOrNull { it.code == op.code } ?:
throw VirtualMachineException("Opcode ${op.code.name} does not have a handler.")
op to handler
}
private var inst: UInt = 0u
private val stack = mutableListOf<Any>(EndOfCode)
private val locals = mutableListOf<MutableMap<UInt, Any>>(
mutableMapOf()
)
private var autoNextInst = true
private var exitFlag = false
fun step(): Boolean {
val (op, handler) = inlined[inst.toInt()]
handler.handle(this, op)
if (autoNextInst) {
inst++
}
autoNextInst = true
return !exitFlag
}
fun pushScope() {
locals.add(mutableMapOf())
}
fun popScope() {
locals.removeLast()
}
fun loadConstant(id: UInt) {
push(world.constantPool.constants[id.toInt()])
}
fun loadLocal(id: UInt) {
val localSet = locals.last()
val local = localSet[id]
?: throw VirtualMachineException("Attempted to load local $id but it was not stored.")
push(local)
}
fun storeLocal(id: UInt) {
val localSet = locals.last()
val value = pop()
localSet[id] = value
}
fun setNextInst(value: UInt) {
inst = value
autoNextInst = false
}
fun push(item: Any) {
stack.add(item)
}
fun pop(): Any = stack.removeLast()
fun exit() {
exitFlag = true
}
fun reset() {
stack.clear()
stack.add(EndOfCode)
locals.clear()
locals.add(mutableMapOf())
inst = 0u
exitFlag = false
autoNextInst = true
}
data object EndOfCode
}

View File

@ -0,0 +1,8 @@
package gay.pizza.pork.vm
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
abstract class OpHandler(val code: Opcode) {
abstract fun handle(machine: InternalMachine, op: Op)
}

View File

@ -0,0 +1,45 @@
package gay.pizza.pork.vm
import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.vm.ops.*
class VirtualMachine(world: CompiledWorld) : ExecutionContext {
private val internal = InternalMachine(world, listOf(
IntegerOpHandler,
ConstantOpHandler,
TrueOpHandler,
FalseOpHandler,
ListOpHandler,
CompareEqualOpHandler,
CompareLesserEqualOpHandler,
AddOpHandler,
JumpOpHandler,
JumpIfOpHandler,
LoadLocalOpHandler,
StoreLocalOpHandler,
CallOpHandler,
RetOpHandler,
NativeOpHandler,
ScopeInOpHandler,
ScopeOutOpHandler
))
override fun execute() {
while (true) {
if (!internal.step()) {
break
}
}
internal.reset()
}
}

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.vm
class VirtualMachineException(message: String) : RuntimeException(message)

View File

@ -0,0 +1,20 @@
package gay.pizza.pork.vm
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.compiler.Compiler
import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.frontend.ImportLocator
import gay.pizza.pork.frontend.World
class VirtualMachineProvider(val world: World) : ExecutionContextProvider {
override fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol): ExecutionContext {
val compiler = Compiler()
val slab = world.load(importLocator)
val compilableSlab = compiler.compilableSlabs.of(slab)
val compilableSymbol = compilableSlab.compilableSymbolOf(entryPointSymbol) ?:
throw RuntimeException("Unable to find compilable symbol for entry point '${entryPointSymbol.id}'")
val compiledWorld = compiler.compile(compilableSymbol)
return VirtualMachine(compiledWorld)
}
}

View File

@ -0,0 +1,19 @@
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
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.")
}
machine.push(left + right)
}
}

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 CallOpHandler : OpHandler(Opcode.Call) {
override fun handle(machine: InternalMachine, op: Op) {
machine.setNextInst(op.args[0])
machine.pushScope()
}
}

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 CompareEqualOpHandler : OpHandler(Opcode.CompareEqual) {
override fun handle(machine: InternalMachine, op: Op) {
machine.push(machine.pop() == machine.pop())
}
}

View File

@ -0,0 +1,19 @@
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
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.")
}
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 ConstantOpHandler : OpHandler(Opcode.Constant) {
override fun handle(machine: InternalMachine, op: Op) {
machine.loadConstant(op.args[0])
}
}

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 FalseOpHandler : OpHandler(Opcode.False) {
override fun handle(machine: InternalMachine, op: Op) {
machine.push(false)
}
}

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 IntegerOpHandler : OpHandler(Opcode.Integer) {
override fun handle(machine: InternalMachine, op: Op) {
machine.push(op.args[0].toInt())
}
}

View File

@ -0,0 +1,20 @@
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
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.")
}
if (value) {
machine.setNextInst(op.args[0])
}
}
}

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 JumpOpHandler : OpHandler(Opcode.Jump) {
override fun handle(machine: InternalMachine, op: Op) {
machine.setNextInst(op.args[0])
}
}

View File

@ -0,0 +1,18 @@
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 ListOpHandler : OpHandler(Opcode.List) {
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()
list.add(item)
}
machine.push(list.reversed())
}
}

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 LoadLocalOpHandler : OpHandler(Opcode.LoadLocal) {
override fun handle(machine: InternalMachine, op: Op) {
machine.loadLocal(op.args[0])
}
}

View File

@ -0,0 +1,16 @@
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 NativeOpHandler : OpHandler(Opcode.Native) {
override fun handle(machine: InternalMachine, op: Op) {
val countOfNativeDefs = op.args[1].toInt()
val defs = mutableListOf<Any>()
for (i in 0 until countOfNativeDefs) {
defs.add(String(machine.pop() as ByteArray))
}
}
}

View File

@ -0,0 +1,18 @@
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 RetOpHandler : 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())
}
}

View File

@ -0,0 +1,10 @@
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 ScopeInOpHandler : OpHandler(Opcode.ScopeIn) {
override fun handle(machine: InternalMachine, op: Op) {}
}

View File

@ -0,0 +1,10 @@
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 ScopeOutOpHandler : OpHandler(Opcode.ScopeOut) {
override fun handle(machine: InternalMachine, op: Op) {}
}

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 StoreLocalOpHandler : OpHandler(Opcode.StoreLocal) {
override fun handle(machine: InternalMachine, op: Op) {
machine.storeLocal(op.args[0])
}
}

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 TrueOpHandler : OpHandler(Opcode.True) {
override fun handle(machine: InternalMachine, op: Op) {
machine.push(true)
}
}