Add detailed error reporting, remove panics
diff --git a/src/lisp/error.h b/src/lisp/error.h
new file mode 100644
index 0000000..d4c47e8
--- /dev/null
+++ b/src/lisp/error.h
@@ -0,0 +1,98 @@
+#pragma once
+
+#include <stdbool.h>
+
+// Error handling code
+
+struct eloc
+{
+ int line;
+ char *file;
+};
+
+enum error_code
+{
+ EOK = 0,
+ /// Expected something but didn't get it. if this is in a
+ /// safe_state we should probably just re-try.
+ EEXPECTED,
+ /// An invalid token was present in the input
+ EINVALID,
+ /// A structure was malformed
+ EMALFORMED,
+ /// The arguments provided were invalid
+ EARGS,
+ /// An external resource (say, a file) was not found
+ ENOTFOUND,
+ /// This is unimplemented
+ EUNIMPL,
+};
+
+struct error
+{
+ enum error_code code;
+ // Is any state safe? I.e. can we continue or must we panic?
+ bool safe_state;
+ struct eloc loc;
+ char *message;
+};
+
+#define E_INIT() \
+ struct error __error; \
+ __error.code = EOK; \
+ __error.loc.line = 0; \
+ __error.safe_state = false; \
+ __error.message = NULL; \
+ __error.loc.file = NULL;
+#define NEARVAL(val) \
+ __error.loc.line = cons_line(val); \
+ __error.loc.file = cons_file(val)
+#define NEARIS(is) (is)->getpos((is), &__error.loc.line, &__error.loc.file)
+#define _TRY(expr, m, c) \
+ { \
+ struct error __sub = (expr); \
+ if (__sub.code) \
+ { \
+ if (!__sub.loc.file || !__sub.loc.line) \
+ __sub.loc.file = __error.loc.file, \
+ __sub.loc.line = __error.loc.line; \
+ if (c) \
+ __sub.code = c; \
+ if (m) \
+ __sub.message = m; \
+ return __sub; \
+ } \
+ }
+#define TRY(expr) _TRY(expr, NULL, 0)
+#define TRY_ELSE(expr, c, ...) _TRY(expr, ehsprintf(__VA_ARGS__), c)
+#define OKAY() return __error
+#define THROW(_c, ...) \
+ { \
+ __error.code = (_c); \
+ __error.message = ehsprintf(__VA_ARGS__); \
+ return __error; \
+ }
+#define THROWSAFE(_c) \
+ { \
+ __error.code = (_c); \
+ __error.safe_state = true; \
+ return __error; \
+ }
+
+#define IS_OKAY(e) ((e).code == EOK)
+#define OKAY_IF(val) \
+ { \
+ struct error __sub = (val); \
+ if (IS_OKAY(__sub)) \
+ OKAY(); \
+ if (!__sub.safe_state) \
+ TRY(__sub) \
+ }
+
+#define WARN_UNUSED __attribute__((warn_unused_result))
+
+// error heap string print formatted
+// returns a heap-allocated string.
+char *ehsprintf(const char *msg, ...);
+
+void ereport(struct error err);