mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 12:50:55 +00:00
ast: utilize extension functions to prevent larger stack frames from default interface methods
This commit is contained in:
parent
290d8d0f0a
commit
9f90e05d8a
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class Block(val expressions: List<Expression>) : Node() {
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitAll(expressions)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitBlock(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Block) return false
|
||||
return other.expressions == expressions
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -8,6 +9,9 @@ import kotlinx.serialization.Serializable
|
||||
class BooleanLiteral(val value: Boolean) : Expression() {
|
||||
override val type: NodeType = NodeType.BooleanLiteral
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitBooleanLiteral(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is BooleanLiteral) return false
|
||||
return other.value == value
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class CompilationUnit(val declarations: List<Declaration>, val definitions: List
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitAll(declarations, definitions)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitCompilationUnit(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is CompilationUnit) return false
|
||||
return other.declarations == declarations && other.definitions == definitions
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class FunctionCall(val symbol: Symbol, val arguments: List<Expression>) : Expres
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitAll(listOf(symbol), arguments)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitFunctionCall(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is FunctionCall) return false
|
||||
return other.symbol == symbol && other.arguments == arguments
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class FunctionDefinition(override val modifiers: DefinitionModifiers, override v
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitAll(listOf(symbol), arguments, listOf(block))
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitFunctionDefinition(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is FunctionDefinition) return false
|
||||
return other.modifiers == modifiers && other.symbol == symbol && other.arguments == arguments && other.block == block
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class If(val condition: Expression, val thenExpression: Expression, val elseExpr
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(condition, thenExpression, elseExpression)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitIf(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is If) return false
|
||||
return other.condition == condition && other.thenExpression == thenExpression && other.elseExpression == elseExpression
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class ImportDeclaration(val path: StringLiteral) : Declaration() {
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(path)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitImportDeclaration(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is ImportDeclaration) return false
|
||||
return other.path == path
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class InfixOperation(val left: Expression, val op: InfixOperator, val right: Exp
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(left, right)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitInfixOperation(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is InfixOperation) return false
|
||||
return other.left == left && other.op == op && other.right == right
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -8,6 +9,9 @@ import kotlinx.serialization.Serializable
|
||||
class IntLiteral(val value: Int) : Expression() {
|
||||
override val type: NodeType = NodeType.IntLiteral
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitIntLiteral(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is IntLiteral) return false
|
||||
return other.value == value
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class Lambda(val arguments: List<Symbol>, val expressions: List<Expression>) : E
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitAll(arguments, expressions)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitLambda(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Lambda) return false
|
||||
return other.arguments == arguments && other.expressions == expressions
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class LetAssignment(val symbol: Symbol, val value: Expression) : Expression() {
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(symbol, value)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitLetAssignment(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is LetAssignment) return false
|
||||
return other.symbol == symbol && other.value == value
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class ListLiteral(val items: List<Expression>) : Expression() {
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitAll(items)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitListLiteral(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is ListLiteral) return false
|
||||
return other.items == items
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -10,4 +11,7 @@ sealed class Node {
|
||||
|
||||
open fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
emptyList()
|
||||
|
||||
open fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visit(this)
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
enum class NodeType(val parent: NodeType? = null) {
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
interface NodeVisitor<T> {
|
||||
@ -34,31 +35,4 @@ interface NodeVisitor<T> {
|
||||
fun visitSymbol(node: Symbol): T
|
||||
|
||||
fun visitSymbolReference(node: SymbolReference): T
|
||||
|
||||
fun visitNodes(vararg nodes: Node?): List<T> =
|
||||
nodes.asSequence().filterNotNull().map { visit(it) }.toList()
|
||||
|
||||
fun visitAll(vararg nodeLists: List<Node>): List<T> =
|
||||
nodeLists.asSequence().flatten().map { visit(it) }.toList()
|
||||
|
||||
fun visit(node: Node): T =
|
||||
when (node) {
|
||||
is Symbol -> visitSymbol(node)
|
||||
is Block -> visitBlock(node)
|
||||
is CompilationUnit -> visitCompilationUnit(node)
|
||||
is LetAssignment -> visitLetAssignment(node)
|
||||
is InfixOperation -> visitInfixOperation(node)
|
||||
is BooleanLiteral -> visitBooleanLiteral(node)
|
||||
is FunctionCall -> visitFunctionCall(node)
|
||||
is FunctionDefinition -> visitFunctionDefinition(node)
|
||||
is If -> visitIf(node)
|
||||
is ImportDeclaration -> visitImportDeclaration(node)
|
||||
is IntLiteral -> visitIntLiteral(node)
|
||||
is Lambda -> visitLambda(node)
|
||||
is ListLiteral -> visitListLiteral(node)
|
||||
is Parentheses -> visitParentheses(node)
|
||||
is PrefixOperation -> visitPrefixOperation(node)
|
||||
is StringLiteral -> visitStringLiteral(node)
|
||||
is SymbolReference -> visitSymbolReference(node)
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,29 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
fun <T> NodeVisitor<T>.visit(node: Node): T =
|
||||
when (node) {
|
||||
is Symbol -> visitSymbol(node)
|
||||
is Block -> visitBlock(node)
|
||||
is CompilationUnit -> visitCompilationUnit(node)
|
||||
is LetAssignment -> visitLetAssignment(node)
|
||||
is InfixOperation -> visitInfixOperation(node)
|
||||
is BooleanLiteral -> visitBooleanLiteral(node)
|
||||
is FunctionCall -> visitFunctionCall(node)
|
||||
is FunctionDefinition -> visitFunctionDefinition(node)
|
||||
is If -> visitIf(node)
|
||||
is ImportDeclaration -> visitImportDeclaration(node)
|
||||
is IntLiteral -> visitIntLiteral(node)
|
||||
is Lambda -> visitLambda(node)
|
||||
is ListLiteral -> visitListLiteral(node)
|
||||
is Parentheses -> visitParentheses(node)
|
||||
is PrefixOperation -> visitPrefixOperation(node)
|
||||
is StringLiteral -> visitStringLiteral(node)
|
||||
is SymbolReference -> visitSymbolReference(node)
|
||||
}
|
||||
|
||||
fun <T> NodeVisitor<T>.visitNodes(vararg nodes: Node?): List<T> =
|
||||
nodes.asSequence().filterNotNull().map { visit(it) }.toList()
|
||||
|
||||
fun <T> NodeVisitor<T>.visitAll(vararg nodeLists: List<Node>): List<T> =
|
||||
nodeLists.asSequence().flatten().map { visit(it) }.toList()
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class Parentheses(val expression: Expression) : Expression() {
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(expression)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitParentheses(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Parentheses) return false
|
||||
return other.expression == expression
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class PrefixOperation(val op: PrefixOperator, val expression: Expression) : Expr
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(expression)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitPrefixOperation(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is PrefixOperation) return false
|
||||
return other.op == op && other.expression == expression
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -8,6 +9,9 @@ import kotlinx.serialization.Serializable
|
||||
class StringLiteral(val text: String) : Expression() {
|
||||
override val type: NodeType = NodeType.StringLiteral
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitStringLiteral(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is StringLiteral) return false
|
||||
return other.text == text
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -8,6 +9,9 @@ import kotlinx.serialization.Serializable
|
||||
class Symbol(val id: String) : Node() {
|
||||
override val type: NodeType = NodeType.Symbol
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitSymbol(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Symbol) return false
|
||||
return other.id == id
|
||||
|
@ -1,3 +1,4 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.ast
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
@ -11,6 +12,9 @@ class SymbolReference(val symbol: Symbol) : Expression() {
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
visitor.visitNodes(symbol)
|
||||
|
||||
override fun <T> visit(visitor: NodeVisitor<T>): T =
|
||||
visitor.visitSymbolReference(this)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is SymbolReference) return false
|
||||
return other.symbol == symbol
|
||||
|
@ -4,10 +4,7 @@ import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.kotlin.dsl.apply
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.kotlin.dsl.*
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
open class PorkModulePlugin : Plugin<Project> {
|
||||
@ -16,6 +13,7 @@ open class PorkModulePlugin : Plugin<Project> {
|
||||
target.apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
|
||||
|
||||
target.repositories.mavenCentral()
|
||||
target.repositories.maven(url = "https://gitlab.com/api/v4/projects/49101454/packages/maven")
|
||||
|
||||
target.extensions.getByType<JavaPluginExtension>().apply {
|
||||
val javaVersion = JavaVersion.toVersion(17)
|
||||
@ -30,6 +28,8 @@ open class PorkModulePlugin : Plugin<Project> {
|
||||
target.dependencies {
|
||||
add("implementation", "org.jetbrains.kotlin:kotlin-bom")
|
||||
add("implementation", "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
|
||||
add("api", "gay.pizza.dough:dough-core:0.1.0-SNAPSHOT")
|
||||
add("api", "gay.pizza.dough:dough-fs:0.1.0-SNAPSHOT")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,9 +72,32 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
)
|
||||
visitorInterface.functions.add(visitFunction)
|
||||
}
|
||||
write("NodeVisitor.kt", KotlinWriter(visitorInterface))
|
||||
|
||||
val visitorExtensionSet = KotlinFunctionSet(pkg)
|
||||
val visitAnyFunction = KotlinFunction(
|
||||
"visit",
|
||||
typeParameters = mutableListOf("T"),
|
||||
extensionOf = "NodeVisitor<T>",
|
||||
returnType = "T",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("node", type = "Node")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitAnyFunction.body.add("when (node) {")
|
||||
for (type in world.typeRegistry.types.filter {
|
||||
world.typeRegistry.roleOfType(it) == AstTypeRole.AstNode
|
||||
}) {
|
||||
visitAnyFunction.body.add(" is ${type.name} -> visit${type.name}(node)")
|
||||
}
|
||||
visitAnyFunction.body.add("}")
|
||||
visitorExtensionSet.functions.add(visitAnyFunction)
|
||||
|
||||
val visitNodesFunction = KotlinFunction(
|
||||
"visitNodes",
|
||||
typeParameters = mutableListOf("T"),
|
||||
extensionOf = "NodeVisitor<T>",
|
||||
returnType = "List<T>",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("nodes", type = "Node?", vararg = true)
|
||||
@ -82,10 +105,12 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitNodesFunction.body.add("nodes.asSequence().filterNotNull().map { visit(it) }.toList()")
|
||||
visitorInterface.functions.add(visitNodesFunction)
|
||||
visitorExtensionSet.functions.add(visitNodesFunction)
|
||||
|
||||
val visitAllFunction = KotlinFunction(
|
||||
"visitAll",
|
||||
typeParameters = mutableListOf("T"),
|
||||
extensionOf = "NodeVisitor<T>",
|
||||
returnType = "List<T>",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("nodeLists", type = "List<Node>", vararg = true)
|
||||
@ -93,25 +118,9 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitAllFunction.body.add("nodeLists.asSequence().flatten().map { visit(it) }.toList()")
|
||||
visitorInterface.functions.add(visitAllFunction)
|
||||
visitorExtensionSet.functions.add(visitAllFunction)
|
||||
|
||||
val visitFunction = KotlinFunction(
|
||||
"visit",
|
||||
returnType = "T",
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("node", type = "Node")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
|
||||
visitFunction.body.add("when (node) {")
|
||||
for (type in world.typeRegistry.types.filter { world.typeRegistry.roleOfType(it) == AstTypeRole.AstNode }) {
|
||||
visitFunction.body.add(" is ${type.name} -> visit${type.name}(node)")
|
||||
}
|
||||
visitFunction.body.add("}")
|
||||
visitorInterface.functions.add(visitFunction)
|
||||
|
||||
write("NodeVisitor.kt", KotlinWriter(visitorInterface))
|
||||
write("NodeVisitorExtensions.kt", KotlinWriter(visitorExtensionSet))
|
||||
}
|
||||
|
||||
private fun writeNodeCoalescer() {
|
||||
@ -193,10 +202,21 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
|
||||
abstractVisitChildrenFunction.body.add("emptyList()")
|
||||
|
||||
kotlinClassLike.functions.add(abstractVisitChildrenFunction)
|
||||
|
||||
val abstractVisitSelfFunction = KotlinFunction(
|
||||
"visit",
|
||||
returnType = "T",
|
||||
open = true,
|
||||
typeParameters = mutableListOf("T"),
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
abstractVisitSelfFunction.body.add("visitor.visit(this)")
|
||||
kotlinClassLike.functions.add(abstractVisitSelfFunction)
|
||||
} else if (role == AstTypeRole.AstNode) {
|
||||
val typeMember = KotlinMember(
|
||||
"type",
|
||||
@ -245,21 +265,21 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
val visitChildrenFunction = KotlinFunction(
|
||||
"visitChildren",
|
||||
returnType = "List<T>",
|
||||
typeParameters = mutableListOf("T")
|
||||
typeParameters = mutableListOf("T"),
|
||||
overridden = true,
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitChildrenFunction.overridden = true
|
||||
val visitorParameter = KotlinParameter("visitor", "NodeVisitor<T>")
|
||||
visitChildrenFunction.parameters.add(visitorParameter)
|
||||
|
||||
visitChildrenFunction.isImmediateExpression = true
|
||||
|
||||
val anyListMembers = type.values.any { it.typeRef.form == AstTypeRefForm.List }
|
||||
val elideVisitChildren: Boolean
|
||||
if (anyListMembers) {
|
||||
val visitParameters = type.values.mapNotNull {
|
||||
if (it.typeRef.primitive != null) {
|
||||
null
|
||||
} else if (it.typeRef.type != null && !world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||
} else if (it.typeRef.type != null &&
|
||||
!world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||
null
|
||||
} else if (it.typeRef.form == AstTypeRefForm.Single) {
|
||||
"listOf(${it.name})"
|
||||
@ -273,7 +293,8 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
val visitParameters = type.values.mapNotNull {
|
||||
if (it.typeRef.primitive != null) {
|
||||
null
|
||||
} else if (it.typeRef.type != null && !world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||
} else if (it.typeRef.type != null &&
|
||||
!world.typeRegistry.roleOfType(it.typeRef.type).isNodeInherited()) {
|
||||
null
|
||||
} else {
|
||||
it.name
|
||||
@ -287,7 +308,22 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
kotlinClassLike.functions.add(visitChildrenFunction)
|
||||
}
|
||||
|
||||
val equalsAndHashCodeMembers = kotlinClassLike.members.map { it.name }.sortedBy { it == "type" }
|
||||
val visitSelfFunction = KotlinFunction(
|
||||
"visit",
|
||||
returnType = "T",
|
||||
typeParameters = mutableListOf("T"),
|
||||
overridden = true,
|
||||
parameters = mutableListOf(
|
||||
KotlinParameter("visitor", "NodeVisitor<T>")
|
||||
),
|
||||
isImmediateExpression = true
|
||||
)
|
||||
visitSelfFunction.body.add("visitor.visit${type.name}(this)")
|
||||
kotlinClassLike.functions.add(visitSelfFunction)
|
||||
|
||||
val equalsAndHashCodeMembers = kotlinClassLike.members.map {
|
||||
it.name
|
||||
}.sortedBy { it == "type" }
|
||||
val equalsFunction = KotlinFunction(
|
||||
"equals",
|
||||
returnType = "Boolean",
|
||||
@ -348,9 +384,10 @@ class AstCodegen(val pkg: String, val outputDirectory: Path, val world: AstWorld
|
||||
}
|
||||
|
||||
private fun write(fileName: String, writer: KotlinWriter) {
|
||||
val content = "// GENERATED CODE FROM PORK AST CODEGEN\n$writer"
|
||||
val path = outputDirectory.resolve(fileName)
|
||||
path.deleteIfExists()
|
||||
path.writeText(writer.toString(), StandardCharsets.UTF_8)
|
||||
path.writeText(content, StandardCharsets.UTF_8)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
@ -3,6 +3,7 @@ package gay.pizza.pork.buildext.codegen
|
||||
class KotlinFunction(
|
||||
val name: String,
|
||||
var typeParameters: MutableList<String> = mutableListOf(),
|
||||
var extensionOf: String? = null,
|
||||
var parameters: MutableList<KotlinParameter> = mutableListOf(),
|
||||
var returnType: String? = null,
|
||||
var abstract: Boolean = false,
|
||||
|
@ -0,0 +1,7 @@
|
||||
package gay.pizza.pork.buildext.codegen
|
||||
|
||||
class KotlinFunctionSet(
|
||||
val pkg: String,
|
||||
var imports: MutableList<String> = mutableListOf(),
|
||||
var functions: MutableList<KotlinFunction> = mutableListOf()
|
||||
)
|
@ -3,8 +3,8 @@ package gay.pizza.pork.buildext.codegen
|
||||
class KotlinWriter() {
|
||||
private val buffer = StringBuilder()
|
||||
|
||||
constructor(kotlinClassLike: KotlinClassLike) : this() {
|
||||
write(kotlinClassLike)
|
||||
constructor(writable: Any) : this() {
|
||||
write(writable)
|
||||
}
|
||||
|
||||
fun writeClass(kotlinClass: KotlinClass): Unit = buffer.run {
|
||||
@ -96,16 +96,7 @@ class KotlinWriter() {
|
||||
classType: String,
|
||||
kotlinClass: KotlinClassLike
|
||||
): Unit = buffer.run {
|
||||
appendLine("package ${kotlinClass.pkg}")
|
||||
appendLine()
|
||||
|
||||
for (import in kotlinClass.imports) {
|
||||
appendLine("import $import")
|
||||
}
|
||||
|
||||
if (kotlinClass.imports.isNotEmpty()) {
|
||||
appendLine()
|
||||
}
|
||||
writeHeader(kotlinClass.pkg, kotlinClass.imports)
|
||||
|
||||
for (annotation in kotlinClass.annotations) {
|
||||
appendLine("@${annotation}")
|
||||
@ -138,76 +129,110 @@ class KotlinWriter() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeFunctions(kotlinClassLike: KotlinClassLike): Unit = buffer.run {
|
||||
for ((index, function) in kotlinClassLike.functions.withIndex()) {
|
||||
append(" ")
|
||||
private fun writeHeader(pkg: String, imports: List<String>): Unit = buffer.run {
|
||||
appendLine("package $pkg")
|
||||
appendLine()
|
||||
|
||||
if (function.overridden) {
|
||||
append("override ")
|
||||
}
|
||||
for (import in imports) {
|
||||
appendLine("import $import")
|
||||
}
|
||||
|
||||
if (function.abstract) {
|
||||
append("abstract ")
|
||||
}
|
||||
|
||||
if (function.open) {
|
||||
append("open ")
|
||||
}
|
||||
|
||||
append("fun ")
|
||||
if (function.typeParameters.isNotEmpty()) {
|
||||
append("<${function.typeParameters.joinToString(", ")}> ")
|
||||
}
|
||||
append("${function.name}(")
|
||||
append(function.parameters.joinToString(", ") {
|
||||
var start = "${it.name}: ${it.type}"
|
||||
if (it.vararg) {
|
||||
start = "vararg $start"
|
||||
}
|
||||
if (it.defaultValue != null) {
|
||||
start + " = ${it.defaultValue}"
|
||||
} else start
|
||||
})
|
||||
append(")")
|
||||
if (function.returnType != null) {
|
||||
append(": ${function.returnType}")
|
||||
}
|
||||
|
||||
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||
append(" {")
|
||||
} else if (!function.abstract && !function.isInterfaceMethod) {
|
||||
append(" =")
|
||||
}
|
||||
|
||||
if (function.body.isNotEmpty()) {
|
||||
appendLine()
|
||||
|
||||
for (item in function.body) {
|
||||
appendLine(" $item")
|
||||
}
|
||||
}
|
||||
|
||||
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||
if (function.body.isNotEmpty()) {
|
||||
append(" ")
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
if (function.abstract || function.isInterfaceMethod) {
|
||||
appendLine()
|
||||
}
|
||||
|
||||
if (index < kotlinClassLike.functions.size - 1) {
|
||||
appendLine()
|
||||
}
|
||||
if (imports.isNotEmpty()) {
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
|
||||
fun write(input: KotlinClassLike): Unit = when (input) {
|
||||
fun writeFunction(function: KotlinFunction, index: Int = 0, functionCount: Int = 1, indent: String = ""): Unit = buffer.run {
|
||||
append(indent)
|
||||
|
||||
if (function.overridden) {
|
||||
append("override ")
|
||||
}
|
||||
|
||||
if (function.abstract) {
|
||||
append("abstract ")
|
||||
}
|
||||
|
||||
if (function.open) {
|
||||
append("open ")
|
||||
}
|
||||
|
||||
append("fun ")
|
||||
if (function.typeParameters.isNotEmpty()) {
|
||||
append("<${function.typeParameters.joinToString(", ")}> ")
|
||||
}
|
||||
|
||||
if (function.extensionOf != null) {
|
||||
append("${function.extensionOf}.")
|
||||
}
|
||||
|
||||
append("${function.name}(")
|
||||
append(function.parameters.joinToString(", ") {
|
||||
var start = "${it.name}: ${it.type}"
|
||||
if (it.vararg) {
|
||||
start = "vararg $start"
|
||||
}
|
||||
if (it.defaultValue != null) {
|
||||
start + " = ${it.defaultValue}"
|
||||
} else start
|
||||
})
|
||||
append(")")
|
||||
if (function.returnType != null) {
|
||||
append(": ${function.returnType}")
|
||||
}
|
||||
|
||||
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||
append(" {")
|
||||
} else if (!function.abstract && !function.isInterfaceMethod) {
|
||||
append(" =")
|
||||
}
|
||||
|
||||
if (function.body.isNotEmpty()) {
|
||||
appendLine()
|
||||
|
||||
for (item in function.body) {
|
||||
appendLine("$indent $item")
|
||||
}
|
||||
}
|
||||
|
||||
if (!function.isImmediateExpression && !function.abstract && !function.isInterfaceMethod) {
|
||||
if (function.body.isNotEmpty()) {
|
||||
append(indent)
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
|
||||
if (function.abstract || function.isInterfaceMethod) {
|
||||
appendLine()
|
||||
}
|
||||
|
||||
if (index < functionCount - 1) {
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
|
||||
fun writeFunctions(kotlinClassLike: KotlinClassLike): Unit = buffer.run {
|
||||
for ((index, function) in kotlinClassLike.functions.withIndex()) {
|
||||
writeFunction(function, index = index, functionCount = kotlinClassLike.functions.size, indent = " ")
|
||||
}
|
||||
}
|
||||
|
||||
fun writeFunctionSet(functionSet: KotlinFunctionSet) {
|
||||
writeHeader(functionSet.pkg, functionSet.imports)
|
||||
for ((index, function) in functionSet.functions.withIndex()) {
|
||||
writeFunction(
|
||||
function,
|
||||
index = index,
|
||||
functionCount = functionSet.functions.size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun write(input: Any): Unit = when (input) {
|
||||
is KotlinClass -> writeClass(input)
|
||||
is KotlinEnum -> writeEnum(input)
|
||||
else -> throw RuntimeException("Unknown Kotlin Class Type")
|
||||
is KotlinFunctionSet -> writeFunctionSet(input)
|
||||
else -> throw RuntimeException("Unknown Kotlin Type")
|
||||
}
|
||||
|
||||
override fun toString(): String = buffer.toString()
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package gay.pizza.pork.evaluator
|
||||
|
||||
interface EvaluationContextProvider {
|
||||
fun provideEvaluationContext(path: String): EvaluationContext
|
||||
}
|
@ -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. " +
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,22 @@
|
||||
package gay.pizza.pork.frontend
|
||||
|
||||
import gay.pizza.dough.fs.FsPath
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
import gay.pizza.dough.fs.readString
|
||||
import gay.pizza.pork.parser.CharSource
|
||||
import gay.pizza.pork.parser.StringCharSource
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolutePathString
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class FsContentSource(val root: Path) : ContentSource {
|
||||
class FsContentSource(val root: FsPath) : ContentSource {
|
||||
override fun loadAsCharSource(path: String): CharSource =
|
||||
StringCharSource(asFsPath(path).readText())
|
||||
StringCharSource(asFsPath(path).readString())
|
||||
|
||||
override fun stableContentIdentity(path: String): String =
|
||||
asFsPath(path).absolutePathString()
|
||||
asFsPath(path).fullPathString
|
||||
|
||||
private fun asFsPath(path: String): Path {
|
||||
private fun asFsPath(path: String): FsPath {
|
||||
val fsPath = root.resolve(path)
|
||||
val absoluteRootPath = root.absolutePathString() + root.fileSystem.separator
|
||||
if (!fsPath.absolutePathString().startsWith(absoluteRootPath)) {
|
||||
val rootPathWithSeparator = root.fullPathString + PlatformFsProvider.separator
|
||||
if (!fsPath.fullPathString.startsWith(rootPathWithSeparator)) {
|
||||
throw RuntimeException("Unable to load path outside of the root: $fsPath (root is ${root})")
|
||||
}
|
||||
return fsPath
|
||||
|
@ -2,6 +2,7 @@ package gay.pizza.pork.parser
|
||||
|
||||
import gay.pizza.pork.ast.NodeCoalescer
|
||||
import gay.pizza.pork.ast.Node
|
||||
import gay.pizza.pork.ast.visit
|
||||
import java.util.IdentityHashMap
|
||||
|
||||
class TokenNodeAttribution : NodeAttribution {
|
||||
|
@ -2,14 +2,14 @@ package gay.pizza.pork.tool
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.types.path
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
import gay.pizza.pork.ast.Node
|
||||
import kotlinx.serialization.ExperimentalSerializationApi
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@OptIn(ExperimentalSerializationApi::class)
|
||||
class AstCommand : CliktCommand(help = "Print AST", name = "ast") {
|
||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||
val path by argument("file")
|
||||
|
||||
private val json = Json {
|
||||
prettyPrint = true
|
||||
@ -18,7 +18,7 @@ class AstCommand : CliktCommand(help = "Print AST", name = "ast") {
|
||||
}
|
||||
|
||||
override fun run() {
|
||||
val tool = FileTool(path)
|
||||
val tool = FileTool(PlatformFsProvider.resolve(path))
|
||||
println(json.encodeToString(Node.serializer(), tool.parse()))
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,16 @@ package gay.pizza.pork.tool
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.types.path
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
import gay.pizza.pork.ast.NodeCoalescer
|
||||
import gay.pizza.pork.ast.visit
|
||||
import gay.pizza.pork.parser.TokenNodeAttribution
|
||||
|
||||
class AttributeCommand : CliktCommand(help = "Attribute AST", name = "attribute") {
|
||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||
val path by argument("file")
|
||||
|
||||
override fun run() {
|
||||
val tool = FileTool(path)
|
||||
val tool = FileTool(PlatformFsProvider.resolve(path))
|
||||
val attribution = TokenNodeAttribution()
|
||||
val compilationUnit = tool.parse(attribution)
|
||||
|
||||
|
@ -1,15 +1,16 @@
|
||||
package gay.pizza.pork.tool
|
||||
|
||||
import gay.pizza.dough.fs.FsPath
|
||||
import gay.pizza.dough.fs.readString
|
||||
import gay.pizza.pork.frontend.ContentSource
|
||||
import gay.pizza.pork.frontend.FsContentSource
|
||||
import gay.pizza.pork.parser.CharSource
|
||||
import gay.pizza.pork.parser.StringCharSource
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.absolute
|
||||
import kotlin.io.path.readText
|
||||
|
||||
class FileTool(val path: Path) : Tool() {
|
||||
override fun createCharSource(): CharSource = StringCharSource(path.readText())
|
||||
override fun createContentSource(): ContentSource = FsContentSource(path.absolute().parent)
|
||||
override fun rootFilePath(): String = path.fileName.toString()
|
||||
class FileTool(val path: FsPath) : Tool() {
|
||||
override fun createCharSource(): CharSource =
|
||||
StringCharSource(path.readString())
|
||||
override fun createContentSource(): ContentSource =
|
||||
FsContentSource(path.parent!!)
|
||||
override fun rootFilePath(): String = path.fullPathString
|
||||
}
|
||||
|
@ -2,14 +2,14 @@ package gay.pizza.pork.tool
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.types.path
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
import gay.pizza.pork.parser.AnsiHighlightScheme
|
||||
|
||||
class HighlightCommand : CliktCommand(help = "Syntax Highlighter", name = "highlight") {
|
||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||
val path by argument("file")
|
||||
|
||||
override fun run() {
|
||||
val tool = FileTool(path)
|
||||
val tool = FileTool(PlatformFsProvider.resolve(path))
|
||||
print(tool.highlight(AnsiHighlightScheme()).joinToString(""))
|
||||
}
|
||||
}
|
||||
|
23
tool/src/main/kotlin/gay/pizza/pork/tool/LoopAndMeasure.kt
Normal file
23
tool/src/main/kotlin/gay/pizza/pork/tool/LoopAndMeasure.kt
Normal file
@ -0,0 +1,23 @@
|
||||
package gay.pizza.pork.tool
|
||||
|
||||
import kotlin.system.measureNanoTime
|
||||
|
||||
fun maybeLoopAndMeasure(loop: Boolean, measure: Boolean, block: () -> Unit) {
|
||||
fun withMaybeMeasurement() {
|
||||
if (measure) {
|
||||
val nanos = measureNanoTime(block)
|
||||
val millis = nanos / 1000000.0
|
||||
System.err.println("time taken: $millis ms (${nanos} ns)")
|
||||
} else {
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
if (loop) {
|
||||
while (true) {
|
||||
withMaybeMeasurement()
|
||||
}
|
||||
} else {
|
||||
withMaybeMeasurement()
|
||||
}
|
||||
}
|
21
tool/src/main/kotlin/gay/pizza/pork/tool/ParseCommand.kt
Normal file
21
tool/src/main/kotlin/gay/pizza/pork/tool/ParseCommand.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package gay.pizza.pork.tool
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.options.flag
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
|
||||
class ParseCommand : CliktCommand(help = "Parse Compilation Unit", name = "parse") {
|
||||
val loop by option("--loop", help = "Loop Parsing").flag()
|
||||
val measure by option("--measure", help = "Measure Time").flag()
|
||||
val path by argument("file")
|
||||
|
||||
override fun run() {
|
||||
val tool = FileTool(PlatformFsProvider.resolve(path))
|
||||
|
||||
maybeLoopAndMeasure(loop, measure) {
|
||||
tool.parse()
|
||||
}
|
||||
}
|
||||
}
|
@ -2,13 +2,13 @@ package gay.pizza.pork.tool
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.types.path
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
|
||||
class ReprintCommand : CliktCommand(help = "Reprint Parsed Compilation Unit", name = "reprint") {
|
||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||
val path by argument("file")
|
||||
|
||||
override fun run() {
|
||||
val tool = FileTool(path)
|
||||
val tool = FileTool(PlatformFsProvider.resolve(path))
|
||||
print(tool.reprint())
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ class RootCommand : CliktCommand(
|
||||
HighlightCommand(),
|
||||
TokenizeCommand(),
|
||||
ReprintCommand(),
|
||||
ParseCommand(),
|
||||
AstCommand(),
|
||||
AttributeCommand()
|
||||
)
|
||||
|
@ -4,45 +4,32 @@ import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.options.flag
|
||||
import com.github.ajalt.clikt.parameters.options.option
|
||||
import com.github.ajalt.clikt.parameters.types.path
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
import gay.pizza.pork.evaluator.CallableFunction
|
||||
import gay.pizza.pork.evaluator.None
|
||||
import gay.pizza.pork.evaluator.Scope
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
class RunCommand : CliktCommand(help = "Run Program", name = "run") {
|
||||
val loop by option("--loop", help = "Loop Program").flag()
|
||||
val measure by option("--measure", help = "Measure Time").flag()
|
||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||
val quiet by option("--quiet", help = "Silence Prints").flag()
|
||||
val path by argument("file")
|
||||
|
||||
override fun run() {
|
||||
if (loop) {
|
||||
while (true) {
|
||||
runProgramMaybeMeasure()
|
||||
}
|
||||
} else {
|
||||
runProgramMaybeMeasure()
|
||||
}
|
||||
}
|
||||
|
||||
private fun runProgramMaybeMeasure() {
|
||||
if (measure) {
|
||||
val time = measureTimeMillis {
|
||||
runProgramOnce()
|
||||
}
|
||||
println("time taken: $time ms")
|
||||
} else {
|
||||
runProgramOnce()
|
||||
}
|
||||
}
|
||||
|
||||
private fun runProgramOnce() {
|
||||
val tool = FileTool(path)
|
||||
val tool = FileTool(PlatformFsProvider.resolve(path))
|
||||
val scope = Scope()
|
||||
scope.define("println", CallableFunction { arguments ->
|
||||
if (quiet) {
|
||||
return@CallableFunction None
|
||||
}
|
||||
for (argument in arguments.values) {
|
||||
println(argument)
|
||||
}
|
||||
None
|
||||
})
|
||||
tool.evaluate(scope)
|
||||
|
||||
maybeLoopAndMeasure(loop, measure) {
|
||||
tool.evaluate(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,13 @@ package gay.pizza.pork.tool
|
||||
|
||||
import com.github.ajalt.clikt.core.CliktCommand
|
||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||
import com.github.ajalt.clikt.parameters.types.path
|
||||
import gay.pizza.dough.fs.PlatformFsProvider
|
||||
|
||||
class TokenizeCommand : CliktCommand(help = "Tokenize Compilation Unit", name = "tokenize") {
|
||||
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||
val path by argument("file")
|
||||
|
||||
override fun run() {
|
||||
val tool = FileTool(path)
|
||||
val tool = FileTool(PlatformFsProvider.resolve(path))
|
||||
val tokenStream = tool.tokenize()
|
||||
for (token in tokenStream.tokens) {
|
||||
println("${token.start} ${token.type.name} '${sanitize(token.text)}'")
|
||||
|
@ -3,6 +3,7 @@ package gay.pizza.pork.tool
|
||||
import gay.pizza.pork.ast.NodeVisitor
|
||||
import gay.pizza.pork.parser.Printer
|
||||
import gay.pizza.pork.ast.CompilationUnit
|
||||
import gay.pizza.pork.ast.visit
|
||||
import gay.pizza.pork.evaluator.Arguments
|
||||
import gay.pizza.pork.evaluator.Evaluator
|
||||
import gay.pizza.pork.evaluator.Scope
|
||||
|
Loading…
Reference in New Issue
Block a user