Add unquote-splice (,@)
diff --git a/src/lisp/compiler.dasc b/src/lisp/compiler.dasc
index 5810f69..bae4a39 100644
--- a/src/lisp/compiler.dasc
+++ b/src/lisp/compiler.dasc
@@ -139,6 +139,8 @@
char *path,
dasm_State **state)
{
+ UNUSED(namespace);
+
E_INIT();
dasm_State *d;
@@ -455,23 +457,46 @@
TRY(compile_expression(env, local, car(args), false, Dst));
}
- else if (symstreq(fsym, "unquote-splice"))
- {
-
- }
else
{
| push nil;
for (int i = n - 1; i >= 0; i--)
{
- TRY(compile_backquote(env, local, elt(val, i), Dst));
- | push eax;
- | call_extern cons;
- | add esp, 8;
+ value_t v = elt(val, i);
- // Remove unnecessary pop
- | push eax;
+ if (listp(v) && symstreq(car(v), "unquote-splice"))
+ {
+ NEARVAL(v);
+
+ if (length(v) != 2)
+ {
+ THROW(EARGS, "unquote-splice (or ,@) takes exactly 1 argument");
+ }
+
+ value_t expr = car(cdr(v));
+
+ if (!listp(expr))
+ {
+ THROW(EINVALID, "unquote-splice (or ,@) argument must be a list");
+ }
+
+ TRY(compile_expression(env, local, expr, false, Dst));
+ | push eax;
+ | call_extern merge2;
+ | add esp, 8;
+ | push eax;
+ }
+ else
+ {
+ TRY(compile_backquote(env, local, v, Dst));
+ | push eax;
+ | call_extern cons;
+ | add esp, 8;
+
+ // Remove unnecessary pop
+ | push eax;
+ }
}
| pop eax;
}
diff --git a/src/lisp/error.h b/src/lisp/error.h
index 7f95870..7577cdf 100644
--- a/src/lisp/error.h
+++ b/src/lisp/error.h
@@ -98,3 +98,5 @@
void ereport(struct error err);
void edebug(struct error err, char *file, int line, const char *func, const char *why);
+
+#define UNUSED(val) (void)(val)
diff --git a/src/lisp/lisp.c b/src/lisp/lisp.c
index e8aed91..f1a02ed 100644
--- a/src/lisp/lisp.c
+++ b/src/lisp/lisp.c
@@ -696,3 +696,33 @@
return nil;
}
}
+
+value_t *nilptr(value_t val)
+{
+ if (!listp(val))
+ return NULL;
+
+ if (nilp(val))
+ return NULL;
+
+ value_t *p;
+
+ for (p = cdrref(val); !nilp(*p); p = cdrref(*p))
+ {
+ }
+
+ return p;
+}
+
+value_t merge2(value_t front, value_t back)
+{
+ if (!listp(front) || !listp(back))
+ return nil;
+
+ if (nilp(front))
+ return back;
+ else
+ *nilptr(front) = back;
+
+ return front;
+}
diff --git a/src/lisp/lisp.h b/src/lisp/lisp.h
index eff35f9..64de5b9 100644
--- a/src/lisp/lisp.h
+++ b/src/lisp/lisp.h
@@ -170,12 +170,16 @@
value_t strval(char *str);
value_t symval(char *str);
value_t cons(value_t car, value_t cdr);
+value_t merge2(value_t front, value_t back);
struct error read1(struct istream *is, value_t *val) WARN_UNUSED;
value_t read(struct istream *is);
value_t readn(struct istream *is);
value_t car(value_t v);
value_t cdr(value_t v);
+/// Return a pointer to the "nil" tail of the list, or NULL if you do
+/// something stupid.
+value_t *nilptr(value_t val);
value_t *carref(value_t v);
value_t *cdrref(value_t v);
/// @returns the `index`-th `cdr`
diff --git a/src/lisp/plat/linux.c b/src/lisp/plat/linux.c
index fa6336f..ad7f873 100644
--- a/src/lisp/plat/linux.c
+++ b/src/lisp/plat/linux.c
@@ -1,4 +1,5 @@
#include "plat.h"
+#include "../error.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -50,6 +51,7 @@
#ifndef NO_READLINE
return readline(prompt);
#else
+ UNUSED(prompt);
return "";
#endif
}
@@ -58,5 +60,7 @@
{
#ifndef NO_READLINE
add_history(line);
+#else
+ UNUSED(line);
#endif
}
diff --git a/src/lisp/test-errors.lisp b/src/lisp/test-errors.lisp
new file mode 100644
index 0000000..27cbdf6
--- /dev/null
+++ b/src/lisp/test-errors.lisp
@@ -0,0 +1 @@
+'(
diff --git a/src/lisp/test-unquote.lisp b/src/lisp/test-unquote.lisp
new file mode 100644
index 0000000..c2f06a0
--- /dev/null
+++ b/src/lisp/test-unquote.lisp
@@ -0,0 +1,5 @@
+(defun numbers ()
+ (list 1 2 3 4 5))
+
+(defun main ()
+ (print `(numbers are ,@(numbers) yeah)))