mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 21:21:33 +00:00
vm: very basic virtual machine
This commit is contained in:
@ -4,6 +4,7 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
api(project(":ast"))
|
||||
api(project(":execution"))
|
||||
api(project(":frontend"))
|
||||
|
||||
implementation(project(":common"))
|
||||
|
@ -1,78 +0,0 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
import gay.pizza.pork.ast.gen.*
|
||||
import gay.pizza.pork.frontend.ImportLocator
|
||||
|
||||
class CompilationUnitContext(
|
||||
val compilationUnit: CompilationUnit,
|
||||
val evaluator: Evaluator,
|
||||
rootScope: Scope,
|
||||
val name: String = "unknown"
|
||||
) {
|
||||
val internalScope = rootScope.fork("internal $name")
|
||||
val externalScope = rootScope.fork("external $name")
|
||||
|
||||
private var initialized = false
|
||||
|
||||
fun initIfNeeded() {
|
||||
if (initialized) {
|
||||
return
|
||||
}
|
||||
initialized = true
|
||||
processAllImports()
|
||||
processAllDefinitions()
|
||||
}
|
||||
|
||||
private fun processAllDefinitions() {
|
||||
for (definition in compilationUnit.definitions) {
|
||||
processDefinition(definition)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processDefinition(definition: Definition) {
|
||||
val internalValue = definitionValue(definition)
|
||||
internalScope.define(definition.symbol.id, internalValue)
|
||||
if (definition.modifiers.export) {
|
||||
externalScope.define(definition.symbol.id, internalValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun definitionValue(definition: Definition): Any = when (definition) {
|
||||
is FunctionDefinition -> FunctionContext(this, definition)
|
||||
is LetDefinition -> {
|
||||
EvaluationVisitor(internalScope.fork("let ${definition.symbol.id}"), CallStack())
|
||||
.visit(definition.value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processAllImports() {
|
||||
processPreludeImport()
|
||||
val imports = compilationUnit.declarations.filterIsInstance<ImportDeclaration>()
|
||||
for (import in imports) {
|
||||
processImport(import)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processImport(import: ImportDeclaration) {
|
||||
val importPath = import.path.components.joinToString("/") { it.id } + ".pork"
|
||||
val importLocator = ImportLocator(import.form.id, importPath)
|
||||
val evaluationContext = evaluator.context(importLocator)
|
||||
internalScope.inherit(evaluationContext.externalScope)
|
||||
}
|
||||
|
||||
private fun processPreludeImport() {
|
||||
processImport(preludeImport)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val preludeImport = ImportDeclaration(
|
||||
Symbol("std"),
|
||||
ImportPath(
|
||||
listOf(
|
||||
Symbol("lang"),
|
||||
Symbol("prelude")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
import gay.pizza.pork.ast.FunctionLevelVisitor
|
||||
import gay.pizza.pork.ast.gen.*
|
||||
import kotlin.math.abs
|
||||
|
||||
@Suppress("JavaIoSerializableObjectMustHaveReadResolve")
|
||||
class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
|
||||
class EvaluationVisitor(root: Scope, val stack: CallStack) : FunctionLevelVisitor<Any>() {
|
||||
private var currentScope: Scope = root
|
||||
|
||||
override fun visitIntegerLiteral(node: IntegerLiteral): Any = node.value
|
||||
@ -66,10 +67,6 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
|
||||
return value
|
||||
}
|
||||
|
||||
override fun visitLetDefinition(node: LetDefinition): Any {
|
||||
topLevelUsedError("LetDefinition", "CompilationUnitContext")
|
||||
}
|
||||
|
||||
override fun visitSymbolReference(node: SymbolReference): Any =
|
||||
currentScope.value(node.symbol.id)
|
||||
|
||||
@ -383,18 +380,6 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFunctionDefinition(node: FunctionDefinition): Any {
|
||||
topLevelUsedError("FunctionDefinition", "FunctionContext")
|
||||
}
|
||||
|
||||
override fun visitImportDeclaration(node: ImportDeclaration): Any {
|
||||
topLevelUsedError("ImportDeclaration", "CompilationUnitContext")
|
||||
}
|
||||
|
||||
override fun visitImportPath(node: ImportPath): Any {
|
||||
topLevelUsedError("ImportPath", "CompilationUnitContext")
|
||||
}
|
||||
|
||||
override fun visitIndexedBy(node: IndexedBy): Any {
|
||||
val value = node.expression.visit(this)
|
||||
val index = node.index.visit(this)
|
||||
@ -410,14 +395,6 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
|
||||
throw RuntimeException("Failed to index '${value}' by '${index}': Unsupported types used.")
|
||||
}
|
||||
|
||||
override fun visitCompilationUnit(node: CompilationUnit): Any {
|
||||
topLevelUsedError("CompilationUnit", "CompilationUnitContext")
|
||||
}
|
||||
|
||||
override fun visitNativeFunctionDescriptor(node: NativeFunctionDescriptor): Any {
|
||||
topLevelUsedError("NativeFunctionDescriptor", "FunctionContext")
|
||||
}
|
||||
|
||||
override fun visitNoneLiteral(node: NoneLiteral): Any = None
|
||||
|
||||
override fun visitContinue(node: Continue): Any = ContinueMarker
|
||||
@ -445,13 +422,6 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor<Any> {
|
||||
throw RuntimeException("Can't perform $operation between floating point types")
|
||||
}
|
||||
|
||||
private fun topLevelUsedError(name: String, alternative: String): Nothing {
|
||||
throw RuntimeException(
|
||||
"$name cannot be visited in an EvaluationVisitor. " +
|
||||
"Utilize an $alternative instead."
|
||||
)
|
||||
}
|
||||
|
||||
private object BreakMarker : RuntimeException("Break Marker")
|
||||
private object ContinueMarker: RuntimeException("Continue Marker")
|
||||
}
|
||||
|
@ -1,25 +1,33 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
import gay.pizza.pork.ast.gen.Symbol
|
||||
import gay.pizza.pork.execution.ExecutionContext
|
||||
import gay.pizza.pork.execution.ExecutionContextProvider
|
||||
import gay.pizza.pork.frontend.ImportLocator
|
||||
import gay.pizza.pork.frontend.Slab
|
||||
import gay.pizza.pork.frontend.World
|
||||
|
||||
class Evaluator(val world: World, val scope: Scope) {
|
||||
private val contexts = mutableMapOf<String, CompilationUnitContext>()
|
||||
class Evaluator(val world: World) : ExecutionContextProvider {
|
||||
private val scope = Scope.root()
|
||||
private val contexts = mutableMapOf<Slab, SlabContext>()
|
||||
private val nativeProviders = mutableMapOf<String, NativeProvider>()
|
||||
|
||||
fun evaluate(locator: ImportLocator): Scope =
|
||||
context(locator).externalScope
|
||||
|
||||
fun context(locator: ImportLocator): CompilationUnitContext {
|
||||
val unit = world.load(locator)
|
||||
val identity = world.stableIdentity(locator)
|
||||
val context = contexts.computeIfAbsent(identity) {
|
||||
CompilationUnitContext(unit, this, scope, name = "${locator.form} ${locator.path}")
|
||||
}
|
||||
context.initIfNeeded()
|
||||
return context
|
||||
fun evaluate(locator: ImportLocator): Scope {
|
||||
val slabContext = context(locator)
|
||||
slabContext.finalizeScope()
|
||||
return slabContext.externalScope
|
||||
}
|
||||
|
||||
fun context(slab: Slab): SlabContext {
|
||||
val slabContext = contexts.computeIfAbsent(slab) {
|
||||
SlabContext(slab, this, scope)
|
||||
}
|
||||
slabContext.ensureImportedContextsExist()
|
||||
return slabContext
|
||||
}
|
||||
|
||||
fun context(locator: ImportLocator): SlabContext = context(world.load(locator))
|
||||
|
||||
fun nativeFunctionProvider(form: String): NativeProvider {
|
||||
return nativeProviders[form] ?:
|
||||
throw RuntimeException("Unknown native function form: $form")
|
||||
@ -28,4 +36,10 @@ class Evaluator(val world: World, val scope: Scope) {
|
||||
fun addNativeProvider(form: String, nativeProvider: NativeProvider) {
|
||||
nativeProviders[form] = nativeProvider
|
||||
}
|
||||
|
||||
override fun prepare(importLocator: ImportLocator, entryPointSymbol: Symbol): ExecutionContext {
|
||||
val slab = context(importLocator)
|
||||
slab.finalizeScope()
|
||||
return EvaluatorExecutionContext(this, slab, entryPointSymbol)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
import gay.pizza.pork.ast.gen.Symbol
|
||||
import gay.pizza.pork.execution.ExecutionContext
|
||||
|
||||
class EvaluatorExecutionContext(val evaluator: Evaluator, val slab: SlabContext, val entryPointSymbol: Symbol) : ExecutionContext {
|
||||
private val function: CallableFunction by lazy {
|
||||
val value = slab.externalScope.value(entryPointSymbol.id)
|
||||
if (value !is CallableFunction) {
|
||||
throw RuntimeException("Symbol '${entryPointSymbol.id}' resolves to a non-function.")
|
||||
}
|
||||
value
|
||||
}
|
||||
|
||||
override fun execute() {
|
||||
function.call(emptyList(), CallStack())
|
||||
}
|
||||
}
|
@ -2,16 +2,16 @@ package gay.pizza.pork.evaluator
|
||||
|
||||
import gay.pizza.pork.ast.gen.FunctionDefinition
|
||||
|
||||
class FunctionContext(val compilationUnitContext: CompilationUnitContext, val node: FunctionDefinition) : CallableFunction {
|
||||
val name: String = "${compilationUnitContext.name} ${node.symbol.id}"
|
||||
class FunctionContext(val slabContext: SlabContext, val node: FunctionDefinition) : CallableFunction {
|
||||
val name: String by lazy { "${slabContext.slab.location.commonFriendlyName} ${node.symbol.id}" }
|
||||
|
||||
private fun resolveMaybeNative(): CallableFunction? = if (node.nativeFunctionDescriptor == null) {
|
||||
null
|
||||
} else {
|
||||
val native = node.nativeFunctionDescriptor!!
|
||||
val nativeFunctionProvider =
|
||||
compilationUnitContext.evaluator.nativeFunctionProvider(native.form.id)
|
||||
nativeFunctionProvider.provideNativeFunction(native.definitions.map { it.text }, node.arguments, compilationUnitContext)
|
||||
slabContext.evaluator.nativeFunctionProvider(native.form.id)
|
||||
nativeFunctionProvider.provideNativeFunction(native.definitions.map { it.text }, node.arguments, slabContext)
|
||||
}
|
||||
|
||||
private val nativeCached by lazy { resolveMaybeNative() }
|
||||
@ -21,7 +21,7 @@ class FunctionContext(val compilationUnitContext: CompilationUnitContext, val no
|
||||
return nativeCached!!.call(arguments, stack)
|
||||
}
|
||||
|
||||
val scope = compilationUnitContext.internalScope.fork(node.symbol.id)
|
||||
val scope = slabContext.internalScope.fork(node.symbol.id)
|
||||
for ((index, spec) in node.arguments.withIndex()) {
|
||||
if (spec.multiple) {
|
||||
val list = arguments.subList(index, arguments.size - 1)
|
||||
|
@ -18,7 +18,7 @@ class InternalNativeProvider(val quiet: Boolean = false) : NativeProvider {
|
||||
override fun provideNativeFunction(
|
||||
definitions: List<String>,
|
||||
arguments: List<ArgumentSpec>,
|
||||
inside: CompilationUnitContext
|
||||
inside: SlabContext
|
||||
): CallableFunction {
|
||||
val definition = definitions[0]
|
||||
return functions[definition] ?: throw RuntimeException("Unknown Internal Function: $definition")
|
||||
|
@ -3,5 +3,5 @@ package gay.pizza.pork.evaluator
|
||||
import gay.pizza.pork.ast.gen.ArgumentSpec
|
||||
|
||||
interface NativeProvider {
|
||||
fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>, inside: CompilationUnitContext): CallableFunction
|
||||
fun provideNativeFunction(definitions: List<String>, arguments: List<ArgumentSpec>, inside: SlabContext): CallableFunction
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
import gay.pizza.pork.ast.gen.Definition
|
||||
import gay.pizza.pork.ast.gen.FunctionDefinition
|
||||
import gay.pizza.pork.ast.gen.LetDefinition
|
||||
import gay.pizza.pork.ast.gen.visit
|
||||
import gay.pizza.pork.frontend.Slab
|
||||
|
||||
class SlabContext(val slab: Slab, val evaluator: Evaluator, rootScope: Scope) {
|
||||
val internalScope = rootScope.fork("internal ${slab.location.commonFriendlyName}")
|
||||
val externalScope = rootScope.fork("external ${slab.location.commonFriendlyName}")
|
||||
|
||||
init {
|
||||
processAllDefinitions()
|
||||
}
|
||||
|
||||
fun ensureImportedContextsExist() {
|
||||
for (importedSlab in slab.importedSlabs) {
|
||||
evaluator.context(importedSlab)
|
||||
}
|
||||
}
|
||||
|
||||
private var initializedFinalScope = false
|
||||
|
||||
fun finalizeScope() {
|
||||
if (initializedFinalScope) {
|
||||
return
|
||||
}
|
||||
initializedFinalScope = true
|
||||
processFinalImportScopes()
|
||||
}
|
||||
|
||||
private fun processAllDefinitions() {
|
||||
for (definition in slab.compilationUnit.definitions) {
|
||||
processDefinition(definition)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processDefinition(definition: Definition) {
|
||||
val internalValue = definitionValue(definition)
|
||||
internalScope.define(definition.symbol.id, internalValue)
|
||||
if (definition.modifiers.export) {
|
||||
externalScope.define(definition.symbol.id, internalValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun definitionValue(definition: Definition): Any = when (definition) {
|
||||
is FunctionDefinition -> FunctionContext(this, definition)
|
||||
is LetDefinition -> {
|
||||
EvaluationVisitor(internalScope.fork("let ${definition.symbol.id}"), CallStack())
|
||||
.visit(definition.value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processFinalImportScopes() {
|
||||
for (importedSlab in slab.importedSlabs) {
|
||||
val importedSlabContext = evaluator.context(importedSlab)
|
||||
importedSlabContext.processFinalImportScopes()
|
||||
internalScope.inherit(importedSlabContext.externalScope)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user