global: a working virtual machine for some of the use cases. APIs and validation still WIP.

This commit is contained in:
Alex Zenla 2023-11-21 22:18:05 -08:00
parent 0a2d029c5c
commit 6211ad4ff1
Signed by: alex
GPG Key ID: C0780728420EBFE5
53 changed files with 434 additions and 182 deletions

View File

@ -4,6 +4,13 @@ import kotlinx.serialization.Serializable
@Serializable
data class Constant(val id: UInt, val tag: ConstantTag, val value: ByteArray) {
fun readAsString(): String {
if (tag != ConstantTag.String) {
throw RuntimeException("Constant $id is not tagged as a string")
}
return String(value)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
other as Constant

View File

@ -3,4 +3,6 @@ package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable
@Serializable
data class ConstantPool(val constants: List<Constant>)
data class ConstantPool(val constants: List<Constant>) {
fun read(index: UInt): Constant = constants[index.toInt()]
}

View File

@ -13,4 +13,9 @@ class CompilableSlab(val compiler: Compiler, val slab: Slab) {
fun resolve(symbol: Symbol): CompilableSymbol? = compilableSymbols.firstOrNull {
it.scopeSymbol.symbol == symbol
}
fun resolveVisible(symbol: Symbol): CompilableSymbol? {
val scopeSymbol = slab.scope.resolve(symbol) ?: return null
return compiler.resolveOrNull(scopeSymbol)
}
}

View File

@ -53,7 +53,7 @@ class LocalState(val symbol: CompilableSymbol) {
return Loadable(stubVar = found)
}
}
val found = this.symbol.compilableSlab.resolve(symbol)
val found = this.symbol.compilableSlab.resolveVisible(symbol)
if (found != null) {
return Loadable(call = found)
}

View File

@ -285,7 +285,9 @@ class StubOpEmitter(val compiler: Compiler, val symbol: CompilableSymbol) : Func
code.emit(Opcode.Constant, listOf(defConstant))
}
val formConstant = compiler.constantPool.assign(ConstantTag.String, node.form.id.toByteArray())
code.emit(Opcode.Native, listOf(formConstant, node.definitions.size.toUInt()))
val functionDefinition = symbol.scopeSymbol.definition as FunctionDefinition
val functionArgumentCount = functionDefinition.arguments.size
code.emit(Opcode.Native, listOf(formConstant, node.definitions.size.toUInt(), functionArgumentCount.toUInt()))
}
private fun load(callOrStubVar: Loadable) {

View File

@ -0,0 +1,15 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.ArgumentSpec
import gay.pizza.pork.execution.NativeProvider
class AdaptedNativeProvider(val provider: NativeProvider) : ExpandedNativeProvider {
override fun provideNativeFunction(
definitions: List<String>,
arguments: List<ArgumentSpec>,
inside: SlabContext
): CallableFunction {
val function = provider.provideNativeFunction(definitions)
return CallableFunction { args, _ -> function.invoke(args) }
}
}

View File

@ -1,3 +0,0 @@
package gay.pizza.pork.evaluator
typealias ArgumentList = List<Any>

View File

@ -1,5 +1,7 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.execution.ArgumentList
fun interface CallableFunction {
fun call(arguments: ArgumentList, stack: CallStack): Any
}

View File

@ -2,6 +2,7 @@ package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.FunctionLevelVisitor
import gay.pizza.pork.ast.gen.*
import gay.pizza.pork.execution.None
import kotlin.math.abs
@Suppress("JavaIoSerializableObjectMustHaveReadResolve")

View File

@ -1,24 +1,21 @@
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) : ExecutionContextProvider {
class Evaluator(val world: World) {
private val scope = Scope.root()
private val contexts = mutableMapOf<Slab, SlabContext>()
private val nativeProviders = mutableMapOf<String, NativeProvider>()
private val nativeProviders = mutableMapOf<String, ExpandedNativeProvider>()
fun evaluate(locator: ImportLocator): Scope {
val slabContext = context(locator)
val slabContext = slabContext(locator)
slabContext.finalizeScope()
return slabContext.externalScope
}
fun context(slab: Slab): SlabContext {
fun slabContext(slab: Slab): SlabContext {
val slabContext = contexts.computeIfAbsent(slab) {
SlabContext(slab, this, scope)
}
@ -26,20 +23,14 @@ class Evaluator(val world: World) : ExecutionContextProvider {
return slabContext
}
fun context(locator: ImportLocator): SlabContext = context(world.load(locator))
fun slabContext(locator: ImportLocator): SlabContext = slabContext(world.load(locator))
fun nativeFunctionProvider(form: String): NativeProvider {
fun nativeFunctionProvider(form: String): ExpandedNativeProvider {
return nativeProviders[form] ?:
throw RuntimeException("Unknown native function form: $form")
}
fun addNativeProvider(form: String, nativeProvider: NativeProvider) {
fun addNativeProvider(form: String, nativeProvider: ExpandedNativeProvider) {
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,20 @@
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.execution.NativeRegistry
import gay.pizza.pork.frontend.ImportLocator
import gay.pizza.pork.frontend.World
class EvaluatorProvider(val world: World) : ExecutionContextProvider {
override fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol, nativeRegistry: NativeRegistry): ExecutionContext {
val evaluator = Evaluator(world)
nativeRegistry.forEachProvider { form, provider ->
evaluator.addNativeProvider(form, AdaptedNativeProvider(provider))
}
val slab = evaluator.slabContext(importLocator)
slab.finalizeScope()
return EvaluatorExecutionContext(evaluator, slab, entryPointSymbol)
}
}

View File

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

View File

@ -1,6 +1,7 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.FunctionDefinition
import gay.pizza.pork.execution.ArgumentList
class FunctionContext(val slabContext: SlabContext, val node: FunctionDefinition) : CallableFunction {
val name: String by lazy { "${slabContext.slab.location.commonFriendlyName} ${node.symbol.id}" }

View File

@ -1,55 +0,0 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.ArgumentSpec
import gay.pizza.pork.common.unused
class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
private val functions = mutableMapOf(
"print" to CallableFunction(::printValues),
"println" to CallableFunction(::printLine),
"listSet" to CallableFunction(::setInList),
"listInitWith" to CallableFunction(::listInitWith)
)
fun add(name: String, function: CallableFunction) {
functions[name] = function
}
override fun provideNativeFunction(
definitions: List<String>,
arguments: List<ArgumentSpec>,
inside: SlabContext
): CallableFunction {
val definition = definitions[0]
return functions[definition] ?: throw RuntimeException("Unknown Internal Function: $definition")
}
private fun printValues(arguments: ArgumentList, stack: CallStack): Any {
unused(stack)
if (quiet || arguments.isEmpty()) return None
print(arguments.joinToString(" "))
return None
}
private fun printLine(arguments: ArgumentList, stack: CallStack): Any {
unused(stack)
if (quiet) return None
println(arguments.joinToString(" "))
return None
}
private fun setInList(arguments: ArgumentList, stack: CallStack): Any {
unused(stack)
@Suppress("UNCHECKED_CAST")
val list = arguments[0] as MutableList<Any>
val value = arguments[2]
list[(arguments[1] as Number).toInt()] = value
return value
}
private fun listInitWith(arguments: ArgumentList, stack: CallStack): Any {
unused(stack)
val size = (arguments[0] as Number).toInt()
return MutableList(size) { arguments[1] }
}
}

View File

@ -1,3 +0,0 @@
package gay.pizza.pork.evaluator
data object None

View File

@ -1,5 +1,7 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.execution.None
class Scope(
var parent: Scope? = null,
var inherits: List<Scope> = emptyList(),

View File

@ -16,7 +16,7 @@ class SlabContext(val slab: Slab, val evaluator: Evaluator, rootScope: Scope) {
fun ensureImportedContextsExist() {
for (importedSlab in slab.importedSlabs) {
evaluator.context(importedSlab)
evaluator.slabContext(importedSlab)
}
}
@ -54,7 +54,7 @@ class SlabContext(val slab: Slab, val evaluator: Evaluator, rootScope: Scope) {
private fun processFinalImportScopes() {
for (importedSlab in slab.importedSlabs) {
val importedSlabContext = evaluator.context(importedSlab)
val importedSlabContext = evaluator.slabContext(importedSlab)
importedSlabContext.processFinalImportScopes()
internalScope.inherit(importedSlabContext.externalScope)
}

View File

@ -1,5 +1,7 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.execution.None
class ValueStore(var value: Any, var type: ValueStoreType) {
var isCurrentlyFree = false

View File

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

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.execution
typealias ArgumentList = List<Any>
inline fun <reified T> ArgumentList.at(index: Int): T = this[index] as T

View File

@ -4,5 +4,5 @@ import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.frontend.ImportLocator
interface ExecutionContextProvider {
fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol): ExecutionContext
fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol, nativeRegistry: NativeRegistry): ExecutionContext
}

View File

@ -0,0 +1,45 @@
package gay.pizza.pork.execution
class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
private val functions = mutableMapOf(
"print" to NativeFunction(::printValues),
"println" to NativeFunction(::printLine),
"listSet" to NativeFunction(::setInList),
"listInitWith" to NativeFunction(::listInitWith)
)
fun add(name: String, function: NativeFunction) {
functions[name] = function
}
override fun provideNativeFunction(definitions: List<String>): NativeFunction {
val definition = definitions[0]
return functions[definition] ?:
throw RuntimeException("Unknown internal function: $definition")
}
private fun printValues(arguments: ArgumentList): Any {
if (quiet || arguments.isEmpty()) return None
print(arguments.at<List<*>>(0).joinToString(" ") { it.toString() })
return None
}
private fun printLine(arguments: ArgumentList): Any {
if (quiet) return None
println(arguments.at<List<*>>(0).joinToString(" ") { it.toString() })
return Unit
}
private fun setInList(arguments: ArgumentList): Any {
@Suppress("UNCHECKED_CAST")
val list = arguments[0] as MutableList<Any>
val value = arguments[2]
list[(arguments.at<Number>(0)).toInt()] = value
return value
}
private fun listInitWith(arguments: ArgumentList): Any {
val size = arguments.at<Number>(0).toInt()
return MutableList(size) { arguments[1] }
}
}

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.execution
fun interface NativeFunction {
fun invoke(args: ArgumentList): Any
}

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.execution
interface NativeProvider {
fun provideNativeFunction(definitions: List<String>): NativeFunction
}

View File

@ -0,0 +1,18 @@
package gay.pizza.pork.execution
class NativeRegistry {
private val providers = mutableMapOf<String, NativeProvider>()
fun add(form: String, provider: NativeProvider) {
providers[form] = provider
}
fun forEachProvider(block: (String, NativeProvider) -> Unit) {
for ((form, provider) in providers) {
block(form, provider)
}
}
fun of(form: String): NativeProvider =
providers[form] ?: throw RuntimeException("Unknown native form: ${form}")
}

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.execution
data object None

View File

@ -4,11 +4,13 @@ import com.kenai.jffi.*
import com.kenai.jffi.Function
import gay.pizza.pork.ast.gen.ArgumentSpec
import gay.pizza.pork.evaluator.*
import gay.pizza.pork.execution.ArgumentList
import gay.pizza.pork.execution.None
import kotlin.io.path.Path
import kotlin.io.path.absolutePathString
import kotlin.io.path.exists
class FfiNativeProvider : NativeProvider {
class FfiNativeProvider : ExpandedNativeProvider {
private val internalFunctions = mutableMapOf<String, (ArgumentList) -> Any>(
"ffiStructDefine" to ::ffiStructDefine,
"ffiStructAllocate" to ::ffiStructAllocate,

View File

@ -2,7 +2,7 @@ package gay.pizza.pork.ffi
import com.kenai.jffi.InvocationBuffer
import com.kenai.jffi.MemoryIO
import gay.pizza.pork.evaluator.None
import gay.pizza.pork.execution.None
enum class FfiPrimitiveType(
val id: kotlin.String,

View File

@ -3,7 +3,7 @@ package gay.pizza.pork.ffi
import com.kenai.jffi.InvocationBuffer
import com.kenai.jffi.MemoryIO
import com.kenai.jffi.Struct
import gay.pizza.pork.evaluator.None
import gay.pizza.pork.execution.None
class FfiStruct(val ffiTypeRegistry: FfiTypeRegistry) : FfiType {
private val fields = LinkedHashMap<String, FfiStructField>()

View File

@ -3,12 +3,12 @@ package gay.pizza.pork.ffi
import gay.pizza.pork.ast.gen.ArgumentSpec
import gay.pizza.pork.evaluator.CallableFunction
import gay.pizza.pork.evaluator.SlabContext
import gay.pizza.pork.evaluator.NativeProvider
import gay.pizza.pork.evaluator.None
import gay.pizza.pork.evaluator.ExpandedNativeProvider
import gay.pizza.pork.execution.None
import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType
class JavaNativeProvider : NativeProvider {
class JavaNativeProvider : ExpandedNativeProvider {
private val lookup = MethodHandles.lookup()
override fun provideNativeFunction(

View File

@ -104,7 +104,7 @@ class ExternalSymbolUsageAnalyzer : FunctionLevelVisitor<Unit>() {
}
override fun visitSetAssignment(node: SetAssignment) {
node.visitChildren(this)
node.value.visit(this)
}
override fun visitStringLiteral(node: StringLiteral) {

View File

@ -10,6 +10,7 @@ dependencies {
api(project(":parser"))
api(project(":frontend"))
api(project(":evaluator"))
api(project(":vm"))
api(project(":stdlib"))
api(project(":ffi"))
implementation(project(":common"))

View File

@ -0,0 +1,11 @@
package gay.pizza.pork.minimal
import gay.pizza.pork.evaluator.EvaluatorProvider
import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.frontend.World
import gay.pizza.pork.vm.VirtualMachineProvider
enum class ExecutionType(val id: String, val create: (World) -> ExecutionContextProvider) {
Evaluator("evaluator", { world -> EvaluatorProvider(world) }),
VirtualMachine("virtual-machine", { world -> VirtualMachineProvider(world) })
}

View File

@ -2,8 +2,13 @@ package gay.pizza.pork.minimal
import gay.pizza.pork.ast.gen.CompilationUnit
import gay.pizza.pork.ast.gen.NodeVisitor
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.ast.gen.visit
import gay.pizza.pork.evaluator.*
import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry
import gay.pizza.pork.ffi.FfiNativeProvider
import gay.pizza.pork.ffi.JavaAutogenContentSource
import gay.pizza.pork.ffi.JavaNativeProvider
@ -36,20 +41,13 @@ abstract class Tool {
fun <T> visit(visitor: NodeVisitor<T>): T = visitor.visit(parse())
fun loadMainFunction(setupEvaluator: Evaluator.() -> Unit = {}): CallableFunction {
val world = buildWorld()
val evaluator = Evaluator(world)
setupEvaluator(evaluator)
val resultingScope = evaluator.evaluate(rootImportLocator)
return resultingScope.value("main") as CallableFunction
}
fun createExecutionContextProvider(type: ExecutionType): ExecutionContextProvider =
type.create(buildWorld())
fun loadMainFunctionStandard(quiet: Boolean = false): CallableFunction =
loadMainFunction(setupEvaluator = {
addNativeProvider("internal", InternalNativeProvider(quiet = quiet))
addNativeProvider("ffi", FfiNativeProvider())
addNativeProvider("java", JavaNativeProvider())
})
fun createExecutionContext(type: ExecutionType, symbol: Symbol, nativeRegistry: NativeRegistry): ExecutionContext {
val executionContextProvider = createExecutionContextProvider(type)
return executionContextProvider.prepare(rootImportLocator, symbol, nativeRegistry)
}
fun buildWorld(): World {
val fileContentSource = createContentSource()
@ -59,9 +57,4 @@ abstract class Tool {
dynamicImportSource.addContentSource("java", JavaAutogenContentSource)
return World(dynamicImportSource)
}
fun run(quiet: Boolean = false) {
val main = loadMainFunctionStandard(quiet = quiet)
main.call(emptyList(), CallStack())
}
}

View File

@ -1,7 +1,10 @@
package gay.pizza.pork.minimal
import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.evaluator.Scope
import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry
import kotlin.system.exitProcess
fun main(args: Array<String>) {
@ -11,5 +14,12 @@ fun main(args: Array<String>) {
}
val path = PlatformFsProvider.resolve(args[0])
val tool = FileTool(path)
tool.run()
val nativeRegistry = NativeRegistry()
nativeRegistry.add("internal", InternalNativeProvider(quiet = false))
val main = tool.createExecutionContext(
ExecutionType.Evaluator,
Symbol("main"),
nativeRegistry
)
main.execute()
}

View File

@ -39,6 +39,7 @@ object PorkElementFactory {
NodeType.ForIn -> ForInElement(node)
NodeType.Break -> BreakElement(node)
NodeType.Continue -> ContinueElement(node)
NodeType.Return -> ReturnElement(node)
NodeType.NoneLiteral -> NoneLiteralElement(node)
NodeType.NativeFunctionDescriptor -> NativeFunctionDescriptorElement(node)
NodeType.IndexedBy -> IndexedByElement(node)

View File

@ -0,0 +1,15 @@
// GENERATED CODE FROM PORK AST CODEGEN
package gay.pizza.pork.idea.psi.gen
import com.intellij.lang.ASTNode
import com.intellij.navigation.ItemPresentation
import gay.pizza.pork.idea.psi.PorkElementHelpers
import javax.swing.Icon
class ReturnElement(node: ASTNode) : PorkElement(node) {
override fun getIcon(flags: Int): Icon? =
PorkElementHelpers.iconOf(this)
override fun getPresentation(): ItemPresentation? =
PorkElementHelpers.presentationOf(this)
}

View File

@ -6,7 +6,6 @@ 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")
@ -32,7 +31,5 @@ class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "c
println(" ${symbol.offset + index.toUInt()} ${op}${annotation}")
}
}
val vm = VirtualMachine(compiledWorld)
vm.execute()
}
}

View File

@ -2,33 +2,33 @@ package gay.pizza.pork.tool
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.options.default
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.enum
import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.evaluator.*
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry
import gay.pizza.pork.minimal.ExecutionType
import gay.pizza.pork.minimal.FileTool
class RunCommand : CliktCommand(help = "Run Program", name = "run") {
val loop by option("--loop", help = "Loop Program").flag()
val measure by option("--measure", help = "Measure Time").flag()
val quiet by option("--quiet", help = "Silence Prints").flag()
val dumpScope by option("--dump-scope", help = "Dump Scope").flag()
val executionType by option("--execution-type", "-E")
.enum<ExecutionType> { it.id }
.default(ExecutionType.VirtualMachine)
val path by argument("file")
override fun run() {
val tool = FileTool(PlatformFsProvider.resolve(path))
val main = tool.loadMainFunctionStandard(quiet = quiet)
if (dumpScope) {
val functionContext = main as FunctionContext
val internalScope = functionContext.slabContext.internalScope
internalScope.crawlScopePath { key, path ->
println("[scope] $key [${path.joinToString(" -> ")}]")
}
}
val nativeRegistry = NativeRegistry()
nativeRegistry.add("internal", InternalNativeProvider(quiet = quiet))
val main = tool.createExecutionContext(executionType, Symbol("main"), nativeRegistry)
maybeLoopAndMeasure(loop, measure) {
main.call(emptyList(), CallStack())
main.execute()
}
}
}

View File

@ -2,8 +2,9 @@ package gay.pizza.pork.vm
import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.bytecode.ConstantTag
import gay.pizza.pork.execution.NativeRegistry
class InternalMachine(val world: CompiledWorld, val handlers: List<OpHandler>) {
class InternalMachine(val world: CompiledWorld, val nativeRegistry: NativeRegistry, 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.")

View File

@ -0,0 +1,56 @@
package gay.pizza.pork.vm
import gay.pizza.pork.vm.ops.*
val StandardOpHandlers: List<OpHandler> = listOf(
IntegerOpHandler,
ConstantOpHandler,
TrueOpHandler,
FalseOpHandler,
NoneOpHandler,
ListMakeOpHandler,
ListSizeOpHandler,
IndexOpHandler,
AndOpHandler,
OrOpHandler,
NotOpHandler,
CompareEqualOpHandler,
CompareLesserOpHandler,
CompareGreaterOpHandler,
CompareLesserEqualOpHandler,
CompareGreaterEqualOpHandler,
AddOpHandler,
SubtractOpHandler,
MultiplyOpHandler,
UnaryMinusOpHandler,
UnaryPlusOpHandler,
BinaryAndOpHandler,
BinaryOrOpHandler,
BinaryNotOpHandler,
JumpOpHandler,
JumpIfOpHandler,
LoadLocalOpHandler,
StoreLocalOpHandler,
ReturnAddressOpHandler,
CallOpHandler,
ReturnOpHandler,
NativeOpHandler,
ScopeInOpHandler,
ScopeOutOpHandler,
EndOpHandler
)

View File

@ -2,57 +2,21 @@ package gay.pizza.pork.vm
import gay.pizza.pork.bytecode.CompiledWorld
import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.vm.ops.*
import gay.pizza.pork.execution.NativeRegistry
class VirtualMachine(world: CompiledWorld) : ExecutionContext {
private val internal = InternalMachine(world, listOf(
IntegerOpHandler,
ConstantOpHandler,
TrueOpHandler,
FalseOpHandler,
NoneOpHandler,
ListMakeOpHandler,
ListSizeOpHandler,
IndexOpHandler,
AndOpHandler,
OrOpHandler,
NotOpHandler,
CompareEqualOpHandler,
CompareLesserEqualOpHandler,
CompareGreaterEqualOpHandler,
AddOpHandler,
JumpOpHandler,
JumpIfOpHandler,
LoadLocalOpHandler,
StoreLocalOpHandler,
ReturnAddressOpHandler,
CallOpHandler,
ReturnOpHandler,
NativeOpHandler,
ScopeInOpHandler,
ScopeOutOpHandler,
EndOpHandler
))
class VirtualMachine(world: CompiledWorld, nativeRegistry: NativeRegistry) : ExecutionContext {
private val internal = InternalMachine(
world = world,
nativeRegistry = nativeRegistry,
handlers = StandardOpHandlers
)
override fun execute() {
internal.reset()
while (true) {
if (!internal.step()) {
break
}
}
internal.reset()
}
}

View File

@ -4,17 +4,18 @@ 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.execution.NativeRegistry
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 {
override fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol, nativeRegistry: NativeRegistry): ExecutionContext {
val compiler = Compiler()
val slab = world.load(importLocator)
val compilableSlab = compiler.compilableSlabs.of(slab)
val compilableSymbol = compilableSlab.resolve(entryPointSymbol) ?:
throw RuntimeException("Unable to find compilable symbol for entry point '${entryPointSymbol.id}'")
val compiledWorld = compiler.compile(compilableSymbol)
return VirtualMachine(compiledWorld)
return VirtualMachine(compiledWorld, nativeRegistry)
}
}

View File

@ -0,0 +1,14 @@
package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
object BinaryAndOpHandler : OpHandler(Opcode.BinaryAnd) {
override fun handle(machine: InternalMachine, op: Op) {
val right = machine.pop<Int>()
val left = machine.pop<Int>()
machine.push(left and 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 BinaryNotOpHandler : OpHandler(Opcode.BinaryNot) {
override fun handle(machine: InternalMachine, op: Op) {
val value = machine.pop<Int>()
machine.push(value.inv())
}
}

View File

@ -0,0 +1,14 @@
package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
object BinaryOrOpHandler : OpHandler(Opcode.BinaryOr) {
override fun handle(machine: InternalMachine, op: Op) {
val right = machine.pop<Int>()
val left = machine.pop<Int>()
machine.push(left or right)
}
}

View File

@ -0,0 +1,14 @@
package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
object CompareGreaterOpHandler : OpHandler(Opcode.CompareGreater) {
override fun handle(machine: InternalMachine, op: Op) {
val right = machine.pop<Int>()
val left = machine.pop<Int>()
machine.push(left > right)
}
}

View File

@ -0,0 +1,14 @@
package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
object CompareLesserOpHandler : OpHandler(Opcode.CompareLesser) {
override fun handle(machine: InternalMachine, op: Op) {
val right = machine.pop<Int>()
val left = machine.pop<Int>()
machine.push(left < right)
}
}

View File

@ -0,0 +1,14 @@
package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
object MultiplyOpHandler : OpHandler(Opcode.Multiply) {
override fun handle(machine: InternalMachine, op: Op) {
val left = machine.pop<Int>()
val right = machine.pop<Int>()
machine.push(left * right)
}
}

View File

@ -7,10 +7,21 @@ 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(machine.pop() as String)
val argumentCount = op.args[2]
val arguments = mutableListOf<Any>()
for (i in 0u until argumentCount) {
machine.loadLocal(i)
arguments.add(machine.popAnyValue())
}
val formConstant = machine.world.constantPool.read(op.args[0])
val form = formConstant.readAsString()
val provider = machine.nativeRegistry.of(form)
val countOfNativeDefs = op.args[1].toInt()
val defs = mutableListOf<String>()
for (i in 0 until countOfNativeDefs) {
defs.add(machine.pop())
}
val function = provider.provideNativeFunction(defs)
function.invoke(arguments)
}
}

View File

@ -0,0 +1,14 @@
package gay.pizza.pork.vm.ops
import gay.pizza.pork.bytecode.Op
import gay.pizza.pork.bytecode.Opcode
import gay.pizza.pork.vm.InternalMachine
import gay.pizza.pork.vm.OpHandler
object SubtractOpHandler : OpHandler(Opcode.Subtract) {
override fun handle(machine: InternalMachine, op: Op) {
val left = machine.pop<Int>()
val right = machine.pop<Int>()
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 UnaryMinusOpHandler : OpHandler(Opcode.UnaryMinus) {
override fun handle(machine: InternalMachine, op: Op) {
val value = machine.pop<Int>()
machine.push(-value)
}
}

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 UnaryPlusOpHandler : OpHandler(Opcode.UnaryPlus) {
override fun handle(machine: InternalMachine, op: Op) {
val value = machine.pop<Int>()
machine.push(+value)
}
}