Split out all code into modules.

This commit is contained in:
2023-09-04 01:56:24 -07:00
parent d46ea1e307
commit 128f40bcf4
53 changed files with 119 additions and 81 deletions

View File

@ -0,0 +1,10 @@
plugins {
pork_module
}
dependencies {
api(project(":ast"))
api(project(":frontend"))
implementation(project(":common"))
}

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.evaluator
class Arguments(val values: List<Any>)

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.evaluator
fun interface BlockFunction {
fun call(): Any
}

View File

@ -0,0 +1,5 @@
package gay.pizza.pork.evaluator
fun interface CallableFunction {
fun call(arguments: Arguments): Any
}

View File

@ -0,0 +1,36 @@
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
) {
private var isAlreadySetup = false
val internalScope = rootScope.fork()
val externalScope = rootScope.fork()
private val evaluationVisitor = EvaluationVisitor(internalScope)
fun setup() {
if (isAlreadySetup) {
return
}
isAlreadySetup = true
val imports = compilationUnit.declarations.filterIsInstance<ImportDeclaration>()
for (import in imports) {
val evaluationContext = evaluationContextProvider.provideEvaluationContext(import.path.text)
internalScope.inherit(evaluationContext.externalScope)
}
for (definition in compilationUnit.definitions) {
evaluationVisitor.visit(definition)
if (definition.modifiers.export) {
externalScope.define(definition.symbol.id, internalScope.value(definition.symbol.id))
}
}
}
}

View File

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

View File

@ -0,0 +1,142 @@
package gay.pizza.pork.evaluator
import gay.pizza.pork.ast.*
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 visitSymbol(node: Symbol): Any = None
override fun visitFunctionCall(node: FunctionCall): Any {
val arguments = node.arguments.map { visit(it) }
return currentScope.call(node.symbol.id, Arguments(arguments))
}
override fun visitDefine(node: Assignment): Any {
val value = visit(node.value)
currentScope.define(node.symbol.id, value)
return value
}
override fun visitSymbolReference(node: SymbolReference): Any =
currentScope.value(node.symbol.id)
override fun visitLambda(node: Lambda): CallableFunction {
return CallableFunction { arguments ->
currentScope = currentScope.fork()
for ((index, argumentSymbol) in node.arguments.withIndex()) {
currentScope.define(argumentSymbol.id, arguments.values[index])
}
try {
var value: Any? = null
for (expression in node.expressions) {
value = visit(expression)
}
value ?: None
} finally {
currentScope = currentScope.leave()
}
}
}
override fun visitParentheses(node: Parentheses): Any = visit(node.expression)
override fun visitPrefixOperation(node: PrefixOperation): Any {
val value = visit(node.expression)
return when (node.op) {
PrefixOperator.Negate -> {
if (value !is Boolean) {
throw RuntimeException("Cannot negate a value which is not a boolean.")
}
!value
}
}
}
override fun visitIf(node: If): Any {
val condition = visit(node.condition)
return if (condition == true) {
visit(node.thenExpression)
} else {
if (node.elseExpression != null) {
visit(node.elseExpression!!)
} else {
None
}
}
}
override fun visitInfixOperation(node: InfixOperation): Any {
val left = visit(node.left)
val right = visit(node.right)
when (node.op) {
InfixOperator.Equals -> {
return left == right
}
InfixOperator.NotEquals -> {
return left != right
}
else -> {}
}
if (left !is Number || right !is Number) {
throw RuntimeException("Failed to evaluate infix operation, bad types.")
}
val leftInt = left.toInt()
val rightInt = right.toInt()
return when (node.op) {
InfixOperator.Plus -> leftInt + rightInt
InfixOperator.Minus -> leftInt - rightInt
InfixOperator.Multiply -> leftInt * rightInt
InfixOperator.Divide -> leftInt / rightInt
else -> throw RuntimeException("Unable to handle operation ${node.op}")
}
}
override fun visitFunctionDeclaration(node: FunctionDefinition): Any {
val blockFunction = visitBlock(node.block) as BlockFunction
val function = CallableFunction { arguments ->
currentScope = currentScope.fork()
for ((index, argumentSymbol) in node.arguments.withIndex()) {
currentScope.define(argumentSymbol.id, arguments.values[index])
}
try {
return@CallableFunction blockFunction.call()
} finally {
currentScope = currentScope.leave()
}
}
currentScope.define(node.symbol.id, function)
return None
}
override fun visitBlock(node: Block): Any = BlockFunction {
var value: Any? = null
for (expression in node.expressions) {
value = visit(expression)
}
value ?: None
}
override fun visitImportDeclaration(node: ImportDeclaration): Any {
throw RuntimeException(
"Import declarations cannot be visited in an EvaluationVisitor. " +
"Utilize an EvaluationContext."
)
}
override fun visitCompilationUnit(node: CompilationUnit): Any {
throw RuntimeException(
"Compilation units cannot be visited in an EvaluationVisitor. " +
"Utilize an EvaluationContext."
)
}
}

View File

@ -0,0 +1,22 @@
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>()
fun evaluate(path: String): Scope {
val context = provideEvaluationContext(path)
return context.externalScope
}
override fun provideEvaluationContext(path: String): EvaluationContext {
val unit = world.load(path)
val identity = world.contentSource.stableContentIdentity(path)
val context = contexts.computeIfAbsent(identity) {
EvaluationContext(unit, this, scope)
}
context.setup()
return context
}
}

View File

@ -0,0 +1,3 @@
package gay.pizza.pork.evaluator
data object None

View File

@ -0,0 +1,60 @@
package gay.pizza.pork.evaluator
class Scope(val parent: Scope? = null, inherits: List<Scope> = emptyList()) {
private val inherited = inherits.toMutableList()
private val variables = mutableMapOf<String, Any>()
fun has(name: String): Boolean =
variables.containsKey(name) ||
(parent?.has(name) ?: false) ||
inherited.any { inherit -> inherit.has(name) }
fun define(name: String, value: Any) {
if (variables.containsKey(name)) {
throw RuntimeException("Variable '${name}' is already defined")
}
variables[name] = value
}
fun value(name: String): Any {
val value = variables[name]
if (value == null) {
if (parent != null) {
if (parent.has(name)) {
return parent.value(name)
}
}
for (inherit in inherited) {
if (inherit.has(name)) {
return inherit.value(name)
}
}
throw RuntimeException("Variable '${name}' not defined.")
}
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 {
return Scope(this)
}
fun leave(): Scope {
if (parent == null) {
throw RuntimeException("Parent context not found")
}
return parent
}
internal fun inherit(scope: Scope) {
inherited.add(scope)
}
}