diff --git a/Makefile b/Makefile index ae4da1e..8da1821 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ ANALYZER=-fanalyzer SANITIZER=-fsanitize=undefined #,address TIMEOUT=timeout 2 -OBJ_COMMON=schaf.o utils.o +OBJ_COMMON=schaf.o utils.o table.o OBJ=$(OBJ_COMMON) main.o OBJ_TEST=$(OBJ_COMMON) basic-test.o @@ -60,8 +60,9 @@ microbench: schaf @$(MAKE) -C $@ utils.o: utils.h -schaf.o main.o: schaf.h utils.h -basic-test.o: schaf.h +schaf.o main.o basic-test.o: schaf.h utils.h +table.o: table.h utils.h +schaf.o basic-test.o: table.h .PHONY: all clean test test-c test-scheme analyze sanitize \ test-san test-c-san test-scheme-san \ diff --git a/basic-test.c b/basic-test.c index f5d047b..35ce6ad 100644 --- a/basic-test.c +++ b/basic-test.c @@ -5,6 +5,8 @@ #include #include "schaf.h" +#include "table.h" +#include "utils.h" #define expect_stringify(exp, v) do { \ char *s = stringify(v); \ @@ -218,3 +220,177 @@ Test(schaf, map) { expect_runtime_error("expected pair but got integer", "(map + 1)"); expect_runtime_error("expected pair but got integer", "(for-each + 1)"); } + +Test(table, get_put) { + Table *t = table_new(); + table_put(t, 1, 100); + cr_assert(eq(int, 100, table_get(t, 1))); + table_put(t, 2, 200); + table_put(t, 3, 300); + table_put(t, 4, 400); + cr_assert(eq(int, 100, table_get(t, 1))); + cr_assert(eq(int, 200, table_get(t, 2))); + cr_assert(eq(int, 300, table_get(t, 3))); + cr_assert(eq(int, 400, table_get(t, 4))); + table_put(t, 1, 42); + cr_assert(eq(int, 42, table_get(t, 1))); + cr_assert(eq(int, 200, table_get(t, 2))); + cr_assert(eq(int, 300, table_get(t, 3))); + cr_assert(eq(int, 400, table_get(t, 4))); + + for (int i = 1; i <= 17; i++) + table_put(t, i, i*10000000); + for (int i = 1; i <= 17; i++) + cr_assert(eq(int, i*10000000, table_get(t, i))); + + table_free(t); +} + +Test(table, merge) { + Table *t = table_new(); + for (size_t i = 1; i <= 11; i++) + table_put(t, i, i*13); + Table *u = table_new(); + for (size_t i = 5; i <= 17; i++) + table_put(u, i, i*19); + table_merge(t, u); + + for (int i = 1; i < 5; i++) + cr_assert(eq(sz, i*13, table_get(t, i))); + for (int i = 5; i <= 17; i++) + cr_assert(eq(sz, i*19, table_get(t, i))); + + table_free(t); + table_free(u); +} + +Test(table, merge_precedence) { + Table *t = table_new(); + for (size_t i = 1; i <= 3; i++) { + table_put(t, 1, i*10); + cr_assert(eq(sz, i*10, table_get(t, 1))); + } + + Table *u = table_new(); + for (size_t i = 1; i <= 3; i++) { + table_put(u, 1, i*20); + cr_assert(eq(sz, i*20, table_get(u, 1))); + } + + table_merge(t, u); + // the latest entry of `u` should take precedence in `t` too + cr_assert(eq(sz, 60, table_get(t, 1))); + + table_free(t); + table_free(u); +} + +static inline uint64_t keydup(uint64_t s) +{ + return (uint64_t) xstrdup((char *) s); +} + +Test(table, string_keys) { + uint64_t k_foo = (uint64_t) "foo"; + uint64_t k_bar = (uint64_t) "bar"; + + Table *t = table_new_str(); + table_put(t, keydup(k_foo), 12); + table_put(t, keydup(k_bar), 34); + table_put(t, keydup(k_foo), 56); + + cr_assert(eq(int, 56, table_get(t, k_foo))); + cr_assert(eq(int, 34, table_get(t, k_bar))); + + table_free(t); +} + +Test(table, set_or_put) { + Table *t = table_new(); + table_put(t, 1, 123); + table_put(t, 2, 456); + + bool s1 = table_set_or_put(t, 2, 789); // overwrite! + bool s2 = table_set_or_put(t, 3, 210); // just add + + cr_assert(s1); + cr_assert(not(s2)); + cr_assert(eq(int, 123, table_get(t, 1))); + cr_assert(eq(int, 789, table_get(t, 2))); + cr_assert(eq(int, 210, table_get(t, 3))); + table_free(t); +} + +Test(table, inherit) { + Table *t = table_new(); + table_put(t, 1, 12); + table_put(t, 2, 34); + + Table *u = table_inherit(t); + table_put(u, 2, 20); + table_put(u, 3, 30); + + cr_assert(eq(int, 12, table_get(t, 1))); + cr_assert(eq(int, 34, table_get(t, 2))); + cr_assert(eq(int, 0, table_get(t, 3))); + + cr_assert(eq(int, 12, table_get(u, 1))); + cr_assert(eq(int, 20, table_get(u, 2))); + cr_assert(eq(int, 30, table_get(u, 3))); + + table_free(u); + table_free(t); +} + +Test(table, inherit_set) { + Table *t = table_new(); + table_put(t, 1, 10); + cr_assert(eq(int, 10, table_get(t, 1))); + + Table *u = table_inherit(t); + table_put(u, 2, 20); + table_set(u, 1, 30); + + cr_assert(eq(int, 30, table_get(t, 1))); + cr_assert(eq(int, 0, table_get(t, 2))); + + cr_assert(eq(int, 30, table_get(u, 1))); + cr_assert(eq(int, 20, table_get(u, 2))); + + table_free(u); + table_free(t); +} + +Test(table, resize_keeping_order) { + Table *t = table_new(); + for (size_t i = 1; i <= 100; i++) { + table_put(t, 1, i*10); + cr_assert(eq(sz, i*10, table_get(t, 1))); + } + for (size_t i = 1; i <= 100; i++) { + table_put(t, i*2, i); + cr_assert(eq(sz, i, table_get(t, i*2))); + } + for (size_t i = 1; i <= 100; i++) { + table_put(t, 1, i*10); + cr_assert(eq(sz, i*10, table_get(t, 1))); + } + table_free(t); +} + +Test(table, set) { + Table *t = table_new(); + table_put(t, 1, 123); + table_put(t, 2, 456); + + bool s1 = table_set(t, 2, 789); // overwrite! + bool s2 = table_set(t, 3, 210); // ignored + + cr_assert(s1); + cr_assert(not(s2)); + cr_assert(eq(int, 123, table_get(t, 1))); + cr_assert(eq(int, 789, table_get(t, 2))); + cr_assert(eq(int, 0, table_get(t, 3))); // still not found! + + table_free(t); +} diff --git a/schaf.c b/schaf.c index 2746b93..2fd255a 100644 --- a/schaf.c +++ b/schaf.c @@ -15,6 +15,7 @@ #include #include "schaf.h" +#include "table.h" #include "utils.h" // @@ -63,7 +64,7 @@ typedef struct { typedef struct { Procedure proc; - Value env; + Table *env; Value params; Value body; } Closure; @@ -115,7 +116,8 @@ static const int64_t CFUNCARG_MAX = 7; // Runtime-locals (aka global variables) // -static Value toplevel_environment = Qnil; // alist of ('symbol . ) +static Table *toplevel_environment; // Symbol => Value +static Table *name_to_symbol; static Value symbol_names = Qnil; // ("name0" "name1" ...) static Value SYM_ELSE, SYM_QUOTE, SYM_QUASIQUOTE, SYM_UNQUOTE, SYM_UNQUOTE_SPLICING, SYM_RARROW; @@ -123,7 +125,8 @@ static const volatile void *stack_base = NULL; #define INIT_STACK() void *basis; stack_base = &basis static const char *load_basedir = NULL; static Value call_stack = Qnil; -static Value source_data = Qnil; +static Table *filename_to_newline_pos; // Value => Value (Pair) +static Table *pair_to_function_location; // Value (Pair) => Location // // value_is_*: Type Checks @@ -337,7 +340,7 @@ static Value value_of_syntax(cfunc_t cfunc, int64_t arity) return sp; } -static Value value_of_closure(Value env, Value params, Value body) +static Value value_of_closure(Table *env, Value params, Value body) { Closure *f = obj_new(sizeof(Closure), TAG_CLOSURE); f->proc.arity = value_is_pair(params) ? length(params) : -1; @@ -838,7 +841,7 @@ static void expect_arity(int64_t expected, Value args) expected, actual); } -static Value apply_cfunc(Value *env, Value proc, Value args) +static Value apply_cfunc(Table *env, Value proc, Value args) { Value a[CFUNCARG_MAX]; CFunc *cf = CFUNC(proc); @@ -881,11 +884,6 @@ static Value apply_cfunc(Value *env, Value proc, Value args) #endif } -static inline void env_put(Value *env, Value name, Value val) -{ - *env = cons(cons(name, val), *env); -} - static Value append2(Value l1, Value l2) { if (l2 == Qnil) @@ -906,21 +904,32 @@ static Value append2(Value l1, Value l2) return ret; } -static Value eval_body(Value *env, Value body); +static Value eval_body(Table *env, Value body); //PTR -static Value apply_closure(Value *env, Value proc, Value args) +static Value apply_closure(Table *env, Value proc, Value args) { Closure *cl = CLOSURE(proc); int64_t arity = cl->proc.arity; - Value clenv = append2(cl->env, *env), params = cl->params; + const Table *oldp; + bool got_oldp = false; + if (cl->env != env) { + oldp = table_set_parent(env, cl->env); + got_oldp = true; + } + Table *clenv = table_inherit(env); + Value params = cl->params; if (arity == -1) - env_put(&clenv, params, args); + table_put(clenv, params, args); else { for (Value p = args; p != Qnil; p = cdr(p), params = cdr(params)) - env_put(&clenv, car(params), car(p)); + table_put(clenv, car(params), car(p)); } - return eval_body(&clenv, cl->body); + Value ret = eval_body(clenv, cl->body); + table_free(clenv); + if (got_oldp) + table_set_parent(env, oldp); + return ret; } static inline void expect_nonnull(const char *msg, Value l) @@ -966,7 +975,7 @@ static void apply_continuation(Value f, Value args) } // expects proc and args are evaluated if necessary -static Value apply(Value *env, Value proc, Value args) +static Value apply(Table *env, Value proc, Value args) { expect_arity(PROCEDURE(proc)->arity, args); switch (VALUE_TAG(proc)) { @@ -983,26 +992,23 @@ static Value apply(Value *env, Value proc, Value args) } // Note: Do not mistake this for "(define-syntax ...)" which related to macros -static void define_syntax(Value *env, const char *name, cfunc_t cfunc, int64_t arity) +static void define_syntax(Table *env, const char *name, cfunc_t cfunc, int64_t arity) { - env_put(env, value_of_symbol(name), value_of_syntax(cfunc, arity)); + table_put(env, value_of_symbol(name), value_of_syntax(cfunc, arity)); } -static void define_procedure(Value *env, const char *name, cfunc_t cfunc, int64_t arity) +static void define_procedure(Table *env, const char *name, cfunc_t cfunc, int64_t arity) { - env_put(env, value_of_symbol(name), value_of_cfunc(cfunc, arity)); + table_put(env, value_of_symbol(name), value_of_cfunc(cfunc, arity)); } static Value assq(Value key, Value l); -static Value lookup(Value env, Value name) +static Value lookup(const Table *env, Value name) { - Value found = assq(name, env); - if (found == Qfalse) + Value found = (Value) table_get(env, name); + if (found == 0) runtime_error("unbound variable: %s", value_to_string(name)); - return cdr(found); -} - static inline Value list4(Value w, Value x, Value y, Value z) { return cons(w, cons(x, list2(y, z))); @@ -1013,6 +1019,7 @@ static Value ast_new(Parser *p, Value syntax_list) { Value filename = value_of_symbol(p->filename); return list4(syntax_list, filename, p->function_locations, reverse(p->newline_pos)); + return found; } static Value parse_program(Parser *p) @@ -1059,9 +1066,9 @@ Value parse_string(const char *in) // // Evaluation // -static Value eval(Value *env, Value v); +static Value eval(Table *env, Value v); -static Value eval_body(Value *env, Value body) +static Value eval_body(Table *env, Value body) { if (body == Qnil) return Qnil; @@ -1071,7 +1078,7 @@ static Value eval_body(Value *env, Value body) return eval(env, car(p)); } -static Value map_eval(Value *env, Value l) +static Value map_eval(Table *env, Value l) { Value mapped = Qnil, last = Qnil; for (Value p = l; p != Qnil; p = cdr(p)) { @@ -1082,7 +1089,7 @@ static Value map_eval(Value *env, Value l) return mapped; } -static Value eval_apply(Value *env, Value symproc, Value args) +static Value eval_apply(Table *env, Value symproc, Value args) { Value proc = eval(env, symproc); expect_type("eval", TYPE_PROC, proc); @@ -1093,10 +1100,10 @@ static Value eval_apply(Value *env, Value symproc, Value args) return ret; } -static Value eval(Value *env, Value v) +static Value eval(Table *env, Value v) { if (value_is_symbol(v)) - return lookup(*env, v); + return lookup(env, v); if (v == Qnil || !value_is_pair(v)) return v; call_stack_push(v); @@ -1261,30 +1268,30 @@ static Value load_inner(const char *path) // 4.1. Primitive expression types // 4.1.2. Literal expressions -static Value syn_quote(UNUSED Value *env, Value datum) +static Value syn_quote(UNUSED Table *env, Value datum) { return datum; } // 4.1.4. Procedures -static Value lambda(Value *env, Value params, Value body) +static Value lambda(Table *env, Value params, Value body) { expect_type_or("lambda", TYPE_PAIR, TYPE_SYMBOL, params); expect_type("lambda", TYPE_PAIR, body); if (body == Qnil) runtime_error("lambda: one or more expressions needed in body"); - return value_of_closure(*env, params, body); + return value_of_closure(env, params, body); } //PTR -- proper tail recursion needed -static Value syn_lambda(Value *env, Value args) +static Value syn_lambda(Table *env, Value args) { return lambda(env, car(args), cdr(args)); } // 4.1.5. Conditionals //PTR -static Value syn_if(Value *env, Value args) +static Value syn_if(Table *env, Value args) { expect_arity_range("if", 2, 3, args); @@ -1298,16 +1305,15 @@ static Value syn_if(Value *env, Value args) } // 4.1.6. Assignments -static Value iset(Value *env, Value ident, Value val) +static Value iset(Table *env, Value ident, Value val) { - Value found = assq(ident, *env); - if (found == Qfalse) + bool found = table_set(env, ident, val); + if (!found) runtime_error("set!: unbound variable: %s", value_to_string(ident)); - PAIR(found)->cdr = val; return Qnil; } -static Value syn_set(Value *env, Value ident, Value expr) +static Value syn_set(Table *env, Value ident, Value expr) { expect_type("set!", TYPE_SYMBOL, ident); return iset(env, ident, eval(env, expr)); @@ -1321,7 +1327,7 @@ static inline void expect_null(const char *msg, Value l) runtime_error("%s: expected null?", msg); } -static Value cond_eval_recipient(Value *env, Value test, Value recipients) +static Value cond_eval_recipient(Table *env, Value test, Value recipients) { expect_nonnull("recipient in cond", recipients); Value recipient = eval(env, car(recipients)), rest = cdr(recipients); @@ -1331,7 +1337,7 @@ static Value cond_eval_recipient(Value *env, Value test, Value recipients) } //PTR -static Value syn_cond(Value *env, Value clauses) +static Value syn_cond(Table *env, Value clauses) { expect_arity_range("cond", 1, -1, clauses); @@ -1357,7 +1363,7 @@ static Value syn_cond(Value *env, Value clauses) static Value memq(Value key, Value l); //PTR -static Value syn_case(Value *env, Value args) +static Value syn_case(Table *env, Value args) { expect_arity_range("case", 2, -1, args); Value key = eval(env, car(args)), clauses = cdr(args); @@ -1374,7 +1380,7 @@ static Value syn_case(Value *env, Value args) } //PTR -static Value syn_and(Value *env, Value args) +static Value syn_and(Table *env, Value args) { if (args == Qnil) return Qtrue; @@ -1387,7 +1393,7 @@ static Value syn_and(Value *env, Value args) } //PTR -static Value syn_or(UNUSED Value *env, Value args) +static Value syn_or(UNUSED Table *env, Value args) { if (args == Qnil) return Qfalse; @@ -1417,12 +1423,12 @@ static Value transpose_2xn(Value ls) // 2 * n return list2(firsts, seconds); } -static Value define_variable(Value *env, Value ident, Value expr); +static Value define_variable(Table *env, Value ident, Value expr); -static Value let(Value *env, const char *func, Value bindings, Value body) +static Value let(Table *env, const char *func, Value bindings, Value body) { expect_type(func, TYPE_PAIR, bindings); - Value letenv = *env; + Table *letenv = table_inherit(env); for (Value p = bindings; p != Qnil; p = cdr(p)) { Value b = car(p); if (b == Qnil) @@ -1430,25 +1436,29 @@ static Value let(Value *env, const char *func, Value bindings, Value body) expect_type(func, TYPE_PAIR, b); Value ident = car(b), expr = cadr(b); expect_type(func, TYPE_SYMBOL, ident); - env_put(&letenv, ident, eval(env, expr)); + table_put(letenv, ident, eval(env, expr)); } - return eval_body(&letenv, body); + Value ret = eval_body(letenv, body); + table_free(letenv); + return ret; } -static Value named_let(Value *env, Value var, Value bindings, Value body) +static Value named_let(Table *env, Value var, Value bindings, Value body) { Value tr = transpose_2xn(bindings); Value params = car(tr), symargs = cadr(tr); Value proc = lambda(env, params, body); expect_type("named let", TYPE_PROC, proc); Value args = map_eval(env, symargs); - Value letenv = *env; - define_variable(&letenv, var, proc); - return apply(&letenv, proc, args); + Table *letenv = table_inherit(env); + define_variable(letenv, var, proc); + Value ret = apply(letenv, proc, args); + table_free(letenv); + return ret; } //PTR -static Value syn_let(Value *env, Value args) +static Value syn_let(Table *env, Value args) { expect_arity_range("let", 2, -1, args); Value bind_or_var = car(args), body = cdr(args); @@ -1458,50 +1468,53 @@ static Value syn_let(Value *env, Value args) } //PTR -static Value syn_let_star(Value *env, Value args) +static Value syn_let_star(Table *env, Value args) { expect_arity_range("let*", 2, -1, args); return let(env, "let*", car(args), cdr(args)); } //PTR -static Value syn_letrec(Value *env, Value args) +static Value syn_letrec(Table *env, Value args) { expect_arity_range("letrec", 2, -1, args); Value bindings = car(args); Value body = cdr(args); expect_type_twin("letrec", TYPE_PAIR, bindings, body); - Value letenv = *env; + Table *letenv = table_inherit(env); for (Value p = bindings; p != Qnil; p = cdr(p)) { Value b = car(p); expect_type("letrec", TYPE_PAIR, b); Value ident = car(b); expect_type("letrec", TYPE_SYMBOL, ident); - Value val = eval(&letenv, cadr(b)); - env_put(&letenv, ident, val); + Value val = eval(letenv, cadr(b)); + table_put(letenv, ident, val); } if (body == Qnil) runtime_error("letrec: one or more expressions needed in body"); - return eval_body(&letenv, body); + Value ret = eval_body(letenv, body); + table_free(letenv); + return ret; } // 4.2.3. Sequencing //PTR -static Value syn_begin(Value *env, Value body) +static Value syn_begin(Table *env, Value body) { return eval_body(env, body); } // 4.2.4. Iteration //PTR -static Value syn_do(Value *env, Value args) +static Value syn_do(Table *env, Value args) { expect_arity_range("do", 2, -1, args); Value bindings = car(args), tests = cadr(args), body = cddr(args); expect_type_twin("do", TYPE_PAIR, bindings, tests); - Value doenv = *env, steps = Qnil; + Table *doenv = table_inherit(env); + Value steps = Qnil; for (Value p = bindings; p != Qnil; p = cdr(p)) { Value b = car(p); expect_nonnull("do", b); @@ -1509,26 +1522,28 @@ static Value syn_do(Value *env, Value args) if (step != Qnil) steps = cons(cons(var, car(step)), steps); expect_type("do", TYPE_SYMBOL, var); - env_put(&doenv, var, eval(env, init)); // in the original env + table_put(doenv, var, eval(env, init)); // in the original env } Value test = car(tests), exprs = cdr(tests); - while (eval(&doenv, test) == Qfalse) { + while (eval(doenv, test) == Qfalse) { if (body != Qnil) - eval_body(&doenv, body); + eval_body(doenv, body); for (Value p = steps; p != Qnil; p = cdr(p)) { Value pstep = car(p); Value var = car(pstep), step = cdr(pstep); - Value val = eval(&doenv, step); - iset(&doenv, var, val); + Value val = eval(doenv, step); + iset(doenv, var, val); } } - return exprs == Qnil ? Qnil : eval_body(&doenv, exprs); + Value ret = exprs == Qnil ? Qnil : eval_body(doenv, exprs); + table_free(doenv); + return ret; } // 4.2.6. Quasiquotation -static Value qq_list(Value *env, Value datum, int64_t depth); +static Value qq_list(Table *env, Value datum, int64_t depth); -static Value qq(Value *env, Value datum, int64_t depth) +static Value qq(Table *env, Value datum, int64_t depth) { if (depth == 0) return eval(env, datum); @@ -1574,7 +1589,7 @@ static Value splice_at(Value last, Value to_splice) return last_pair(to_splice); } -static Value qq_list(Value *env, Value datum, int64_t depth) +static Value qq_list(Table *env, Value datum, int64_t depth) { Value ret = Qnil, last = Qnil; for (Value p = datum; p != Qnil; p = cdr(p)) { @@ -1594,43 +1609,42 @@ static Value qq_list(Value *env, Value datum, int64_t depth) return ret; } -static Value syn_quasiquote(Value *env, Value datum) +static Value syn_quasiquote(Table *env, Value datum) { return qq(env, datum, 1); } -static Value syn_unquote(UNUSED Value *env, UNUSED Value args) +static Value syn_unquote(UNUSED Table *env, UNUSED Value args) { runtime_error("unquote: applied out of quasiquote (`)"); } -static Value syn_unquote_splicing(UNUSED Value *env, UNUSED Value args) +static Value syn_unquote_splicing(UNUSED Table *env, UNUSED Value args) { runtime_error("unquote-splicing: applied out of quasiquote (`)"); } // 5.2. Definitions -static Value define_variable(Value *env, Value ident, Value expr) +static Value define_variable(Table *env, Value ident, Value expr) { expect_type("define", TYPE_SYMBOL, ident); - Value val = eval(env, expr), found; - if (env == &toplevel_environment && - (found = assq(ident, *env)) != Qfalse) { - PAIR(found)->cdr = val; // set! - } else - env_put(env, ident, val); // prepend new + Value val = eval(env, expr); + if (env == toplevel_environment) + table_set_or_put(env, ident, val); + else + table_put(env, ident, val); // prepend new return Qnil; } -static Value define_proc_internal(Value *env, Value heads, Value body) +static Value define_proc_internal(Table *env, Value heads, Value body) { Value ident = car(heads), params = cdr(heads); Value val = lambda(env, params, body); return define_variable(env, ident, val); } -static Value syn_define(Value *env, Value args) +static Value syn_define(Table *env, Value args) { if (args == Qnil) runtime_error("define: wrong number of arguments: expected 1+"); @@ -1654,7 +1668,7 @@ static inline bool eq(Value x, Value y) return x == y; } -static Value proc_eq(UNUSED Value *env, Value x, Value y) +static Value proc_eq(UNUSED Table *env, Value x, Value y) { return OF_BOOL(eq(x, y)); } @@ -1679,7 +1693,7 @@ static bool equal(Value x, Value y) } } -static Value proc_equal(UNUSED Value *env, Value x, Value y) +static Value proc_equal(UNUSED Table *env, Value x, Value y) { return OF_BOOL(equal(x, y)); } @@ -1691,12 +1705,12 @@ static int64_t value_get_int(const char *header, Value v) return value_to_int(v); } -static Value proc_integer_p(UNUSED Value *env, Value obj) +static Value proc_integer_p(UNUSED Table *env, Value obj) { return OF_BOOL(value_is_int(obj)); } -static Value proc_numeq(UNUSED Value *env, Value args) +static Value proc_numeq(UNUSED Table *env, Value args) { expect_arity_range("=", 2, -1, args); @@ -1709,7 +1723,7 @@ static Value proc_numeq(UNUSED Value *env, Value args) return Qtrue; } -static Value proc_lt(UNUSED Value *env, Value args) +static Value proc_lt(UNUSED Table *env, Value args) { expect_arity_range("<", 2, -1, args); @@ -1723,7 +1737,7 @@ static Value proc_lt(UNUSED Value *env, Value args) return Qtrue; } -static Value proc_gt(UNUSED Value *env, Value args) +static Value proc_gt(UNUSED Table *env, Value args) { expect_arity_range(">", 2, -1, args); @@ -1737,7 +1751,7 @@ static Value proc_gt(UNUSED Value *env, Value args) return Qtrue; } -static Value proc_le(UNUSED Value *env, Value args) +static Value proc_le(UNUSED Table *env, Value args) { expect_arity_range("<=", 2, -1, args); @@ -1751,7 +1765,7 @@ static Value proc_le(UNUSED Value *env, Value args) return Qtrue; } -static Value proc_ge(UNUSED Value *env, Value args) +static Value proc_ge(UNUSED Table *env, Value args) { expect_arity_range(">=", 2, -1, args); @@ -1765,32 +1779,32 @@ static Value proc_ge(UNUSED Value *env, Value args) return Qtrue; } -static Value proc_zero_p(UNUSED Value *env, Value obj) +static Value proc_zero_p(UNUSED Table *env, Value obj) { return OF_BOOL(value_is_int(obj) && value_to_int(obj) == 0); } -static Value proc_positive_p(UNUSED Value *env, Value obj) +static Value proc_positive_p(UNUSED Table *env, Value obj) { return OF_BOOL(value_is_int(obj) && value_to_int(obj) > 0); } -static Value proc_negative_p(UNUSED Value *env, Value obj) +static Value proc_negative_p(UNUSED Table *env, Value obj) { return OF_BOOL(value_is_int(obj) && value_to_int(obj) < 0); } -static Value proc_odd_p(UNUSED Value *env, Value obj) +static Value proc_odd_p(UNUSED Table *env, Value obj) { return OF_BOOL(value_is_int(obj) && (value_to_int(obj) % 2) != 0); } -static Value proc_even_p(UNUSED Value *env, Value obj) +static Value proc_even_p(UNUSED Table *env, Value obj) { return OF_BOOL(value_is_int(obj) && (value_to_int(obj) % 2) == 0); } -static Value proc_max(UNUSED Value *env, Value args) +static Value proc_max(UNUSED Table *env, Value args) { expect_arity_range("max", 1, -1, args); int64_t max = value_get_int("max", car(args)); @@ -1802,7 +1816,7 @@ static Value proc_max(UNUSED Value *env, Value args) return value_of_int(max); } -static Value proc_min(UNUSED Value *env, Value args) +static Value proc_min(UNUSED Table *env, Value args) { expect_arity_range("min", 1, -1, args); int64_t min = value_get_int("min", car(args)); @@ -1814,7 +1828,7 @@ static Value proc_min(UNUSED Value *env, Value args) return value_of_int(min); } -static Value proc_add(UNUSED Value *env, Value args) +static Value proc_add(UNUSED Table *env, Value args) { int64_t y = 0; for (Value p = args; p != Qnil; p = cdr(p)) @@ -1822,7 +1836,7 @@ static Value proc_add(UNUSED Value *env, Value args) return value_of_int(y); } -static Value proc_sub(UNUSED Value *env, Value args) +static Value proc_sub(UNUSED Table *env, Value args) { expect_arity_range("-", 1, -1, args); @@ -1837,7 +1851,7 @@ static Value proc_sub(UNUSED Value *env, Value args) return value_of_int(y); } -static Value proc_mul(UNUSED Value *env, Value args) +static Value proc_mul(UNUSED Table *env, Value args) { int64_t y = 1; for (Value p = args; p != Qnil; p = cdr(p)) @@ -1845,7 +1859,7 @@ static Value proc_mul(UNUSED Value *env, Value args) return value_of_int(y); } -static Value proc_div(UNUSED Value *env, Value args) +static Value proc_div(UNUSED Table *env, Value args) { expect_arity_range("/", 1, -1, args); @@ -1864,13 +1878,13 @@ static Value proc_div(UNUSED Value *env, Value args) return value_of_int(y); } -static Value proc_abs(UNUSED Value *env, Value x) +static Value proc_abs(UNUSED Table *env, Value x) { int64_t n = value_get_int("abs", x); return value_of_int(n < 0 ? -n : n); } -static Value proc_quotient(UNUSED Value *env, Value x, Value y) +static Value proc_quotient(UNUSED Table *env, Value x, Value y) { int64_t b = value_get_int("quotient", y); if (b == 0) @@ -1881,7 +1895,7 @@ static Value proc_quotient(UNUSED Value *env, Value x, Value y) } -static Value proc_remainder(UNUSED Value *env, Value x, Value y) +static Value proc_remainder(UNUSED Table *env, Value x, Value y) { int64_t b = value_get_int("remainder", y); if (b == 0) @@ -1891,7 +1905,7 @@ static Value proc_remainder(UNUSED Value *env, Value x, Value y) return value_of_int(c); } -static Value proc_modulo(UNUSED Value *env, Value x, Value y) +static Value proc_modulo(UNUSED Table *env, Value x, Value y) { int64_t b = value_get_int("modulo", y); if (b == 0) @@ -1918,7 +1932,7 @@ static int64_t expt(int64_t x, int64_t y) return z; } -static Value proc_expt(UNUSED Value *env, Value x, Value y) +static Value proc_expt(UNUSED Table *env, Value x, Value y) { int64_t a = value_get_int("expt", x); int64_t b = value_get_int("expt", y); @@ -1935,18 +1949,18 @@ static Value proc_expt(UNUSED Value *env, Value x, Value y) } // 6.3.1. Booleans -static Value proc_not(UNUSED Value *env, Value x) +static Value proc_not(UNUSED Table *env, Value x) { return OF_BOOL(x == Qfalse); } -static Value proc_boolean_p(UNUSED Value *env, Value x) +static Value proc_boolean_p(UNUSED Table *env, Value x) { return OF_BOOL(x == Qtrue || x == Qfalse); } // 6.3.2. Pairs and lists -static Value proc_pair_p(UNUSED Value *env, Value o) +static Value proc_pair_p(UNUSED Table *env, Value o) { return OF_BOOL(o != Qnil && value_is_pair(o)); } @@ -1969,29 +1983,29 @@ inline Value cdr(Value v) return PAIR(v)->cdr; } -static Value proc_cons(UNUSED Value *env, Value car, Value cdr) +static Value proc_cons(UNUSED Table *env, Value car, Value cdr) { return cons(car, cdr); } -static Value proc_car(UNUSED Value *env, Value pair) +static Value proc_car(UNUSED Table *env, Value pair) { expect_nonnull("car", pair); return car(pair); } -static Value proc_cdr(UNUSED Value *env, Value pair) +static Value proc_cdr(UNUSED Table *env, Value pair) { expect_nonnull("cdr", pair); return cdr(pair); } -static Value proc_null_p(UNUSED Value *env, Value list) +static Value proc_null_p(UNUSED Table *env, Value list) { return OF_BOOL(list == Qnil); } -static Value proc_list_p(UNUSED Value *env, Value list) +static Value proc_list_p(UNUSED Table *env, Value list) { for (Value p = list; p != Qnil; p = cdr(p)) { if (!value_is_pair(p)) @@ -2000,7 +2014,7 @@ static Value proc_list_p(UNUSED Value *env, Value list) return Qtrue; } -static Value proc_list(UNUSED Value *env, Value args) +static Value proc_list(UNUSED Table *env, Value args) { return args; } @@ -2013,7 +2027,7 @@ int64_t length(Value list) return len; } -static Value proc_length(UNUSED Value *env, Value list) +static Value proc_length(UNUSED Table *env, Value list) { expect_type("length", TYPE_PAIR, list); return value_of_int(length(list)); @@ -2032,7 +2046,7 @@ static Value dup_list(Value l, Value *plast) return dup; } -static Value proc_append(UNUSED Value *env, Value args) +static Value proc_append(UNUSED Table *env, Value args) { Value l = Qnil, last = Qnil; Value p, next; @@ -2059,7 +2073,7 @@ static Value reverse(Value l) return ret; } -static Value proc_reverse(UNUSED Value *env, Value list) +static Value proc_reverse(UNUSED Table *env, Value list) { expect_type("reverse", TYPE_PAIR, list); return reverse(list); @@ -2082,12 +2096,12 @@ static Value list_tail(const char *func, Value list, Value k) return p; } -static Value proc_list_tail(UNUSED Value *env, Value list, Value k) +static Value proc_list_tail(UNUSED Table *env, Value list, Value k) { return list_tail("list-tail", list, k); } -static Value proc_list_ref(UNUSED Value *env, Value list, Value k) +static Value proc_list_ref(UNUSED Table *env, Value list, Value k) { return car(list_tail("list-ref", list, k)); } @@ -2102,7 +2116,7 @@ static Value memq(Value key, Value l) return Qfalse; } -static Value proc_memq(UNUSED Value *env, Value obj, Value list) +static Value proc_memq(UNUSED Table *env, Value obj, Value list) { expect_type("memq", TYPE_PAIR, list); return memq(obj, list); @@ -2118,7 +2132,7 @@ static Value member(Value key, Value l) return Qfalse; } -static Value proc_member(UNUSED Value *env, Value obj, Value list) +static Value proc_member(UNUSED Table *env, Value obj, Value list) { expect_type("member", TYPE_PAIR, list); return member(obj, list); @@ -2134,7 +2148,7 @@ static Value assq(Value key, Value l) return Qfalse; } -static Value proc_assq(UNUSED Value *env, Value obj, Value alist) +static Value proc_assq(UNUSED Table *env, Value obj, Value alist) { expect_type("assq", TYPE_PAIR, alist); return assq(obj, alist); @@ -2150,38 +2164,38 @@ static Value assoc(Value key, Value l) return Qfalse; } -static Value proc_assoc(UNUSED Value *env, Value obj, Value alist) +static Value proc_assoc(UNUSED Table *env, Value obj, Value alist) { expect_type("assoc", TYPE_PAIR, alist); return assoc(obj, alist); } // 6.3.3. Symbols -static Value proc_symbol_p(UNUSED Value *env, Value obj) +static Value proc_symbol_p(UNUSED Table *env, Value obj) { return OF_BOOL(value_is_symbol(obj)); } // 6.3.5. Strings -static Value proc_string_p(UNUSED Value *env, Value obj) +static Value proc_string_p(UNUSED Table *env, Value obj) { return OF_BOOL(value_is_string(obj)); } -static Value proc_string_length(UNUSED Value *env, Value s) +static Value proc_string_length(UNUSED Table *env, Value s) { expect_type("string-length", TYPE_STR, s); return value_of_int(strlen(STRING(s)->body)); } -static Value proc_string_eq(UNUSED Value *env, Value s1, Value s2) +static Value proc_string_eq(UNUSED Table *env, Value s1, Value s2) { expect_type_twin("string=?", TYPE_STR, s1, s2); return OF_BOOL(strcmp(STRING(s1)->body, STRING(s2)->body) == 0); } // 6.4. Control features -static Value proc_procedure_p(UNUSED Value *env, Value o) +static Value proc_procedure_p(UNUSED Table *env, Value o) { return OF_BOOL(value_is_procedure(o)); } @@ -2199,7 +2213,7 @@ static Value apply_args(Value args) return append2(heads, rest); } -static Value proc_apply(Value *env, Value args) +static Value proc_apply(Table *env, Value args) { expect_arity_range("apply", 2, -1, args); @@ -2230,7 +2244,7 @@ static bool cars_cdrs(Value ls, Value *pcars, Value *pcdrs) return true; } -static Value proc_map(Value *env, Value args) +static Value proc_map(Table *env, Value args) { expect_arity_range("map", 2, -1, args); @@ -2249,7 +2263,7 @@ static Value proc_map(Value *env, Value args) return ret; } -static Value proc_for_each(Value *env, Value args) +static Value proc_for_each(Table *env, Value args) { expect_arity_range("for-each", 2, -1, args); @@ -2280,11 +2294,11 @@ static bool continuation_set(Value c) cont->shelter_len = stack_base - sp; cont->shelter = xmalloc(cont->shelter_len); memcpy(cont->shelter, (void *) sp, cont->shelter_len); - cont->call_stack = call_stack; + cont->call_stack = scary_dup(call_stack); return setjmp(cont->state) != 0; } -static Value proc_callcc(Value *env, Value proc) +static Value proc_callcc(Table *env, Value proc) { expect_type("call/cc", TYPE_PROC, proc); Value c = value_of_continuation(); @@ -2353,7 +2367,7 @@ void display(Value v) fdisplay(stdout, v); } -static Value proc_display(UNUSED Value *env, Value obj) +static Value proc_display(UNUSED Table *env, Value obj) { display(obj); return obj; @@ -2366,14 +2380,14 @@ static Value proc_newline(void) } // 6.6.4. System interface -static Value proc_load(UNUSED Value *env, Value path) +static Value proc_load(UNUSED Table *env, Value path) { // Current spec: path is always relative return load_inner(value_to_string(path)); } // Local Extensions -static Value proc_print(UNUSED Value *env, Value obj) +static Value proc_print(UNUSED Table *env, Value obj) { display(obj); puts(""); @@ -2390,7 +2404,7 @@ static Value proc_cputime(void) // in micro sec } #define DEF_CXR_BUILTIN(x, y) \ - static Value proc_c##x##y##r(UNUSED Value *env, Value v) \ + static Value proc_c##x##y##r(UNUSED Table *env, Value v) \ { \ expect_type("c" #x #y "r", TYPE_PAIR, v); \ return c##x##y##r(v); \ @@ -2408,8 +2422,11 @@ static void initialize(void) SYM_UNQUOTE = value_of_symbol("unquote"); SYM_UNQUOTE_SPLICING = value_of_symbol("unquote-splicing"); SYM_RARROW = value_of_symbol("=>"); + filename_to_newline_pos = table_new(); + pair_to_function_location = table_new(); - Value *e = &toplevel_environment; + toplevel_environment = table_new(); + Table *e = toplevel_environment; // 4. Expressions diff --git a/table.c b/table.c new file mode 100644 index 0000000..b3c9dee --- /dev/null +++ b/table.c @@ -0,0 +1,300 @@ +#include + +#include "utils.h" +#include "table.h" + +// List + +typedef struct List { + uint64_t key, value; + struct List *next; +} List; + +static List *list_new(uint64_t key, uint64_t value) +{ + List *l = xmalloc(sizeof(List)); + l->key = key; + l->value = value; + l->next = NULL; + return l; +} + +static void list_free(List *l, TableFreeFunc free_key) +{ + for (List *next; l != NULL; l = next) { + next = l->next; + (*free_key)((void *) l->key); + free(l); + } +} + +static List *list_reverse(const List *l) +{ + List *ret = NULL; + for (const List *p = l; p != NULL; p = p->next) { + List *e = list_new(p->key, p->value); + e->next = ret; + ret = e; + } + return ret; +} + +// Table + +enum { + TABLE_INIT_SIZE = 1, + TABLE_TOO_MANY_FACTOR = 3, +}; + +struct Table { + size_t size, body_size; + List **body; + TableHashFunc hash; + TableEqualFunc eq; + TableFreeFunc free_key; + const Table *parent; +}; + +static inline uint64_t direct_hash(uint64_t x) // simplified xorshift +{ + x ^= x << 7U; + x ^= x >> 9U; + return x; +} + +static inline bool direct_equal(uint64_t x, uint64_t y) +{ + return x == y; +} + +static inline void free_nop(ATTR(unused) void *p) { } + +Table *table_new(void) +{ + return table_new_full(direct_hash, direct_equal, free_nop); +} + +static uint64_t str_hash(uint64_t x) // modified djb2 +{ + uint64_t h = 30011; + for (const char *s = (char *) x; *s != '\0'; s++) + h = h * 61 + *s; + return h; +} + +static inline bool str_equal(uint64_t s, uint64_t t) +{ + return strcmp((const char *) s, (const char *) t) == 0; +} + +Table *table_new_str(void) +{ + return table_new_full(str_hash, str_equal, free); // expects strdup for keys +} + +#define PTR_OR(x, y) (((x) != NULL) ? (x) : (y)) + +Table *table_new_full(TableHashFunc hash, TableEqualFunc eq, TableFreeFunc free_key) +{ + Table *t = xmalloc(sizeof(Table)); + t->body = xcalloc(sizeof(List *), TABLE_INIT_SIZE); // set NULL + t->size = 0; + t->body_size = TABLE_INIT_SIZE; + for (size_t i = 0; i < t->body_size; i++) + t->body[i] = NULL; + t->hash = PTR_OR(hash, direct_hash); + t->eq = PTR_OR(eq, direct_equal); + t->free_key = PTR_OR(free_key, free_nop); + t->parent = NULL; + return t; +} + +Table *table_inherit(const Table *t) +{ + Table *u = table_new_full(t->hash, t->eq, t->free_key); + u->parent = t; + return u; +} + +void table_free(Table *t) +{ + if (t == NULL) + return; + for (size_t i = 0; i < t->body_size; i++) + list_free(t->body[i], t->free_key); + free(t->body); + free(t); +} + +#if 0 +static size_t list_length(List *l) +{ + size_t len = 0; + for (; l != NULL; l = l->next) + len++; + return len; +} + +void table_dump(const Table *t) +{ + fprintf(stderr, "size, body_size: %zu, %zu\n", t->size, t->body_size); + for (size_t i = 0; i < t->body_size; i++) { + fprintf(stderr, "body[%zu]: %zu\n", i, list_length(t->body[i])); + } +} +#endif + +static inline List **table_find_listp(const Table *t, uint64_t key) +{ + uint64_t i = (*t->hash)(key) % t->body_size; + return &t->body[i]; +} + +static inline bool table_too_many_elements(const Table *t) +{ + return t->size > t->body_size * TABLE_TOO_MANY_FACTOR; +} + +// "next" is twice or more larger than `curr` +static size_t next_prime(size_t curr) +{ + static const size_t prime_max = 823117; + static const size_t primes[] = { + 1, 2, 5, 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, + 12853, 25717, 51437, 102877, 205759, 411527, prime_max, + }; + static const size_t size = sizeof(primes) / sizeof(primes[0]); + + if (prime_max <= curr) + goto last; + for (size_t i = 0; i < size; i++) { + if (primes[i] > curr) + return primes[i]; + } + last: + return curr*2+1; +} + +static void table_resize(Table *t) +{ + const size_t old_body_size = t->body_size; + List *old_body[old_body_size]; + memcpy(old_body, t->body, sizeof(List *) * t->body_size); + t->body_size = next_prime(t->body_size); + t->body = xcalloc(sizeof(List *), t->body_size); // set NULL + for (size_t i = 0; i < old_body_size; i++) { + List *rev = list_reverse(old_body[i]); + for (List *l = rev, *next; l != NULL; l = next) { + next = l->next; + List **p = table_find_listp(t, l->key); + l->next = *p; // prepend + *p = l; + } + list_free(old_body[i], free_nop); + } +} + +static inline bool table_ensure_size(Table *t) +{ + if (table_too_many_elements(t)) { + table_resize(t); + return true; + } + return false; +} + +static void table_put_raw(Table *t, List **p, uint64_t key, uint64_t value) +{ + List *l = list_new(key, value); + l->next = *p; // prepend even if the same key exists + *p = l; + t->size++; +} + +// `value` can't be 0 +Table *table_put(Table *t, uint64_t key, uint64_t value) +{ + if (value == 0) + error("%s: got invalid value == 0", __func__); + table_ensure_size(t); + List **p = table_find_listp(t, key); + table_put_raw(t, p, key, value); + return t; +} + +static List *table_find_pair_raw(List **p, uint64_t key, TableEqualFunc eq) +{ + for (List *l = *p; l != NULL; l = l->next) { + if ((*eq)(l->key, key)) + return l; + } + return NULL; +} + +// chained! +static inline List *table_find_pair(const Table *t, uint64_t key) +{ + for (const Table *curr = t; curr != NULL; curr = curr->parent) { + List **p = table_find_listp(curr, key); + List *found = table_find_pair_raw(p, key, curr->eq); + if (found != NULL) + return found; + } + return NULL; +} + +uint64_t table_get(const Table *t, uint64_t key) +{ + const List *l = table_find_pair(t, key); + if (l == NULL) // not found + return 0; + return l->value; +} + +bool table_set(Table *t, uint64_t key, uint64_t value) +{ + if (value == 0) + error("%s: got invalid value == 0", __func__); + List *l = table_find_pair(t, key); + if (l == NULL) + return false; // did nothing + l->value = value; // overwrite! + return true; +} + +bool table_set_or_put(Table *t, uint64_t key, uint64_t value) +{ + if (value == 0) + error("%s: got invalid value == 0", __func__); + List *l = table_find_pair(t, key); + if (l == NULL) { // to put + table_put(t, key, value); + return false; // not overwritten + } + l->value = value; // overwrite! + return true; +} + +Table *table_merge(Table *dst, const Table *src) +{ + const size_t size = src->body_size; + for (size_t i = 0; i < size; i++) { + List *rev = list_reverse(src->body[i]); + for (List *l = rev; l != NULL; l = l->next) + table_set_or_put(dst, l->key, l->value); + list_free(rev, free_nop); + } + return dst; +} + +const Table *table_get_parent(const Table *t) +{ + return t->parent; +} + +const Table *table_set_parent(Table *t, const Table *parent) +{ + const Table *oldp = t->parent; + t->parent = parent; + return oldp; +} diff --git a/table.h b/table.h new file mode 100644 index 0000000..3b1c8e4 --- /dev/null +++ b/table.h @@ -0,0 +1,25 @@ +#ifndef TABLE_H +#define TABLE_H + +#include +#include + +typedef struct Table Table; +typedef bool (*TableEqualFunc)(uint64_t x, uint64_t y); +typedef uint64_t (*TableHashFunc)(uint64_t x); +typedef void (*TableFreeFunc)(void *p); + +Table *table_new(void); +Table *table_new_str(void); +Table *table_new_full(TableHashFunc hash, TableEqualFunc eq, TableFreeFunc free_key); +Table *table_inherit(const Table *t); +void table_free(Table *t); +Table *table_put(Table *t, uint64_t key, uint64_t val); // `val` can't be 0 +uint64_t table_get(const Table *t, uint64_t key); +bool table_set_or_put(Table *t, uint64_t key, uint64_t val); +bool table_set(Table *t, uint64_t key, uint64_t val); // set only if found +Table *table_merge(Table *dst, const Table *src); +const Table *table_get_parent(const Table *t); +const Table *table_set_parent(Table *t, const Table *parent); + +#endif