From f345fadd492ea5bb09e515d007be438fc08c9b93 Mon Sep 17 00:00:00 2001 From: nea Date: Thu, 10 Aug 2023 02:26:42 +0200 Subject: Add imports --- src/Builtins.kt | 13 ------------ src/CoreBindings.kt | 14 +++++++++++++ src/LispExecutionContext.kt | 50 +++++++++++++++++++++++++++++++++++++++++++-- test/res/secondary.lisp | 3 +++ test/res/test.lisp | 4 ++++ test/src/TestLisp.kt | 1 + 6 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 test/res/secondary.lisp diff --git a/src/Builtins.kt b/src/Builtins.kt index 6ca60e9..f9bf329 100644 --- a/src/Builtins.kt +++ b/src/Builtins.kt @@ -3,17 +3,4 @@ package moe.nea.lisp object Builtins { val builtinSource = Builtins::class.java.getResourceAsStream("/builtins.lisp")!!.bufferedReader().readText() val builtinProgram = LispParser.parse("builtins.lisp", builtinSource) - fun loadBuiltins( - lispExecutionContext: LispExecutionContext, - consumer: (String, LispData) -> Unit, - ) { - val stackFrame = lispExecutionContext.genBindings() - stackFrame.setValueLocal("export", LispData.externalRawCall { context, callsite, stackFrame, args -> - args.forEach { name -> - consumer((name as LispAst.Reference).label, context.resolveValue(stackFrame, name)) - } - return@externalRawCall LispData.LispNil - }) - lispExecutionContext.executeProgram(stackFrame, builtinProgram) - } } \ No newline at end of file diff --git a/src/CoreBindings.kt b/src/CoreBindings.kt index f139e2e..b344dc7 100644 --- a/src/CoreBindings.kt +++ b/src/CoreBindings.kt @@ -172,6 +172,19 @@ object CoreBindings { ?: return@externalCall reportError("Unexpected argument $b, expected number")).value }) } + val import = LispData.externalRawCall { context, callsite, stackFrame, args -> + if (args.size != 1) { + return@externalRawCall context.reportError("import needs at least one argument", callsite) + } + // TODO: aliased / namespaced imports + val moduleName = when (val moduleObject = context.resolveValue(stackFrame, args[0])) { + is LispData.Atom -> moduleObject.label + is LispData.LispString -> moduleObject.string + else -> return@externalRawCall context.reportError("import needs a string or atom as argument", callsite) + } + context.importModule(moduleName, stackFrame, callsite) + return@externalRawCall LispData.LispNil + } fun offerArithmeticTo(bindings: StackFrame) { bindings.setValueLocal("+", add) @@ -188,6 +201,7 @@ object CoreBindings { bindings.setValueLocal("lambda", lambda) bindings.setValueLocal("defun", defun) bindings.setValueLocal("seq", seq) + bindings.setValueLocal("import", import) bindings.setValueLocal("debuglog", debuglog) offerArithmeticTo(bindings) } diff --git a/src/LispExecutionContext.kt b/src/LispExecutionContext.kt index f85cc4b..181c06b 100644 --- a/src/LispExecutionContext.kt +++ b/src/LispExecutionContext.kt @@ -4,7 +4,8 @@ class LispExecutionContext() { private val errorReporter = LispErrorReporter() val rootStackFrame = StackFrame(null) - + val unloadedModules = mutableMapOf() + val modules = mutableMapOf>() fun reportError(name: String, position: HasLispPosition): LispData.LispNil { println("Error: $name ${position.position}") @@ -18,7 +19,51 @@ class LispExecutionContext() { fun setupStandardBindings() { CoreBindings.offerAllTo(rootStackFrame) - Builtins.loadBuiltins(this, rootStackFrame::setValueLocal) + registerModule("builtins", Builtins.builtinProgram) + importModule("builtins", rootStackFrame, object : HasLispPosition { + override val position: LispPosition + get() = error("Builtin import failed") + + }) + } + + fun registerModule(moduleName: String, program: LispAst.Program) { + if (moduleName in unloadedModules || moduleName in modules) { + error("Cannot register already registered module $moduleName") + } + unloadedModules[moduleName] = program + } + + fun importModule(moduleName: String, into: StackFrame, position: HasLispPosition) { + var exports = modules[moduleName] + if (exports == null) { + val module = unloadedModules[moduleName] + if (module == null) { + reportError("Could not find module $moduleName", position) + return + } + exports = realizeModule(moduleName) + } + into.variables.putAll(exports) + } + + private fun realizeModule(moduleName: String): Map { + val map = mutableMapOf() + modules[moduleName] = map + val module = unloadedModules.remove(moduleName) ?: error("Could not find module $moduleName") + val stackFrame = genBindings() + stackFrame.setValueLocal("export", LispData.externalRawCall { context, callsite, stackFrame, args -> + args.forEach { name -> + if (name !is LispAst.Reference) { + context.reportError("Invalid export", name) + return@forEach + } + map[name.label] = context.resolveValue(stackFrame, name) + } + return@externalRawCall LispData.LispNil + }) + executeProgram(stackFrame, module) + return map } fun executeProgram(stackFrame: StackFrame, program: LispAst.Program): LispData? { @@ -57,6 +102,7 @@ class LispExecutionContext() { is LispAst.Parenthesis -> executeLisp(stackFrame, node) is LispAst.Reference -> stackFrame.resolveReference(node.label) ?: reportError("Could not resolve variable ${node.label}", node) + is LispAst.NumberLiteral -> LispData.LispNumber(node.numberValue) is LispAst.StringLiteral -> LispData.LispString(node.parsedString) } diff --git a/test/res/secondary.lisp b/test/res/secondary.lisp new file mode 100644 index 0000000..4231721 --- /dev/null +++ b/test/res/secondary.lisp @@ -0,0 +1,3 @@ + +(def sc 42) +(export sc) diff --git a/test/res/test.lisp b/test/res/test.lisp index 5f32bee..c4406b0 100644 --- a/test/res/test.lisp +++ b/test/res/test.lisp @@ -17,3 +17,7 @@ (testsomething true) (testsomething false) (noop) +(debuglog "============") +(debuglog "This should fail" sc) +(import :secondary) +(debuglog "This should work" sc) \ No newline at end of file diff --git a/test/src/TestLisp.kt b/test/src/TestLisp.kt index 7a520fb..5563042 100644 --- a/test/src/TestLisp.kt +++ b/test/src/TestLisp.kt @@ -8,6 +8,7 @@ fun main() { val otherP = LispParser.parse(File(T::class.java.getResource("/test.lisp")!!.file)) val executionContext = LispExecutionContext() executionContext.setupStandardBindings() + executionContext.registerModule("secondary", LispParser.parse(File(T::class.java.getResource("/secondary.lisp")!!.file))) val bindings = executionContext.genBindings() executionContext.executeProgram(bindings, otherP) } -- cgit