mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-02 12:50:55 +00:00
Declaration based compilation units.
This commit is contained in:
parent
04c78c35e0
commit
b1f9e02253
@ -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
|
||||
|
@ -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)
|
@ -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)
|
||||
}
|
||||
|
@ -1,53 +1,55 @@
|
||||
three = 3
|
||||
two = 2
|
||||
fn main() {
|
||||
three = 3
|
||||
two = 2
|
||||
|
||||
calculateSimple = { in
|
||||
(50 + three) * two
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -16,7 +16,10 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
|
||||
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)
|
||||
|
@ -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<NodeType>
|
||||
|
||||
|
@ -16,7 +16,10 @@ interface NodeVisitor<T> {
|
||||
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<T> {
|
||||
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<T> =
|
||||
|
@ -82,7 +82,6 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
visit(node.symbol)
|
||||
}
|
||||
|
||||
|
||||
override fun visitLambda(node: Lambda) {
|
||||
append("{")
|
||||
if (node.arguments.isNotEmpty()) {
|
||||
@ -148,10 +147,37 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -6,15 +6,15 @@ import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("program")
|
||||
class Program(val expressions: List<Expression>) : Node() {
|
||||
override val type: NodeType = NodeType.Program
|
||||
@SerialName("block")
|
||||
class Block(val expressions: List<Expression>) : Node() {
|
||||
override val type: NodeType = NodeType.Block
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
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
|
||||
}
|
||||
|
26
src/main/kotlin/gay/pizza/pork/ast/nodes/CompilationUnit.kt
Normal file
26
src/main/kotlin/gay/pizza/pork/ast/nodes/CompilationUnit.kt
Normal file
@ -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<Declaration>) : Node() {
|
||||
override val type: NodeType = NodeType.CompilationUnit
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
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
|
||||
}
|
||||
}
|
6
src/main/kotlin/gay/pizza/pork/ast/nodes/Declaration.kt
Normal file
6
src/main/kotlin/gay/pizza/pork/ast/nodes/Declaration.kt
Normal file
@ -0,0 +1,6 @@
|
||||
package gay.pizza.pork.ast.nodes
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed class Declaration : Node()
|
@ -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<Symbol>, val block: Block) : Declaration() {
|
||||
override val type: NodeType = NodeType.FunctionDeclaration
|
||||
|
||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||
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
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
@ -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()))
|
||||
}
|
||||
}
|
@ -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() {
|
||||
|
@ -14,9 +14,7 @@ class RootCommand : CliktCommand(
|
||||
TokenizeCommand(),
|
||||
ReprintCommand(),
|
||||
AstCommand(),
|
||||
AttributeCommand(),
|
||||
GenerateKotlinCommand(),
|
||||
GenerateDartCommand()
|
||||
AttributeCommand()
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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<String> {
|
||||
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("}")
|
||||
}
|
||||
}
|
@ -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<String> {
|
||||
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("}")
|
||||
}
|
||||
}
|
5
src/main/kotlin/gay/pizza/pork/eval/BlockFunction.kt
Normal file
5
src/main/kotlin/gay/pizza/pork/eval/BlockFunction.kt
Normal file
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.eval
|
||||
|
||||
fun interface BlockFunction {
|
||||
fun call(): Any
|
||||
}
|
@ -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<Any> {
|
||||
@ -102,11 +102,35 @@ class Evaluator(root: Scope) : NodeVisitor<Any> {
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -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<Highlight> =
|
||||
Highlighter(scheme).highlight(tokenize())
|
||||
|
@ -160,6 +160,35 @@ class Parser(source: PeekableSource<Token>, 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<Token>, 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 <T> collect(
|
||||
@ -209,7 +238,7 @@ class Parser(source: PeekableSource<Token>, 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}'"
|
||||
)
|
||||
}
|
||||
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user