mirror of
				https://github.com/GayPizzaSpecifications/pork.git
				synced 2025-11-04 01:49:39 +00:00 
			
		
		
		
	Declaration based compilation units.
This commit is contained in:
		@ -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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn main() {
 | 
			
		||||
  result = fib(20)
 | 
			
		||||
  println(result)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,4 @@
 | 
			
		||||
fn main() {
 | 
			
		||||
  three = 3
 | 
			
		||||
  two = 2
 | 
			
		||||
 | 
			
		||||
@ -51,3 +52,4 @@ 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 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) {
 | 
			
		||||
  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) {
 | 
			
		||||
      out.emitIndent()
 | 
			
		||||
        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(
 | 
			
		||||
 | 
			
		||||
@ -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),
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user