evaluator: implement an elaborate system that should mean that recursive functions don't require reaching far up the stack

This commit is contained in:
Alex Zenla 2023-09-05 01:35:27 -07:00
parent cc23789203
commit 65f61fcd90
Signed by: alex
GPG Key ID: C0780728420EBFE5
3 changed files with 36 additions and 5 deletions

View File

@ -4,6 +4,7 @@ import gay.pizza.pork.ast.*
class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
private var currentScope: Scope = root
private var insideFastCachePreservation = false
override fun visitIntLiteral(node: IntLiteral): Any = node.value
override fun visitStringLiteral(node: StringLiteral): Any = node.text
@ -28,7 +29,7 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitLambda(node: Lambda): CallableFunction {
return CallableFunction { arguments ->
currentScope = currentScope.fork()
currentScope = currentScope.fork(inheritFastCache = insideFastCachePreservation)
for ((index, argumentSymbol) in node.arguments.withIndex()) {
currentScope.define(argumentSymbol.id, arguments.values[index])
}
@ -104,7 +105,8 @@ class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
override fun visitFunctionDefinition(node: FunctionDefinition): Any {
val blockFunction = visitBlock(node.block) as BlockFunction
val function = CallableFunction { arguments ->
currentScope = currentScope.fork()
currentScope = currentScope.fork(inheritFastCache = insideFastCachePreservation)
currentScope.fastVariableCache.put(node.symbol.id, currentScope.value(node.symbol.id))
for ((index, argumentSymbol) in node.arguments.withIndex()) {
currentScope.define(argumentSymbol.id, arguments.values[index])
}

View File

@ -0,0 +1,18 @@
package gay.pizza.pork.evaluator
class FastVariableCache {
private val cache = mutableMapOf<String, Any>()
fun put(key: String, value: Any) {
cache[key] = value
}
fun lookup(key: String): Any = cache[key] ?: NotFound
fun has(key: String): Boolean = cache.containsKey(key)
fun invalidate(key: String) {
cache.remove(key)
}
object NotFound
}

View File

@ -1,10 +1,15 @@
package gay.pizza.pork.evaluator
class Scope(val parent: Scope? = null, inherits: List<Scope> = emptyList()) {
class Scope(
val parent: Scope? = null,
inherits: List<Scope> = emptyList(),
val fastVariableCache: FastVariableCache = FastVariableCache()
) {
private val inherited = inherits.toMutableList()
private val variables = mutableMapOf<String, Any>()
fun define(name: String, value: Any) {
fastVariableCache.invalidate(name)
if (variables.containsKey(name)) {
throw RuntimeException("Variable '${name}' is already defined")
}
@ -12,6 +17,11 @@ class Scope(val parent: Scope? = null, inherits: List<Scope> = emptyList()) {
}
fun value(name: String): Any {
val fastCached = fastVariableCache.lookup(name)
if (fastCached != FastVariableCache.NotFound) {
return fastCached
}
val value = valueOrNotFound(name)
if (value == NotFound) {
throw RuntimeException("Variable '${name}' not defined.")
@ -48,8 +58,9 @@ class Scope(val parent: Scope? = null, inherits: List<Scope> = emptyList()) {
return value.call(arguments)
}
fun fork(): Scope {
return Scope(this)
fun fork(inheritFastCache: Boolean = false): Scope {
val fastCache = if (inheritFastCache) fastVariableCache else FastVariableCache()
return Scope(this, fastVariableCache = fastCache)
}
fun leave(): Scope {