mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 21:21:33 +00:00
Declaration based compilation units.
This commit is contained in:
@ -6,7 +6,7 @@ Very WIP. Like VERY.
|
|||||||
|
|
||||||
```pork
|
```pork
|
||||||
/* fibonacci sequence */
|
/* fibonacci sequence */
|
||||||
fib = { n in
|
fn fib(n) {
|
||||||
if n == 0
|
if n == 0
|
||||||
then 0
|
then 0
|
||||||
else if n == 1
|
else if n == 1
|
||||||
@ -14,8 +14,10 @@ fib = { n in
|
|||||||
else fib(n - 1) + fib(n - 2)
|
else fib(n - 1) + fib(n - 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
result = fib(20)
|
fn main() {
|
||||||
println(result)
|
result = fib(20)
|
||||||
|
println(result)
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## 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 */
|
/* fibonacci sequence */
|
||||||
fib = { n in
|
fn fib(n) {
|
||||||
if n == 0
|
if n == 0
|
||||||
then 0
|
then 0
|
||||||
else if n == 1
|
else if n == 1
|
||||||
@ -7,5 +7,7 @@ fib = { n in
|
|||||||
else fib(n - 1) + fib(n - 2)
|
else fib(n - 1) + fib(n - 2)
|
||||||
}
|
}
|
||||||
|
|
||||||
result = fib(20)
|
fn main() {
|
||||||
println(result)
|
result = fib(20)
|
||||||
|
println(result)
|
||||||
|
}
|
||||||
|
@ -1,40 +1,41 @@
|
|||||||
three = 3
|
fn main() {
|
||||||
two = 2
|
three = 3
|
||||||
|
two = 2
|
||||||
|
|
||||||
calculateSimple = { in
|
calculateSimple = { in
|
||||||
(50 + three) * two
|
(50 + three) * two
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateComplex = { in
|
calculateComplex = { in
|
||||||
three + two + 50
|
three + two + 50
|
||||||
}
|
}
|
||||||
|
|
||||||
multiply = { a, b in
|
multiply = { a, b in
|
||||||
a * b
|
a * b
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculates the result
|
// calculates the result
|
||||||
calculateSimpleResult = calculateSimple()
|
calculateSimpleResult = calculateSimple()
|
||||||
calculateComplexResult = calculateComplex()
|
calculateComplexResult = calculateComplex()
|
||||||
multiplyResult = multiply(50, 50)
|
multiplyResult = multiply(50, 50)
|
||||||
|
|
||||||
list = [10, 20, 30]
|
list = [10, 20, 30]
|
||||||
trueValue = true
|
trueValue = true
|
||||||
falseValue = false
|
falseValue = false
|
||||||
|
|
||||||
invert = { value in
|
invert = { value in
|
||||||
!value
|
!value
|
||||||
}
|
}
|
||||||
|
|
||||||
notEqual = { a, b in
|
notEqual = { a, b in
|
||||||
a != b
|
a != b
|
||||||
}
|
}
|
||||||
|
|
||||||
equal = { a, b in
|
equal = { a, b in
|
||||||
a == b
|
a == b
|
||||||
}
|
}
|
||||||
|
|
||||||
results = [
|
results = [
|
||||||
calculateSimpleResult,
|
calculateSimpleResult,
|
||||||
calculateComplexResult,
|
calculateComplexResult,
|
||||||
multiplyResult,
|
multiplyResult,
|
||||||
@ -47,7 +48,8 @@ results = [
|
|||||||
equal(5, 6),
|
equal(5, 6),
|
||||||
notEqual(5, 5),
|
notEqual(5, 5),
|
||||||
notEqual(5, 6)
|
notEqual(5, 6)
|
||||||
]
|
]
|
||||||
|
|
||||||
println("results:")
|
println("results:")
|
||||||
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 visitPrefixOperation(node: PrefixOperation): Unit = handle(node)
|
||||||
override fun visitIf(node: If): Unit = handle(node)
|
override fun visitIf(node: If): Unit = handle(node)
|
||||||
override fun visitInfixOperation(node: InfixOperation): 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) {
|
private fun handle(node: Node) {
|
||||||
handler(node)
|
handler(node)
|
||||||
|
@ -4,7 +4,9 @@ enum class NodeType(val parent: NodeType? = null) {
|
|||||||
Node,
|
Node,
|
||||||
Symbol(Node),
|
Symbol(Node),
|
||||||
Expression(Node),
|
Expression(Node),
|
||||||
Program(Node),
|
Declaration(Node),
|
||||||
|
Block(Node),
|
||||||
|
CompilationUnit(Node),
|
||||||
IntLiteral(Expression),
|
IntLiteral(Expression),
|
||||||
BooleanLiteral(Expression),
|
BooleanLiteral(Expression),
|
||||||
ListLiteral(Expression),
|
ListLiteral(Expression),
|
||||||
@ -16,7 +18,8 @@ enum class NodeType(val parent: NodeType? = null) {
|
|||||||
InfixOperation(Expression),
|
InfixOperation(Expression),
|
||||||
SymbolReference(Expression),
|
SymbolReference(Expression),
|
||||||
FunctionCall(Expression),
|
FunctionCall(Expression),
|
||||||
If(Expression);
|
If(Expression),
|
||||||
|
FunctionDeclaration(Declaration);
|
||||||
|
|
||||||
val parents: Set<NodeType>
|
val parents: Set<NodeType>
|
||||||
|
|
||||||
|
@ -16,7 +16,10 @@ interface NodeVisitor<T> {
|
|||||||
fun visitPrefixOperation(node: PrefixOperation): T
|
fun visitPrefixOperation(node: PrefixOperation): T
|
||||||
fun visitIf(node: If): T
|
fun visitIf(node: If): T
|
||||||
fun visitInfixOperation(node: InfixOperation): 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) {
|
fun visitExpression(node: Expression): T = when (node) {
|
||||||
is IntLiteral -> visitIntLiteral(node)
|
is IntLiteral -> visitIntLiteral(node)
|
||||||
@ -33,10 +36,16 @@ interface NodeVisitor<T> {
|
|||||||
is InfixOperation -> visitInfixOperation(node)
|
is InfixOperation -> visitInfixOperation(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visitDeclaration(node: Declaration): T = when (node) {
|
||||||
|
is FunctionDeclaration -> visitFunctionDeclaration(node)
|
||||||
|
}
|
||||||
|
|
||||||
fun visit(node: Node): T = when (node) {
|
fun visit(node: Node): T = when (node) {
|
||||||
is Symbol -> visitSymbol(node)
|
is Symbol -> visitSymbol(node)
|
||||||
is Expression -> visitExpression(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> =
|
fun visitNodes(vararg nodes: Node?): List<T> =
|
||||||
|
@ -82,7 +82,6 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
|||||||
visit(node.symbol)
|
visit(node.symbol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun visitLambda(node: Lambda) {
|
override fun visitLambda(node: Lambda) {
|
||||||
append("{")
|
append("{")
|
||||||
if (node.arguments.isNotEmpty()) {
|
if (node.arguments.isNotEmpty()) {
|
||||||
@ -148,10 +147,37 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
|
|||||||
visit(node.right)
|
visit(node.right)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitProgram(node: Program) {
|
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) {
|
for (expression in node.expressions) {
|
||||||
out.emitIndent()
|
appendLine()
|
||||||
visit(expression)
|
visit(expression)
|
||||||
|
}
|
||||||
|
out.decreaseIndent()
|
||||||
|
appendLine()
|
||||||
|
}
|
||||||
|
append("}")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitCompilationUnit(node: CompilationUnit) {
|
||||||
|
for (declaration in node.declarations) {
|
||||||
|
visit(declaration)
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,15 @@ import kotlinx.serialization.SerialName
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
@SerialName("program")
|
@SerialName("block")
|
||||||
class Program(val expressions: List<Expression>) : Node() {
|
class Block(val expressions: List<Expression>) : Node() {
|
||||||
override val type: NodeType = NodeType.Program
|
override val type: NodeType = NodeType.Block
|
||||||
|
|
||||||
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
|
||||||
visitor.visitAll(expressions)
|
visitor.visitAll(expressions)
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is Program) return false
|
if (other !is Block) return false
|
||||||
return other.expressions == expressions
|
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() {
|
override fun run() {
|
||||||
val frontend = FileFrontend(path)
|
val frontend = FileFrontend(path)
|
||||||
val attribution = TokenNodeAttribution()
|
val attribution = TokenNodeAttribution()
|
||||||
val program = frontend.parse(attribution)
|
val compilationUnit = frontend.parse(attribution)
|
||||||
|
|
||||||
val coalescer = NodeCoalescer { node ->
|
val coalescer = NodeCoalescer { node ->
|
||||||
val tokens = attribution.assembleTokens(node)
|
val tokens = attribution.assembleTokens(node)
|
||||||
@ -22,6 +22,6 @@ class AttributeCommand : CliktCommand(help = "Attribute AST", name = "attribute"
|
|||||||
println("token $token")
|
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 com.github.ajalt.clikt.parameters.types.path
|
||||||
import gay.pizza.pork.frontend.FileFrontend
|
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)
|
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||||
|
|
||||||
override fun run() {
|
override fun run() {
|
||||||
|
@ -14,9 +14,7 @@ class RootCommand : CliktCommand(
|
|||||||
TokenizeCommand(),
|
TokenizeCommand(),
|
||||||
ReprintCommand(),
|
ReprintCommand(),
|
||||||
AstCommand(),
|
AstCommand(),
|
||||||
AttributeCommand(),
|
AttributeCommand()
|
||||||
GenerateKotlinCommand(),
|
|
||||||
GenerateDartCommand()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package gay.pizza.pork.cli
|
|||||||
import com.github.ajalt.clikt.core.CliktCommand
|
import com.github.ajalt.clikt.core.CliktCommand
|
||||||
import com.github.ajalt.clikt.parameters.arguments.argument
|
import com.github.ajalt.clikt.parameters.arguments.argument
|
||||||
import com.github.ajalt.clikt.parameters.types.path
|
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.CallableFunction
|
||||||
import gay.pizza.pork.eval.Scope
|
import gay.pizza.pork.eval.Scope
|
||||||
import gay.pizza.pork.frontend.FileFrontend
|
import gay.pizza.pork.frontend.FileFrontend
|
||||||
@ -19,5 +20,6 @@ class RunCommand : CliktCommand(help = "Run Program", name = "run") {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
frontend.evaluate(scope)
|
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 com.github.ajalt.clikt.parameters.types.path
|
||||||
import gay.pizza.pork.frontend.FileFrontend
|
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)
|
val path by argument("file").path(mustExist = true, canBeDir = false)
|
||||||
|
|
||||||
override fun run() {
|
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
|
package gay.pizza.pork.eval
|
||||||
|
|
||||||
import gay.pizza.pork.ast.*
|
import gay.pizza.pork.ast.NodeVisitor
|
||||||
import gay.pizza.pork.ast.nodes.*
|
import gay.pizza.pork.ast.nodes.*
|
||||||
|
|
||||||
class Evaluator(root: Scope) : NodeVisitor<Any> {
|
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
|
var value: Any? = null
|
||||||
for (expression in node.expressions) {
|
for (expression in node.expressions) {
|
||||||
value = visit(expression)
|
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.NodeVisitor
|
||||||
import gay.pizza.pork.ast.Printer
|
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.Evaluator
|
||||||
import gay.pizza.pork.eval.Scope
|
import gay.pizza.pork.eval.Scope
|
||||||
import gay.pizza.pork.parse.*
|
import gay.pizza.pork.parse.*
|
||||||
@ -13,8 +13,8 @@ abstract class Frontend {
|
|||||||
fun tokenize(): TokenStream =
|
fun tokenize(): TokenStream =
|
||||||
Tokenizer(createCharSource()).tokenize()
|
Tokenizer(createCharSource()).tokenize()
|
||||||
|
|
||||||
fun parse(attribution: NodeAttribution = DiscardNodeAttribution): Program =
|
fun parse(attribution: NodeAttribution = DiscardNodeAttribution): CompilationUnit =
|
||||||
Parser(TokenStreamSource(tokenize()), attribution).readProgram()
|
Parser(TokenStreamSource(tokenize()), attribution).readCompilationUnit()
|
||||||
|
|
||||||
fun highlight(scheme: HighlightScheme): List<Highlight> =
|
fun highlight(scheme: HighlightScheme): List<Highlight> =
|
||||||
Highlighter(scheme).highlight(tokenize())
|
Highlighter(scheme).highlight(tokenize())
|
||||||
|
@ -160,6 +160,35 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
|||||||
} else expression
|
} 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 =
|
private fun convertInfixOperator(token: Token): InfixOperator =
|
||||||
when (token.type) {
|
when (token.type) {
|
||||||
TokenType.Plus -> InfixOperator.Plus
|
TokenType.Plus -> InfixOperator.Plus
|
||||||
@ -171,10 +200,10 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
|||||||
else -> throw RuntimeException("Unknown Infix Operator")
|
else -> throw RuntimeException("Unknown Infix Operator")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readProgram(): Program = within {
|
fun readCompilationUnit(): CompilationUnit = within {
|
||||||
val items = collect(TokenType.EndOfFile) { readExpression() }
|
val declarations = collect(TokenType.EndOfFile) { readDeclaration() }
|
||||||
expect(TokenType.EndOfFile)
|
expect(TokenType.EndOfFile)
|
||||||
Program(items)
|
CompilationUnit(declarations)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> collect(
|
private fun <T> collect(
|
||||||
@ -209,7 +238,7 @@ class Parser(source: PeekableSource<Token>, val attribution: NodeAttribution) {
|
|||||||
val token = next()
|
val token = next()
|
||||||
if (!types.contains(token.type)) {
|
if (!types.contains(token.type)) {
|
||||||
throw RuntimeException(
|
throw RuntimeException(
|
||||||
"Expected one of ${types.joinToString(", ")} " +
|
"Expected one of ${types.joinToString(", ")}" +
|
||||||
" but got type ${token.type} '${token.text}'"
|
" but got type ${token.type} '${token.text}'"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ enum class TokenType(vararg properties: TokenTypeProperty) {
|
|||||||
If(Keyword("if"), KeywordFamily),
|
If(Keyword("if"), KeywordFamily),
|
||||||
Then(Keyword("then"), KeywordFamily),
|
Then(Keyword("then"), KeywordFamily),
|
||||||
Else(Keyword("else"), KeywordFamily),
|
Else(Keyword("else"), KeywordFamily),
|
||||||
|
Fn(Keyword("fn"), KeywordFamily),
|
||||||
Whitespace(CharConsumer { it == ' ' || it == '\r' || it == '\n' || it == '\t' }),
|
Whitespace(CharConsumer { it == ' ' || it == '\r' || it == '\n' || it == '\t' }),
|
||||||
BlockComment(CommentFamily),
|
BlockComment(CommentFamily),
|
||||||
LineComment(CommentFamily),
|
LineComment(CommentFamily),
|
||||||
|
Reference in New Issue
Block a user