summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Builtins.kt13
-rw-r--r--src/CoreBindings.kt14
-rw-r--r--src/LispExecutionContext.kt50
-rw-r--r--test/res/secondary.lisp3
-rw-r--r--test/res/test.lisp4
-rw-r--r--test/src/TestLisp.kt1
6 files changed, 70 insertions, 15 deletions
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<String, LispAst.Program>()
+ val modules = mutableMapOf<String, Map<String, LispData>>()
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<String, LispData> {
+ val map = mutableMapOf<String, LispData>()
+ 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)
}