From 8f60039d6e623c599d66efd0fa1990276a8763c5 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Mon, 11 Sep 2023 04:57:13 -0400 Subject: [PATCH] language: implement let definitions --- ast/src/main/ast/pork.yml | 9 ++++++ ast/src/main/graph/types.dot | 5 ++++ .../gay/pizza/pork/ast/LetDefinition.kt | 30 +++++++++++++++++++ .../gay/pizza/pork/ast/NodeCoalescer.kt | 3 ++ .../kotlin/gay/pizza/pork/ast/NodeType.kt | 1 + .../kotlin/gay/pizza/pork/ast/NodeVisitor.kt | 2 ++ .../pizza/pork/ast/NodeVisitorExtensions.kt | 1 + .../pork/evaluator/CompilationUnitContext.kt | 4 +++ .../pizza/pork/evaluator/EvaluationVisitor.kt | 4 +++ examples/count.pork | 6 ++-- .../kotlin/gay/pizza/pork/parser/Parser.kt | 22 +++++++++++--- .../kotlin/gay/pizza/pork/parser/Printer.kt | 16 ++++++++-- 12 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 ast/src/main/kotlin/gay/pizza/pork/ast/LetDefinition.kt diff --git a/ast/src/main/ast/pork.yml b/ast/src/main/ast/pork.yml index 608ad1e..c8b1174 100644 --- a/ast/src/main/ast/pork.yml +++ b/ast/src/main/ast/pork.yml @@ -137,6 +137,15 @@ types: type: Block? - name: native type: Native? + LetDefinition: + parent: Definition + values: + - name: modifiers + type: DefinitionModifiers + - name: symbol + type: Symbol + - name: value + type: Expression If: parent: Expression values: diff --git a/ast/src/main/graph/types.dot b/ast/src/main/graph/types.dot index aaf3fa6..99e31bc 100644 --- a/ast/src/main/graph/types.dot +++ b/ast/src/main/graph/types.dot @@ -16,6 +16,7 @@ digraph A { type_FunctionCall [shape=box,label="FunctionCall"] type_ArgumentSpec [shape=box,label="ArgumentSpec"] type_FunctionDefinition [shape=box,label="FunctionDefinition"] + type_LetDefinition [shape=box,label="LetDefinition"] type_If [shape=box,label="If"] type_ImportDeclaration [shape=box,label="ImportDeclaration"] type_IntegerLiteral [shape=box,label="IntegerLiteral"] @@ -58,6 +59,7 @@ digraph A { type_Expression -> type_Break type_Expression -> type_Continue type_Definition -> type_FunctionDefinition + type_Definition -> type_LetDefinition type_Declaration -> type_ImportDeclaration type_Definition -> type_Symbol [style=dotted] type_Definition -> type_DefinitionModifiers [style=dotted] @@ -80,6 +82,9 @@ digraph A { type_FunctionDefinition -> type_ArgumentSpec [style=dotted] type_FunctionDefinition -> type_Block [style=dotted] type_FunctionDefinition -> type_Native [style=dotted] + type_LetDefinition -> type_DefinitionModifiers [style=dotted] + type_LetDefinition -> type_Symbol [style=dotted] + type_LetDefinition -> type_Expression [style=dotted] type_If -> type_Expression [style=dotted] type_If -> type_Block [style=dotted] type_ImportDeclaration -> type_Symbol [style=dotted] diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/LetDefinition.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/LetDefinition.kt new file mode 100644 index 0000000..73b43a5 --- /dev/null +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/LetDefinition.kt @@ -0,0 +1,30 @@ +// GENERATED CODE FROM PORK AST CODEGEN +package gay.pizza.pork.ast + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("letDefinition") +class LetDefinition(override val modifiers: DefinitionModifiers, override val symbol: Symbol, val value: Expression) : Definition() { + override val type: NodeType = NodeType.LetDefinition + + override fun visitChildren(visitor: NodeVisitor): List = + visitor.visitNodes(symbol, value) + + override fun visit(visitor: NodeVisitor): T = + visitor.visitLetDefinition(this) + + override fun equals(other: Any?): Boolean { + if (other !is LetDefinition) return false + return other.modifiers == modifiers && other.symbol == symbol && other.value == value + } + + override fun hashCode(): Int { + var result = modifiers.hashCode() + result = 31 * result + symbol.hashCode() + result = 31 * result + value.hashCode() + result = 31 * result + type.hashCode() + return result + } +} 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 4a4e34d..24472ab 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt @@ -41,6 +41,9 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor { override fun visitLetAssignment(node: LetAssignment): Unit = handle(node) + override fun visitLetDefinition(node: LetDefinition): Unit = + handle(node) + override fun visitListLiteral(node: ListLiteral): Unit = handle(node) 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 abb3d64..859960a 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt @@ -19,6 +19,7 @@ enum class NodeType(val parent: NodeType? = null) { InfixOperation(Expression), IntegerLiteral(Expression), LetAssignment(Expression), + LetDefinition(Definition), ListLiteral(Expression), Native(Node), Parentheses(Expression), 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 886f047..7278924 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt @@ -28,6 +28,8 @@ interface NodeVisitor { fun visitLetAssignment(node: LetAssignment): T + fun visitLetDefinition(node: LetDefinition): T + fun visitListLiteral(node: ListLiteral): T fun visitNative(node: Native): T diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt index ae6ce01..ce4ab1b 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt @@ -13,6 +13,7 @@ fun NodeVisitor.visit(node: Node): T = is BooleanLiteral -> visitBooleanLiteral(node) is FunctionCall -> visitFunctionCall(node) is FunctionDefinition -> visitFunctionDefinition(node) + is LetDefinition -> visitLetDefinition(node) is If -> visitIf(node) is ImportDeclaration -> visitImportDeclaration(node) is IntegerLiteral -> visitIntegerLiteral(node) diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt index b9b9fa7..2f13558 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt @@ -39,6 +39,10 @@ class CompilationUnitContext( private fun definitionValue(definition: Definition): Any = when (definition) { is FunctionDefinition -> FunctionContext(this, definition) + is LetDefinition -> { + EvaluationVisitor(internalScope.fork("let ${definition.symbol.id}")) + .visit(definition.value) + } } private fun processAllImports() { 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 8cb023b..976017e 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt @@ -30,6 +30,10 @@ class EvaluationVisitor(root: Scope) : NodeVisitor { return value } + override fun visitLetDefinition(node: LetDefinition): Any { + topLevelUsedError("LetDefinition", "CompilationUnitContext") + } + override fun visitSymbolReference(node: SymbolReference): Any = currentScope.value(node.symbol.id) diff --git a/examples/count.pork b/examples/count.pork index 6aa1a33..7736387 100644 --- a/examples/count.pork +++ b/examples/count.pork @@ -1,7 +1,9 @@ +let count = 5 + export func main() { var x = 1 - while x <= 5 { + while x <= count { println(x) - x = x + 1 + x++ } } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt index 08ac4fe..72b090f 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt @@ -226,7 +226,7 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { ImportDeclaration(form, components) } - private fun readFunctionDeclaration(): FunctionDefinition = within { + private fun readDefinitionModifiers(): DefinitionModifiers { val modifiers = DefinitionModifiers(export = false) while (true) { val token = peek() @@ -238,12 +238,16 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { else -> break } } + return modifiers + } + + private fun readFunctionDeclaration(modifiers: DefinitionModifiers): FunctionDefinition = within { expect(TokenType.Func) val name = readSymbolRaw() expect(TokenType.LeftParentheses) val arguments = collect(TokenType.RightParentheses, TokenType.Comma) { val symbol = readSymbolRaw() - var multiple: Boolean = false + var multiple = false if (next(TokenType.DotDotDot)) { multiple = true } @@ -261,11 +265,21 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { FunctionDefinition(modifiers, name, arguments, block, native) } + private fun readLetDefinition(modifiers: DefinitionModifiers): LetDefinition = within { + val modifiers = readDefinitionModifiers() + expect(TokenType.Let) + val name = readSymbolRaw() + expect(TokenType.Equals) + val value = readExpression() + LetDefinition(modifiers, name, value) + } + private fun maybeReadDefinition(): Definition? { + val modifiers = readDefinitionModifiers() val token = peek() return when (token.type) { - TokenType.Export, - TokenType.Func -> readFunctionDeclaration() + TokenType.Func -> readFunctionDeclaration(modifiers) + TokenType.Let -> readLetDefinition(modifiers) else -> null } } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt index e0de25e..2f1ed39 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt @@ -91,6 +91,14 @@ class Printer(buffer: StringBuilder) : NodeVisitor { visit(node.value) } + override fun visitLetDefinition(node: LetDefinition) { + visitDefinitionModifiers(node.modifiers) + append("let ") + visit(node.symbol) + append(" = ") + visit(node.value) + } + override fun visitSymbolReference(node: SymbolReference) { visit(node.symbol) } @@ -152,10 +160,14 @@ class Printer(buffer: StringBuilder) : NodeVisitor { visit(node.right) } - override fun visitFunctionDefinition(node: FunctionDefinition) { - if (node.modifiers.export) { + private fun visitDefinitionModifiers(modifiers: DefinitionModifiers) { + if (modifiers.export) { append("export ") } + } + + override fun visitFunctionDefinition(node: FunctionDefinition) { + visitDefinitionModifiers(node.modifiers) append("func ") visit(node.symbol) append("(")