From 81296ee1d1788743cab3fd003253992bdb43110d Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Wed, 6 Sep 2023 22:43:03 -0700 Subject: [PATCH] imports are now python style with a twist: forms! import std ffi and import local myfile --- ast/src/main/ast/pork.yml | 6 ++--- .../gay/pizza/pork/ast/ImportDeclaration.kt | 8 +++---- .../pork/evaluator/CompilationUnitContext.kt | 3 ++- examples/complex/b.pork | 2 +- examples/complex/c.pork | 2 +- examples/complex/d.pork | 2 +- examples/complex/main.pork | 2 +- examples/ffi.pork | 2 +- examples/imports.pork | 5 ----- examples/loop.pork | 1 - examples/module.pork | 3 --- ...ImportSource.kt => DynamicImportSource.kt} | 2 +- .../gay/pizza/pork/frontend/ImportLocator.kt | 2 +- .../gay/pizza/pork/frontend/ImportSource.kt | 2 -- .../kotlin/gay/pizza/pork/frontend/World.kt | 13 +++++------ .../kotlin/gay/pizza/pork/parser/Parser.kt | 22 +++++++++++++------ .../kotlin/gay/pizza/pork/parser/Printer.kt | 9 +++++++- .../kotlin/gay/pizza/pork/parser/TokenType.kt | 1 + .../gay/pizza/pork/stdlib/PorkStdlib.kt | 7 ++++-- .../{numbers.pork => numerics/operator.pork} | 0 stdlib/src/main/pork/stdlib.manifest | 3 ++- .../main/kotlin/gay/pizza/pork/tool/Tool.kt | 11 +++++----- 22 files changed, 58 insertions(+), 50 deletions(-) delete mode 100644 examples/imports.pork delete mode 100644 examples/module.pork rename frontend/src/main/kotlin/gay/pizza/pork/frontend/{StandardImportSource.kt => DynamicImportSource.kt} (80%) rename stdlib/src/main/pork/{numbers.pork => numerics/operator.pork} (100%) diff --git a/ast/src/main/ast/pork.yml b/ast/src/main/ast/pork.yml index a8f6999..8cdae1b 100644 --- a/ast/src/main/ast/pork.yml +++ b/ast/src/main/ast/pork.yml @@ -111,9 +111,9 @@ types: parent: Declaration values: - name: form - type: Symbol? - - name: path - type: StringLiteral + type: Symbol + - name: components + type: List IntLiteral: parent: Expression values: diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/ImportDeclaration.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/ImportDeclaration.kt index 5f08de0..225d902 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/ImportDeclaration.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/ImportDeclaration.kt @@ -6,23 +6,23 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("importDeclaration") -class ImportDeclaration(val form: Symbol?, val path: StringLiteral) : Declaration() { +class ImportDeclaration(val form: Symbol, val components: List) : Declaration() { override val type: NodeType = NodeType.ImportDeclaration override fun visitChildren(visitor: NodeVisitor): List = - visitor.visitNodes(form, path) + visitor.visitAll(listOf(form), components) override fun visit(visitor: NodeVisitor): T = visitor.visitImportDeclaration(this) override fun equals(other: Any?): Boolean { if (other !is ImportDeclaration) return false - return other.form == form && other.path == path + return other.form == form && other.components == components } override fun hashCode(): Int { var result = form.hashCode() - result = 31 * result + path.hashCode() + result = 31 * result + components.hashCode() result = 31 * result + type.hashCode() return result } diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt index 14d4705..0cb3822 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/CompilationUnitContext.kt @@ -51,7 +51,8 @@ class CompilationUnitContext( } private fun processImport(import: ImportDeclaration) { - val importLocator = ImportLocator(import.path.text, import.form?.id) + val importPath = import.components.joinToString("/") { it.id } + ".pork" + val importLocator = ImportLocator(import.form.id, importPath) val evaluationContext = evaluator.context(importLocator) internalScope.inherit(evaluationContext.externalScope) } diff --git a/examples/complex/b.pork b/examples/complex/b.pork index de0cc07..095127c 100644 --- a/examples/complex/b.pork +++ b/examples/complex/b.pork @@ -1,4 +1,4 @@ -import "c.pork" +import local c export func b() { c() diff --git a/examples/complex/c.pork b/examples/complex/c.pork index f1750b1..93c1ac0 100644 --- a/examples/complex/c.pork +++ b/examples/complex/c.pork @@ -1,4 +1,4 @@ -import "a.pork" +import local a export func c() { a() diff --git a/examples/complex/d.pork b/examples/complex/d.pork index 103db93..e9c244e 100644 --- a/examples/complex/d.pork +++ b/examples/complex/d.pork @@ -1,4 +1,4 @@ -import "b.pork" +import local b export func d() { b() diff --git a/examples/complex/main.pork b/examples/complex/main.pork index cd40019..b55cd22 100644 --- a/examples/complex/main.pork +++ b/examples/complex/main.pork @@ -1,4 +1,4 @@ -import "d.pork" +import local d export func main() { d() diff --git a/examples/ffi.pork b/examples/ffi.pork index 367dc2c..715fe4c 100644 --- a/examples/ffi.pork +++ b/examples/ffi.pork @@ -1,4 +1,4 @@ -import std "ffi/malloc.pork" +import std ffi.malloc export func main() { while true { diff --git a/examples/imports.pork b/examples/imports.pork deleted file mode 100644 index d7138e6..0000000 --- a/examples/imports.pork +++ /dev/null @@ -1,5 +0,0 @@ -import "module.pork" - -export func main() { - hello() -} diff --git a/examples/loop.pork b/examples/loop.pork index 18555d0..9ec9ffe 100644 --- a/examples/loop.pork +++ b/examples/loop.pork @@ -1,6 +1,5 @@ export func main() { while true { println("Hello World") - break } } diff --git a/examples/module.pork b/examples/module.pork deleted file mode 100644 index 29a0c87..0000000 --- a/examples/module.pork +++ /dev/null @@ -1,3 +0,0 @@ -export func hello() { - println("Hello World") -} diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/StandardImportSource.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/DynamicImportSource.kt similarity index 80% rename from frontend/src/main/kotlin/gay/pizza/pork/frontend/StandardImportSource.kt rename to frontend/src/main/kotlin/gay/pizza/pork/frontend/DynamicImportSource.kt index eeb20ad..1a3d47c 100644 --- a/frontend/src/main/kotlin/gay/pizza/pork/frontend/StandardImportSource.kt +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/DynamicImportSource.kt @@ -1,6 +1,6 @@ package gay.pizza.pork.frontend -class StandardImportSource(override val fileContentSource: ContentSource) : ImportSource { +class DynamicImportSource : ImportSource { private val providers = mutableMapOf() override fun provideContentSource(form: String): ContentSource { diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/ImportLocator.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/ImportLocator.kt index ee7e160..1702969 100644 --- a/frontend/src/main/kotlin/gay/pizza/pork/frontend/ImportLocator.kt +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/ImportLocator.kt @@ -1,3 +1,3 @@ package gay.pizza.pork.frontend -data class ImportLocator(val path: String, val form: String? = null) +data class ImportLocator(val form: String, val path: String) diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/ImportSource.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/ImportSource.kt index 4b93067..7742a69 100644 --- a/frontend/src/main/kotlin/gay/pizza/pork/frontend/ImportSource.kt +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/ImportSource.kt @@ -1,7 +1,5 @@ package gay.pizza.pork.frontend interface ImportSource { - val fileContentSource: ContentSource - fun provideContentSource(form: String): ContentSource } diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt index edf7328..27a2c06 100644 --- a/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt @@ -32,7 +32,8 @@ class World(val importSource: ImportSource) { private fun resolveAllImports(unit: CompilationUnit): Set { val units = mutableSetOf() for (declaration in unit.declarations.filterIsInstance()) { - val importLocator = ImportLocator(declaration.path.text, form = declaration.form?.id) + val importPath = declaration.components.joinToString("/") { it.id } + ".pork" + val importLocator = ImportLocator(declaration.form.id, importPath) val importedUnit = loadOneUnit(importLocator) units.add(importedUnit) } @@ -45,18 +46,14 @@ class World(val importSource: ImportSource) { return unit } - private fun pickContentSource(form: String? = null): ContentSource { - if (form != null) { - return importSource.provideContentSource(form) - } - return importSource.fileContentSource - } + private fun pickContentSource(form: String): ContentSource = + importSource.provideContentSource(form) fun stableIdentity( importLocator: ImportLocator, contentSource: ContentSource = pickContentSource(importLocator.form) ): String { - val formKey = importLocator.form ?: "file" + val formKey = importLocator.form val stableIdentity = contentSource.stableContentIdentity(importLocator.path) return "[${formKey}][${stableIdentity}]" } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt index 9650eb1..8ee12ce 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt @@ -183,11 +183,9 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { private fun readImportDeclaration(): ImportDeclaration = within { expect(TokenType.Import) - var form: Symbol? = null - if (peek(TokenType.Symbol)) { - form = readSymbolRaw() - } - ImportDeclaration(form, readStringLiteral()) + val form = readSymbolRaw() + val components = oneAndContinuedBy(TokenType.Period) { readSymbolRaw() } + ImportDeclaration(form, components) } private fun readFunctionDeclaration(): FunctionDefinition = within { @@ -292,11 +290,21 @@ class Parser(source: PeekableSource, val attribution: NodeAttribution) { ): List { val items = mutableListOf() while (!peek(peeking)) { - val expression = read() + val item = read() if (consuming != null) { next(consuming) } - items.add(expression) + items.add(item) + } + return items + } + + private fun oneAndContinuedBy(separator: TokenType, read: () -> T): List { + val items = mutableListOf() + items.add(read()) + while (peek(separator)) { + expect(separator) + items.add(read()) } return items } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt index 4ae9d95..caa1fe4 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt @@ -166,7 +166,14 @@ class Printer(buffer: StringBuilder) : NodeVisitor { override fun visitImportDeclaration(node: ImportDeclaration) { append("import ") - visit(node.path) + visit(node.form) + append(" ") + for ((index, component) in node.components.withIndex()) { + visit(component) + if (index != node.components.size - 1) { + append(".") + } + } } override fun visitCompilationUnit(node: CompilationUnit) { diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt index 76191b1..72d9df9 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt @@ -22,6 +22,7 @@ enum class TokenType(vararg properties: TokenTypeProperty) { RightParentheses(SingleChar(')')), Negation(SingleChar('!'), Promotion('=', Inequality), OperatorFamily), Comma(SingleChar(',')), + Period(SingleChar('.')), False(Keyword("false"), KeywordFamily), True(Keyword("true"), KeywordFamily), If(Keyword("if"), KeywordFamily), diff --git a/stdlib/src/main/kotlin/gay/pizza/pork/stdlib/PorkStdlib.kt b/stdlib/src/main/kotlin/gay/pizza/pork/stdlib/PorkStdlib.kt index d0011dd..e9d2cda 100644 --- a/stdlib/src/main/kotlin/gay/pizza/pork/stdlib/PorkStdlib.kt +++ b/stdlib/src/main/kotlin/gay/pizza/pork/stdlib/PorkStdlib.kt @@ -8,7 +8,7 @@ object PorkStdlib : ContentSource { private val stdlibClass = PorkStdlib::class.java private fun readManifestFiles(): List { - val manifestContent = read("stdlib.manifest") + val manifestContent = read("stdlib.manifest", check = false) return manifestContent.split("\n").filter { line -> val trimmed = line.trim() trimmed.isNotEmpty() && !trimmed.startsWith("#") @@ -17,7 +17,10 @@ object PorkStdlib : ContentSource { val files: List = readManifestFiles() - private fun read(path: String): String { + private fun read(path: String, check: Boolean = true): String { + if (check && !files.contains(path)) { + throw RuntimeException("Stdlib does not contain file '${path}'") + } val stream = stdlibClass.getResourceAsStream("/pork/stdlib/${path}") ?: throw RuntimeException("Stdlib does not contain file '${path}'") return String(stream.readAllBytes()) diff --git a/stdlib/src/main/pork/numbers.pork b/stdlib/src/main/pork/numerics/operator.pork similarity index 100% rename from stdlib/src/main/pork/numbers.pork rename to stdlib/src/main/pork/numerics/operator.pork diff --git a/stdlib/src/main/pork/stdlib.manifest b/stdlib/src/main/pork/stdlib.manifest index 965bf61..cf63f4b 100644 --- a/stdlib/src/main/pork/stdlib.manifest +++ b/stdlib/src/main/pork/stdlib.manifest @@ -1 +1,2 @@ -numbers.pork +ffi/malloc.pork +numerics/operator.pork diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/Tool.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/Tool.kt index 43bc8a4..7ad1db5 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/Tool.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/Tool.kt @@ -8,7 +8,7 @@ import gay.pizza.pork.evaluator.Evaluator import gay.pizza.pork.evaluator.Scope import gay.pizza.pork.frontend.ContentSource import gay.pizza.pork.frontend.ImportLocator -import gay.pizza.pork.frontend.StandardImportSource +import gay.pizza.pork.frontend.DynamicImportSource import gay.pizza.pork.frontend.World import gay.pizza.pork.parser.* import gay.pizza.pork.stdlib.PorkStdlib @@ -33,12 +33,13 @@ abstract class Tool { fun loadMainFunction(scope: Scope, setupEvaluator: Evaluator.() -> Unit = {}): CallableFunction { val fileContentSource = createContentSource() - val standardImportSource = StandardImportSource(fileContentSource) - standardImportSource.addContentSource("std", PorkStdlib) - val world = World(standardImportSource) + val dynamicImportSource = DynamicImportSource() + dynamicImportSource.addContentSource("std", PorkStdlib) + dynamicImportSource.addContentSource("local", fileContentSource) + val world = World(dynamicImportSource) val evaluator = Evaluator(world, scope) setupEvaluator(evaluator) - val resultingScope = evaluator.evaluate(ImportLocator(rootFilePath())) + val resultingScope = evaluator.evaluate(ImportLocator("local", rootFilePath())) return resultingScope.value("main") as CallableFunction } }