ast: utilize extension functions to prevent larger stack frames from default interface methods

This commit is contained in:
2023-09-05 14:04:39 -07:00
parent 290d8d0f0a
commit 9f90e05d8a
53 changed files with 491 additions and 282 deletions

View File

@ -0,0 +1,57 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.CompilationUnit
import gay.pizza.pork.ast.Definition
import gay.pizza.pork.ast.FunctionDefinition
import gay.pizza.pork.ast.ImportDeclaration
class CompilationUnitContext(
val compilationUnit: CompilationUnit,
val evaluator: Evaluator,
rootScope: Scope
) {
val internalScope = rootScope.fork()
val externalScope = rootScope.fork()
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(definition, internalScope)
}
private fun processAllImports() {
val imports = compilationUnit.declarations.filterIsInstance<ImportDeclaration>()
for (import in imports) {
processImport(import)
}
}
private fun processImport(import: ImportDeclaration) {
val path = import.path.text
val evaluationContext = evaluator.context(path)
internalScope.inherit(evaluationContext.externalScope)
}
}

View File

@ -1,37 +0,0 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.CompilationUnit
import gay.pizza.pork.ast.ImportDeclaration
class EvaluationContext(
val compilationUnit: CompilationUnit,
val evaluationContextProvider: EvaluationContextProvider,
rootScope: Scope
) {
val internalRootScope = rootScope.fork()
val externalRootScope = rootScope.fork()
private var initialized = false
fun init() {
if (initialized) {
return
}
initialized = true
val imports = compilationUnit.declarations.filterIsInstance<ImportDeclaration>()
for (import in imports) {
val evaluationContext = evaluationContextProvider.provideEvaluationContext(import.path.text)
internalRootScope.inherit(evaluationContext.externalRootScope)
}
for (definition in compilationUnit.definitions) {
val evaluationVisitor = EvaluationVisitor(internalRootScope)
evaluationVisitor.visit(definition)
if (!definition.modifiers.export) {
continue
}
val internalValue = internalRootScope.value(definition.symbol.id)
externalRootScope.define(definition.symbol.id, internalValue)
}
}
}

View File

@ -1,5 +0,0 @@
package gay.pizza.pork.evaluator
interface EvaluationContextProvider {
fun provideEvaluationContext(path: String): EvaluationContext
}

View File

