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)))