diff options
Diffstat (limited to 'c')
| -rw-r--r-- | c/core.c | 96 | ||||
| -rw-r--r-- | c/core.h | 16 | ||||
| -rw-r--r-- | c/step1_read_print.c | 1 | ||||
| -rw-r--r-- | c/step2_eval.c | 14 | ||||
| -rw-r--r-- | c/step3_env.c | 13 | ||||
| -rw-r--r-- | c/step4_if_fn_do.c | 23 | ||||
| -rw-r--r-- | c/step5_tco.c | 177 | ||||
| -rw-r--r-- | c/step6_file.c | 249 | ||||
| -rw-r--r-- | c/step7_quote.c | 271 | ||||
| -rw-r--r-- | c/step8_macros.c | 311 | ||||
| -rw-r--r-- | c/step9_interop.c | 320 | ||||
| -rw-r--r-- | c/stepA_more.c | 368 | ||||
| -rw-r--r-- | c/types.c | 30 | ||||
| -rw-r--r-- | c/types.h | 16 |
14 files changed, 885 insertions, 1020 deletions
@@ -2,9 +2,12 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <sys/stat.h> +#include <fcntl.h> #include "types.h" #include "core.h" +#include "reader.h" #include "printer.h" // Errors/Exceptions @@ -80,17 +83,49 @@ MalVal *println(MalVal *args) { return &mal_nil; } +MalVal *mal_readline(MalVal *str) { + assert_type(str, MAL_STRING, "readline of non-string"); + char * line = _readline(str->val.string); + if (line) { return malval_new_string(line); } + else { return &mal_nil; } +} -// Number functions +MalVal *read_string(MalVal *str) { + assert_type(str, MAL_STRING, "read_string of non-string"); + return read_str(str->val.string); +} -#define WRAP_INTEGER_OP(name, op) \ - MalVal *int_ ## name(MalVal *a, MalVal *b) { \ - return malval_new_integer(a->val.intnum op b->val.intnum); \ +char *slurp_raw(char *path) { + char *data; + struct stat fst; + int fd = open(path, O_RDONLY), + sz; + if (fd < 0) { + abort("slurp failed to open '%s'", path); + } + if (fstat(fd, &fst) < 0) { + abort("slurp failed to stat '%s'", path); } -#define WRAP_INTEGER_CMP_OP(name, op) \ - MalVal *int_ ## name(MalVal *a, MalVal *b) { \ - return a->val.intnum op b->val.intnum ? &mal_true : &mal_false; \ + data = malloc(fst.st_size+1); + sz = read(fd, data, fst.st_size); + if (sz < fst.st_size) { + abort("slurp failed to read '%s'", path); } + data[sz] = '\0'; + return data; +} +MalVal *slurp(MalVal *path) { + assert_type(path, MAL_STRING, "slurp of non-string"); + char *data = slurp_raw(path->val.string); + if (!data || mal_error) { return NULL; } + return malval_new_string(data); +} + + + + +// Number functions + WRAP_INTEGER_OP(plus,+) WRAP_INTEGER_OP(minus,-) WRAP_INTEGER_OP(multiply,*) @@ -115,7 +150,6 @@ MalVal *vector_Q(MalVal *seq) { return _vector_Q(seq) ? &mal_true : &mal_false; // Hash map functions -MalVal *hash_map(MalVal *args) { return _hash_map(args); } MalVal *hash_map_Q(MalVal *seq) { return _hash_map_Q(seq) ? &mal_true : &mal_false; } MalVal *assoc(MalVal *args) { @@ -123,15 +157,15 @@ MalVal *assoc(MalVal *args) { "assoc called with non-sequential arguments"); assert(_count(args) >= 2, "assoc needs at least 2 arguments"); - GHashTable *htable = g_hash_table_copy(first(args)->val.hash_table); + GHashTable *htable = g_hash_table_copy(_first(args)->val.hash_table); MalVal *hm = malval_new_hash_map(htable); - return _assoc_BANG(hm, rest(args)); + return _assoc_BANG(hm, _rest(args)); } MalVal *dissoc(MalVal* args) { - GHashTable *htable = g_hash_table_copy(first(args)->val.hash_table); + GHashTable *htable = g_hash_table_copy(_first(args)->val.hash_table); MalVal *hm = malval_new_hash_map(htable); - return _dissoc_BANG(hm, rest(args)); + return _dissoc_BANG(hm, _rest(args)); } MalVal *keys(MalVal *obj) { @@ -249,19 +283,6 @@ MalVal *nth(MalVal *seq, MalVal *idx) { return _nth(seq, idx->val.intnum); } -MalVal *first(MalVal *seq) { - assert_type(seq, MAL_LIST|MAL_VECTOR, - "first called with non-sequential"); - if (_count(seq) == 0) { - return &mal_nil; - } - return g_array_index(seq->val.array, MalVal*, 0); -} - -MalVal *rest(MalVal *seq) { - return _slice(seq, 1, _count(seq)); -} - MalVal *empty_Q(MalVal *seq) { assert_type(seq, MAL_LIST|MAL_VECTOR, "empty? called with non-sequential"); @@ -296,20 +317,11 @@ MalVal *sconj(MalVal *args) { return malval_new_list(src_lst->type, new_arr); } -MalVal *last(MalVal *seq) { - assert_type(seq, MAL_LIST|MAL_VECTOR, - "last called with non-sequential"); - if (_count(seq) == 0) { - return &mal_nil; - } - return g_array_index(seq->val.array, MalVal*, _count(seq)-1); -} - MalVal *apply(MalVal *args) { assert_type(args, MAL_LIST|MAL_VECTOR, "apply called with non-sequential"); MalVal *f = _nth(args, 0); - MalVal *last_arg = last(args); + MalVal *last_arg = _last(args); assert_type(last_arg, MAL_LIST|MAL_VECTOR, "last argument to apply is non-sequential"); int i, len = _count(args) - 2 + _count(last_arg); @@ -410,7 +422,7 @@ MalVal *swap_BANG(MalVal *args) { -core_ns_entry core_ns[50] = { +core_ns_entry core_ns[53] = { {"=", (void*(*)(void*))equal_Q, 2}, {"throw", (void*(*)(void*))throw, 1}, {"nil?", (void*(*)(void*))nil_Q, 1}, @@ -418,10 +430,14 @@ core_ns_entry core_ns[50] = { {"false?", (void*(*)(void*))false_Q, 1}, {"symbol", (void*(*)(void*))symbol, 1}, {"symbol?", (void*(*)(void*))symbol_Q, 1}, + {"pr-str", (void*(*)(void*))pr_str, -1}, {"str", (void*(*)(void*))str, -1}, {"prn", (void*(*)(void*))prn, -1}, {"println", (void*(*)(void*))println, -1}, + {"readline", (void*(*)(void*))mal_readline, 1}, + {"read-string", (void*(*)(void*))read_string, 1}, + {"slurp", (void*(*)(void*))slurp, 1}, {"<", (void*(*)(void*))int_lt, 2}, {"<=", (void*(*)(void*))int_lte, 2}, {">", (void*(*)(void*))int_gt, 2}, @@ -435,7 +451,7 @@ core_ns_entry core_ns[50] = { {"list?", (void*(*)(void*))list_Q, 1}, {"vector", (void*(*)(void*))vector, -1}, {"vector?", (void*(*)(void*))vector_Q, 1}, - {"hash-map", (void*(*)(void*))hash_map, -1}, + {"hash-map", (void*(*)(void*))_hash_map, -1}, {"map?", (void*(*)(void*))hash_map_Q, 1}, {"assoc", (void*(*)(void*))assoc, -1}, {"dissoc", (void*(*)(void*))dissoc, -1}, @@ -448,9 +464,9 @@ core_ns_entry core_ns[50] = { {"cons", (void*(*)(void*))cons, 2}, {"concat", (void*(*)(void*))concat, -1}, {"nth", (void*(*)(void*))nth, 2}, - {"first", (void*(*)(void*))first, 1}, - {"rest", (void*(*)(void*))rest, 1}, - {"last", (void*(*)(void*))last, 1}, + {"first", (void*(*)(void*))_first, 1}, + {"rest", (void*(*)(void*))_rest, 1}, + {"last", (void*(*)(void*))_last, 1}, {"empty?", (void*(*)(void*))empty_Q, 1}, {"count", (void*(*)(void*))count, 1}, {"conj", (void*(*)(void*))sconj, -1}, @@ -3,20 +3,6 @@ #include <glib.h> -// These are just used by step2 and step3 before then core_ns environment is -// imported - -MalVal *int_plus(MalVal *a, MalVal *b); -MalVal *int_minus(MalVal *a, MalVal *b); -MalVal *int_multiply(MalVal *a, MalVal *b); -MalVal *int_divide(MalVal *a, MalVal *b); - -// Useful for step implementation -MalVal *first(MalVal *seq); -MalVal *rest(MalVal *seq); -MalVal *last(MalVal *seq); -MalVal *hash_map(MalVal *args); - // namespace of type functions typedef struct { char *name; @@ -24,6 +10,6 @@ typedef struct { int arg_cnt; } core_ns_entry; -extern core_ns_entry core_ns[50]; +extern core_ns_entry core_ns[53]; #endif diff --git a/c/step1_read_print.c b/c/step1_read_print.c index 77c75b9..d28439e 100644 --- a/c/step1_read_print.c +++ b/c/step1_read_print.c @@ -1,6 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" diff --git a/c/step2_eval.c b/c/step2_eval.c index 85746f8..5d24ff0 100644 --- a/c/step2_eval.c +++ b/c/step2_eval.c @@ -1,10 +1,10 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" -#include "core.h" // Declarations MalVal *EVAL(MalVal *ast, GHashTable *env); @@ -54,7 +54,7 @@ MalVal *eval_ast(MalVal *ast, GHashTable *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -62,8 +62,8 @@ MalVal *eval_ast(MalVal *ast, GHashTable *env) { } MalVal *EVAL(MalVal *ast, GHashTable *env) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); if (ast->type != MAL_LIST) { return eval_ast(ast, env); } @@ -76,7 +76,7 @@ MalVal *EVAL(MalVal *ast, GHashTable *env) { assert_type(a0, MAL_SYMBOL, "Cannot invoke %s", _pr_str(a0,1)); MalVal *el = eval_ast(ast, env); if (!el || mal_error) { return NULL; } - MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))first(el); + MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))_first(el); //g_print("eval_invoke el: %s\n", _pr_str(el,1)); return f(_nth(el, 1), _nth(el, 2)); } @@ -109,9 +109,15 @@ MalVal *RE(GHashTable *env, char *prompt, char *str) { // Setup the initial REPL environment GHashTable *repl_env; + void init_repl_env() { repl_env = g_hash_table_new(g_str_hash, g_str_equal); + WRAP_INTEGER_OP(plus,+) + WRAP_INTEGER_OP(minus,-) + WRAP_INTEGER_OP(multiply,*) + WRAP_INTEGER_OP(divide,/) + g_hash_table_insert(repl_env, "+", int_plus); g_hash_table_insert(repl_env, "-", int_minus); g_hash_table_insert(repl_env, "*", int_multiply); diff --git a/c/step3_env.c b/c/step3_env.c index 4abf4d6..7c36b38 100644 --- a/c/step3_env.c +++ b/c/step3_env.c @@ -1,10 +1,10 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" -#include "core.h" // Declarations MalVal *EVAL(MalVal *ast, Env *env); @@ -53,7 +53,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -61,8 +61,8 @@ MalVal *eval_ast(MalVal *ast, Env *env) { } MalVal *EVAL(MalVal *ast, Env *env) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); if (ast->type != MAL_LIST) { return eval_ast(ast, env); } @@ -102,7 +102,7 @@ MalVal *EVAL(MalVal *ast, Env *env) { //g_print("eval apply\n"); MalVal *el = eval_ast(ast, env); if (!el || mal_error) { return NULL; } - MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))first(el); + MalVal *(*f)(void *, void*) = (MalVal *(*)(void*, void*))_first(el); return f(_nth(el, 1), _nth(el, 2)); } } @@ -138,6 +138,11 @@ Env *repl_env; void init_repl_env() { repl_env = new_env(NULL, NULL, NULL); + WRAP_INTEGER_OP(plus,+) + WRAP_INTEGER_OP(minus,-) + WRAP_INTEGER_OP(multiply,*) + WRAP_INTEGER_OP(divide,/) + env_set(repl_env, "+", (MalVal *)int_plus); env_set(repl_env, "-", (MalVal *)int_minus); env_set(repl_env, "*", (MalVal *)int_multiply); diff --git a/c/step4_if_fn_do.c b/c/step4_if_fn_do.c index 3662816..87e1241 100644 --- a/c/step4_if_fn_do.c +++ b/c/step4_if_fn_do.c @@ -1,6 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" @@ -53,7 +54,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -61,8 +62,8 @@ MalVal *eval_ast(MalVal *ast, Env *env) { } MalVal *EVAL(MalVal *ast, Env *env) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); if (ast->type != MAL_LIST) { return eval_ast(ast, env); } @@ -102,8 +103,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { } else if ((a0->type & MAL_SYMBOL) && strcmp("do", a0->val.string) == 0) { //g_print("eval apply do\n"); - MalVal *el = eval_ast(rest(ast), env); - return last(el); + MalVal *el = eval_ast(_rest(ast), env); + return _last(el); } else if ((a0->type & MAL_SYMBOL) && strcmp("if", a0->val.string) == 0) { //g_print("eval apply if\n"); @@ -136,8 +137,8 @@ MalVal *EVAL(MalVal *ast, Env *env) { //g_print("eval apply\n"); MalVal *el = eval_ast(ast, env); if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); + MalVal *f = _first(el), + *args = _rest(el); assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, "cannot apply '%s'", _pr_str(f,1)); return _apply(f, args); @@ -173,18 +174,16 @@ MalVal *RE(Env *env, char *prompt, char *str) { Env *repl_env; void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); } diff --git a/c/step5_tco.c b/c/step5_tco.c index 99d6826..6938e47 100644 --- a/c/step5_tco.c +++ b/c/step5_tco.c @@ -1,6 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> + #include "types.h" #include "readline.h" #include "reader.h" @@ -53,7 +54,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -62,93 +63,95 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); + + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; + + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + int i, len; + if (_count(ast) == 0) { return ast; } + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); } - if (!ast || mal_error) return NULL; - - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - int i, len; - if (_count(ast) == 0) { return ast; } - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -180,18 +183,16 @@ MalVal *RE(Env *env, char *prompt, char *str) { Env *repl_env; void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); } diff --git a/c/step6_file.c b/c/step6_file.c index acde758..ae48693 100644 --- a/c/step6_file.c +++ b/c/step6_file.c @@ -1,13 +1,11 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" #include "core.h" -#include "interop.h" // Declarations MalVal *EVAL(MalVal *ast, Env *env); @@ -56,7 +54,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -65,93 +63,95 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - int i, len; - if (_count(ast) == 0) { return ast; } - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; + + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + int i, len; + if (_count(ast) == 0) { return ast; } + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -182,58 +182,20 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); - } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); - } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); @@ -252,21 +214,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } diff --git a/c/step7_quote.c b/c/step7_quote.c index 7da47ee..ac17955 100644 --- a/c/step7_quote.c +++ b/c/step7_quote.c @@ -1,13 +1,11 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" #include "core.h" -#include "interop.h" // Declarations MalVal *EVAL(MalVal *ast, Env *env); @@ -49,12 +47,12 @@ MalVal *quasiquote(MalVal *ast) { strcmp("splice-unquote", a00->val.string) == 0) { return _listX(3, malval_new_symbol("concat"), _nth(a0, 1), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } return _listX(3, malval_new_symbol("cons"), quasiquote(a0), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } @@ -83,7 +81,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -92,102 +90,104 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - if (!ast || mal_error) return NULL; - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - int i, len; - if (_count(ast) == 0) { return ast; } - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quote", a0->val.string) == 0) { - //g_print("eval apply quote\n"); - return _nth(ast, 1); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quasiquote", a0->val.string) == 0) { - //g_print("eval apply quasiquote\n"); - MalVal *a1 = _nth(ast, 1); - return EVAL(quasiquote(a1), env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; + + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + int i, len; + if (_count(ast) == 0) { return ast; } + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quote", a0->val.string) == 0) { + //g_print("eval apply quote\n"); + return _nth(ast, 1); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quasiquote", a0->val.string) == 0) { + //g_print("eval apply quasiquote\n"); + MalVal *a1 = _nth(ast, 1); + return EVAL(quasiquote(a1), env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -218,58 +218,20 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); - } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); - } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); @@ -288,21 +250,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } diff --git a/c/step8_macros.c b/c/step8_macros.c index eb715b1..93c83fa 100644 --- a/c/step8_macros.c +++ b/c/step8_macros.c @@ -1,13 +1,11 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" #include "core.h" -#include "interop.h" // Declarations MalVal *EVAL(MalVal *ast, Env *env); @@ -50,12 +48,12 @@ MalVal *quasiquote(MalVal *ast) { strcmp("splice-unquote", a00->val.string) == 0) { return _listX(3, malval_new_symbol("concat"), _nth(a0, 1), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } return _listX(3, malval_new_symbol("cons"), quasiquote(a0), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } @@ -73,7 +71,7 @@ MalVal *macroexpand(MalVal *ast, Env *env) { MalVal *a0 = _nth(ast, 0); MalVal *mac = env_get(env, a0->val.string); // TODO: this is weird and limits it to 20. FIXME - ast = _apply(mac, rest(ast)); + ast = _apply(mac, _rest(ast)); } return ast; } @@ -103,7 +101,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -112,121 +110,123 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - if (!ast || mal_error) return NULL; - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - ast = macroexpand(ast, env); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { return ast; } - if (_count(ast) == 0) { return ast; } + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; - int i, len; - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quote", a0->val.string) == 0) { - //g_print("eval apply quote\n"); - return _nth(ast, 1); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quasiquote", a0->val.string) == 0) { - //g_print("eval apply quasiquote\n"); - MalVal *a1 = _nth(ast, 1); - return EVAL(quasiquote(a1), env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("defmacro!", a0->val.string) == 0) { - //g_print("eval apply defmacro!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - res->ismacro = TRUE; - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("macroexpand", a0->val.string) == 0) { - //g_print("eval apply macroexpand\n"); - MalVal *a1 = _nth(ast, 1); - return macroexpand(a1, env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + ast = macroexpand(ast, env); + if (!ast || mal_error) return NULL; + if (ast->type != MAL_LIST) { return ast; } + if (_count(ast) == 0) { return ast; } + + int i, len; + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quote", a0->val.string) == 0) { + //g_print("eval apply quote\n"); + return _nth(ast, 1); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quasiquote", a0->val.string) == 0) { + //g_print("eval apply quasiquote\n"); + MalVal *a1 = _nth(ast, 1); + return EVAL(quasiquote(a1), env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("defmacro!", a0->val.string) == 0) { + //g_print("eval apply defmacro!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + res->ismacro = TRUE; + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("macroexpand", a0->val.string) == 0) { + //g_print("eval apply macroexpand\n"); + MalVal *a1 = _nth(ast, 1); + return macroexpand(a1, env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->ismacro = FALSE; + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->ismacro = FALSE; - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -257,61 +257,25 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); - } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); - } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + RE(repl_env, "", "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); + RE(repl_env, "", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); } int main(int argc, char *argv[]) @@ -327,21 +291,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } diff --git a/c/step9_interop.c b/c/step9_interop.c index dcd1526..743fb22 100644 --- a/c/step9_interop.c +++ b/c/step9_interop.c @@ -1,8 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" @@ -50,12 +49,12 @@ MalVal *quasiquote(MalVal *ast) { strcmp("splice-unquote", a00->val.string) == 0) { return _listX(3, malval_new_symbol("concat"), _nth(a0, 1), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } return _listX(3, malval_new_symbol("cons"), quasiquote(a0), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } @@ -73,7 +72,7 @@ MalVal *macroexpand(MalVal *ast, Env *env) { MalVal *a0 = _nth(ast, 0); MalVal *mac = env_get(env, a0->val.string); // TODO: this is weird and limits it to 20. FIXME - ast = _apply(mac, rest(ast)); + ast = _apply(mac, _rest(ast)); } return ast; } @@ -103,7 +102,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -112,126 +111,128 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - if (!ast || mal_error) return NULL; - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - ast = macroexpand(ast, env); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { return ast; } - if (_count(ast) == 0) { return ast; } + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; - int i, len; - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quote", a0->val.string) == 0) { - //g_print("eval apply quote\n"); - return _nth(ast, 1); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quasiquote", a0->val.string) == 0) { - //g_print("eval apply quasiquote\n"); - MalVal *a1 = _nth(ast, 1); - return EVAL(quasiquote(a1), env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("defmacro!", a0->val.string) == 0) { - //g_print("eval apply defmacro!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - res->ismacro = TRUE; - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("macroexpand", a0->val.string) == 0) { - //g_print("eval apply macroexpand\n"); - MalVal *a1 = _nth(ast, 1); - return macroexpand(a1, env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp(".", a0->val.string) == 0) { - //g_print("eval apply .\n"); - MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env); - return invoke_native(el); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + ast = macroexpand(ast, env); + if (!ast || mal_error) return NULL; + if (ast->type != MAL_LIST) { return ast; } + if (_count(ast) == 0) { return ast; } + + int i, len; + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quote", a0->val.string) == 0) { + //g_print("eval apply quote\n"); + return _nth(ast, 1); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quasiquote", a0->val.string) == 0) { + //g_print("eval apply quasiquote\n"); + MalVal *a1 = _nth(ast, 1); + return EVAL(quasiquote(a1), env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("defmacro!", a0->val.string) == 0) { + //g_print("eval apply defmacro!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + res->ismacro = TRUE; + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("macroexpand", a0->val.string) == 0) { + //g_print("eval apply macroexpand\n"); + MalVal *a1 = _nth(ast, 1); + return macroexpand(a1, env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp(".", a0->val.string) == 0) { + //g_print("eval apply .\n"); + MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env); + return invoke_native(el); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { + return &mal_nil; } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->ismacro = FALSE; + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->ismacro = FALSE; - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -262,61 +263,25 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); - } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); - } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + RE(repl_env, "", "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); + RE(repl_env, "", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); } int main(int argc, char *argv[]) @@ -332,21 +297,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } diff --git a/c/stepA_more.c b/c/stepA_more.c index 4e4152c..82bf3db 100644 --- a/c/stepA_more.c +++ b/c/stepA_more.c @@ -1,8 +1,7 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <sys/stat.h> -#include <fcntl.h> + #include "types.h" #include "readline.h" #include "reader.h" @@ -50,12 +49,12 @@ MalVal *quasiquote(MalVal *ast) { strcmp("splice-unquote", a00->val.string) == 0) { return _listX(3, malval_new_symbol("concat"), _nth(a0, 1), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } return _listX(3, malval_new_symbol("cons"), quasiquote(a0), - quasiquote(rest(ast))); + quasiquote(_rest(ast))); } } @@ -73,7 +72,7 @@ MalVal *macroexpand(MalVal *ast, Env *env) { MalVal *a0 = _nth(ast, 0); MalVal *mac = env_get(env, a0->val.string); // TODO: this is weird and limits it to 20. FIXME - ast = _apply(mac, rest(ast)); + ast = _apply(mac, _rest(ast)); } return ast; } @@ -103,7 +102,7 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *new_val = EVAL((MalVal *)value, env); g_array_append_val(seq->val.array, new_val); } - return hash_map(seq); + return _hash_map(seq); } else { //g_print("EVAL scalar: %s\n", _pr_str(ast,1)); return ast; @@ -112,147 +111,149 @@ MalVal *eval_ast(MalVal *ast, Env *env) { MalVal *EVAL(MalVal *ast, Env *env) { while (TRUE) { - if (!ast || mal_error) return NULL; - //g_print("EVAL: %s\n", _pr_str(ast,1)); - if (ast->type != MAL_LIST) { - return eval_ast(ast, env); - } - if (!ast || mal_error) return NULL; - // apply list - //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); - ast = macroexpand(ast, env); - if (!ast || mal_error) return NULL; - if (ast->type != MAL_LIST) { return ast; } - if (_count(ast) == 0) { return ast; } + if (!ast || mal_error) return NULL; + //g_print("EVAL: %s\n", _pr_str(ast,1)); + if (ast->type != MAL_LIST) { + return eval_ast(ast, env); + } + if (!ast || mal_error) return NULL; - int i, len; - MalVal *a0 = _nth(ast, 0); - if ((a0->type & MAL_SYMBOL) && - strcmp("def!", a0->val.string) == 0) { - //g_print("eval apply def!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - env_set(env, a1->val.string, res); - return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("let*", a0->val.string) == 0) { - //g_print("eval apply let*\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2), - *key, *val; - assert_type(a1, MAL_LIST|MAL_VECTOR, - "let* bindings must be list or vector"); - len = _count(a1); - assert((len % 2) == 0, "odd number of let* bindings forms"); - Env *let_env = new_env(env, NULL, NULL); - for(i=0; i<len; i+=2) { - key = g_array_index(a1->val.array, MalVal*, i); - val = g_array_index(a1->val.array, MalVal*, i+1); - assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); - env_set(let_env, key->val.string, EVAL(val, let_env)); - } - return EVAL(a2, let_env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quote", a0->val.string) == 0) { - //g_print("eval apply quote\n"); - return _nth(ast, 1); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("quasiquote", a0->val.string) == 0) { - //g_print("eval apply quasiquote\n"); - MalVal *a1 = _nth(ast, 1); - return EVAL(quasiquote(a1), env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("defmacro!", a0->val.string) == 0) { - //g_print("eval apply defmacro!\n"); - MalVal *a1 = _nth(ast, 1), - *a2 = _nth(ast, 2); - MalVal *res = EVAL(a2, env); - res->ismacro = TRUE; - env_set(env, a1->val.string, res); + // apply list + //g_print("EVAL apply list: %s\n", _pr_str(ast,1)); + ast = macroexpand(ast, env); + if (!ast || mal_error) return NULL; + if (ast->type != MAL_LIST) { return ast; } + if (_count(ast) == 0) { return ast; } + + int i, len; + MalVal *a0 = _nth(ast, 0); + if ((a0->type & MAL_SYMBOL) && + strcmp("def!", a0->val.string) == 0) { + //g_print("eval apply def!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("let*", a0->val.string) == 0) { + //g_print("eval apply let*\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2), + *key, *val; + assert_type(a1, MAL_LIST|MAL_VECTOR, + "let* bindings must be list or vector"); + len = _count(a1); + assert((len % 2) == 0, "odd number of let* bindings forms"); + Env *let_env = new_env(env, NULL, NULL); + for(i=0; i<len; i+=2) { + key = g_array_index(a1->val.array, MalVal*, i); + val = g_array_index(a1->val.array, MalVal*, i+1); + assert_type(key, MAL_SYMBOL, "let* bind to non-symbol"); + env_set(let_env, key->val.string, EVAL(val, let_env)); + } + return EVAL(a2, let_env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quote", a0->val.string) == 0) { + //g_print("eval apply quote\n"); + return _nth(ast, 1); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("quasiquote", a0->val.string) == 0) { + //g_print("eval apply quasiquote\n"); + MalVal *a1 = _nth(ast, 1); + return EVAL(quasiquote(a1), env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("defmacro!", a0->val.string) == 0) { + //g_print("eval apply defmacro!\n"); + MalVal *a1 = _nth(ast, 1), + *a2 = _nth(ast, 2); + MalVal *res = EVAL(a2, env); + res->ismacro = TRUE; + env_set(env, a1->val.string, res); + return res; + } else if ((a0->type & MAL_SYMBOL) && + strcmp("macroexpand", a0->val.string) == 0) { + //g_print("eval apply macroexpand\n"); + MalVal *a1 = _nth(ast, 1); + return macroexpand(a1, env); + } else if ((a0->type & MAL_SYMBOL) && + strcmp(".", a0->val.string) == 0) { + //g_print("eval apply .\n"); + MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env); + return invoke_native(el); + } else if ((a0->type & MAL_SYMBOL) && + strcmp("try*", a0->val.string) == 0) { + //g_print("eval apply try*\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *a2 = _nth(ast, 2); + MalVal *res = EVAL(a1, env); + if (!mal_error) { return res; } + MalVal *a20 = _nth(a2, 0); + if (strcmp("catch*", a20->val.string) == 0) { + MalVal *a21 = _nth(a2, 1); + MalVal *a22 = _nth(a2, 2); + Env *catch_env = new_env(env, + _listX(1, a21), + _listX(1, mal_error)); + //malval_free(mal_error); + mal_error = NULL; + res = EVAL(a22, catch_env); return res; - } else if ((a0->type & MAL_SYMBOL) && - strcmp("macroexpand", a0->val.string) == 0) { - //g_print("eval apply macroexpand\n"); - MalVal *a1 = _nth(ast, 1); - return macroexpand(a1, env); - } else if ((a0->type & MAL_SYMBOL) && - strcmp(".", a0->val.string) == 0) { - //g_print("eval apply .\n"); - MalVal *el = eval_ast(_slice(ast, 1, _count(ast)), env); - return invoke_native(el); - } else if ((a0->type & MAL_SYMBOL) && - strcmp("try*", a0->val.string) == 0) { - //g_print("eval apply try*\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *a2 = _nth(ast, 2); - MalVal *res = EVAL(a1, env); - if (!mal_error) { return res; } - MalVal *a20 = _nth(a2, 0); - if (strcmp("catch*", a20->val.string) == 0) { - MalVal *a21 = _nth(a2, 1); - MalVal *a22 = _nth(a2, 2); - Env *catch_env = new_env(env, - _listX(1, a21), - _listX(1, mal_error)); - //malval_free(mal_error); - mal_error = NULL; - res = EVAL(a22, catch_env); - return res; - } else { + } else { + return &mal_nil; + } + } else if ((a0->type & MAL_SYMBOL) && + strcmp("do", a0->val.string) == 0) { + //g_print("eval apply do\n"); + eval_ast(_slice(ast, 1, _count(ast)-1), env); + ast = _last(ast); + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("if", a0->val.string) == 0) { + //g_print("eval apply if\n"); + MalVal *a1 = _nth(ast, 1); + MalVal *cond = EVAL(a1, env); + if (!cond || mal_error) return NULL; + if (cond->type & (MAL_FALSE|MAL_NIL)) { + // eval false slot form + ast = _nth(ast, 3); + if (!ast) { return &mal_nil; } - } else if ((a0->type & MAL_SYMBOL) && - strcmp("do", a0->val.string) == 0) { - //g_print("eval apply do\n"); - eval_ast(_slice(ast, 1, _count(ast)-1), env); - ast = last(ast); - // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("if", a0->val.string) == 0) { - //g_print("eval apply if\n"); - MalVal *a1 = _nth(ast, 1); - MalVal *cond = EVAL(a1, env); - if (!cond || mal_error) return NULL; - if (cond->type & (MAL_FALSE|MAL_NIL)) { - // eval false slot form - ast = _nth(ast, 3); - if (!ast) { - return &mal_nil; - } - } else { - // eval true slot form - ast = _nth(ast, 2); - } + } else { + // eval true slot form + ast = _nth(ast, 2); + } + // Continue loop + } else if ((a0->type & MAL_SYMBOL) && + strcmp("fn*", a0->val.string) == 0) { + //g_print("eval apply fn*\n"); + MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); + mf->ismacro = FALSE; + mf->val.func.evaluator = EVAL; + mf->val.func.args = _nth(ast, 1); + mf->val.func.body = _nth(ast, 2); + mf->val.func.env = env; + return mf; + } else { + //g_print("eval apply\n"); + MalVal *el = eval_ast(ast, env); + if (!el || mal_error) { return NULL; } + MalVal *f = _first(el), + *args = _rest(el); + assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, + "cannot apply '%s'", _pr_str(f,1)); + if (f->type & MAL_FUNCTION_MAL) { + ast = f->val.func.body; + env = new_env(f->val.func.env, f->val.func.args, args); // Continue loop - } else if ((a0->type & MAL_SYMBOL) && - strcmp("fn*", a0->val.string) == 0) { - //g_print("eval apply fn*\n"); - MalVal *mf = malval_new(MAL_FUNCTION_MAL, NULL); - mf->ismacro = FALSE; - mf->val.func.evaluator = EVAL; - mf->val.func.args = _nth(ast, 1); - mf->val.func.body = _nth(ast, 2); - mf->val.func.env = env; - return mf; } else { - //g_print("eval apply\n"); - MalVal *el = eval_ast(ast, env); - if (!el || mal_error) { return NULL; } - MalVal *f = first(el), - *args = rest(el); - assert_type(f, MAL_FUNCTION_C|MAL_FUNCTION_MAL, - "cannot invoke '%s'", _pr_str(f,1)); - if (f->type & MAL_FUNCTION_MAL) { - ast = f->val.func.body; - env = new_env(f->val.func.env, f->val.func.args, args); - // Continue loop - } else { - return _apply(f, args); - } + return _apply(f, args); } } + + } // TCO while loop } // print @@ -283,71 +284,25 @@ MalVal *RE(Env *env, char *prompt, char *str) { // Setup the initial REPL environment Env *repl_env; -char *slurp_raw(char *path) { - char *data; - struct stat fst; - int fd = open(path, O_RDONLY), - sz; - if (fd < 0) { - abort("slurp failed to open '%s'", path); - } - if (fstat(fd, &fst) < 0) { - abort("slurp failed to stat '%s'", path); - } - data = malloc(fst.st_size+1); - sz = read(fd, data, fst.st_size); - if (sz < fst.st_size) { - abort("slurp failed to read '%s'", path); - } - data[sz] = '\0'; - return data; -} - void init_repl_env() { - void _ref(char *name, MalVal*(*func)(MalVal*), int arg_cnt) { - void *(*f)(void *) = (void*(*)(void*))func; - env_set(repl_env, name, malval_new_function(f, arg_cnt, NULL)); - } repl_env = new_env(NULL, NULL, NULL); + // core.c: defined using C int i; for(i=0; i< (sizeof(core_ns) / sizeof(core_ns[0])); i++) { - MalVal *(*f)(MalVal *) = (MalVal*(*)(MalVal*))core_ns[i].func; - _ref(core_ns[i].name, f, core_ns[i].arg_cnt); - } - - MalVal *readline(MalVal *str) { - assert_type(str, MAL_STRING, "readline of non-string"); - char * line = _readline(str->val.string); - if (line) { return malval_new_string(line); } - else { return &mal_nil; } - } - _ref("readline", readline, 1); - - MalVal *read_string(MalVal *str) { - assert_type(str, MAL_STRING, "read_string of non-string"); - return read_str(str->val.string); + env_set(repl_env, core_ns[i].name, + malval_new_function(core_ns[i].func, core_ns[i].arg_cnt)); } - _ref("read-string", read_string, 1); - - MalVal *do_eval(MalVal *ast) { - return EVAL(ast, repl_env); - } - _ref("eval", do_eval, 1); - - MalVal *slurp(MalVal *path) { - assert_type(path, MAL_STRING, "slurp of non-string"); - char *data = slurp_raw(path->val.string); - if (!data || mal_error) { return NULL; } - return malval_new_string(data); - } - _ref("slurp", slurp, 1); + MalVal *do_eval(MalVal *ast) { return EVAL(ast, repl_env); } + env_set(repl_env, "eval", + malval_new_function((void*(*)(void *))do_eval, 1)); + // core.mal: defined using the language itself RE(repl_env, "", "(def! not (fn* (a) (if a false true)))"); - RE(repl_env, "", "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); - RE(repl_env, "", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); RE(repl_env, "", "(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))"); + RE(repl_env, "", "(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))"); + RE(repl_env, "", "(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))"); } int main(int argc, char *argv[]) @@ -363,21 +318,22 @@ int main(int argc, char *argv[]) if (argc > 1) { char *cmd = g_strdup_printf("(load-file \"%s\")", argv[1]); RE(repl_env, "", cmd); - } else { - // REPL loop - for(;;) { - exp = RE(repl_env, prompt, NULL); - if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { - return 0; - } - output = PRINT(exp); + return 0; + } - if (output) { - g_print("%s\n", output); - free(output); // Free output string - } + // REPL loop + for(;;) { + exp = RE(repl_env, prompt, NULL); + if (mal_error && strcmp("EOF", mal_error->val.string) == 0) { + return 0; + } + output = PRINT(exp); - //malval_free(exp); // Free evaluated expression + if (output) { + g_print("%s\n", output); + free(output); // Free output string } + + //malval_free(exp); // Free evaluated expression } } @@ -124,8 +124,8 @@ MalVal *malval_new_atom(MalVal *val) { } -MalVal *malval_new_function(void *(*func)(void *), int arg_cnt, MalVal* metadata) { - MalVal *mv = malval_new(MAL_FUNCTION_C, metadata); +MalVal *malval_new_function(void *(*func)(void *), int arg_cnt) { + MalVal *mv = malval_new(MAL_FUNCTION_C, NULL); mv->func_arg_cnt = arg_cnt; assert(mv->func_arg_cnt <= 20, "native function restricted to 20 args (%d given)", @@ -420,13 +420,37 @@ int _sequential_Q(MalVal *seq) { MalVal *_nth(MalVal *seq, int idx) { assert_type(seq, MAL_LIST|MAL_VECTOR, - "nth called with non-sequential"); + "_nth called with non-sequential"); if (idx >= _count(seq)) { return &mal_nil; } return g_array_index(seq->val.array, MalVal*, idx); } +MalVal *_first(MalVal *seq) { + assert_type(seq, MAL_LIST|MAL_VECTOR, + "_first called with non-sequential"); + if (_count(seq) == 0) { + return &mal_nil; + } + return g_array_index(seq->val.array, MalVal*, 0); +} + +MalVal *_last(MalVal *seq) { + assert_type(seq, MAL_LIST|MAL_VECTOR, + "_last called with non-sequential"); + if (_count(seq) == 0) { + return &mal_nil; + } + return g_array_index(seq->val.array, MalVal*, _count(seq)-1); +} + + +MalVal *_rest(MalVal *seq) { + return _slice(seq, 1, _count(seq)); +} + + MalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2) { MalVal *e, *el; assert_type(lst, MAL_LIST|MAL_VECTOR, @@ -133,8 +133,19 @@ MalVal *malval_new_symbol(char *val); MalVal *malval_new_list(MalType type, GArray *val); MalVal *malval_new_hash_map(GHashTable *val); MalVal *malval_new_atom(MalVal *val); -MalVal *malval_new_function(void *(*func)(void *), int arg_cnt, MalVal* metadata); +MalVal *malval_new_function(void *(*func)(void *), int arg_cnt); +// Numbers +#define WRAP_INTEGER_OP(name, op) \ + MalVal *int_ ## name(MalVal *a, MalVal *b) { \ + return malval_new_integer(a->val.intnum op b->val.intnum); \ + } +#define WRAP_INTEGER_CMP_OP(name, op) \ + MalVal *int_ ## name(MalVal *a, MalVal *b) { \ + return a->val.intnum op b->val.intnum ? &mal_true : &mal_false; \ + } + +// Collections MalVal *_listX(int count, ...); MalVal *_list(MalVal *args); MalVal *_vector(MalVal *args); @@ -148,6 +159,9 @@ char *_pr_str(MalVal *args, int print_readably); MalVal *_slice(MalVal *seq, int start, int end); MalVal *_nth(MalVal *seq, int idx); +MalVal *_first(MalVal *seq); +MalVal *_rest(MalVal *seq); +MalVal *_last(MalVal *seq); MalVal *_map2(MalVal *(*func)(void*, void*), MalVal *lst, void *arg2); |
