mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 12:50:55 +00:00
global: a working virtual machine for some of the use cases. APIs and validation still WIP.
This commit is contained in:
parent
0a2d029c5c
commit
6211ad4ff1
@ -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
|
||||
|
@ -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()]
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) }
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
typealias ArgumentList = List<Any>
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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}" }
|
||||
|
@ -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] }
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
data object None
|
@ -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(),
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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++
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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
|
||||
}
|
||||
|
@ -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] }
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.execution
|
||||
|
||||
fun interface NativeFunction {
|
||||
fun invoke(args: ArgumentList): Any
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.execution
|
||||
|
||||
interface NativeProvider {
|
||||
fun provideNativeFunction(definitions: List<String>): NativeFunction
|
||||
}
|
@ -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}")
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package gay.pizza.pork.execution
|
||||
|
||||
data object None
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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>()
|
||||
|
@ -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(
|
||||
|
@ -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) {
|
||||
|
@ -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"))
|
||||
|
@ -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) })
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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.")
|
||||
|
56
vm/src/main/kotlin/gay/pizza/pork/vm/StandardOpHandlers.kt
Normal file
56
vm/src/main/kotlin/gay/pizza/pork/vm/StandardOpHandlers.kt
Normal 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
|
||||
)
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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())
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user