Skip to content

Commit

Permalink
Implement a very dumb import system.
Browse files Browse the repository at this point in the history
  • Loading branch information
azenla committed Sep 3, 2023
1 parent e7be1f6 commit ed5bbec
Show file tree
Hide file tree
Showing 18 changed files with 171 additions and 29 deletions.
5 changes: 5 additions & 0 deletions examples/imports.pork
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "module.pork"

func main() {
hello()
}
3 changes: 3 additions & 0 deletions examples/module.pork
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
func hello() {
println("Hello World")
}
5 changes: 3 additions & 2 deletions src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,16 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor<Unit> {
override fun visitListLiteral(node: ListLiteral): Unit = handle(node)
override fun visitSymbol(node: Symbol): Unit = handle(node)
override fun visitFunctionCall(node: FunctionCall): Unit = handle(node)
override fun visitDefine(node: Define): Unit = handle(node)
override fun visitDefine(node: Assignment): Unit = handle(node)
override fun visitSymbolReference(node: SymbolReference): Unit = handle(node)
override fun visitLambda(node: Lambda): Unit = handle(node)
override fun visitParentheses(node: Parentheses): Unit = handle(node)
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 visitFunctionDeclaration(node: FunctionDeclaration): Unit = handle(node)
override fun visitFunctionDeclaration(node: FunctionDefinition): Unit = handle(node)
override fun visitBlock(node: Block): Unit = handle(node)
override fun visitImportDeclaration(node: ImportDeclaration): Unit = handle(node)

override fun visitCompilationUnit(node: CompilationUnit): Unit = handle(node)

Expand Down
3 changes: 2 additions & 1 deletion src/main/kotlin/gay/pizza/pork/ast/NodeType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ enum class NodeType(val parent: NodeType? = null) {
ListLiteral(Expression),
StringLiteral(Expression),
Parentheses(Expression),
Define(Expression),
Assignment(Expression),
Lambda(Expression),
PrefixOperation(Expression),
InfixOperation(Expression),
SymbolReference(Expression),
FunctionCall(Expression),
If(Expression),
ImportDeclaration(Declaration),
FunctionDeclaration(Declaration);

val parents: Set<NodeType>
Expand Down
14 changes: 10 additions & 4 deletions src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@ interface NodeVisitor<T> {
fun visitListLiteral(node: ListLiteral): T
fun visitSymbol(node: Symbol): T
fun visitFunctionCall(node: FunctionCall): T
fun visitDefine(node: Define): T
fun visitDefine(node: Assignment): T
fun visitSymbolReference(node: SymbolReference): T
fun visitLambda(node: Lambda): T
fun visitParentheses(node: Parentheses): T
fun visitPrefixOperation(node: PrefixOperation): T
fun visitIf(node: If): T
fun visitInfixOperation(node: InfixOperation): T
fun visitFunctionDeclaration(node: FunctionDeclaration): T
fun visitFunctionDeclaration(node: FunctionDefinition): T
fun visitBlock(node: Block): T

fun visitImportDeclaration(node: ImportDeclaration): T
fun visitCompilationUnit(node: CompilationUnit): T

fun visitExpression(node: Expression): T = when (node) {
Expand All @@ -27,7 +28,7 @@ interface NodeVisitor<T> {
is BooleanLiteral -> visitBooleanLiteral(node)
is ListLiteral -> visitListLiteral(node)
is FunctionCall -> visitFunctionCall(node)
is Define -> visitDefine(node)
is Assignment -> visitDefine(node)
is SymbolReference -> visitSymbolReference(node)
is Lambda -> visitLambda(node)
is Parentheses -> visitParentheses(node)
Expand All @@ -37,7 +38,11 @@ interface NodeVisitor<T> {
}

fun visitDeclaration(node: Declaration): T = when (node) {
is FunctionDeclaration -> visitFunctionDeclaration(node)
is ImportDeclaration -> visitImportDeclaration(node)
}

fun visitDefinition(node: Definition): T = when (node) {
is FunctionDefinition -> visitFunctionDeclaration(node)
}

fun visit(node: Node): T = when (node) {
Expand All @@ -46,6 +51,7 @@ interface NodeVisitor<T> {
is CompilationUnit -> visitCompilationUnit(node)
is Block -> visitBlock(node)
is Declaration -> visitDeclaration(node)
is Definition -> visitDefinition(node)
}

fun visitNodes(vararg nodes: Node?): List<T> =
Expand Down
18 changes: 16 additions & 2 deletions src/main/kotlin/gay/pizza/pork/ast/Printer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
append(")")
}

override fun visitDefine(node: Define) {
override fun visitDefine(node: Assignment) {
visit(node.symbol)
append(" = ")
visit(node.value)
Expand Down Expand Up @@ -147,7 +147,7 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
visit(node.right)
}

override fun visitFunctionDeclaration(node: FunctionDeclaration) {
override fun visitFunctionDeclaration(node: FunctionDefinition) {
append("fn ")
visit(node.symbol)
append("(")
Expand Down Expand Up @@ -175,10 +175,24 @@ class Printer(buffer: StringBuilder) : NodeVisitor<Unit> {
append("}")
}

override fun visitImportDeclaration(node: ImportDeclaration) {
append("import ")
visit(node.path)
}

override fun visitCompilationUnit(node: CompilationUnit) {
for (declaration in node.declarations) {
visit(declaration)
appendLine()
}

if (node.declarations.isNotEmpty()) {
appendLine()
}

for (definition in node.definitions) {
visit(definition)
appendLine()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
@SerialName("define")
class Define(val symbol: Symbol, val value: Expression) : Expression() {
override val type: NodeType = NodeType.Define
@SerialName("assignment")
class Assignment(val symbol: Symbol, val value: Expression) : Expression() {
override val type: NodeType = NodeType.Assignment

override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(symbol, value)

override fun equals(other: Any?): Boolean {
if (other !is Define) return false
if (other !is Assignment) return false
return other.symbol == symbol && other.value == value
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import kotlinx.serialization.Serializable

@Serializable
@SerialName("compilationUnit")
class CompilationUnit(val declarations: List<Declaration>) : Node() {
class CompilationUnit(val declarations: List<Declaration>, val definitions: List<Definition>) : Node() {
override val type: NodeType = NodeType.CompilationUnit

override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
Expand Down
6 changes: 6 additions & 0 deletions src/main/kotlin/gay/pizza/pork/ast/nodes/Definition.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package gay.pizza.pork.ast.nodes

import kotlinx.serialization.Serializable

@Serializable
sealed class Definition : Node()
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
@SerialName("functionDeclaration")
class FunctionDeclaration(val symbol: Symbol, val arguments: List<Symbol>, val block: Block) : Declaration() {
@SerialName("functionDefinition")
class FunctionDefinition(val symbol: Symbol, val arguments: List<Symbol>, val block: Block) : Definition() {
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
if (other !is FunctionDefinition) 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()
result = 31 * result + type.hashCode()
return result
}
}
26 changes: 26 additions & 0 deletions src/main/kotlin/gay/pizza/pork/ast/nodes/ImportDeclaration.kt
Original file line number Diff line number Diff line change
@@ -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("importDeclaration")
class ImportDeclaration(val path: StringLiteral) : Declaration() {
override val type: NodeType = NodeType.FunctionDeclaration

override fun <T> visitChildren(visitor: NodeVisitor<T>): List<T> =
visitor.visitNodes(path)

override fun equals(other: Any?): Boolean {
if (other !is ImportDeclaration) return false
return other.path == path
}

override fun hashCode(): Int {
var result = path.hashCode()
result = 31 * result + type.hashCode()
return result
}
}
17 changes: 14 additions & 3 deletions src/main/kotlin/gay/pizza/pork/eval/Evaluator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package gay.pizza.pork.eval
import gay.pizza.pork.ast.NodeVisitor
import gay.pizza.pork.ast.nodes.*

class Evaluator(root: Scope) : NodeVisitor<Any> {
class Evaluator(root: Scope, val importLoader: ImportLoader) : NodeVisitor<Any> {
private var currentScope: Scope = root

override fun visitIntLiteral(node: IntLiteral): Any = node.value
Expand All @@ -18,7 +18,7 @@ class Evaluator(root: Scope) : NodeVisitor<Any> {
return currentScope.call(node.symbol.id, Arguments(arguments))
}

override fun visitDefine(node: Define): Any {
override fun visitDefine(node: Assignment): Any {
val value = visit(node.value)
currentScope.define(node.symbol.id, value)
return value
Expand Down Expand Up @@ -102,7 +102,7 @@ class Evaluator(root: Scope) : NodeVisitor<Any> {
}
}

override fun visitFunctionDeclaration(node: FunctionDeclaration): Any {
override fun visitFunctionDeclaration(node: FunctionDefinition): Any {
val blockFunction = visitBlock(node.block) as BlockFunction
val function = CallableFunction { arguments ->
currentScope = currentScope.fork()
Expand All @@ -127,10 +127,21 @@ class Evaluator(root: Scope) : NodeVisitor<Any> {
value ?: None
}

override fun visitImportDeclaration(node: ImportDeclaration): Any {
val importPath = node.path.text
val compilationUnit = importLoader.load(importPath)
visit(compilationUnit)
return None
}

override fun visitCompilationUnit(node: CompilationUnit): Any {
for (declaration in node.declarations) {
visit(declaration)
}

for (definition in node.definitions) {
visit(definition)
}
return None
}
}
7 changes: 7 additions & 0 deletions src/main/kotlin/gay/pizza/pork/eval/ImportLoader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package gay.pizza.pork.eval

import gay.pizza.pork.ast.nodes.CompilationUnit

interface ImportLoader {
fun load(path: String): CompilationUnit
}
9 changes: 9 additions & 0 deletions src/main/kotlin/gay/pizza/pork/eval/NullImportLoader.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package gay.pizza.pork.eval

import gay.pizza.pork.ast.nodes.CompilationUnit

object NullImportLoader : ImportLoader {
override fun load(path: String): CompilationUnit {
throw RuntimeException("NullImportLoader cannot import compilation units.")
}
}
2 changes: 2 additions & 0 deletions src/main/kotlin/gay/pizza/pork/frontend/FileFrontend.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ import kotlin.io.path.readText

class FileFrontend(val path: Path) : Frontend() {
override fun createCharSource(): CharSource = StringCharSource(path.readText())
override fun resolveImportSource(path: String): CharSource =
StringCharSource(this.path.parent.resolve(path).readText())
}
11 changes: 10 additions & 1 deletion src/main/kotlin/gay/pizza/pork/frontend/Frontend.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import gay.pizza.pork.ast.NodeVisitor
import gay.pizza.pork.ast.Printer
import gay.pizza.pork.ast.nodes.CompilationUnit
import gay.pizza.pork.eval.Evaluator
import gay.pizza.pork.eval.ImportLoader
import gay.pizza.pork.eval.Scope
import gay.pizza.pork.parse.*

abstract class Frontend {
abstract fun createCharSource(): CharSource
abstract fun resolveImportSource(path: String): CharSource

fun tokenize(): TokenStream =
Tokenizer(createCharSource()).tokenize()
Expand All @@ -20,9 +22,16 @@ abstract class Frontend {
Highlighter(scheme).highlight(tokenize())

fun evaluate(scope: Scope = Scope()): Any =
visit(Evaluator(scope))
visit(Evaluator(scope, FrontendImportLoader(this)))

fun reprint(): String = buildString { visit(Printer(this)) }

fun <T> visit(visitor: NodeVisitor<T>): T = visitor.visit(parse())

private class FrontendImportLoader(val frontend: Frontend) : ImportLoader {
override fun load(path: String): CompilationUnit {
val tokenStream = Tokenizer(frontend.resolveImportSource(path)).tokenize()
return Parser(TokenStreamSource(tokenStream), DiscardNodeAttribution).readCompilationUnit()
}
}
}
Loading

0 comments on commit ed5bbec

Please sign in to comment.