diff --git a/README.md b/README.md
index a6ae7bb..52a1909 100644
--- a/README.md
+++ b/README.md
@@ -9,12 +9,32 @@ The garbage collector is optional, like in C.
```
defmodule C3 do
+
def compile (c3, opts) when is_string(c3) do
c3
|> C3.parse(opts)
+ |> C3.compile()
+ end
+
+ def eval (c3, env, opts) when is_string(c3) do
+ c3
+ |> C3.parse(opts)
+ |> C3.eval(env)
+ end
+
+ defguard is_c3 (x) do
+ is_tuple(x)
+ end
+
+ def compile (c3, opts) when is_c3(c3) do
+ c3
|> C3.to_assembly(opts)
|> Assembly.to_elf(opts)
end
+
+ def eval (c3, env) when is_c3(c3) do
+ [...]
+ end
end
```
diff --git a/libc3/buf_file.h b/libc3/buf_file.h
index 4ee919f..bda9d92 100644
--- a/libc3/buf_file.h
+++ b/libc3/buf_file.h
@@ -21,6 +21,10 @@
#include <stdio.h>
#include "types.h"
+/* observers */
+bool buf_file_is_open (s_buf *buf);
+
+/* modifiers */
s_buf * buf_file_open_r (s_buf *buf, FILE *fp);
s_buf * buf_file_open_w (s_buf *buf, FILE *fp);
void buf_file_close (s_buf *buf);
diff --git a/libc3/buf_inspect.h b/libc3/buf_inspect.h
index c687128..e915b4b 100644
--- a/libc3/buf_inspect.h
+++ b/libc3/buf_inspect.h
@@ -32,6 +32,8 @@ sw buf_inspect_call_args (s_buf *buf, const s_list *args);
sw buf_inspect_call_size (const s_call *call);
sw buf_inspect_character (s_buf *buf, character c);
sw buf_inspect_character_size (character c);
+sw buf_inspect_error_handler (s_buf *buf,
+ const s_error_handler *error_handler);
sw buf_inspect_f32 (s_buf *buf, f32 x);
sw buf_inspect_f32_size (f32 x);
sw buf_inspect_f64 (s_buf *buf, f64 x);
diff --git a/libc3/buf_parse.c b/libc3/buf_parse.c
index 86d96d6..0fab5c4 100644
--- a/libc3/buf_parse.c
+++ b/libc3/buf_parse.c
@@ -104,10 +104,11 @@ sw buf_parse_call_args_paren (s_buf *buf, s_call *dest)
goto clean;
}
while (1) {
- *args = list_new();
if ((r = buf_parse_tag(buf, &tag)) <= 0)
goto restore;
result += r;
+ *args = list_new();
+ (*args)->tag = tag;
if ((r = buf_parse_comments(buf)) < 0)
goto restore;
result += r;
diff --git a/libc3/env.c b/libc3/env.c
index 3d827e1..263cb7b 100644
--- a/libc3/env.c
+++ b/libc3/env.c
@@ -12,9 +12,180 @@
* THIS SOFTWARE.
*/
#include <assert.h>
+#include <err.h>
+#include <stdlib.h>
+#include "binding.h"
+#include "buf.h"
+#include "buf_file.h"
+#include "buf_inspect.h"
#include "env.h"
#include "error_handler.h"
+#include "eval.h"
#include "frame.h"
+#include "list.h"
+#include "str.h"
+#include "tag.h"
+
+void env_clean (s_env *env)
+{
+ assert(env);
+ frame_delete_all(env->frame);
+ error_handler_delete_all(env->error_handler);
+}
+
+void env_error_f (s_env *env, const char *fmt, ...)
+{
+ va_list ap;
+ s_tag tag;
+ assert(env);
+ assert(fmt);
+ va_start(ap, fmt);
+ tag.type.type = TAG_STR;
+ str_init_vf(&tag.data.str, fmt, ap);
+ va_end(ap);
+ env_error_tag(env, &tag);
+}
+
+void env_error_tag (s_env *env, const s_tag *tag)
+{
+ s_error_handler *error_handler;
+ assert(env);
+ assert(str);
+ error_handler = env->error_handler;
+ if (error_handler) {
+ tag_copy(tag, &error_handler->tag);
+ error_handler->backtrace = env->backtrace;
+ env_longjmp(env, &error_handler->jmp_buf);
+ /* never reached */
+ return;
+ }
+ if (buf_file_is_open(&env->err)) {
+ buf_write_1(&env->err, "error: ");
+ buf_inspect_tag(&env->err, tag);
+ buf_write_1(&env->err, "\n");
+ return;
+ }
+}
+
+s_tag * env_eval_call_fn (s_env *env, s_call *call, s_tag *dest)
+{
+ s_arg *args;
+ s_list *call_args;
+ s_frame frame;
+ s_fn *fn;
+ s_tag tmp;
+ assert(env);
+ assert(call);
+ assert(dest);
+ fn = call->fn;
+ assert(fn);
+ frame_init(&frame, env->frame);
+ frame.bindings = fn->bindings;
+ args = fn->args;
+ call_args = call->arguments;
+ while (args) {
+ if (! call_args) {
+ assert(! "eval_call_fn: missing argument");
+ errx(1, "eval_call_fn: missing argument");
+ return NULL;
+ }
+ /* TODO: check type */
+ eval_tag(env, &call_args->tag, &tmp);
+ frame.bindings = binding_new(args->name, &call_args->tag,
+ frame.bindings);
+ args = args->next;
+ call_args = list_next(call_args);
+ }
+ if (call_args) {
+ assert(! "eval_call_fn: too many arguments");
+ errx(1, "eval_call_fn: too many arguments");
+ return NULL;
+ }
+ env->frame = &frame;
+ eval_progn(env, fn->algo, dest);
+ env->frame = frame_clean(&frame);
+ return dest;
+}
+
+s_tag * env_eval_call_macro (s_env *env, s_call *call, s_tag *dest)
+{
+ s_tag *expanded;
+ assert(env);
+ assert(call);
+ assert(dest);
+ (void) env;
+ (void) call;
+ (void) expanded;
+ return dest;
+}
+
+const s_tag * env_eval_ident (s_env *env, s_ident *ident)
+{
+ const s_tag *tag;
+ assert(env);
+ assert(ident);
+ if (! (tag = frame_get(env->frame, ident->sym))) {
+ assert(! "eval_ident: unbound variable");
+ errx(1, "eval_ident: unbound variable");
+ }
+ return tag;
+}
+
+s_tag * env_eval_progn (s_env *env, s_list *program, s_tag *dest)
+{
+ s_tag tmp;
+ assert(env);
+ assert(program);
+ assert(dest);
+ while (program) {
+ env_eval_tag(env, &program->tag, &tmp);
+ program = list_next(program);
+ }
+ *dest = tmp;
+ return dest;
+}
+
+s_tag * env_eval_tag (s_env *env, s_tag *tag, s_tag *dest)
+{
+ switch (tag->type.type) {
+ case TAG_VOID: return tag_init_void(dest);
+ case TAG_CALL:
+ assert(! "eval_tag: invalid tag type: TAG_CALL");
+ errx(1, "eval_tag: invalid tag type TAG_CALL");
+ return NULL;
+ case TAG_CALL_FN:
+ return env_eval_call_fn(env, &tag->data.call, dest);
+ case TAG_CALL_MACRO:
+ return env_eval_call_macro(env, &tag->data.call, dest);
+ case TAG_IDENT:
+ return tag_copy(env_eval_ident(env, &tag->data.ident), dest);
+ case TAG_BOOL:
+ case TAG_CHARACTER:
+ case TAG_F32:
+ case TAG_F64:
+ case TAG_FN:
+ case TAG_INTEGER:
+ case TAG_LIST:
+ case TAG_PTAG:
+ case TAG_QUOTE:
+ case TAG_S16:
+ case TAG_S32:
+ case TAG_S64:
+ case TAG_S8:
+ case TAG_STR:
+ case TAG_SYM:
+ case TAG_TUPLE:
+ case TAG_U16:
+ case TAG_U32:
+ case TAG_U64:
+ case TAG_U8:
+ case TAG_VAR:
+ return tag_copy(tag, dest);
+ }
+ assert(! "eval_tag: invalid tag");
+ errx(1, "eval_tag: invalid tag");
+ return NULL;
+}
s_env * env_init (s_env *env)
{
@@ -24,9 +195,60 @@ s_env * env_init (s_env *env)
return env;
}
-void env_clean (s_env *env)
+void env_longjmp (s_env *env, jmp_buf *jmp_buf)
+{
+ if (env->unwind_protect && *jmp_buf > env->unwind_protect->buf) {
+ s_unwind_protect *unwind_protect = env->unwind_protect;
+ while (unwind_protect->next && *jmp_buf > unwind_protect->next->buf) {
+ unwind_protect->jmp = &unwind_protect->next->buf;
+ unwind_protect = unwind_protect->next;
+ }
+ unwind_protect->jmp = jmp_buf;
+ longjmp(env->unwind_protect->buf, 1);
+ }
+ longjmp(*jmp_buf, 1);
+}
+
+void env_pop_error_handler (s_env *env)
+{
+ if (env->error_handler)
+ env->error_handler = error_handler_delete(env->error_handler);
+}
+
+void env_pop_unwind_protect (s_env *env)
{
+ if (env->unwind_protect)
+ env->unwind_protect = env->unwind_protect->next;
+}
+
+void env_push_error_handler (s_env *env, s_error_handler *error_handler)
+{
+ tag_init_void(&error_handler->tag);
+ error_handler->next = env->error_handler;
+ env->error_handler = error_handler;
+}
+
+void env_push_unwind_protect (s_env *env,
+ s_unwind_protect *unwind_protect)
+{
+ unwind_protect->next = env->unwind_protect;
+ env->unwind_protect = unwind_protect;
+}
+
+s_tag * env_unwind_protect (s_env *env, s_tag *protected, s_list *cleanup,
+ s_tag *dest)
+{
+ s_tag tmp;
+ s_unwind_protect unwind_protect;
assert(env);
- frame_delete_all(env->frame);
- error_handler_delete_all(env->error_handler);
+ assert(protect);
+ if (setjmp(unwind_protect.buf)) {
+ env_pop_unwind_protect(env);
+ env_eval_progn(env, cleanup, &tmp);
+ longjmp(*unwind_protect.jmp, 1);
+ }
+ eval_tag(env, protected, dest);
+ env_pop_unwind_protect(env);
+ env_eval_progn(env, cleanup, &tmp);
+ return dest;
}
diff --git a/libc3/env.h b/libc3/env.h
index dd67e70..6458e99 100644
--- a/libc3/env.h
+++ b/libc3/env.h
@@ -16,7 +16,22 @@
#include "types.h"
-s_env * env_init (s_env *env);
+/* stack allocation compatible functions */
void env_clean (s_env *env);
+s_env * env_init (s_env *env);
+
+/* modifiers */
+s_tag * env_eval_call_fn (s_env *env, s_call *call, s_tag *dest);
+s_tag * env_eval_call_macro (s_env *env, s_call *call,
+ s_tag *dest);
+s_tag * env_eval_fn (s_env *env, s_fn *fn, s_tag *dest);
+const s_tag * env_eval_ident (s_env *env, s_ident *ident);
+s_tag * env_eval_progn (s_env *env, s_list *program, s_tag *dest);
+s_tag * env_eval_tag (s_env *env, s_tag *tag, s_tag *dest);
+
+/* control structures */
+void env_error_f (s_env *env, const char *fmt, ...);
+void env_error_tag (s_env *env, const s_tag *tag);
+void env_longjmp (s_env *env, jmp_buf *jmp_buf);
#endif /* ENV_H */
diff --git a/libc3/error.c b/libc3/error.c
new file mode 100644
index 0000000..51c8ef4
--- /dev/null
+++ b/libc3/error.c
@@ -0,0 +1,35 @@
+/* c3
+ * Copyright 2022 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software excepted
+ * on Apple computers granted the above copyright notice and
+ * this permission paragraph are included in all copies and
+ * substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include "buf.h"
+#include "buf_inspect.h"
+#include "error.h"
+
+void error_print (s_buf *buf, const s_error_handler *error_handler)
+{
+ buf_write_1(buf, "error: ");
+ buf_inspect_tag(buf, &error_handler->tag);
+ buf_write_1(buf, "\nBacktrace:\n");
+ error_print_backtrace(buf, error_handler->backtrace);
+}
+
+void error_print_backtrace (s_buf *buf, const s_list *backtrace)
+{
+ while (backtrace) {
+ buf_inspect_tag(buf, &backtrace->tag);
+ buf_write_1(buf, "\n");
+ }
+}
diff --git a/libc3/error.h b/libc3/error.h
new file mode 100644
index 0000000..7edf426
--- /dev/null
+++ b/libc3/error.h
@@ -0,0 +1,22 @@
+/* c3
+ * Copyright 2022 kmx.io <contact@kmx.io>
+ *
+ * Permission is hereby granted to use this software excepted
+ * on Apple computers granted the above copyright notice and
+ * this permission paragraph are included in all copies and
+ * substantial portions of this software.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS-IS" WITHOUT ANY GUARANTEE OF
+ * PURPOSE AND PERFORMANCE. IN NO EVENT WHATSOEVER SHALL THE
+ * AUTHOR BE CONSIDERED LIABLE FOR THE USE AND PERFORMANCE OF
+ * THIS SOFTWARE.
+ */
+#ifndef ERROR_H
+#define ERROR_H
+
+#include "types.h"
+
+void error_print (s_buf *buf, const s_error_handler *error_handler);
+void error_print_backtrace (s_buf *buf, const s_list *backtrace);
+
+#endif /* ERROR_H */
diff --git a/libc3/error_handler.c b/libc3/error_handler.c
index aa3e133..50885a1 100644
--- a/libc3/error_handler.c
+++ b/libc3/error_handler.c
@@ -22,21 +22,20 @@ void error_handler_clean (s_error_handler *error_handler)
(void) error_handler;
}
-void error_handler_delete (s_error_handler *error_handler)
+s_error_handler * error_handler_delete (s_error_handler *error_handler)
{
+ s_error_handler *next;
assert(error_handler);
+ next = error_handler->next;
error_handler_clean(error_handler);
free(error_handler);
+ return next;
}
void error_handler_delete_all (s_error_handler *error_handler)
{
- s_error_handler *next;
- while (error_handler) {
- next = error_handler->next;
- error_handler_delete(error_handler);
- error_handler = next;
- }
+ while (error_handler)
+ error_handler = error_handler_delete(error_handler);
}
s_error_handler *
diff --git a/libc3/error_handler.h b/libc3/error_handler.h
index bb463c2..f608d96 100644
--- a/libc3/error_handler.h
+++ b/libc3/error_handler.h
@@ -25,7 +25,7 @@ s_error_handler * error_handler_init (s_error_handler *error_handler,
s_error_handler * error_handler_new (s_error_handler *next);
/* destructors */
-void error_handler_delete (s_error_handler *error_handler);
-void error_handler_delete_all (s_error_handler *error_handler);
+s_error_handler * error_handler_delete (s_error_handler *error_handler);
+void error_handler_delete_all (s_error_handler *error_handler);
#endif /* ERROR_HANDLER_H */
diff --git a/libc3/eval.c b/libc3/eval.c
index 5e22d76..4a9a095 100644
--- a/libc3/eval.c
+++ b/libc3/eval.c
@@ -21,122 +21,3 @@
#include "list.h"
#include "tag.h"
-s_tag * eval_call_fn (s_env *env, s_call *call, s_tag *dest)
-{
- s_arg *args;
- s_list *call_args;
- s_frame frame;
- s_fn *fn;
- s_tag tmp;
- assert(env);
- assert(call);
- assert(dest);
- fn = call->fn;
- assert(fn);
- frame_init(&frame, env->frame);
- frame.bindings = fn->bindings;
- args = fn->args;
- call_args = call->arguments;
- while (args) {
- if (! call_args) {
- assert(! "eval_call_fn: missing argument");
- errx(1, "eval_call_fn: missing argument");
- return NULL;
- }
- /* TODO: check type */
- eval_tag(env, &call_args->tag, &tmp);
- frame.bindings = binding_new(args->name, &call_args->tag,
- frame.bindings);
- args = args->next;
- call_args = list_next(call_args);
- }
- if (call_args) {
- assert(! "eval_call_fn: too many arguments");
- errx(1, "eval_call_fn: too many arguments");
- return NULL;
- }
- env->frame = &frame;
- eval_progn(env, fn->algo, dest);
- env->frame = frame_clean(&frame);
- return dest;
-}
-
-s_tag * eval_call_macro (s_env *env, s_call *call, s_tag *dest)
-{
- s_tag *expanded;
- assert(env);
- assert(call);
- assert(dest);
- (void) env;
- (void) call;
- (void) expanded;
- return dest;
-}
-
-const s_tag * eval_ident (s_env *env, s_ident *ident)
-{
- const s_tag *tag;
- assert(env);
- assert(ident);
- if (! (tag = frame_get(env->frame, ident->sym))) {
- assert(! "eval_ident: unbound variable");
- errx(1, "eval_ident: unbound variable");
- }
- return tag;
-}
-
-s_tag * eval_progn (s_env *env, s_list *program, s_tag *dest)
-{
- s_tag tmp;
- assert(env);
- assert(program);
- assert(dest);
- while (program) {
- eval_tag(env, &program->tag, &tmp);
- program = list_next(program);
- }
- *dest = tmp;
- return dest;
-}
-
-s_tag * eval_tag (s_env *env, s_tag *tag, s_tag *dest)
-{
- switch (tag->type.type) {
- case TAG_VOID: return tag_init_void(dest);
- case TAG_CALL:
- assert(! "eval_tag: invalid tag type: TAG_CALL");
- errx(1, "eval_tag: invalid tag type TAG_CALL");
- return NULL;
- case TAG_CALL_FN:
- return eval_call_fn(env, &tag->data.call, dest);
- case TAG_CALL_MACRO:
- return eval_call_macro(env, &tag->data.call, dest);
- case TAG_IDENT:
- return tag_copy(eval_ident(env, &tag->data.ident), dest);
- case TAG_BOOL:
- case TAG_CHARACTER:
- case TAG_F32:
- case TAG_F64:
- case TAG_FN:
- case TAG_INTEGER:
- case TAG_LIST:
- case TAG_PTAG:
- case TAG_QUOTE:
- case TAG_S16:
- case TAG_S32:
- case TAG_S64:
- case TAG_S8:
- case TAG_STR:
- case TAG_SYM:
- case TAG_TUPLE:
- case TAG_U16:
- case TAG_U32:
- case TAG_U64:
- case TAG_U8:
- case TAG_VAR:
- return tag_copy(tag, dest);
- }
- assert(! "eval_tag: invalid tag");
- errx(1, "eval_tag: invalid tag");
- return NULL;
-}
diff --git a/libc3/str.c b/libc3/str.c
index edeca76..fb61530 100644
--- a/libc3/str.c
+++ b/libc3/str.c
@@ -154,6 +154,25 @@ s_str * str_init_empty (s_str *str)
return str;
}
+s_str * str_init_f (s_str *str, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ str_init_vf(str, fmt, ap);
+ va_end(ap);
+ return str;
+}
+
+s_str * str_init_vf (s_str *str, const char *fmt, va_list ap)
+{
+ int len;
+ s8 *s;
+ len = vasprintf(&s, fmt, ap);
+ if (len < 0)
+ err(1, "vasprintf");
+ return str_init(str, s, len, s);
+}
+
s_str * str_init_str (s_str *str, const s_str *src)
{
assert(str);
diff --git a/libc3/str.h b/libc3/str.h
index 13e7a6c..bd81c24 100644
--- a/libc3/str.h
+++ b/libc3/str.h
@@ -28,14 +28,16 @@
#define STR_MAX (16 * 1024 * 1024)
/* Stack allocation compatible functions */
+void str_clean (s_str *str);
s_str * str_init (s_str *str, s8 *free, uw size, const s8 *p);
s_str * str_init_1 (s_str *str, s8 *free, const s8 *p);
s_str * str_init_alloc (s_str *str, uw size, const s8 *p);
s_str * str_init_dup (s_str *str, const s_str *src);
s_str * str_init_dup_1 (s_str *str, const s8 *p);
s_str * str_init_empty (s_str *str);
+s_str * str_init_f (s_str *str, const char *fmt, ...);
s_str * str_init_str (s_str *str, const s_str *src);
-void str_clean (s_str *str);
+s_str * str_init_vf (s_str *str, const char *fmt, va_list ap);
/* Constructors, call str_delete after use */
s_str * str_new (s8 *free, uw size, const s8 *p);
diff --git a/libc3/types.h b/libc3/types.h
index 9d63876..6ac238b 100644
--- a/libc3/types.h
+++ b/libc3/types.h
@@ -98,6 +98,7 @@ typedef struct sym s_sym;
typedef struct sym_list s_sym_list;
typedef struct tag s_tag;
typedef struct tuple s_tuple;
+typedef struct unwind_protect s_unwind_protect;
/* unions */
typedef union ptr u_ptr;
@@ -119,17 +120,6 @@ struct buf_save {
uw wpos;
};
-struct env {
- s_frame *frame;
- s_error_handler *error_handler;
-};
-
-struct error_handler
-{
- jmp_buf jmp_buf;
- s_error_handler *next;
-};
-
struct fact {
const s_tag *subject;
const s_tag *predicate;
@@ -185,6 +175,12 @@ struct tuple {
s_tag *tag;
};
+struct unwind_protect {
+ jmp_buf buf;
+ jmp_buf *jmp;
+ s_unwind_protect *next;
+};
+
/* 2 */
struct arg {
const s_sym *name;
@@ -237,6 +233,17 @@ struct str {
u_ptr ptr; /**< Pointer to memory. */
};
+/* 3 */
+struct env {
+ s_list *backtrace;
+ s_error_handler *error_handler;
+ s_frame *frame;
+ s_buf err;
+ s_buf in;
+ s_buf out;
+ s_unwind_protect *unwind_protect;
+};
+
struct sym {
s_str str;
};
@@ -267,13 +274,21 @@ union tag_data {
p_var var;
};
-/* 3 */
+/* 4 */
struct tag {
u_tag_type type;
u_tag_data data;
};
-/* 4 */
+/* 5 */
+struct error_handler
+{
+ s_list *backtrace;
+ jmp_buf jmp_buf;
+ s_error_handler *next;
+ s_tag tag;
+};
+
struct list {
s_tag tag;
s_tag next;
diff --git a/test/call_test.c b/test/call_test.c
index 8b459b6..ea540c4 100644
--- a/test/call_test.c
+++ b/test/call_test.c
@@ -31,8 +31,7 @@
call_init_1(&call, (test)); \
TEST_EQ(call_inspect(&call, &result), &result); \
TEST_EQ(result.size, strlen(test)); \
- if (g_test_last_ok) \
- TEST_STRNCMP(result.ptr.p, (test), result.size); \
+ TEST_STRNCMP(result.ptr.p, (test), result.size); \
call_clean(&call); \
str_clean(&result); \
} while (0)
diff --git a/test/ic3/call.in b/test/ic3/call.in
index c96e590..75715fc 100644
--- a/test/ic3/call.in
+++ b/test/ic3/call.in
@@ -1,2 +1,13 @@
-Module.ident(arg, arg, arg)
-a(b)
+defmodule(Test) do
+
+ def(test) do
+ test()
+ test(1)
+ test(1, 2)
+ test(1, 2, 3)
+ Test.test()
+ Test.test(1)
+ Test.test(1, 2)
+ Test.test(1, 2, 3)
+ end
+end