From b1f9e02253e5688d4add0329d9aef281a4fc3eff Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Sat, 2 Sep 2023 20:22:08 -0700 Subject: [PATCH] Declaration based compilation units. --- README.md | 8 +- examples/commented.pork | 17 --- examples/fib.pork | 8 +- examples/syntax.pork | 106 +++++++++--------- .../gay/pizza/pork/ast/NodeCoalescer.kt | 5 +- .../kotlin/gay/pizza/pork/ast/NodeType.kt | 7 +- .../kotlin/gay/pizza/pork/ast/NodeVisitor.kt | 13 ++- src/main/kotlin/gay/pizza/pork/ast/Printer.kt | 36 +++++- .../pork/ast/nodes/{Program.kt => Block.kt} | 8 +- .../pizza/pork/ast/nodes/CompilationUnit.kt | 26 +++++ .../gay/pizza/pork/ast/nodes/Declaration.kt | 6 + .../pork/ast/nodes/FunctionDeclaration.kt | 28 +++++ .../gay/pizza/pork/cli/AttributeCommand.kt | 4 +- .../gay/pizza/pork/cli/GenerateDartCommand.kt | 16 --- .../pizza/pork/cli/GenerateKotlinCommand.kt | 16 --- .../gay/pizza/pork/cli/ReprintCommand.kt | 2 +- .../kotlin/gay/pizza/pork/cli/RootCommand.kt | 4 +- .../kotlin/gay/pizza/pork/cli/RunCommand.kt | 2 + .../gay/pizza/pork/cli/TokenizeCommand.kt | 2 +- .../gay/pizza/pork/compiler/DartCompiler.kt | 90 --------------- .../gay/pizza/pork/compiler/KotlinCompiler.kt | 78 ------------- .../gay/pizza/pork/eval/BlockFunction.kt | 5 + .../kotlin/gay/pizza/pork/eval/Evaluator.kt | 30 ++++- .../gay/pizza/pork/frontend/Frontend.kt | 6 +- .../kotlin/gay/pizza/pork/parse/Parser.kt | 37 +++++- .../kotlin/gay/pizza/pork/parse/TokenType.kt | 1 + 26 files changed, 255 insertions(+), 306 deletions(-) delete mode 100644 examples/commented.pork rename src/main/kotlin/gay/pizza/pork/ast/nodes/{Program.kt => Block.kt} (75%) create mode 100644 src/main/kotlin/gay/pizza/pork/ast/nodes/CompilationUnit.kt create mode 100644 src/main/kotlin/gay/pizza/pork/ast/nodes/Declaration.kt create mode 100644 src/main/kotlin/gay/pizza/pork/ast/nodes/FunctionDeclaration.kt delete mode 100644 src/main/kotlin/gay/pizza/pork/cli/GenerateDartCommand.kt delete mode 100644 src/main/kotlin/gay/pizza/pork/cli/GenerateKotlinCommand.kt delete mode 100644 src/main/kotlin/gay/pizza/pork/compiler/DartCompiler.kt delete mode 100644 src/main/kotlin/gay/pizza/pork/compiler/KotlinCompiler.kt create mode 100644 src/main/kotlin/gay/pizza/pork/eval/BlockFunction.kt diff --git a/README.md b/README.md index 68516e9..ae836cc 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Very WIP. Like VERY. ```pork /* fibonacci sequence */ -fib = { n in +fn fib(n) { if n == 0 then 0 else if n == 1 @@ -14,8 +14,10 @@ fib = { n in else fib(n - 1) + fib(n - 2) } -result = fib(20) -println(result) +fn main() { + result = fib(20) + println(result) +} ``` ## Usage diff --git a/examples/commented.pork b/examples/commented.pork deleted file mode 100644 index 3912860..0000000 --- a/examples/commented.pork +++ /dev/null @@ -1,17 +0,0 @@ -/* fibonacci sequence */ -/** - * fib(n): calculate the fibonacci sequence. - * @input n the number to calculate fibonacci for - * @result the value of the fibonacci sequence for the number - */ -fib = { n in - if n == 0 // if n is zero, return zero - then 0 - else if n == 1 // if n is one, return one - then 1 - else fib(n - 1) + fib(n - 2) -} - -// result of fib(20) -result = fib(20) -println(result) diff --git a/examples/fib.pork b/examples/fib.pork index 8a5490c..34e2345 100644 --- a/examples/fib.pork +++ b/examples/fib.pork @@ -1,5 +1,5 @@ /* fibonacci sequence */ -fib = { n in +fn fib(n) { if n == 0 then 0 else if n == 1 @@ -7,5 +7,7 @@ fib = { n in else fib(n - 1) + fib(n - 2) } -result = fib(20) -println(result) +fn main() { + result = fib(20) + println(result) +} diff --git a/examples/syntax.pork b/examples/syntax.pork index f186372..a141a20 100644 --- a/examples/syntax.pork +++ b/examples/syntax.pork @@ -1,53 +1,55 @@ -three = 3 -two = 2 - -calculateSimple = { in - (50 + three) * two -} - -calculateComplex = { in - three + two + 50 -} - -multiply = { a, b in - a * b -} - -// calculates the result -calculateSimpleResult = calculateSimple() -calculateComplexResult = calculateComplex() -multiplyResult = multiply(50, 50) - -list = [10, 20, 30] -trueValue = true -falseValue = false - -invert = { value in - !value -} - -notEqual = { a, b in - a != b +fn main() { + three = 3 + two = 2 + + calculateSimple = { in + (50 + three) * two + } + + calculateComplex = { in + three + two + 50 + } + + multiply = { a, b in + a * b + } + + // calculates the result + calculateSimpleResult = calculateSimple() + calculateComplexResult = calculateComplex() + multiplyResult = multiply(50, 50) + + list = [10, 20, 30] + trueValue = true + falseValue = false + + invert = { value in + !value + } + + notEqual = { a, b in + a != b + } + + equal = { a, b in + a == b + } + + results = [ + calculateSimpleResult, + calculateComplexResult, + multiplyResult, + list, + trueValue, + falseValue, + invert(true), + invert(false), + equal(5, 5), + equal(5, 6), + notEqual(5, 5), + notEqual(5, 6) + ] + + println("results:") + println(results) } - -equal = { a, b in - a == b -} - -results = [ - calculateSimpleResult, - calculateComplexResult, - multiplyResult, - list, - trueValue, - falseValue, - invert(true), - invert(false), - equal(5, 5), - equal(5, 6), - notEqual(5, 5), - notEqual(5, 6) -] - -println("results:") -println(results) diff --git a/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt b/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt index 3b8d27a..41a38b2 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt @@ -16,7 +16,10 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor { override fun visitPrefixOperation(node: PrefixOperation): Unit = handle(node) override fun visitIf(node: If): Unit = handle(node) override fun visitInfixOperation(node: InfixOperation): Unit = handle(node) - override fun visitProgram(node: Program): Unit = handle(node) + override fun visitFunctionDeclaration(node: FunctionDeclaration): Unit = handle(node) + override fun visitBlock(node: Block): Unit = handle(node) + + override fun visitCompilationUnit(node: CompilationUnit): Unit = handle(node) private fun handle(node: Node) { handler(node) diff --git a/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt b/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt index 6a2e66d..7baef87 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt @@ -4,7 +4,9 @@ enum class NodeType(val parent: NodeType? = null) { Node, Symbol(Node), Expression(Node), - Program(Node), + Declaration(Node), + Block(Node), + CompilationUnit(Node), IntLiteral(Expression), BooleanLiteral(Expression), ListLiteral(Expression), @@ -16,7 +18,8 @@ enum class NodeType(val parent: NodeType? = null) { InfixOperation(Expression), SymbolReference(Expression), FunctionCall(Expression), - If(Expression); + If(Expression), + FunctionDeclaration(Declaration); val parents: Set diff --git a/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt b/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt index 27d712a..c97179b 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt @@ -16,7 +16,10 @@ interface NodeVisitor { fun visitPrefixOperation(node: PrefixOperation): T fun visitIf(node: If): T fun visitInfixOperation(node: InfixOperation): T - fun visitProgram(node: Program): T + fun visitFunctionDeclaration(node: FunctionDeclaration): T + fun visitBlock(node: Block): T + + fun visitCompilationUnit(node: CompilationUnit): T fun visitExpression(node: Expression): T = when (node) { is IntLiteral -> visitIntLiteral(node) @@ -33,10 +36,16 @@ interface NodeVisitor { is InfixOperation -> visitInfixOperation(node) } + fun visitDeclaration(node: Declaration): T = when (node) { + is FunctionDeclaration -> visitFunctionDeclaration(node) + } + fun visit(node: Node): T = when (node) { is Symbol -> visitSymbol(node) is Expression -> visitExpression(node) - is Program -> visitProgram(node) + is CompilationUnit -> visitCompilationUnit(node) + is Block -> visitBlock(node) + is Declaration -> visitDeclaration(node) } fun visitNodes(vararg nodes: Node?): List = diff --git a/src/main/kotlin/gay/pizza/pork/ast/Printer.kt b/src/main/kotlin/gay/pizza/pork/ast/Printer.kt index cd74d99..d432197 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/Printer.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/Printer.kt @@ -82,7 +82,6 @@ class Printer(buffer: StringBuilder) : NodeVisitor { visit(node.symbol) } - override fun visitLambda(node: Lambda) { append("{") if (node.arguments.isNotEmpty()) { @@ -148,10 +147,37 @@ class Printer(buffer: StringBuilder) : NodeVisitor { visit(node.right) } - override fun visitProgram(node: Program) { - for (expression in node.expressions) { - out.emitIndent() - visit(expression) + override fun visitFunctionDeclaration(node: FunctionDeclaration) { + append("fn ") + visit(node.symbol) + append("(") + for ((index, argument) in node.arguments.withIndex()) { + visit(argument) + if (index + 1 != node.arguments.size) { + append(", ") + } + } + append(") ") + visit(node.block) + } + + override fun visitBlock(node: Block) { + append("{") + if (node.expressions.isNotEmpty()) { + out.increaseIndent() + for (expression in node.expressions) { + appendLine() + visit(expression) + } + out.decreaseIndent() + appendLine() + } + append("}") + } + + override fun visitCompilationUnit(node: CompilationUnit) { + for (declaration in node.declarations) { + visit(declaration) appendLine() } } diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/Program.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/Block.kt similarity index 75% rename from src/main/kotlin/gay/pizza/pork/ast/nodes/Program.kt rename to src/main/kotlin/gay/pizza/pork/ast/nodes/Block.kt index a664124..a1a433e 100644 --- a/src/main/kotlin/gay/pizza/pork/ast/nodes/Program.kt +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/Block.kt @@ -6,15 +6,15 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @Serializable -@SerialName("program") -class Program(val expressions: List) : Node() { - override val type: NodeType = NodeType.Program +@SerialName("block") +class Block(val expressions: List) : Node() { + override val type: NodeType = NodeType.Block override fun visitChildren(visitor: NodeVisitor): List = visitor.visitAll(expressions) override fun equals(other: Any?): Boolean { - if (other !is Program) return false + if (other !is Block) return false return other.expressions == expressions } diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/CompilationUnit.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/CompilationUnit.kt new file mode 100644 index 0000000..077d605 --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/CompilationUnit.kt @@ -0,0 +1,26 @@ +package gay.pizza.pork.ast.nodes + +import gay.pizza.pork.ast.NodeType +import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("compilationUnit") +class CompilationUnit(val declarations: List) : Node() { + override val type: NodeType = NodeType.CompilationUnit + + override fun visitChildren(visitor: NodeVisitor): List = + visitor.visitAll(declarations) + + override fun equals(other: Any?): Boolean { + if (other !is CompilationUnit) return false + return other.declarations == declarations + } + + override fun hashCode(): Int { + var result = declarations.hashCode() + result = 31 * result + type.hashCode() + return result + } +} diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/Declaration.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/Declaration.kt new file mode 100644 index 0000000..d439565 --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/Declaration.kt @@ -0,0 +1,6 @@ +package gay.pizza.pork.ast.nodes + +import kotlinx.serialization.Serializable + +@Serializable +sealed class Declaration : Node() diff --git a/src/main/kotlin/gay/pizza/pork/ast/nodes/FunctionDeclaration.kt b/src/main/kotlin/gay/pizza/pork/ast/nodes/FunctionDeclaration.kt new file mode 100644 index 0000000..90dab09 --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/ast/nodes/FunctionDeclaration.kt @@ -0,0 +1,28 @@ +package gay.pizza.pork.ast.nodes + +import gay.pizza.pork.ast.NodeType +import gay.pizza.pork.ast.NodeVisitor +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("functionDeclaration") +class FunctionDeclaration(val symbol: Symbol, val arguments: List, val block: Block) : Declaration() { + override val type: NodeType = NodeType.FunctionDeclaration + + override fun visitChildren(visitor: NodeVisitor): List = + visitor.visitNodes(symbol) + + override fun equals(other: Any?): Boolean { + if (other !is FunctionDeclaration) return false + return other.symbol == symbol && other.arguments == arguments && other.block == block + } + + override fun hashCode(): Int { + var result = symbol.hashCode() + result = 31 * result + symbol.hashCode() + result = 31 * result + arguments.hashCode() + result = 31 * result + block.hashCode() + return result + } +} diff --git a/src/main/kotlin/gay/pizza/pork/cli/AttributeCommand.kt b/src/main/kotlin/gay/pizza/pork/cli/AttributeCommand.kt index 5b43584..824e78f 100644 --- a/src/main/kotlin/gay/pizza/pork/cli/AttributeCommand.kt +++ b/src/main/kotlin/gay/pizza/pork/cli/AttributeCommand.kt @@ -13,7 +13,7 @@ class AttributeCommand : CliktCommand(help = "Attribute AST", name = "attribute" override fun run() { val frontend = FileFrontend(path) val attribution = TokenNodeAttribution() - val program = frontend.parse(attribution) + val compilationUnit = frontend.parse(attribution) val coalescer = NodeCoalescer { node -> val tokens = attribution.assembleTokens(node) @@ -22,6 +22,6 @@ class AttributeCommand : CliktCommand(help = "Attribute AST", name = "attribute" println("token $token") } } - coalescer.visit(program) + coalescer.visit(compilationUnit) } } diff --git a/src/main/kotlin/gay/pizza/pork/cli/GenerateDartCommand.kt b/src/main/kotlin/gay/pizza/pork/cli/GenerateDartCommand.kt deleted file mode 100644 index 1b86212..0000000 --- a/src/main/kotlin/gay/pizza/pork/cli/GenerateDartCommand.kt +++ /dev/null @@ -1,16 +0,0 @@ -package gay.pizza.pork.cli - -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.pork.compiler.DartCompiler -import gay.pizza.pork.frontend.FileFrontend - -class GenerateDartCommand : CliktCommand(help = "Generate Dart Code", name = "generate-dart") { - val path by argument("file").path(mustExist = true, canBeDir = false) - - override fun run() { - val frontend = FileFrontend(path) - println(frontend.visit(DartCompiler())) - } -} diff --git a/src/main/kotlin/gay/pizza/pork/cli/GenerateKotlinCommand.kt b/src/main/kotlin/gay/pizza/pork/cli/GenerateKotlinCommand.kt deleted file mode 100644 index fdfc9a5..0000000 --- a/src/main/kotlin/gay/pizza/pork/cli/GenerateKotlinCommand.kt +++ /dev/null @@ -1,16 +0,0 @@ -package gay.pizza.pork.cli - -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.pork.compiler.KotlinCompiler -import gay.pizza.pork.frontend.FileFrontend - -class GenerateKotlinCommand : CliktCommand(help = "Generate Kotlin Code", name = "generate-kotlin") { - val path by argument("file").path(mustExist = true, canBeDir = false) - - override fun run() { - val frontend = FileFrontend(path) - println(frontend.visit(KotlinCompiler())) - } -} diff --git a/src/main/kotlin/gay/pizza/pork/cli/ReprintCommand.kt b/src/main/kotlin/gay/pizza/pork/cli/ReprintCommand.kt index 8a53c67..0c1b675 100644 --- a/src/main/kotlin/gay/pizza/pork/cli/ReprintCommand.kt +++ b/src/main/kotlin/gay/pizza/pork/cli/ReprintCommand.kt @@ -5,7 +5,7 @@ import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.types.path import gay.pizza.pork.frontend.FileFrontend -class ReprintCommand : CliktCommand(help = "Reprint Parsed Program", name = "reprint") { +class ReprintCommand : CliktCommand(help = "Reprint Parsed Compilation Unit", name = "reprint") { val path by argument("file").path(mustExist = true, canBeDir = false) override fun run() { diff --git a/src/main/kotlin/gay/pizza/pork/cli/RootCommand.kt b/src/main/kotlin/gay/pizza/pork/cli/RootCommand.kt index e069864..611964c 100644 --- a/src/main/kotlin/gay/pizza/pork/cli/RootCommand.kt +++ b/src/main/kotlin/gay/pizza/pork/cli/RootCommand.kt @@ -14,9 +14,7 @@ class RootCommand : CliktCommand( TokenizeCommand(), ReprintCommand(), AstCommand(), - AttributeCommand(), - GenerateKotlinCommand(), - GenerateDartCommand() + AttributeCommand() ) } diff --git a/src/main/kotlin/gay/pizza/pork/cli/RunCommand.kt b/src/main/kotlin/gay/pizza/pork/cli/RunCommand.kt index a2182b8..e606767 100644 --- a/src/main/kotlin/gay/pizza/pork/cli/RunCommand.kt +++ b/src/main/kotlin/gay/pizza/pork/cli/RunCommand.kt @@ -3,6 +3,7 @@ package gay.pizza.pork.cli 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.pork.eval.Arguments import gay.pizza.pork.eval.CallableFunction import gay.pizza.pork.eval.Scope import gay.pizza.pork.frontend.FileFrontend @@ -19,5 +20,6 @@ class RunCommand : CliktCommand(help = "Run Program", name = "run") { } }) frontend.evaluate(scope) + scope.call("main", Arguments(emptyList())) } } diff --git a/src/main/kotlin/gay/pizza/pork/cli/TokenizeCommand.kt b/src/main/kotlin/gay/pizza/pork/cli/TokenizeCommand.kt index d9e67c6..a99374b 100644 --- a/src/main/kotlin/gay/pizza/pork/cli/TokenizeCommand.kt +++ b/src/main/kotlin/gay/pizza/pork/cli/TokenizeCommand.kt @@ -5,7 +5,7 @@ import com.github.ajalt.clikt.parameters.arguments.argument import com.github.ajalt.clikt.parameters.types.path import gay.pizza.pork.frontend.FileFrontend -class TokenizeCommand : CliktCommand(help = "Tokenize Program", name = "tokenize") { +class TokenizeCommand : CliktCommand(help = "Tokenize Compilation Unit", name = "tokenize") { val path by argument("file").path(mustExist = true, canBeDir = false) override fun run() { diff --git a/src/main/kotlin/gay/pizza/pork/compiler/DartCompiler.kt b/src/main/kotlin/gay/pizza/pork/compiler/DartCompiler.kt deleted file mode 100644 index 2764ea0..0000000 --- a/src/main/kotlin/gay/pizza/pork/compiler/DartCompiler.kt +++ /dev/null @@ -1,90 +0,0 @@ -package gay.pizza.pork.compiler - -import gay.pizza.pork.ast.NodeVisitor -import gay.pizza.pork.ast.nodes.* -import gay.pizza.pork.util.StringEscape - -class DartCompiler : NodeVisitor { - override fun visitIntLiteral(node: IntLiteral): String = - node.value.toString() - - override fun visitStringLiteral(node: StringLiteral): String = - "\"" + StringEscape.escape(node.text) + "\"" - - override fun visitBooleanLiteral(node: BooleanLiteral): String = - node.value.toString() - - override fun visitListLiteral(node: ListLiteral): String = buildString { - append("[") - for ((index, item) in node.items.withIndex()) { - appendLine() - append(visit(item)) - if (index + 1 != node.items.size) { - append(",") - } - } - append("]") - } - - override fun visitSymbol(node: Symbol): String = - node.id - - override fun visitFunctionCall(node: FunctionCall): String = - "${visit(node.symbol)}(${node.arguments.joinToString(", ") { visit(it) }})" - - override fun visitDefine(node: Define): String = - "final ${visit(node.symbol)} = ${visit(node.value)};" - - override fun visitSymbolReference(node: SymbolReference): String = - visit(node.symbol) - - override fun visitLambda(node: Lambda): String = buildString { - append("(${node.arguments.joinToString(", ") { visit(it) }}) {") - appendLine() - for ((index, expression) in node.expressions.withIndex()) { - val code = visit(expression) - if (index == node.expressions.size - 1) { - append("return "); - } - append(code) - append(";") - } - appendLine() - append("}") - } - - override fun visitParentheses(node: Parentheses): String = - "(${visit(node.expression)})" - - override fun visitPrefixOperation(node: PrefixOperation): String = - "${node.op.token}${visit(node.expression)}" - - override fun visitIf(node: If): String = buildString { - append("if (") - append(visit(node.condition)) - append(") {") - append(visit(node.thenExpression)) - append("}") - if (node.elseExpression != null) { - append(" else {") - append(visit(node.elseExpression)) - append("}") - } - } - - override fun visitInfixOperation(node: InfixOperation): String = - "${visit(node.left)} ${node.op.token} ${visit(node.right)}" - - override fun visitProgram(node: Program): String = buildString { - appendLine("void main() {") - for (item in node.expressions) { - append(visit(item)) - if (!endsWith(";")) { - append(";") - } - append(";") - appendLine() - } - appendLine("}") - } -} diff --git a/src/main/kotlin/gay/pizza/pork/compiler/KotlinCompiler.kt b/src/main/kotlin/gay/pizza/pork/compiler/KotlinCompiler.kt deleted file mode 100644 index dc6dcc3..0000000 --- a/src/main/kotlin/gay/pizza/pork/compiler/KotlinCompiler.kt +++ /dev/null @@ -1,78 +0,0 @@ -package gay.pizza.pork.compiler - -import gay.pizza.pork.ast.* -import gay.pizza.pork.ast.nodes.* -import gay.pizza.pork.util.StringEscape - -class KotlinCompiler : NodeVisitor { - override fun visitIntLiteral(node: IntLiteral): String = - node.value.toString() - - override fun visitStringLiteral(node: StringLiteral): String = - "\"" + StringEscape.escape(node.text) + "\"" - - override fun visitBooleanLiteral(node: BooleanLiteral): String = - node.value.toString() - - override fun visitListLiteral(node: ListLiteral): String = buildString { - append("listOf(") - for ((index, item) in node.items.withIndex()) { - appendLine() - append(visit(item)) - if (index + 1 != node.items.size) { - append(",") - } - } - append(")") - } - - override fun visitSymbol(node: Symbol): String = - node.id - - override fun visitFunctionCall(node: FunctionCall): String = - "${visit(node.symbol)}(${node.arguments.joinToString(", ") { visit(it) }})" - - override fun visitDefine(node: Define): String = - "val ${visit(node.symbol)} = ${visit(node.value)}" - - override fun visitSymbolReference(node: SymbolReference): String = - visit(node.symbol) - - override fun visitLambda(node: Lambda): String = buildString { - append("{ ${node.arguments.joinToString(", ") { visit(it) }} ->") - appendLine() - append(visitAll(node.expressions).joinToString("\n")) - appendLine() - append("}") - } - - override fun visitParentheses(node: Parentheses): String = - "(${visit(node.expression)})" - - override fun visitPrefixOperation(node: PrefixOperation): String = - "${node.op.token}${visit(node.expression)}" - - override fun visitIf(node: If): String = buildString { - append("if (") - append(visit(node.condition)) - append(") {") - append(visit(node.thenExpression)) - append("}") - if (node.elseExpression != null) { - append(" else {") - append(visit(node.elseExpression)) - append("}") - } - } - - override fun visitInfixOperation(node: InfixOperation): String = - "${visit(node.left)} ${node.op.token} ${visit(node.right)}" - - override fun visitProgram(node: Program): String = buildString { - appendLine("fun main() {") - for (item in node.expressions) { - appendLine(visit(item)) - } - appendLine("}") - } -} diff --git a/src/main/kotlin/gay/pizza/pork/eval/BlockFunction.kt b/src/main/kotlin/gay/pizza/pork/eval/BlockFunction.kt new file mode 100644 index 0000000..46d34a1 --- /dev/null +++ b/src/main/kotlin/gay/pizza/pork/eval/BlockFunction.kt @@ -0,0 +1,5 @@ +package gay.pizza.pork.eval + +fun interface BlockFunction { + fun call(): Any +} diff --git a/src/main/kotlin/gay/pizza/pork/eval/Evaluator.kt b/src/main/kotlin/gay/pizza/pork/eval/Evaluator.kt index a32c3e3..49f5eae 100644 --- a/src/main/kotlin/gay/pizza/pork/eval/Evaluator.kt +++ b/src/main/kotlin/gay/pizza/pork/eval/Evaluator.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.eval -import gay.pizza.pork.ast.* +import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.nodes.* class Evaluator(root: Scope) : NodeVisitor { @@ -102,11 +102,35 @@ class Evaluator(root: Scope) : NodeVisitor { } } - override fun visitProgram(node: Program): Any { + override fun visitFunctionDeclaration(node: FunctionDeclaration): Any { + val blockFunction = visitBlock(node.block) as BlockFunction + val function = CallableFunction { arguments -> + currentScope = currentScope.fork() + for ((index, argumentSymbol) in node.arguments.withIndex()) { + currentScope.define(argumentSymbol.id, arguments.values[index]) + } + try { + return@CallableFunction blockFunction.call() + } finally { + currentScope = currentScope.leave() + } + } + currentScope.define(node.symbol.id, function) + return None + } + + override fun visitBlock(node: Block): Any = BlockFunction { var value: Any? = null for (expression in node.expressions) { value = visit(expression) } - return value ?: None + value ?: None + } + + override fun visitCompilationUnit(node: CompilationUnit): Any { + for (declaration in node.declarations) { + visit(declaration) + } + return None } } diff --git a/src/main/kotlin/gay/pizza/pork/frontend/Frontend.kt b/src/main/kotlin/gay/pizza/pork/frontend/Frontend.kt index 9e36045..acdfd1d 100644 --- a/src/main/kotlin/gay/pizza/pork/frontend/Frontend.kt +++ b/src/main/kotlin/gay/pizza/pork/frontend/Frontend.kt @@ -2,7 +2,7 @@ package gay.pizza.pork.frontend import gay.pizza.pork.ast.NodeVisitor import gay.pizza.pork.ast.Printer -import gay.pizza.pork.ast.nodes.Program +import gay.pizza.pork.ast.nodes.CompilationUnit import gay.pizza.pork.eval.Evaluator import gay.pizza.pork.eval.Scope import gay.pizza.pork.parse.* @@ -13,8 +13,8 @@ abstract class Frontend { fun tokenize(): TokenStream = Tokenizer(createCharSource()).tokenize() - fun parse(attribution: NodeAttribution = DiscardNodeAttribution): Program = - Parser(TokenStreamSource(tokenize()), attribution).readProgram() + fun parse(attribution: NodeAttribution = DiscardNodeAttribution): CompilationUnit = + Parser(TokenStreamSource(tokenize()), attribution).readCompilationUnit() fun highlight(scheme: HighlightScheme): List = Highlighter(scheme).highlight(tokenize()) diff --git a/src/main/kotlin/gay/pizza/pork/parse/Parser.kt b/src/main/kotlin/gay/pizza/pork/parse/Parser.kt index 13fa65c..ad3d34a 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/Parser.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/Parser.kt @@ -160,6 +160,35 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { } else expression } + private fun readBlock(): Block = within { + expect(TokenType.LeftCurly) + val items = collect(TokenType.RightCurly) { + readExpression() + } + expect(TokenType.RightCurly) + Block(items) + } + + private fun readFunctionDeclaration(): FunctionDeclaration = within { + expect(TokenType.Fn) + val name = readSymbolRaw() + expect(TokenType.LeftParentheses) + val arguments = collect(TokenType.RightParentheses, TokenType.Comma) { readSymbolRaw() } + expect(TokenType.RightParentheses) + FunctionDeclaration(name, arguments, readBlock()) + } + + fun readDeclaration(): Declaration { + val token = peek() + return when (token.type) { + TokenType.Fn -> readFunctionDeclaration() + else -> throw RuntimeException( + "Failed to parse token: ${token.type} '${token.text}' as" + + " declaration (index ${unsanitizedSource.currentIndex})" + ) + } + } + private fun convertInfixOperator(token: Token): InfixOperator = when (token.type) { TokenType.Plus -> InfixOperator.Plus @@ -171,10 +200,10 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { else -> throw RuntimeException("Unknown Infix Operator") } - fun readProgram(): Program = within { - val items = collect(TokenType.EndOfFile) { readExpression() } + fun readCompilationUnit(): CompilationUnit = within { + val declarations = collect(TokenType.EndOfFile) { readDeclaration() } expect(TokenType.EndOfFile) - Program(items) + CompilationUnit(declarations) } private fun collect( @@ -209,7 +238,7 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { val token = next() if (!types.contains(token.type)) { throw RuntimeException( - "Expected one of ${types.joinToString(", ")} " + + "Expected one of ${types.joinToString(", ")}" + " but got type ${token.type} '${token.text}'" ) } diff --git a/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt b/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt index 36a4bb3..4d2b059 100644 --- a/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt +++ b/src/main/kotlin/gay/pizza/pork/parse/TokenType.kt @@ -28,6 +28,7 @@ enum class TokenType(vararg properties: TokenTypeProperty) { If(Keyword("if"), KeywordFamily), Then(Keyword("then"), KeywordFamily), Else(Keyword("else"), KeywordFamily), + Fn(Keyword("fn"), KeywordFamily), Whitespace(CharConsumer { it == ' ' || it == '\r' || it == '\n' || it == '\t' }), BlockComment(CommentFamily), LineComment(CommentFamily),