From 9f90e05d8acb8d5de0d3cbe97de64cac85290130 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Tue, 5 Sep 2023 14:04:39 -0700 Subject: [PATCH] ast: utilize extension functions to prevent larger stack frames from default interface methods --- .../main/kotlin/gay/pizza/pork/ast/Block.kt | 4 + .../gay/pizza/pork/ast/BooleanLiteral.kt | 4 + .../gay/pizza/pork/ast/CompilationUnit.kt | 4 + .../kotlin/gay/pizza/pork/ast/Declaration.kt | 1 + .../kotlin/gay/pizza/pork/ast/Definition.kt | 1 + .../gay/pizza/pork/ast/DefinitionModifiers.kt | 1 + .../kotlin/gay/pizza/pork/ast/Expression.kt | 1 + .../kotlin/gay/pizza/pork/ast/FunctionCall.kt | 4 + .../gay/pizza/pork/ast/FunctionDefinition.kt | 4 + ast/src/main/kotlin/gay/pizza/pork/ast/If.kt | 4 + .../gay/pizza/pork/ast/ImportDeclaration.kt | 4 + .../gay/pizza/pork/ast/InfixOperation.kt | 4 + .../gay/pizza/pork/ast/InfixOperator.kt | 1 + .../kotlin/gay/pizza/pork/ast/IntLiteral.kt | 4 + .../main/kotlin/gay/pizza/pork/ast/Lambda.kt | 4 + .../gay/pizza/pork/ast/LetAssignment.kt | 4 + .../kotlin/gay/pizza/pork/ast/ListLiteral.kt | 4 + .../main/kotlin/gay/pizza/pork/ast/Node.kt | 4 + .../gay/pizza/pork/ast/NodeCoalescer.kt | 1 + .../kotlin/gay/pizza/pork/ast/NodeType.kt | 1 + .../kotlin/gay/pizza/pork/ast/NodeVisitor.kt | 28 +-- .../pizza/pork/ast/NodeVisitorExtensions.kt | 29 +++ .../kotlin/gay/pizza/pork/ast/Parentheses.kt | 4 + .../gay/pizza/pork/ast/PrefixOperation.kt | 4 + .../gay/pizza/pork/ast/PrefixOperator.kt | 1 + .../gay/pizza/pork/ast/StringLiteral.kt | 4 + .../main/kotlin/gay/pizza/pork/ast/Symbol.kt | 4 + .../gay/pizza/pork/ast/SymbolReference.kt | 4 + .../pizza/pork/buildext/PorkModulePlugin.kt | 8 +- .../gay/pizza/pork/buildext/ast/AstCodegen.kt | 101 ++++++---- .../pork/buildext/codegen/KotlinFunction.kt | 1 + .../buildext/codegen/KotlinFunctionSet.kt | 7 + .../pork/buildext/codegen/KotlinWriter.kt | 175 ++++++++++-------- .../pork/evaluator/CompilationUnitContext.kt | 57 ++++++ .../pizza/pork/evaluator/EvaluationContext.kt | 37 ---- .../evaluator/EvaluationContextProvider.kt | 5 - .../pizza/pork/evaluator/EvaluationVisitor.kt | 56 +++--- .../gay/pizza/pork/evaluator/Evaluator.kt | 16 +- .../pizza/pork/evaluator/FunctionContext.kt | 15 ++ .../kotlin/gay/pizza/pork/evaluator/Scope.kt | 8 +- .../pizza/pork/frontend/FsContentSource.kt | 18 +- .../pizza/pork/parser/TokenNodeAttribution.kt | 1 + .../kotlin/gay/pizza/pork/tool/AstCommand.kt | 6 +- .../gay/pizza/pork/tool/AttributeCommand.kt | 7 +- .../kotlin/gay/pizza/pork/tool/FileTool.kt | 15 +- .../gay/pizza/pork/tool/HighlightCommand.kt | 6 +- .../gay/pizza/pork/tool/LoopAndMeasure.kt | 23 +++ .../gay/pizza/pork/tool/ParseCommand.kt | 21 +++ .../gay/pizza/pork/tool/ReprintCommand.kt | 6 +- .../kotlin/gay/pizza/pork/tool/RootCommand.kt | 1 + .../kotlin/gay/pizza/pork/tool/RunCommand.kt | 39 ++-- .../gay/pizza/pork/tool/TokenizeCommand.kt | 6 +- .../main/kotlin/gay/pizza/pork/tool/Tool.kt | 1 + 53 files changed, 491 insertions(+), 282 deletions(-) create mode 100644 ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt create mode 100644 buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunctionSet.kt create mode 100644 evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt delete mode 100644 evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationContext.kt delete mode 100644 evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationContextProvider.kt create mode 100644 evaluator/src/main/kotlin/gay/pizza/pork/evaluator/FunctionContext.kt create mode 100644 tool/src/main/kotlin/gay/pizza/pork/tool/LoopAndMeasure.kt create mode 100644 tool/src/main/kotlin/gay/pizza/pork/tool/ParseCommand.kt diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Block.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Block.kt index 866798a..dc1e0cf 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Block.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Block.kt @@ -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) : Node() { override fun visitChildren(visitor: NodeVisitor): List = visitor.visitAll(expressions) + override fun visit(visitor: NodeVisitor): T = + visitor.visitBlock(this) + override fun equals(other: Any?): Boolean { if (other !is Block) return false return other.expressions == expressions diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/BooleanLiteral.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/BooleanLiteral.kt index dafc4ad..19bcd26 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/BooleanLiteral.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/BooleanLiteral.kt @@ -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 visit(visitor: NodeVisitor): T = + visitor.visitBooleanLiteral(this) + override fun equals(other: Any?): Boolean { if (other !is BooleanLiteral) return false return other.value == value diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/CompilationUnit.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/CompilationUnit.kt index d08b249..32f9e68 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/CompilationUnit.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/CompilationUnit.kt @@ -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, val definitions: List override fun visitChildren(visitor: NodeVisitor): List = visitor.visitAll(declarations, definitions) + override fun visit(visitor: NodeVisitor): T = + visitor.visitCompilationUnit(this) + override fun equals(other: Any?): Boolean { if (other !is CompilationUnit) return false return other.declarations == declarations && other.definitions == definitions diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Declaration.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Declaration.kt index c1d23d6..cadd3ef 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Declaration.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Declaration.kt @@ -1,3 +1,4 @@ +// GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.ast import kotlinx.serialization.SerialName diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Definition.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Definition.kt index e92792a..92abe44 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Definition.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Definition.kt @@ -1,3 +1,4 @@ +// GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.ast import kotlinx.serialization.SerialName diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/DefinitionModifiers.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/DefinitionModifiers.kt index b3b570e..f96eba4 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/DefinitionModifiers.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/DefinitionModifiers.kt @@ -1,3 +1,4 @@ +// GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.ast import kotlinx.serialization.SerialName diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Expression.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Expression.kt index d8497bc..03828d6 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Expression.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Expression.kt @@ -1,3 +1,4 @@ +// GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.ast import kotlinx.serialization.SerialName diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionCall.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionCall.kt index 1f26a6a..a985e52 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionCall.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionCall.kt @@ -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) : Expres override fun visitChildren(visitor: NodeVisitor): List = visitor.visitAll(listOf(symbol), arguments) + override fun visit(visitor: NodeVisitor): T = + visitor.visitFunctionCall(this) + override fun equals(other: Any?): Boolean { if (other !is FunctionCall) return false return other.symbol == symbol && other.arguments == arguments diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt index 46424ef..3a337fd 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt @@ -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 visitChildren(visitor: NodeVisitor): List = visitor.visitAll(listOf(symbol), arguments, listOf(block)) + override fun visit(visitor: NodeVisitor): 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 diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/If.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/If.kt index 090e4af..f13cbc9 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/If.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/If.kt @@ -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 visitChildren(visitor: NodeVisitor): List = visitor.visitNodes(condition, thenExpression, elseExpression) + override fun visit(visitor: NodeVisitor): 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 diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/ImportDeclaration.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/ImportDeclaration.kt index aaf61aa..830128f 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/ImportDeclaration.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/ImportDeclaration.kt @@ -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 visitChildren(visitor: NodeVisitor): List = visitor.visitNodes(path) + override fun visit(visitor: NodeVisitor): T = + visitor.visitImportDeclaration(this) + override fun equals(other: Any?): Boolean { if (other !is ImportDeclaration) return false return other.path == path diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt index e77a6a0..4cb0a5b 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperation.kt @@ -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 visitChildren(visitor: NodeVisitor): List = visitor.visitNodes(left, right) + override fun visit(visitor: NodeVisitor): 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 diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt index 43ee975..9c7a4b9 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/InfixOperator.kt @@ -1,3 +1,4 @@ +// GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.ast import kotlinx.serialization.SerialName diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt index fc33523..4ae3a76 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/IntLiteral.kt @@ -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 visit(visitor: NodeVisitor): T = + visitor.visitIntLiteral(this) + override fun equals(other: Any?): Boolean { if (other !is IntLiteral) return false return other.value == value diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Lambda.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Lambda.kt index 0c3c849..b97037a 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Lambda.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Lambda.kt @@ -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, val expressions: List) : E override fun visitChildren(visitor: NodeVisitor): List = visitor.visitAll(arguments, expressions) + override fun visit(visitor: NodeVisitor): T = + visitor.visitLambda(this) + override fun equals(other: Any?): Boolean { if (other !is Lambda) return false return other.arguments == arguments && other.expressions == expressions diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/LetAssignment.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/LetAssignment.kt index 0873984..9d955e8 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/LetAssignment.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/LetAssignment.kt @@ -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 visitChildren(visitor: NodeVisitor): List = visitor.visitNodes(symbol, value) + override fun visit(visitor: NodeVisitor): T = + visitor.visitLetAssignment(this) + override fun equals(other: Any?): Boolean { if (other !is LetAssignment) return false return other.symbol == symbol && other.value == value diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/ListLiteral.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/ListLiteral.kt index c384cca..d58fc08 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/ListLiteral.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/ListLiteral.kt @@ -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() { override fun visitChildren(visitor: NodeVisitor): List = visitor.visitAll(items) + override fun visit(visitor: NodeVisitor): T = + visitor.visitListLiteral(this) + override fun equals(other: Any?): Boolean { if (other !is ListLiteral) return false return other.items == items diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Node.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Node.kt index 9063c01..f9bac99 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Node.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Node.kt @@ -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 visitChildren(visitor: NodeVisitor): List = emptyList() + + open fun visit(visitor: NodeVisitor): T = + visitor.visit(this) } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt index 30e7b97..657e973 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt @@ -1,3 +1,4 @@ +// GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.ast class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor { diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt index cd5bb3b..e30bfed 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt @@ -1,3 +1,4 @@ +// GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.ast enum class NodeType(val parent: NodeType? = null) { diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt index 6073352..ca57eb4 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt @@ -1,3 +1,4 @@ +// GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.ast interface NodeVisitor { @@ -34,31 +35,4 @@ interface NodeVisitor { fun visitSymbol(node: Symbol): T fun visitSymbolReference(node: SymbolReference): T - - fun visitNodes(vararg nodes: Node?): List = - nodes.asSequence().filterNotNull().map { visit(it) }.toList() - - fun visitAll(vararg nodeLists: List): List = - 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) - } } diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt new file mode 100644 index 0000000..b57ad24 --- /dev/null +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt @@ -0,0 +1,29 @@ +// GENERATED CODE FROM PORK AST CODEGEN +package gay.pizza.pork.ast + +fun NodeVisitor.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 NodeVisitor.visitNodes(vararg nodes: Node?): List = + nodes.asSequence().filterNotNull().map { visit(it) }.toList() + +fun NodeVisitor.visitAll(vararg nodeLists: List): List = + nodeLists.asSequence().flatten().map { visit(it) }.toList() diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Parentheses.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Parentheses.kt index e9ecd79..fb2761e 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Parentheses.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Parentheses.kt @@ -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 visitChildren(visitor: NodeVisitor): List = visitor.visitNodes(expression) + override fun visit(visitor: NodeVisitor): T = + visitor.visitParentheses(this) + override fun equals(other: Any?): Boolean { if (other !is Parentheses) return false return other.expression == expression diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperation.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperation.kt index 34dadf8..9ff9b97 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperation.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperation.kt @@ -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 visitChildren(visitor: NodeVisitor): List = visitor.visitNodes(expression) + override fun visit(visitor: NodeVisitor): T = + visitor.visitPrefixOperation(this) + override fun equals(other: Any?): Boolean { if (other !is PrefixOperation) return false return other.op == op && other.expression == expression diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt index 432daa0..3bc9094 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/PrefixOperator.kt @@ -1,3 +1,4 @@ +// GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.ast import kotlinx.serialization.SerialName diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/StringLiteral.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/StringLiteral.kt index 8af42e1..98a2dae 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/StringLiteral.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/StringLiteral.kt @@ -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 visit(visitor: NodeVisitor): T = + visitor.visitStringLiteral(this) + override fun equals(other: Any?): Boolean { if (other !is StringLiteral) return false return other.text == text diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/Symbol.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/Symbol.kt index b5622b8..db094fa 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/Symbol.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/Symbol.kt @@ -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 visit(visitor: NodeVisitor): T = + visitor.visitSymbol(this) + override fun equals(other: Any?): Boolean { if (other !is Symbol) return false return other.id == id diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/SymbolReference.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/SymbolReference.kt index d57496c..a4ed18f 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/SymbolReference.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/SymbolReference.kt @@ -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 visitChildren(visitor: NodeVisitor): List = visitor.visitNodes(symbol) + override fun visit(visitor: NodeVisitor): T = + visitor.visitSymbolReference(this) + override fun equals(other: Any?): Boolean { if (other !is SymbolReference) return false return other.symbol == symbol diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkModulePlugin.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkModulePlugin.kt index ae17489..d69105b 100644 --- a/buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkModulePlugin.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/PorkModulePlugin.kt @@ -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 { @@ -16,6 +13,7 @@ open class PorkModulePlugin : Plugin { 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().apply { val javaVersion = JavaVersion.toVersion(17) @@ -30,6 +28,8 @@ open class PorkModulePlugin : Plugin { 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") } } } diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt index cd048fa..c82cec9 100644 --- a/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/ast/AstCodegen.kt @@ -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", + 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", returnType = "List", 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", returnType = "List", parameters = mutableListOf( KotlinParameter("nodeLists", type = "List", 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") + ), + 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", - typeParameters = mutableListOf("T") + typeParameters = mutableListOf("T"), + overridden = true, + parameters = mutableListOf( + KotlinParameter("visitor", "NodeVisitor") + ), + isImmediateExpression = true ) - visitChildrenFunction.overridden = true - val visitorParameter = KotlinParameter("visitor", "NodeVisitor") - 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") + ), + 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 { diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunction.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunction.kt index 62e4bdf..27ef3fe 100644 --- a/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunction.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunction.kt @@ -3,6 +3,7 @@ package gay.pizza.pork.buildext.codegen class KotlinFunction( val name: String, var typeParameters: MutableList = mutableListOf(), + var extensionOf: String? = null, var parameters: MutableList = mutableListOf(), var returnType: String? = null, var abstract: Boolean = false, diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunctionSet.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunctionSet.kt new file mode 100644 index 0000000..e9ac66e --- /dev/null +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinFunctionSet.kt @@ -0,0 +1,7 @@ +package gay.pizza.pork.buildext.codegen + +class KotlinFunctionSet( + val pkg: String, + var imports: MutableList = mutableListOf(), + var functions: MutableList = mutableListOf() +) diff --git a/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinWriter.kt b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinWriter.kt index e445861..05abb08 100644 --- a/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinWriter.kt +++ b/buildext/src/main/kotlin/gay/pizza/pork/buildext/codegen/KotlinWriter.kt @@ -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): 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() diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt new file mode 100644 index 0000000..31a73b6 --- /dev/null +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt @@ -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() + 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) + } +} diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationContext.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationContext.kt deleted file mode 100644 index bc33d65..0000000 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationContext.kt +++ /dev/null @@ -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() - 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) - } - } -} diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationContextProvider.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationContextProvider.kt deleted file mode 100644 index 8a65d75..0000000 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationContextProvider.kt +++ /dev/null @@ -1,5 +0,0 @@ -package gay.pizza.pork.evaluator - -interface EvaluationContextProvider { - fun provideEvaluationContext(path: String): EvaluationContext -} diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt index 5904f74..16840fa 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt @@ -2,23 +2,24 @@ package gay.pizza.pork.evaluator import gay.pizza.pork.ast.* -class EvaluationVisitor(val root: Scope) : NodeVisitor { +class EvaluationVisitor(root: Scope) : NodeVisitor { 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 { 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 { } } - 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 { } 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 { } } - 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. " + diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Evaluator.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Evaluator.kt index 13fb1d4..924e87a 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Evaluator.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Evaluator.kt @@ -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() +class Evaluator(val world: World, val scope: Scope) { + private val contexts = mutableMapOf() - 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 } } diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/FunctionContext.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/FunctionContext.kt new file mode 100644 index 0000000..09f4653 --- /dev/null +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/FunctionContext.kt @@ -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() + } +} diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Scope.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Scope.kt index 25df9bd..eedcff2 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Scope.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/Scope.kt @@ -14,8 +14,8 @@ class Scope(val parent: Scope? = null, inherits: List = 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 = 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 } } diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/FsContentSource.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/FsContentSource.kt index 505b241..04b8046 100644 --- a/frontend/src/main/kotlin/gay/pizza/pork/frontend/FsContentSource.kt +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/FsContentSource.kt @@ -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 diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/TokenNodeAttribution.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/TokenNodeAttribution.kt index 2d1edef..36abdc0 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/TokenNodeAttribution.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/TokenNodeAttribution.kt @@ -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 { diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/AstCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/AstCommand.kt index c93ea38..e43f025 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/AstCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/AstCommand.kt @@ -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())) } } diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/AttributeCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/AttributeCommand.kt index cf6338f..440830a 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/AttributeCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/AttributeCommand.kt @@ -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) diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/FileTool.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/FileTool.kt index 815b420..8db9942 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/FileTool.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/FileTool.kt @@ -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 } diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/HighlightCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/HighlightCommand.kt index 131b706..5384893 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/HighlightCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/HighlightCommand.kt @@ -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("")) } } diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/LoopAndMeasure.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/LoopAndMeasure.kt new file mode 100644 index 0000000..7e4544c --- /dev/null +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/LoopAndMeasure.kt @@ -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() + } +} diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/ParseCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/ParseCommand.kt new file mode 100644 index 0000000..32d8296 --- /dev/null +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/ParseCommand.kt @@ -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() + } + } +} diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/ReprintCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/ReprintCommand.kt index 18190a3..7ccb9a3 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/ReprintCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/ReprintCommand.kt @@ -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()) } } diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/RootCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/RootCommand.kt index 155b55e..810d455 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/RootCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/RootCommand.kt @@ -13,6 +13,7 @@ class RootCommand : CliktCommand( HighlightCommand(), TokenizeCommand(), ReprintCommand(), + ParseCommand(), AstCommand(), AttributeCommand() ) diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt index f710432..d097930 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/RunCommand.kt @@ -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) + } } } diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/TokenizeCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/TokenizeCommand.kt index 2e1bce3..888282b 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/TokenizeCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/TokenizeCommand.kt @@ -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)}'") diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/Tool.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/Tool.kt index 56a726a..6a2a47e 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/Tool.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/Tool.kt @@ -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