summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/builtins.lisp8
-rw-r--r--src/CoreBindings.kt55
-rw-r--r--src/LispData.kt1
-rw-r--r--test/res/test.lisp11
4 files changed, 70 insertions, 5 deletions
diff --git a/res/builtins.lisp b/res/builtins.lisp
index d1828f6..64c59d3 100644
--- a/res/builtins.lisp
+++ b/res/builtins.lisp
@@ -52,3 +52,11 @@
(defun not (v) (if v false true))
(defun ^ (l r) (if l (not r) r))
(export | & not ^)
+
+(comment "Re-export hashes")
+(def hash.new core.newhash)
+(def hash.merge core.mergehash)
+(def hash.get core.gethash)
+(export hash.new hash.merge hash.get)
+
+
diff --git a/src/CoreBindings.kt b/src/CoreBindings.kt
index 3691744..94b3852 100644
--- a/src/CoreBindings.kt
+++ b/src/CoreBindings.kt
@@ -116,6 +116,7 @@ object CoreBindings {
is LispData.LispNode -> thing.node.toSource()
is LispData.LispList -> thing.elements.joinToString(", ", "[", "]") { stringify(it) }
is LispData.LispString -> thing.string
+ is LispData.LispHash -> thing.map.asIterable().joinToString(", ", "{", "}") { it.key + ": " + it.value }
is LispData.LispNumber -> thing.value.toString()
is LispData.LispInterpretedCallable -> "<function ${thing.name ?: "<anonymous>"} ${thing.argNames} ${thing.body.toSource()}>"
}
@@ -192,16 +193,22 @@ object CoreBindings {
}
LispData.boolean(left < right)
}
+
+ private fun atomOrStringToString(thing: LispData): String? {
+ return when (thing) {
+ is LispData.Atom -> thing.label
+ is LispData.LispString -> thing.string
+ else -> null
+ }
+ }
+
val import = LispData.externalRawCall("import") { context, callsite, stackFrame, args ->
if (args.size != 1) {
return@externalRawCall stackFrame.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 stackFrame.reportError("import needs a string or atom as argument", callsite)
- }
+ val moduleName = atomOrStringToString(context.resolveValue(stackFrame, args[0]))
+ ?: return@externalRawCall stackFrame.reportError("import needs a string or atom as argument", callsite)
context.importModule(moduleName, stackFrame, callsite)
return@externalRawCall LispData.LispNil
}
@@ -216,6 +223,7 @@ object CoreBindings {
is LispData.LispExecutable -> LispData.Atom("callable")
is LispData.LispList -> LispData.Atom("list")
LispData.LispNil -> LispData.Atom("nil")
+ is LispData.LispHash -> LispData.Atom("hash")
is LispData.LispNode -> LispData.Atom("ast")
is LispData.LispNumber -> LispData.Atom("number")
is LispData.LispString -> LispData.Atom("string")
@@ -231,6 +239,42 @@ object CoreBindings {
bindings.setValueLocal("core.arith.eq", eq)
}
+ fun offerHashesTo(bindings: StackFrame) {
+ bindings.setValueLocal("core.newhash", LispData.externalCall("newhash") { args, reportError ->
+ if (args.size % 2 != 0) {
+ return@externalCall reportError("Hash creation needs to have an even number of arguments")
+ }
+
+ LispData.LispHash(
+ args.chunked(2).associate { (a, b) ->
+ (atomOrStringToString(a)
+ ?: return@externalCall reportError("Hash key needs to be string or atom")) to b
+ })
+ })
+ bindings.setValueLocal("core.gethash", LispData.externalCall("gethash") { args, reportError ->
+ if (args.size != 2) {
+ return@externalCall reportError("Hash access needs 2 arguments")
+ }
+ val (hash, name) = args
+ if (hash !is LispData.LispHash) {
+ return@externalCall reportError("$hash is not a hash")
+ }
+ val nameS =
+ atomOrStringToString(name) ?: return@externalCall reportError("$name is not an atom or a string")
+ hash.map[nameS] ?: LispData.LispNil
+ })
+ bindings.setValueLocal("core.mergehash", LispData.externalCall("mergehash") { args, reportError ->
+ val m = mutableMapOf<String, LispData>()
+ for (arg in args) {
+ if (arg !is LispData.LispHash) {
+ return@externalCall reportError("$arg is not a hash")
+ }
+ m.putAll(arg.map)
+ }
+ LispData.LispHash(m)
+ })
+ }
+
fun offerAllTo(bindings: StackFrame) {
bindings.setValueLocal("core.if", ifFun)
bindings.setValueLocal("core.nil", LispData.LispNil)
@@ -244,5 +288,6 @@ object CoreBindings {
bindings.setValueLocal("core.reflect.type", reflect)
bindings.setValueLocal("core.debuglog", debuglog)
offerArithmeticTo(bindings)
+ offerHashesTo(bindings)
}
} \ No newline at end of file
diff --git a/src/LispData.kt b/src/LispData.kt
index 97fcc36..ea3af32 100644
--- a/src/LispData.kt
+++ b/src/LispData.kt
@@ -7,6 +7,7 @@ sealed class LispData {
data class LispString(val string: String) : LispData()
data class LispNumber(val value: Double) : LispData()
data class LispNode(val node: LispAst.LispNode) : LispData()
+ data class LispHash(val map: Map<String, LispData>) : LispData()
class LispList(val elements: List<LispData>) : LispData()
sealed class LispExecutable() : LispData() {
abstract fun execute(
diff --git a/test/res/test.lisp b/test/res/test.lisp
index 19594b3..b5a9f65 100644
--- a/test/res/test.lisp
+++ b/test/res/test.lisp
@@ -19,3 +19,14 @@
((test.assert-eq (| true false) true))
((test.assert-eq (| false true) true))
((test.assert-eq (| false false) false))))
+
+(test.test "Hashes" (seq
+ (def funnyhash (hash.new :test1 1 :test2 2))
+ ((test.assert-eq
+ (hash.merge funnyhash (hash.new :test1 2))
+ (hash.new :test1 2 :test2 2)))
+ ((test.assert-eq funnyhash (hash.new :test1 1 :test2 2)))
+ ((test.assert-eq (hash.get funnyhash :test1) 1))
+ ((test.assert-eq (hash.get funnyhash :tesst3) nil))
+ ))
+