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 @Serializable
data class Constant(val id: UInt, val tag: ConstantTag, val value: ByteArray) { 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 { override fun equals(other: Any?): Boolean {
if (this === other) return true if (this === other) return true
other as Constant other as Constant

View File

@ -3,4 +3,6 @@ package gay.pizza.pork.bytecode
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@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 { fun resolve(symbol: Symbol): CompilableSymbol? = compilableSymbols.firstOrNull {
it.scopeSymbol.symbol == symbol 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) return Loadable(stubVar = found)
} }
} }
val found = this.symbol.compilableSlab.resolve(symbol) val found = this.symbol.compilableSlab.resolveVisible(symbol)
if (found != null) { if (found != null) {
return Loadable(call = found) 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)) code.emit(Opcode.Constant, listOf(defConstant))
} }
val formConstant = compiler.constantPool.assign(ConstantTag.String, node.form.id.toByteArray()) val formConstant = compiler.constantPool.assign(ConstantTag.String, node.form.id.toByteArray())
code.emit(Opcode.Native, listOf(formConstant, node.definitions.size.toUInt())) 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) { 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 package gay.pizza.pork.evaluator
import gay.pizza.pork.execution.ArgumentList
fun interface CallableFunction { fun interface CallableFunction {
fun call(arguments: ArgumentList, stack: CallStack): Any 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.FunctionLevelVisitor
import gay.pizza.pork.ast.gen.* import gay.pizza.pork.ast.gen.*
import gay.pizza.pork.execution.None
import kotlin.math.abs import kotlin.math.abs
@Suppress("JavaIoSerializableObjectMustHaveReadResolve") @Suppress("JavaIoSerializableObjectMustHaveReadResolve")

View File

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

View File

@ -0,0 +1,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 import gay.pizza.pork.ast.gen.ArgumentSpec
interface NativeProvider { interface ExpandedNativeProvider {
fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>, inside: SlabContext): CallableFunction fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>, inside: SlabContext): CallableFunction
} }

View File

@ -1,6 +1,7 @@
package gay.pizza.pork.evaluator package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.gen.FunctionDefinition import gay.pizza.pork.ast.gen.FunctionDefinition
import gay.pizza.pork.execution.ArgumentList
class FunctionContext(val slabContext: SlabContext, val node: FunctionDefinition) : CallableFunction { class FunctionContext(val slabContext: SlabContext, val node: FunctionDefinition) : CallableFunction {
val name: String by lazy { "${slabContext.slab.location.commonFriendlyName} ${node.symbol.id}" } 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 package gay.pizza.pork.evaluator
import gay.pizza.pork.execution.None
class Scope( class Scope(
var parent: Scope? = null, var parent: Scope? = null,
var inherits: List<Scope> = emptyList(), var inherits: List<Scope> = emptyList(),

View File

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

View File

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

View File

@ -1,11 +1,7 @@
export func main() { export func main() {
var x = 1 var x = 1
while x <= 5 { while x <= 5 {
if x == 3 { println(x)
println("The value is 3")
} else {
println("The value is not 3")
}
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 import gay.pizza.pork.frontend.ImportLocator
interface ExecutionContextProvider { 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 com.kenai.jffi.Function
import gay.pizza.pork.ast.gen.ArgumentSpec import gay.pizza.pork.ast.gen.ArgumentSpec
import gay.pizza.pork.evaluator.* 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.Path
import kotlin.io.path.absolutePathString import kotlin.io.path.absolutePathString
import kotlin.io.path.exists import kotlin.io.path.exists
class FfiNativeProvider : NativeProvider { class FfiNativeProvider : ExpandedNativeProvider {
private val internalFunctions = mutableMapOf<String, (ArgumentList) -> Any>( private val internalFunctions = mutableMapOf<String, (ArgumentList) -> Any>(
"ffiStructDefine" to ::ffiStructDefine, "ffiStructDefine" to ::ffiStructDefine,
"ffiStructAllocate" to ::ffiStructAllocate, "ffiStructAllocate" to ::ffiStructAllocate,

View File

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

View File

@ -3,7 +3,7 @@ package gay.pizza.pork.ffi
import com.kenai.jffi.InvocationBuffer import com.kenai.jffi.InvocationBuffer
import com.kenai.jffi.MemoryIO import com.kenai.jffi.MemoryIO
import com.kenai.jffi.Struct import com.kenai.jffi.Struct
import gay.pizza.pork.evaluator.None import gay.pizza.pork.execution.None
class FfiStruct(val ffiTypeRegistry: FfiTypeRegistry) : FfiType { class FfiStruct(val ffiTypeRegistry: FfiTypeRegistry) : FfiType {
private val fields = LinkedHashMap<String, FfiStructField>() 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.ast.gen.ArgumentSpec
import gay.pizza.pork.evaluator.CallableFunction import gay.pizza.pork.evaluator.CallableFunction
import gay.pizza.pork.evaluator.SlabContext import gay.pizza.pork.evaluator.SlabContext
import gay.pizza.pork.evaluator.NativeProvider import gay.pizza.pork.evaluator.ExpandedNativeProvider
import gay.pizza.pork.evaluator.None import gay.pizza.pork.execution.None
import java.lang.invoke.MethodHandles import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType import java.lang.invoke.MethodType
class JavaNativeProvider : NativeProvider { class JavaNativeProvider : ExpandedNativeProvider {
private val lookup = MethodHandles.lookup() private val lookup = MethodHandles.lookup()
override fun provideNativeFunction( override fun provideNativeFunction(

View File

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

View File

@ -10,6 +10,7 @@ dependencies {
api(project(":parser")) api(project(":parser"))
api(project(":frontend")) api(project(":frontend"))
api(project(":evaluator")) api(project(":evaluator"))
api(project(":vm"))
api(project(":stdlib")) api(project(":stdlib"))
api(project(":ffi")) api(project(":ffi"))
implementation(project(":common")) 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.CompilationUnit
import gay.pizza.pork.ast.gen.NodeVisitor 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.ast.gen.visit
import gay.pizza.pork.evaluator.* 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.FfiNativeProvider
import gay.pizza.pork.ffi.JavaAutogenContentSource import gay.pizza.pork.ffi.JavaAutogenContentSource
import gay.pizza.pork.ffi.JavaNativeProvider import gay.pizza.pork.ffi.JavaNativeProvider
@ -36,20 +41,13 @@ abstract class Tool {
fun <T> visit(visitor: NodeVisitor<T>): T = visitor.visit(parse()) fun <T> visit(visitor: NodeVisitor<T>): T = visitor.visit(parse())
fun loadMainFunction(setupEvaluator: Evaluator.() -> Unit = {}): CallableFunction { fun createExecutionContextProvider(type: ExecutionType): ExecutionContextProvider =
val world = buildWorld() type.create(buildWorld())
val evaluator = Evaluator(world)
setupEvaluator(evaluator)
val resultingScope = evaluator.evaluate(rootImportLocator)
return resultingScope.value("main") as CallableFunction
}
fun loadMainFunctionStandard(quiet: Boolean = false): CallableFunction = fun createExecutionContext(type: ExecutionType, symbol: Symbol, nativeRegistry: NativeRegistry): ExecutionContext {
loadMainFunction(setupEvaluator = { val executionContextProvider = createExecutionContextProvider(type)
addNativeProvider("internal", InternalNativeProvider(quiet = quiet)) return executionContextProvider.prepare(rootImportLocator, symbol, nativeRegistry)
addNativeProvider("ffi", FfiNativeProvider()) }
addNativeProvider("java", JavaNativeProvider())
})
fun buildWorld(): World { fun buildWorld(): World {
val fileContentSource = createContentSource() val fileContentSource = createContentSource()
@ -59,9 +57,4 @@ abstract class Tool {
dynamicImportSource.addContentSource("java", JavaAutogenContentSource) dynamicImportSource.addContentSource("java", JavaAutogenContentSource)
return World(dynamicImportSource) 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 package gay.pizza.pork.minimal
import gay.pizza.dough.fs.PlatformFsProvider import gay.pizza.dough.fs.PlatformFsProvider
import gay.pizza.pork.ast.gen.Symbol
import gay.pizza.pork.evaluator.Scope import gay.pizza.pork.evaluator.Scope
import gay.pizza.pork.execution.InternalNativeProvider
import gay.pizza.pork.execution.NativeRegistry
import kotlin.system.exitProcess import kotlin.system.exitProcess
fun main(args: Array<String>) { fun main(args: Array<String>) {
@ -11,5 +14,12 @@ fun main(args: Array<String>) {
} }
val path = PlatformFsProvider.resolve(args[0]) val path = PlatformFsProvider.resolve(args[0])
val tool = FileTool(path) 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.ForIn -> ForInElement(node)
NodeType.Break -> BreakElement(node) NodeType.Break -> BreakElement(node)
NodeType.Continue -> ContinueElement(node) NodeType.Continue -> ContinueElement(node)
NodeType.Return -> ReturnElement(node)
NodeType.NoneLiteral -> NoneLiteralElement(node) NodeType.NoneLiteral -> NoneLiteralElement(node)
NodeType.NativeFunctionDescriptor -> NativeFunctionDescriptorElement(node) NodeType.NativeFunctionDescriptor -> NativeFunctionDescriptorElement(node)
NodeType.IndexedBy -> IndexedByElement(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.ast.gen.Symbol
import gay.pizza.pork.compiler.Compiler import gay.pizza.pork.compiler.Compiler
import gay.pizza.pork.minimal.FileTool import gay.pizza.pork.minimal.FileTool
import gay.pizza.pork.vm.VirtualMachine
class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "compile") { class CompileCommand : CliktCommand(help = "Compile Pork to Bytecode", name = "compile") {
val path by argument("file") 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}") 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.core.CliktCommand
import com.github.ajalt.clikt.parameters.arguments.argument 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.flag
import com.github.ajalt.clikt.parameters.options.option 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.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 import gay.pizza.pork.minimal.FileTool
class RunCommand : CliktCommand(help = "Run Program", name = "run") { class RunCommand : CliktCommand(help = "Run Program", name = "run") {
val loop by option("--loop", help = "Loop Program").flag() val loop by option("--loop", help = "Loop Program").flag()
val measure by option("--measure", help = "Measure Time").flag() val measure by option("--measure", help = "Measure Time").flag()
val quiet by option("--quiet", help = "Silence Prints").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") val path by argument("file")
override fun run() { override fun run() {
val tool = FileTool(PlatformFsProvider.resolve(path)) val tool = FileTool(PlatformFsProvider.resolve(path))
val main = tool.loadMainFunctionStandard(quiet = quiet) val nativeRegistry = NativeRegistry()
nativeRegistry.add("internal", InternalNativeProvider(quiet = quiet))
if (dumpScope) { val main = tool.createExecutionContext(executionType, Symbol("main"), nativeRegistry)
val functionContext = main as FunctionContext
val internalScope = functionContext.slabContext.internalScope
internalScope.crawlScopePath { key, path ->
println("[scope] $key [${path.joinToString(" -> ")}]")
}
}
maybeLoopAndMeasure(loop, measure) { 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.CompiledWorld
import gay.pizza.pork.bytecode.ConstantTag 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 -> private val inlined = world.code.map { op ->
val handler = handlers.firstOrNull { it.code == op.code } ?: val handler = handlers.firstOrNull { it.code == op.code } ?:
throw VirtualMachineException("Opcode ${op.code.name} does not have a handler.") 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.bytecode.CompiledWorld
import gay.pizza.pork.execution.ExecutionContext import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.vm.ops.* import gay.pizza.pork.execution.NativeRegistry
class VirtualMachine(world: CompiledWorld) : ExecutionContext { class VirtualMachine(world: CompiledWorld, nativeRegistry: NativeRegistry) : ExecutionContext {
private val internal = InternalMachine(world, listOf( private val internal = InternalMachine(
IntegerOpHandler, world = world,
ConstantOpHandler, nativeRegistry = nativeRegistry,
handlers = StandardOpHandlers
TrueOpHandler, )
FalseOpHandler,
NoneOpHandler,
ListMakeOpHandler,
ListSizeOpHandler,
IndexOpHandler,
AndOpHandler,
OrOpHandler,
NotOpHandler,
CompareEqualOpHandler,
CompareLesserEqualOpHandler,
CompareGreaterEqualOpHandler,
AddOpHandler,
JumpOpHandler,
JumpIfOpHandler,
LoadLocalOpHandler,
StoreLocalOpHandler,
ReturnAddressOpHandler,
CallOpHandler,
ReturnOpHandler,
NativeOpHandler,
ScopeInOpHandler,
ScopeOutOpHandler,
EndOpHandler
))
override fun execute() { override fun execute() {
internal.reset()
while (true) { while (true) {
if (!internal.step()) { if (!internal.step()) {
break 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.compiler.Compiler
import gay.pizza.pork.execution.ExecutionContext import gay.pizza.pork.execution.ExecutionContext
import gay.pizza.pork.execution.ExecutionContextProvider import gay.pizza.pork.execution.ExecutionContextProvider
import gay.pizza.pork.execution.NativeRegistry
import gay.pizza.pork.frontend.ImportLocator import gay.pizza.pork.frontend.ImportLocator
import gay.pizza.pork.frontend.World import gay.pizza.pork.frontend.World
class VirtualMachineProvider(val world: World) : ExecutionContextProvider { 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 compiler = Compiler()
val slab = world.load(importLocator) val slab = world.load(importLocator)
val compilableSlab = compiler.compilableSlabs.of(slab) val compilableSlab = compiler.compilableSlabs.of(slab)
val compilableSymbol = compilableSlab.resolve(entryPointSymbol) ?: val compilableSymbol = compilableSlab.resolve(entryPointSymbol) ?:
throw RuntimeException("Unable to find compilable symbol for entry point '${entryPointSymbol.id}'") throw RuntimeException("Unable to find compilable symbol for entry point '${entryPointSymbol.id}'")
val compiledWorld = compiler.compile(compilableSymbol) 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) { object NativeOpHandler : OpHandler(Opcode.Native) {
override fun handle(machine: InternalMachine, op: Op) { override fun handle(machine: InternalMachine, op: Op) {
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 countOfNativeDefs = op.args[1].toInt()
val defs = mutableListOf<Any>() val defs = mutableListOf<String>()
for (i in 0 until countOfNativeDefs) { for (i in 0 until countOfNativeDefs) {
defs.add(machine.pop() as String) 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)
}
}