mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 05:10:55 +00:00
Split out all code into modules.
This commit is contained in:
10
evaluator/build.gradle.kts
Normal file
10
evaluator/build.gradle.kts
Normal file
@ -0,0 +1,10 @@
|
||||
plugins {
|
||||
pork_module
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":ast"))
|
||||
api(project(":frontend"))
|
||||
|
||||
implementation(project(":common"))
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
class Arguments(val values: List<Any>)
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
fun interface BlockFunction {
|
||||
fun call(): Any
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
fun interface CallableFunction {
|
||||
fun call(arguments: Arguments): Any
|
||||
}
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
interface EvaluationContextProvider {
|
||||
fun provideEvaluationContext(path: String): EvaluationContext
|
||||
}
|
@ -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."
|
||||
)
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
data object None
|
60
evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Scope.kt
Normal file
60
evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Scope.kt
Normal 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)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user