@ -2,23 +2,24 @@ package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.*
class EvaluationVisitor(val root: Scope) : NodeVisitor<Any> {
class EvaluationVisitor(root: Scope) : NodeVisitor<Any> {
private var currentScope: Scope = root
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 visitListLiteral(node: ListLiteral): Any = node.items.map { visit(it) }
override fun visitListLiteral(node: ListLiteral): Any =
node.items.map { it.visit(this) }
override fun visitSymbol(node: Symbol): Any = None
override fun visitFunctionCall(node: FunctionCall): Any {
val arguments = node.arguments.map { visit(it) }
val arguments = node.arguments.map { it.visit(this) }
return currentScope.call(node.symbol.id, Arguments(arguments))
}
override fun visitLetAssignment(node: LetAssignment): Any {
val value = visit(node.value)
val value = node.value.visit(this)
currentScope.define(node.symbol.id, value)
return value
}
@ -35,7 +36,7 @@ class EvaluationVisitor(val root: Scope) : NodeVisitor<Any> {
try {
var value: Any? = null
for (expression in node.expressions) {
value = visit(expression)
value = expression.visit(this)
}
value ?: None
} finally {
@ -44,10 +45,11 @@ class EvaluationVisitor(val root: Scope) : NodeVisitor<Any> {
}
}
override fun visitParentheses(node: Parentheses): Any = visit(node.expression)
override fun visitParentheses(node: Parentheses): Any =
node.expression.visit(this)
override fun visitPrefixOperation(node: PrefixOperation): Any {
val value = visit(node.expression)
val value = node.expression.visit(this)
return when (node.op) {
PrefixOperator.Negate -> {
if (value !is Boolean) {
@ -59,21 +61,18 @@ class EvaluationVisitor(val root: Scope) : NodeVisitor<Any> {
}
override fun visitIf(node: If): Any {
val condition = visit(node.condition)
val condition = node.condition.visit(this)
return if (condition == true) {
visit(node.thenExpression)
node.thenExpression.visit(this)
} else {
if (node.elseExpression != null) {
visit(node.elseExpression!!)
} else {
None
}
val elseExpression = node.elseExpression
elseExpression?.visit(this) ?: None
}
}
override fun visitInfixOperation(node: InfixOperation): Any {
val left = visit(node.left)
val right = visit(node.right)
val left = node.left.visit(this)
val right = node.right.visit(this)
when (node.op) {
InfixOperator.Equals -> {
@ -101,28 +100,21 @@ class EvaluationVisitor(val root: Scope) : NodeVisitor<Any> {
}
}
override fun visitFunctionDefinition(node: FunctionDefinition): Any {
val function = CallableFunction { arguments ->
currentScope = root.fork()
for ((index, argumentSymbol) in node.arguments.withIndex()) {
currentScope.define(argumentSymbol.id, arguments.values[index])
}
val visitor = EvaluationVisitor(currentScope)
val blockFunction = visitor.visitBlock(node.block) as BlockFunction
return@CallableFunction blockFunction.call()
}
currentScope.define(node.symbol.id, function)
return None
}
override fun visitBlock(node: Block): Any = BlockFunction {
override fun visitBlock(node: Block): BlockFunction = BlockFunction {
var value: Any? = null
for (expression in node.expressions) {
value = visit(expression)
value = expression.visit(this)
}
value ?: None
}
override fun visitFunctionDefinition(node: FunctionDefinition): Any {
throw RuntimeException(
"Function declarations cannot be visited in an EvaluationVisitor. " +
"Utilize a FunctionContext."
)
}
override fun visitImportDeclaration(node: ImportDeclaration): Any {
throw RuntimeException(
"Import declarations cannot be visited in an EvaluationVisitor. " +

View File

@ -2,21 +2,19 @@ package gay.pizza.pork.evaluator
import gay.pizza.pork.frontend.World
class Evaluator(val world: World, val scope: Scope) : EvaluationContextProvider {
private val contexts = mutableMapOf<String, EvaluationContext>()
class Evaluator(val world: World, val scope: Scope) {
private val contexts = mutableMapOf<String, CompilationUnitContext>()
fun evaluate(path: String): Scope {
val context = provideEvaluationContext(path)
return context.externalRootScope
}
fun evaluate(path: String): Scope =
context(path).externalScope
override fun provideEvaluationContext(path: String): EvaluationContext {
fun context(path: String): CompilationUnitContext {
val unit = world.load(path)
val identity = world.contentSource.stableContentIdentity(path)
val context = contexts.computeIfAbsent(identity) {
EvaluationContext(unit, this, scope)
CompilationUnitContext(unit, this, scope)
}
context.init()
context.initIfNeeded()
return context
}
}

View File

@ -0,0 +1,15 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.FunctionDefinition
class FunctionContext(val node: FunctionDefinition, val internalScope: Scope) : CallableFunction {
override fun call(arguments: Arguments): Any {
val scope = internalScope.fork()
for ((index, argumentSymbol) in node.arguments.withIndex()) {
scope.define(argumentSymbol.id, arguments.values[index])
}
val visitor = EvaluationVisitor(scope)
val blockFunction = visitor.visitBlock(node.block)
return blockFunction.call()
}
}

View File

@ -14,8 +14,8 @@ class Scope(val parent: Scope? = null, inherits: List<Scope> = emptyList()) {
fun value(name: String): Any {
val value = valueOrNotFound(name)
if (value == NotFound) {
throw RuntimeException("Variable '${name}' not defined.")
if (value === NotFound) {
throw RuntimeException("Variable '${name}' not defined")
}
return value
}
@ -25,14 +25,14 @@ class Scope(val parent: Scope? = null, inherits: List<Scope> = emptyList()) {
if (value == null) {
if (parent != null) {
val parentMaybeFound = parent.valueOrNotFound(name)
if (parentMaybeFound != NotFound) {
if (parentMaybeFound !== NotFound) {
return parentMaybeFound
}
}
for (inherit in inherited) {
val inheritMaybeFound = inherit.valueOrNotFound(name)
if (inheritMaybeFound != NotFound) {
if (inheritMaybeFound !== NotFound) {
return inheritMaybeFound
}
}