mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 12:50:55 +00:00
vm: very basic virtual machine
This commit is contained in:
parent
8c48c93663
commit
041848c14e
@ -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.")
|
||||
}
|
||||
}
|
7
bytecode/build.gradle.kts
Normal file
7
bytecode/build.gradle.kts
Normal file
@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
id("gay.pizza.pork.module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":common"))
|
||||
}
|
@ -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>
|
||||
)
|
@ -0,0 +1,3 @@
|
||||
package gay.pizza.pork.bytecode
|
||||
|
||||
class Constant(val id: UInt, val value: ByteArray)
|
@ -0,0 +1,6 @@
|
||||
package gay.pizza.pork.bytecode
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ConstantPool(val constants: List<ByteArray>)
|
@ -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
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package gay.pizza.pork.bytecode
|
||||
|
||||
class MutableRel(var rel: UInt)
|
6
bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Op.kt
Normal file
6
bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Op.kt
Normal 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>)
|
43
bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Opcode.kt
Normal file
43
bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Opcode.kt
Normal 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),
|
||||
}
|
3
bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Ops.kt
Normal file
3
bytecode/src/main/kotlin/gay/pizza/pork/bytecode/Ops.kt
Normal file
@ -0,0 +1,3 @@
|
||||
package gay.pizza.pork.bytecode
|
||||
|
||||
class Ops(val ops: List<Op>)
|
@ -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
|
||||
)
|
@ -0,0 +1,8 @@
|
||||
package gay.pizza.pork.bytecode
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SymbolTable(
|
||||
val symbols: List<SymbolInfo>
|
||||
)
|
@ -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
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
|
||||
)
|
@ -4,6 +4,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
api(project(":ast"))
|
||||
api(project(":execution"))
|
||||
api(project(":frontend"))
|
||||
|
||||
implementation(project(":common"))
|
||||
|
@ -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")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@ -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")
|
||||
}
|
||||
|
@ -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 evaluate(locator: ImportLocator): Scope {
|
||||
val slabContext = context(locator)
|
||||
slabContext.finalizeScope()
|
||||
return slabContext.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}")
|
||||
fun context(slab: Slab): SlabContext {
|
||||
val slabContext = contexts.computeIfAbsent(slab) {
|
||||
SlabContext(slab, this, scope)
|
||||
}
|
||||
context.initIfNeeded()
|
||||
return context
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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++
|
||||
}
|
||||
}
|
||||
|
8
execution/build.gradle.kts
Normal file
8
execution/build.gradle.kts
Normal file
@ -0,0 +1,8 @@
|
||||
plugins {
|
||||
id("gay.pizza.pork.module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":frontend"))
|
||||
implementation(project(":common"))
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.execution
|
||||
|
||||
interface ExecutionContext {
|
||||
fun execute()
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -15,5 +15,5 @@ object JavaAutogenContentSource : ContentSource {
|
||||
return StringCharSource(content)
|
||||
}
|
||||
|
||||
override fun stableContentIdentity(path: String): String = path
|
||||
override fun stableContentPath(path: String): String = path
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 {
|
||||
|
12
frontend/src/main/kotlin/gay/pizza/pork/frontend/Slab.kt
Normal file
12
frontend/src/main/kotlin/gay/pizza/pork/frontend/Slab.kt
Normal 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) }
|
||||
}
|
@ -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)
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.frontend
|
||||
|
||||
data class StableSourceKey(val form: String, val path: String) {
|
||||
fun asSourceLocation(): SourceLocation = SourceLocation(form, path)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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) }
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -6,8 +6,12 @@ include(
|
||||
":common",
|
||||
":tokenizer",
|
||||
":ast",
|
||||
":bytecode",
|
||||
":parser",
|
||||
":frontend",
|
||||
":compiler",
|
||||
":execution",
|
||||
":vm",
|
||||
":evaluator",
|
||||
":stdlib",
|
||||
":ffi",
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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> =
|
||||
|
@ -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"))
|
||||
|
33
tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt
Normal file
33
tool/src/main/kotlin/gay/pizza/pork/tool/CompileCommand.kt
Normal 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()
|
||||
}
|
||||
}
|
@ -17,7 +17,8 @@ class RootCommand : CliktCommand(
|
||||
AstCommand(),
|
||||
AttributeCommand(),
|
||||
ScopeAnalysisCommand(),
|
||||
CopyStdlibCommand()
|
||||
CopyStdlibCommand(),
|
||||
CompileCommand()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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(" -> ")}]")
|
||||
}
|
||||
|
@ -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
11
vm/build.gradle.kts
Normal file
@ -0,0 +1,11 @@
|
||||
plugins {
|
||||
id("gay.pizza.pork.module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":execution"))
|
||||
api(project(":bytecode"))
|
||||
api(project(":compiler"))
|
||||
|
||||
implementation(project(":common"))
|
||||
}
|
80
vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt
Normal file
80
vm/src/main/kotlin/gay/pizza/pork/vm/InternalMachine.kt
Normal 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
|
||||
}
|
8
vm/src/main/kotlin/gay/pizza/pork/vm/OpHandler.kt
Normal file
8
vm/src/main/kotlin/gay/pizza/pork/vm/OpHandler.kt
Normal 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)
|
||||
}
|
45
vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt
Normal file
45
vm/src/main/kotlin/gay/pizza/pork/vm/VirtualMachine.kt
Normal 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()
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package gay.pizza.pork.vm
|
||||
|
||||
class VirtualMachineException(message: String) : RuntimeException(message)
|
@ -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)
|
||||
}
|
||||
}
|
19
vm/src/main/kotlin/gay/pizza/pork/vm/ops/AddOpHandler.kt
Normal file
19
vm/src/main/kotlin/gay/pizza/pork/vm/ops/AddOpHandler.kt
Normal 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)
|
||||
}
|
||||
}
|
13
vm/src/main/kotlin/gay/pizza/pork/vm/ops/CallOpHandler.kt
Normal file
13
vm/src/main/kotlin/gay/pizza/pork/vm/ops/CallOpHandler.kt
Normal 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()
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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])
|
||||
}
|
||||
}
|
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/FalseOpHandler.kt
Normal file
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/FalseOpHandler.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package gay.pizza.pork.vm.ops
|
||||
|
||||
import gay.pizza.pork.bytecode.Op
|
||||
import gay.pizza.pork.bytecode.Opcode
|
||||
import gay.pizza.pork.vm.InternalMachine
|
||||
import gay.pizza.pork.vm.OpHandler
|
||||
|
||||
object FalseOpHandler : OpHandler(Opcode.False) {
|
||||
override fun handle(machine: InternalMachine, op: Op) {
|
||||
machine.push(false)
|
||||
}
|
||||
}
|
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/IntegerOpHandler.kt
Normal file
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/IntegerOpHandler.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package gay.pizza.pork.vm.ops
|
||||
|
||||
import gay.pizza.pork.bytecode.Op
|
||||
import gay.pizza.pork.bytecode.Opcode
|
||||
import gay.pizza.pork.vm.InternalMachine
|
||||
import gay.pizza.pork.vm.OpHandler
|
||||
|
||||
object IntegerOpHandler : OpHandler(Opcode.Integer) {
|
||||
override fun handle(machine: InternalMachine, op: Op) {
|
||||
machine.push(op.args[0].toInt())
|
||||
}
|
||||
}
|
20
vm/src/main/kotlin/gay/pizza/pork/vm/ops/JumpIfOpHandler.kt
Normal file
20
vm/src/main/kotlin/gay/pizza/pork/vm/ops/JumpIfOpHandler.kt
Normal 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])
|
||||
}
|
||||
}
|
||||
}
|
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/JumpOpHandler.kt
Normal file
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/JumpOpHandler.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package gay.pizza.pork.vm.ops
|
||||
|
||||
import gay.pizza.pork.bytecode.Op
|
||||
import gay.pizza.pork.bytecode.Opcode
|
||||
import gay.pizza.pork.vm.InternalMachine
|
||||
import gay.pizza.pork.vm.OpHandler
|
||||
|
||||
object JumpOpHandler : OpHandler(Opcode.Jump) {
|
||||
override fun handle(machine: InternalMachine, op: Op) {
|
||||
machine.setNextInst(op.args[0])
|
||||
}
|
||||
}
|
18
vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListOpHandler.kt
Normal file
18
vm/src/main/kotlin/gay/pizza/pork/vm/ops/ListOpHandler.kt
Normal 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())
|
||||
}
|
||||
}
|
@ -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])
|
||||
}
|
||||
}
|
16
vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt
Normal file
16
vm/src/main/kotlin/gay/pizza/pork/vm/ops/NativeOpHandler.kt
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
18
vm/src/main/kotlin/gay/pizza/pork/vm/ops/RetOpHandler.kt
Normal file
18
vm/src/main/kotlin/gay/pizza/pork/vm/ops/RetOpHandler.kt
Normal 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())
|
||||
}
|
||||
}
|
10
vm/src/main/kotlin/gay/pizza/pork/vm/ops/ScopeInOpHandler.kt
Normal file
10
vm/src/main/kotlin/gay/pizza/pork/vm/ops/ScopeInOpHandler.kt
Normal 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) {}
|
||||
}
|
@ -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) {}
|
||||
}
|
@ -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])
|
||||
}
|
||||
}
|
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/TrueOpHandler.kt
Normal file
12
vm/src/main/kotlin/gay/pizza/pork/vm/ops/TrueOpHandler.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package gay.pizza.pork.vm.ops
|
||||
|
||||
import gay.pizza.pork.bytecode.Op
|
||||
import gay.pizza.pork.bytecode.Opcode
|
||||
import gay.pizza.pork.vm.InternalMachine
|
||||
import gay.pizza.pork.vm.OpHandler
|
||||
|
||||
object TrueOpHandler : OpHandler(Opcode.True) {
|
||||
override fun handle(machine: InternalMachine, op: Op) {
|
||||
machine.push(true)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user