while loop support, and native functions (including ffi!)

This commit is contained in:
2023-09-06 19:07:28 -07:00
parent ddff6cb365
commit 236f812caf
34 changed files with 467 additions and 115 deletions

View File

@ -39,7 +39,7 @@ class CompilationUnitContext(
}
private fun definitionValue(definition: Definition): Any = when (definition) {
is FunctionDefinition -> FunctionContext(definition, internalScope)
is FunctionDefinition -> FunctionContext(this, definition)
}
private fun processAllImports() {

View File

@ -8,6 +8,9 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitIntLiteral(node: IntLiteral): Any = node.value
override fun visitStringLiteral(node: StringLiteral): Any = node.text
override fun visitBooleanLiteral(node: BooleanLiteral): Any = node.value
override fun visitBreak(node: Break): Any = throw BreakMarker
override fun visitListLiteral(node: ListLiteral): Any =
node.items.map { it.visit(this) }
@ -15,7 +18,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitFunctionCall(node: FunctionCall): Any {
val arguments = node.arguments.map { it.visit(this) }
return currentScope.call(node.symbol.id, Arguments(arguments))
val functionValue = currentScope.value(node.symbol.id) as CallableFunction
return functionValue.call(Arguments(arguments))
}
override fun visitLetAssignment(node: LetAssignment): Any {
@ -27,6 +31,26 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitSymbolReference(node: SymbolReference): Any =
currentScope.value(node.symbol.id)
override fun visitWhile(node: While): Any {
val blockFunction = node.block.visit(this) as BlockFunction
var result: Any? = null
while (true) {
val value = node.condition.visit(this)
if (value !is Boolean) {
throw RuntimeException("While loop attempted on non-boolean value: $value")
}
if (!value) break
try {
scoped { result = blockFunction.call() }
} catch (_: BreakMarker) {
break
} catch (_: ContinueMarker) {
continue
}
}
return result ?: None
}
override fun visitParentheses(node: Parentheses): Any =
node.expression.visit(this)
@ -45,11 +69,12 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitIf(node: If): Any {
val condition = node.condition.visit(this)
return if (condition == true) {
node.thenExpression.visit(this)
} else {
val elseExpression = node.elseExpression
elseExpression?.visit(this) ?: None
}
val blockFunction = node.thenBlock.visit(this) as BlockFunction
scoped { blockFunction.call() }
} else if (node.elseBlock != null) {
val blockFunction = node.elseBlock!!.visit(this) as BlockFunction
scoped { blockFunction.call() }
} else None
}
override fun visitInfixOperation(node: InfixOperation): Any {
@ -93,21 +118,42 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitFunctionDefinition(node: FunctionDefinition): Any {
throw RuntimeException(
"Function declarations cannot be visited in an EvaluationVisitor. " +
"Utilize a FunctionContext."
"Utilize a FunctionContext."
)
}
override fun visitImportDeclaration(node: ImportDeclaration): Any {
throw RuntimeException(
"Import declarations cannot be visited in an EvaluationVisitor. " +
"Utilize an EvaluationContext."
"Utilize an CompilationUnitContext."
)
}
override fun visitCompilationUnit(node: CompilationUnit): Any {
throw RuntimeException(
"Compilation units cannot be visited in an EvaluationVisitor. " +
"Utilize an EvaluationContext."
"Utilize an CompilationUnitContext."
)
}
override fun visitNative(node: Native): Any {
throw RuntimeException(
"Native definition cannot be visited in an EvaluationVisitor. " +
"Utilize an FunctionContext."
)
}
override fun visitContinue(node: Continue): Any = ContinueMarker
private inline fun <T> scoped(block: () -> T): T {
currentScope = currentScope.fork()
try {
return block()
} finally {
currentScope = currentScope.leave()
}
}
private object BreakMarker : RuntimeException("Break Marker")
private object ContinueMarker: RuntimeException("Continue Marker")
}

View File

@ -4,6 +4,7 @@ import gay.pizza.pork.frontend.World
class Evaluator(val world: World, val scope: Scope) {
private val contexts = mutableMapOf<String, CompilationUnitContext>()
private val nativeFunctionProviders = mutableMapOf<String, NativeFunctionProvider>()
fun evaluate(path: String): Scope =
context(path).externalScope
@ -17,4 +18,13 @@ class Evaluator(val world: World, val scope: Scope) {
context.initIfNeeded()
return context
}
fun nativeFunctionProvider(form: String): NativeFunctionProvider {
return nativeFunctionProviders[form] ?:
throw RuntimeException("Unknown native function form: $form")
}
fun addNativeFunctionProvider(form: String, nativeFunctionProvider: NativeFunctionProvider) {
nativeFunctionProviders[form] = nativeFunctionProvider
}
}

View File

@ -2,14 +2,27 @@ package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.FunctionDefinition
class FunctionContext(val node: FunctionDefinition, val internalScope: Scope) : CallableFunction {
class FunctionContext(val compilationUnitContext: CompilationUnitContext, val node: FunctionDefinition) : CallableFunction {
override fun call(arguments: Arguments): Any {
val scope = internalScope.fork()
val scope = compilationUnitContext.internalScope.fork()
for ((index, argumentSymbol) in node.arguments.withIndex()) {
scope.define(argumentSymbol.id, arguments.values[index])
}
if (node.native != null) {
val native = node.native!!
val nativeFunctionProvider =
compilationUnitContext.evaluator.nativeFunctionProvider(native.form.id)
val nativeFunction = nativeFunctionProvider.provideNativeFunction(native.definition.text)
return nativeFunction.call(arguments)
}
if (node.block == null) {
throw RuntimeException("Native or Block is required for FunctionDefinition")
}
val visitor = EvaluationVisitor(scope)
val blockFunction = visitor.visitBlock(node.block)
val blockFunction = visitor.visitBlock(node.block!!)
return blockFunction.call()
}
}

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.evaluator
interface NativeFunctionProvider {
fun provideNativeFunction(definition: String): CallableFunction
}

View File

@ -41,27 +41,19 @@ class Scope(val parent: Scope? = null, inherits: List<Scope> = emptyList()) {
return value
}
fun call(name: String, arguments: Arguments): Any {
val value = value(name)
if (value !is CallableFunction) {
throw RuntimeException("$value is not callable")
}
return value.call(arguments)
}
fun fork(): Scope =
Scope(this)
fun leave(): Scope {
if (parent == null) {
throw RuntimeException("Parent context not found")
}
return parent
}
internal fun inherit(scope: Scope) {
inherited.add(scope)
}
fun leave(): Scope {
if (parent == null) {
throw RuntimeException("Attempted to leave the root scope!")
}
return parent
}
private object NotFound
}