mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 13:11:32 +00:00
while loop support, and native functions (including ffi!)
This commit is contained in:
@ -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() {
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
interface NativeFunctionProvider {
|
||||
fun provideNativeFunction(definition: String): CallableFunction
|
||||
}
|
@ -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
|
||||
}
|
||||
|
Reference in New Issue
Block a user