Mercurial > lbo > hg > ylisp
changeset 184:263256062455
Make project compile again. Implement basic ref logic
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Sun, 29 Sep 2019 14:14:04 +0200 |
parents | 37b01b114b50 |
children | db386ec98dc3 |
files | doc/syntax.md src/bin/ylisp.c src/built-ins.c src/built-ins.h src/built-ins_test.c src/eval.c src/eval.h src/eval_test.c src/expr.c src/expr.h src/expr_test.c src/preprocess_test.c src/sizes_test.c src/types.h src/value.c src/value.h src/value_test.c |
diffstat | 17 files changed, 246 insertions(+), 556 deletions(-) [+] |
line wrap: on
line diff
--- a/doc/syntax.md Sat Sep 28 19:13:26 2019 +0200 +++ b/doc/syntax.md Sun Sep 29 14:14:04 2019 +0200 @@ -10,7 +10,7 @@ of them has a `ybuiltin_fn` associated with it that can be called with a pointer to the call stack, executing the defined logic. -#### `defn`: Define function +#### `defn`: Define closure ``` (defn name (arg arg ...) (body body) (body) ...)
--- a/src/bin/ylisp.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/bin/ylisp.c Sun Sep 29 14:14:04 2019 +0200 @@ -6,7 +6,7 @@ #include <src/eval.h> yexpr_t run(FILE* input) { - return result; + return yexpr_new(); } int main(int argc, char** argv) {
--- a/src/built-ins.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/built-ins.c Sun Sep 29 14:14:04 2019 +0200 @@ -8,6 +8,7 @@ // TODO: Write standard library! + static FILE* output_handle = NULL; void ybuiltin_set_output(FILE* out) { output_handle = out; } @@ -17,7 +18,7 @@ * * The functions return a yexpr_t of which the caller assumes ownership. */ -typedef yexpr_t (*ybuiltin_fn)(yeval_state_t* state); +typedef yexpr_t (*ybuiltin_fn)(yeval_t* state, yexpr_t* self); struct ybuiltin_id_mapping { const char* id; @@ -42,8 +43,6 @@ {"push", YBUILTIN_PUSH}, {"raise", YBUILTIN_RAISE}, {"recover", YBUILTIN_RECOVER}, - - {"_mkclsr", YBUILTIN_INTERNAL_MKCLSR}, }; static const char* YBUILTIN_ENUM_STR[] = { @@ -122,8 +121,7 @@ /// Returns a YEXPR_UNDEF expression. TODO: Automatically add symbolic reference /// "undef". -yexpr_t ybuiltin_fn_undef(yeval_state_t* state) { - yvec_pop(&state->call_stack, NULL); +yexpr_t ybuiltin_fn_undef(yeval_t* state, yexpr_t* self) { return yexpr_new(); } @@ -136,14 +134,13 @@ /// Returns a list of each of the last expressions in the for loop. If the last /// expression is an undefined value (`(undef)`), the value is not added to the /// resulting list. -yexpr_t ybuiltin_fn_for(yeval_state_t* state) { +yexpr_t ybuiltin_fn_for(yeval_t* state, yexpr_t* for_exprp) { #define _pattern "(for ref (item [...]) [exprs...] expr)" - yexpr_t for_expr; + yexpr_t for_expr = *for_exprp; yvec_t* for_list = &for_expr.value.list; size_t items = 0, counter = 0; yvec_t result; - if (!yvec_pop(&state->call_stack, &for_expr)) NOTENOUGHFAIL(FOR); if (for_expr.typ != YEXPR_LIST) BADEXPRFAIL(FOR, for_expr); if (for_expr.value.list.len < 4) TYPEFAIL(FOR, for_expr, _pattern); @@ -165,11 +162,11 @@ // nested loop: first items, then exprs for (size_t item = 0; item < for_items_l->len; item++) { yexpr_t* expr = YVEC_AT(for_items_l, item, yexpr_t); - yexpr_t evald = yeval(state, expr, false); - yvalue_t value = {.typ = YVALUE_EXPR, .value.expr = evald}; + yexpr_t evald = yeval(state, expr); + + // TODO: Proper binding // For each item, bind reference and execute body. - yvalue_set(for_ref->value.ref, &value); yexpr_t exprs_result = yeval_list_return_last(state, for_list, counter); // Make deep copy of result @@ -179,7 +176,6 @@ YVEC_PUSH(&result, &expr_result_cp); yexpr_destroy(&exprs_result); - yvalue_destroy(&value); } yexpr_t result_expr = yexpr_new(); @@ -194,18 +190,19 @@ /// /// Returns an UNDEF expression by default, or an EXCEPTION if something went /// wrong. -yexpr_t ybuiltin_fn_let(yeval_state_t* state) { +yexpr_t ybuiltin_fn_let(yeval_t* state, yexpr_t* self) { #define _pattern "(let ref [expr ...] expr)" + // TODO: implement + assert(false); #undef _pattern } /// Expects a list expression starting with built-in "+" on the stack. It is not /// modified. Returns a number expression or an exception. -yexpr_t ybuiltin_fn_plus(yeval_state_t* state) { +yexpr_t ybuiltin_fn_plus(yeval_t* state, yexpr_t* self) { #define _pattern "(+ numeric-expr numeric-expr [numeric-expr ...])" - yexpr_t plus; + yexpr_t plus = *self; - if (!yvec_pop(&state->call_stack, &plus)) NOTENOUGHFAIL(PLUS); if (plus.typ != YEXPR_LIST) BADEXPRFAIL(PLUS, plus); if (plus.value.list.len < 3) BADEXPRFAIL(PLUS, plus); @@ -214,7 +211,7 @@ long long result = 0; for (size_t i = 1; i < plus_list->len; i++) { yexpr_t* expr = YVEC_AT(plus_list, i, yexpr_t); - yexpr_t evald = yeval(state, expr, false); + yexpr_t evald = yeval(state, expr); if (evald.typ != YEXPR_NUMBER) TYPEFAIL(PLUS, evald, _pattern); result += evald.value.n; yexpr_destroy(&evald); @@ -229,11 +226,10 @@ /// Expects a list expression starting with built-in "-" on the stack. It is not /// modified. Returns a number expression or an exception. -yexpr_t ybuiltin_fn_minus(yeval_state_t* state) { +yexpr_t ybuiltin_fn_minus(yeval_t* state, yexpr_t* self) { #define _pattern "(- numeric-expr numeric-expr [numeric-expr ...])" - yexpr_t minus; + yexpr_t minus = *self; - if (!yvec_pop(&state->call_stack, &minus)) NOTENOUGHFAIL(MINUS); if (minus.typ != YEXPR_LIST) BADEXPRFAIL(MINUS, minus); if (minus.value.list.len < 3) BADEXPRFAIL(MINUS, minus); @@ -243,7 +239,7 @@ bool first = true; for (size_t i = 1; i < minus_list->len; i++) { yexpr_t* expr = YVEC_AT(minus_list, i, yexpr_t); - yexpr_t evald = yeval(state, expr, false); + yexpr_t evald = yeval(state, expr); if (evald.typ != YEXPR_NUMBER) TYPEFAIL(MINUS, evald, _pattern); if (first) { result += evald.value.n; @@ -263,11 +259,10 @@ /// Expects a list expression starting with built-in "*" on the stack. It is not /// modified. Returns a number expression or an exception. -yexpr_t ybuiltin_fn_times(yeval_state_t* state) { +yexpr_t ybuiltin_fn_times(yeval_t* state, yexpr_t* self) { #define _pattern "(* numeric-expr numeric-expr [numeric-expr ...])" - yexpr_t times; + yexpr_t times = *self; - if (!yvec_pop(&state->call_stack, ×)) NOTENOUGHFAIL(MULT); if (times.typ != YEXPR_LIST) BADEXPRFAIL(MULT, times); if (times.value.list.len < 3) BADEXPRFAIL(MULT, times); @@ -276,7 +271,7 @@ long long result = 1; for (size_t i = 1; i < times_list->len; i++) { yexpr_t* expr = YVEC_AT(times_list, i, yexpr_t); - yexpr_t evald = yeval(state, expr, false); + yexpr_t evald = yeval(state, expr); if (evald.typ != YEXPR_NUMBER) TYPEFAIL(MULT, evald, _pattern); result *= evald.value.n; yexpr_destroy(&evald); @@ -289,12 +284,11 @@ #undef _pattern } -yexpr_t ybuiltin_fn_if(yeval_state_t* state) { +yexpr_t ybuiltin_fn_if(yeval_t* state, yexpr_t* self) { #define _pattern \ "(if condition then-expr/(then-exprs) [else-expr/(else-exprs)])" yexpr_t if_expr; - if (!yvec_pop(&state->call_stack, &if_expr)) NOTENOUGHFAIL(IF); if (if_expr.typ != YEXPR_LIST) BADEXPRFAIL(IF, if_expr); if (if_expr.value.list.len < 3) BADEXPRFAIL(IF, if_expr); @@ -304,7 +298,7 @@ if (if_expr.value.list.len > 3) else_expr = YVEC_AT(&if_expr.value.list, 3, yexpr_t); - yexpr_t cond_result = yeval(state, condition, false); + yexpr_t cond_result = yeval(state, condition); bool result = false; if (cond_result.typ == YEXPR_ATOM && yatom_get_or_add("true") == cond_result.value.atom) @@ -315,11 +309,11 @@ if (result) { if (then_expr->typ == YEXPR_LIST) return yeval_list_return_last(state, &then_expr->value.list, 0); - return yeval(state, then_expr, false); + return yeval(state, then_expr); } else if (else_expr != NULL) { if (else_expr->typ == YEXPR_LIST) return yeval_list_return_last(state, &else_expr->value.list, 0); - return yeval(state, else_expr, false); + return yeval(state, else_expr); } else { return yexpr_new(); } @@ -327,18 +321,17 @@ #undef _pattern } -yexpr_t ybuiltin_fn_print(yeval_state_t* state) { +yexpr_t ybuiltin_fn_print(yeval_t* state, yexpr_t* self) { #define _pattern "(print expr [expr...])" - yexpr_t print; + yexpr_t print = *self; FILE* out = output_handle == NULL ? stdout : output_handle; - if (!yvec_pop(&state->call_stack, &print)) NOTENOUGHFAIL(PRINT); if (print.typ != YEXPR_LIST) BADEXPRFAIL(PRINT, print); if (print.value.list.len < 1) BADEXPRFAIL(PRINT, print); for (size_t i = 1; i < print.value.list.len; i++) { yexpr_t evald = - yeval(state, YVEC_AT(&print.value.list, i, yexpr_t), false); + yeval(state, YVEC_AT(&print.value.list, i, yexpr_t)); if (evald.typ == YEXPR_STRING) { fputs(ystr_str(&evald.value.str), out); goto cleanup_continue; @@ -357,18 +350,17 @@ #undef _pattern } -yexpr_t ybuiltin_fn_eq(yeval_state_t* state) { +yexpr_t ybuiltin_fn_eq(yeval_t* state, yexpr_t* self) { #define _pattern "(== expr expr [expr...])" - yexpr_t eq; + yexpr_t eq = *self; - if (!yvec_pop(&state->call_stack, &eq)) NOTENOUGHFAIL(EQ); if (eq.typ != YEXPR_LIST) BADEXPRFAIL(EQ, eq); if (eq.value.list.len < 3) BADEXPRFAIL(EQ, eq); yexpr_t* first = YVEC_AT(&eq.value.list, 1, yexpr_t); yexpr_t* second = YVEC_AT(&eq.value.list, 2, yexpr_t); - yexpr_t firstevald = yeval(state, first, false); - yexpr_t secondevald = yeval(state, second, false); + yexpr_t firstevald = yeval(state, first); + yexpr_t secondevald = yeval(state, second); bool equal = yexpr_equal(&firstevald, &secondevald); yexpr_destroy(&firstevald); yexpr_destroy(&secondevald); @@ -382,16 +374,6 @@ #undef _pattern } -yexpr_t ybuiltin_fn_mkclsr(yeval_state_t* state) { -#define _pattern \ - "(_mkclsr function-ref captured1 captured2 ...)" // Sets function-ref to a - // yexpr_t of type - // YEXPR_FUNC, with - // captured values. - // TODO: continue implementation -#undef _pattern -} - /** * @brief Table of built-in functions, indexed by YBUILTIN_TYPE. * @@ -413,16 +395,15 @@ NULL, NULL, NULL, - NULL, - ybuiltin_fn_mkclsr}; + NULL}; const char* ybuiltin_name(YBUILTIN_TYPE t) { assert(t * sizeof(const char*) < sizeof(YBUILTIN_ENUM_STR)); return YBUILTIN_ENUM_STR[t]; } -yexpr_t ybuiltin_run(YBUILTIN_TYPE t, yeval_state_t* state) { +yexpr_t ybuiltin_run(YBUILTIN_TYPE t, yeval_t* state, yexpr_t* expr) { assert((t * sizeof(ybuiltin_fn)) < sizeof(YBUILTIN_FNS)); - return YBUILTIN_FNS[t](state); + return YBUILTIN_FNS[t](state, expr); }
--- a/src/built-ins.h Sat Sep 28 19:13:26 2019 +0200 +++ b/src/built-ins.h Sun Sep 29 14:14:04 2019 +0200 @@ -26,7 +26,7 @@ /** * @brief Run a built-in function. The caller assumes ownership of the returned value. */ -yexpr_t ybuiltin_run(YBUILTIN_TYPE t, yeval_state_t* state); +yexpr_t ybuiltin_run(YBUILTIN_TYPE t, yeval_t* state, yexpr_t* self); /** * @brief Set standard output for output built-ins.
--- a/src/built-ins_test.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/built-ins_test.c Sun Sep 29 14:14:04 2019 +0200 @@ -19,7 +19,7 @@ assert(!ybuiltin_translate(&expr)); assert(YEXPR_ATOM == expr.typ); - yexpr_set_ref(&expr, yref_new()); + yexpr_set_ref(&expr, yref_new_anon()); assert(!ybuiltin_translate(&expr)); assert(YEXPR_REF == expr.typ); @@ -34,322 +34,9 @@ assert(YBUILTIN_LET == expr.value.builtin); } -void test_builtin_for(void) { - fprintf(stderr, "test_builtin_for ============\n"); - - ystr_t input = ystr_new("(for loopvar (1 2 3) loopvar)"), - error = ystr_new(NULL); - yexpr_t program = yexpr_new(); - assert(yparse_str(&input, &program, &error)); - yexpr_t* for_expr = YVEC_AT(&program.value.list, 0, yexpr_t); - assert(for_expr->typ == YEXPR_LIST); - - yexpr_debug(&program); - ypreprocess_resolve_builtins(&program); - ypreprocess_refs(&program); - yexpr_debug(&program); - - yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)}; - state.locals = YVEC_NEW(NULL, 0, yeval_local_def_t); - YVEC_PUSH(&state.call_stack, for_expr); - - yexpr_t result = ybuiltin_run(YBUILTIN_FOR, &state); - assert(result.typ == YEXPR_LIST); - - yexpr_debug(&result); - - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - yexpr_destroy(&program); - yexpr_destroy(&result); - ystr_destroy(&input); -} - -void test_builtin_for_complex(void) { - fprintf(stderr, "test_builtin_for_complex ============\n"); - - ystr_t input = ystr_new("(for loopvar (1 2 3) (+ loopvar 1 2))"), - error = ystr_new(NULL); - yexpr_t program = yexpr_new(); - assert(yparse_str(&input, &program, &error)); - yexpr_t* for_expr = YVEC_AT(&program.value.list, 0, yexpr_t); - assert(for_expr->typ == YEXPR_LIST); - - ypreprocess(&program); - yexpr_debug(for_expr); - - yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)}; - state.locals = YVEC_NEW(NULL, 0, yeval_local_def_t); - YVEC_PUSH(&state.call_stack, for_expr); - - yexpr_t result = ybuiltin_run(YBUILTIN_FOR, &state); - assert(result.typ == YEXPR_LIST); - - // (1 2 3) should each be increased by 3. - yexpr_debug(&result); - assert(4 == YVEC_AT(&result.value.list, 0, yexpr_t)->value.n); - - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - yexpr_destroy(&program); - yexpr_destroy(&result); - ystr_destroy(&input); -} - -void test_builtin_for_noresult(void) { - fprintf(stderr, "test_builtin_for_noresult ============\n"); - - ystr_t input = ystr_new("(for loopvar (1 2 3) (undef))"), - error = ystr_new(NULL); - yexpr_t program = yexpr_new(); - assert(yparse_str(&input, &program, &error)); - yexpr_t* for_expr = YVEC_AT(&program.value.list, 0, yexpr_t); - assert(for_expr->typ == YEXPR_LIST); - - ypreprocess_resolve_builtins(&program); - ypreprocess_refs(&program); - yexpr_debug(for_expr); - - yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)}; - state.locals = YVEC_NEW(NULL, 0, yeval_local_def_t); - YVEC_PUSH(&state.call_stack, for_expr); - - yexpr_t result = ybuiltin_run(YBUILTIN_FOR, &state); - assert(result.typ == YEXPR_LIST); - assert(result.value.list.len == 0); - - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - yexpr_destroy(&program); - ystr_destroy(&input); - yexpr_destroy(&result); -} - -void test_builtin_let(void) { - fprintf(stderr, "test_builtin_let ============\n"); - - yexpr_t ref_expr = yexpr_new(); - yexpr_t let_expr = yexpr_new(); - yref_t ref = yref_new(); - yexpr_t val_expr = yexpr_new(); - yexpr_t let_call = yexpr_new(); - - // (let <ref> 'my-atom) - yexpr_set_ref(&ref_expr, ref); - yexpr_set_builtin(&let_expr, YBUILTIN_LET); - yexpr_set_atom(&val_expr, yatom_get_or_add("my-atom")); - - yvec_t let_args; - YVEC_INIT(&let_args, 4, yexpr_t); - YVEC_PUSH(&let_args, &let_expr); - YVEC_PUSH(&let_args, &ref_expr); - YVEC_PUSH(&let_args, &val_expr); - - yexpr_set_list(&let_call, let_args); - - // NOTE: The expressions should be copied, but for this test we can store - // them directly. - yeval_state_t state; - yvec_t* stack = &state.call_stack; - YVEC_INIT(stack, 4, yexpr_t); - YVEC_PUSH(stack, &let_call); - - yexpr_t result = ybuiltin_run(YBUILTIN_LET, &state); - yexpr_debug(&result); - assert(result.typ == YEXPR_UNDEF); - assert(stack->len == 0); - - yvalue_t* stored = yvalue_get(ref); - assert(stored->typ == YVALUE_EXPR); - assert(stored->value.expr.typ == YEXPR_ATOM); - assert(stored->value.expr.value.atom == val_expr.value.atom); - - // Type mismatch exception. - yexpr_set_number(YVEC_AT(&let_call.value.list, 1, yexpr_t), 124); - YVEC_PUSH(stack, &let_call); - result = ybuiltin_run(YBUILTIN_LET, &state); - yexpr_debug(&result); - assert(result.typ == YEXPR_EXCEPTION); - assert(stack->len == 0); - yexpr_destroy(&result); - - // Not enough arguments on stack. - result = ybuiltin_run(YBUILTIN_LET, &state); - assert(result.typ == YEXPR_EXCEPTION); - fputs(ystr_str(&result.value.str), stderr); - fputs("\n", stderr); - assert(0 == - ystr_cmp_str( - &result.value.str, - "BUILTIN:LET: BUG: not enough arguments on call stack: NULL")); - - yexpr_destroy(&result); - yexpr_destroy(&ref_expr); - yexpr_destroy(&val_expr); - yexpr_destroy(&let_call); - yvec_destroy(stack); -}; - -void test_builtin_let_parsed(void) { - fprintf(stderr, "test_builtin_let_parsed ============\n"); - - ystr_t input = ystr_new("(let my-var (1 2 3))"), error = ystr_new(NULL); - yexpr_t program = yexpr_new(), *let_expr; - assert(yparse_str(&input, &program, &error)); - ypreprocess_resolve_builtins(&program); - - let_expr = YVEC_AT(&program.value.list, 0, yexpr_t); - - yeval_state_t state; - YVEC_INIT(&state.call_stack, 4, yexpr_t); - state.locals = YVEC_NEW(NULL, 0, yeval_local_def_t); - YVEC_PUSH(&state.call_stack, let_expr); - yexpr_t result = ybuiltin_run(YBUILTIN_LET, &state); - assert(result.typ == YEXPR_UNDEF); - assert(state.call_stack.len == 0); - - // Retrieve stored value. - yref_t my_var_ref = yref_new_c("my-var"); - yvalue_t* stored = yvalue_get(my_var_ref); - assert(stored->typ == YVALUE_EXPR); - assert(stored->value.expr.typ == YEXPR_LIST); - assert(stored->value.expr.value.list.len == 3); - assert(YVEC_AT(&stored->value.expr.value.list, 1, yexpr_t)->value.n == 2); - - yexpr_destroy(&result); - yexpr_destroy(&program); - yref_destroy(&my_var_ref); - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - ystr_destroy(&input); - ystr_destroy(&error); -} - -void test_builtin_let_recall(void) { - fprintf(stderr, "test_builtin_let_recall ============\n"); - - ystr_t input = ystr_new( - "(let my-var ('a 'b) \"cd\")" - "(my-var)"); - ystr_t error = ystr_new(NULL); - yexpr_t program = yexpr_new(); - assert(yparse_str(&input, &program, &error)); - assert(YEXPR_LIST == program.typ && 2 == program.value.list.len); - - ypreprocess_resolve_builtins(&program); - - yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)}; - state.locals = YVEC_NEW(NULL, 0, yeval_local_def_t); - // Evaluate whole program - yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0); - - // Check that result is "cd" - assert(YEXPR_LIST == result.typ && 1 == result.value.list.len); - yexpr_t* inner = YVEC_AT(&result.value.list, 0, yexpr_t); - assert(YEXPR_STRING == inner->typ); - yexpr_debug(inner); - assert(0 == ystr_cmp_str(&inner->value.str, "cd")); - - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - yexpr_destroy(&result); - ystr_destroy(&error); - ystr_destroy(&input); - yexpr_destroy(&program); -} - -void test_builtin_eq(void) { - fprintf(stderr, "test_builtin_eq ============\n"); - - ystr_t input = ystr_new( - "(let var-a 1)" - "(let var-b 'atom)" - "(let var-c \"string\")" - "((== var-a 1) (== 1 var-a) (== var-a var-a) (== for for) (== (1 2 3) " - "(var-a 2 3)) (== var-b 'atom) (== " - "'atom2 'atom2) (== " - "var-c \"string\"))"); - ystr_t error = ystr_new(NULL); - yexpr_t program = yexpr_new(); - assert(yparse_str(&input, &program, &error)); - - yexpr_debug(&program); - ypreprocess(&program); - yexpr_debug(&program); - - yeval_state_t state; - YVEC_INIT(&state.call_stack, 4, yexpr_t); - state.locals = YVEC_NEW(NULL, 0, yeval_local_def_t); - yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0); - assert(result.typ == YEXPR_LIST); - yexpr_debug(&result); - for (size_t i = 0; i < result.value.list.len; i++) { - yexpr_t* elem = YVEC_AT(&result.value.list, i, yexpr_t); - assert(YEXPR_ATOM == elem->typ && - yatom_get_or_add("true") == elem->value.atom); - } - - yexpr_destroy(&result); - yexpr_destroy(&program); - ystr_destroy(&input); - ystr_destroy(&error); - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); -} - -void test_builtin_print(void) { - fprintf(stderr, "test_builtin_print ============\n"); - - char buf[512]; - memset(buf, 0, 512); - FILE* output = fmemopen(buf, 512, "w"); - - ybuiltin_set_output(output); - - ystr_t input = ystr_new( - "(let a \"hello\")" - "(let b 'world)" - "(print 1 a b)"); - ystr_t expected = ystr_new("1 hello world"); - - ystr_t error = ystr_new(NULL); - yexpr_t program = yexpr_new(); - assert(yparse_str(&input, &program, &error)); - - ypreprocess(&program); - - yeval_state_t state; - YVEC_INIT(&state.call_stack, 4, yexpr_t); - state.locals = YVEC_NEW(NULL, 0, yeval_local_def_t); - - yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0); - assert(result.typ == YEXPR_UNDEF); - - fclose(output); - - assert(0 == ystr_cmp_str(&expected, buf)); - - ystr_destroy(&expected); - yexpr_destroy(&result); - yexpr_destroy(&program); - ystr_destroy(&input); - ystr_destroy(&error); - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); -} int main(void) { test_builtin_translate(); - test_builtin_for(); - test_builtin_for_complex(); - test_builtin_for_noresult(); - test_builtin_let(); - test_builtin_let_parsed(); - test_builtin_let_recall(); - test_builtin_eq(); - test_builtin_print(); - - yvalue_free_all(); yatom_free_all(); return 0; }
--- a/src/eval.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/eval.c Sun Sep 29 14:14:04 2019 +0200 @@ -5,3 +5,14 @@ #include "expr.h" #include "value.h" +yexpr_t yeval(yeval_t* state, yexpr_t* expr) { + return yexpr_new(); +} + +yexpr_t yeval_noresult(yeval_t* state, yexpr_t* expr) { + return yexpr_new(); +} + +yexpr_t yeval_list_return_last(yeval_t* state, yvec_t* list, size_t start) { + return yexpr_new(); +}
--- a/src/eval.h Sat Sep 28 19:13:26 2019 +0200 +++ b/src/eval.h Sun Sep 29 14:14:04 2019 +0200 @@ -11,6 +11,38 @@ * @{ */ +/// A mapping from reference to value. +typedef struct { + yref_t ref; + yexpr_t val; +} yeval_map_t; + +/// Evaluation state. +typedef struct { + yvec_t /* <yeval_map_t> */ vars; +} yeval_t; + +/** + * @brief Create a valid initial evaluation state. + */ +yeval_t yeval_new_state(void); + +/** + * Evaluate an expression yielding a result expression. + */ +yexpr_t yeval(yeval_t* state, yexpr_t* expr); + +/** + * @brief Evaluate an expression purely for side effects. Only exceptions are + * returned as such, if no exception occurred, a YEXPR_UNDEF value is returned. + */ +yexpr_t yeval_noresult(yeval_t* state, yexpr_t* expr); + +/** + * @brief Evaluate expressions in `list` starting at `start` and return the + * result of the last expression. + */ +yexpr_t yeval_list_return_last(yeval_t* state, yvec_t* list, size_t start); /** * @}
--- a/src/eval_test.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/eval_test.c Sun Sep 29 14:14:04 2019 +0200 @@ -8,7 +8,6 @@ int main(void) { - yvalue_free_all(); yatom_free_all(); return 0; }
--- a/src/expr.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/expr.c Sun Sep 29 14:14:04 2019 +0200 @@ -46,10 +46,13 @@ #undef list break; case YEXPR_FUNC: - for (size_t i = 0; i < v->value.closure->captured.len; i++) - yexpr_destroy(YVEC_AT(&v->value.closure->captured, i, yexpr_t)); + for (size_t i = 0; i < v->value.closure->captured.len; i++) { + yref_destroy(&YVEC_AT(&v->value.closure->captured, i, yeval_map_t)->ref); + yexpr_destroy(&YVEC_AT(&v->value.closure->captured, i, yeval_map_t)->val); + } + for (size_t i = 0; i < v->value.closure->args.len; i++) + yref_destroy(YVEC_AT(&v->value.closure->args, i, yref_t)); yvec_destroy(&v->value.closure->captured); - yref_destroy(&v->value.closure->id); free(v->value.closure); break; default: @@ -82,15 +85,7 @@ copy.value.ref = yref_clone(&orig->value.ref); break; case YEXPR_FUNC: - copy.value.closure = malloc(sizeof(yclosure_t)); - copy.value.closure->captured = - yvec_clone(&orig->value.closure->captured); - yvec_t* captured = ©.value.closure->captured; - for (size_t i = 0; i < captured->len; i++) { - *YVEC_AT(captured, i, yexpr_t) = yexpr_copy( - YVEC_AT(&orig->value.closure->captured, i, yexpr_t)); - } - copy.value.closure->id = yref_clone(&orig->value.closure->id); + copy.value.closure = orig->value.closure; break; case YEXPR_LIST: // Recursively copy list... @@ -162,13 +157,10 @@ v->value.str = message; } -void yexpr_set_closure(yexpr_t* v, yref_t funcref, yvec_t captured) { +void yexpr_set_closure(yexpr_t* v, yclosure_t* cl) { yexpr_destroy(v); v->typ = YEXPR_FUNC; - v->value.closure = malloc(sizeof(yclosure_t)); - yclosure_t* closure = v->value.closure; - closure->id = funcref; - closure->captured = captured; + v->value.closure = cl; } void yexpr_append_value(yexpr_t* v, yexpr_t new_val) { @@ -196,11 +188,7 @@ case YEXPR_EXCEPTION: return 0 == ystr_cmp(&a->value.str, &b->value.str); case YEXPR_REF: - if (yref_eq(&a->value.ref, &b->value.ref)) return true; - yvalue_t* aexpr = yvalue_get(a->value.ref); - yvalue_t* bexpr = yvalue_get(b->value.ref); - if (aexpr->typ != bexpr->typ) return false; - return yexpr_equal(&aexpr->value.expr, &bexpr->value.expr); + return yref_eq(&a->value.ref, &b->value.ref); case YEXPR_LIST: if (a->value.list.len != b->value.list.len) return false; for (size_t i = 0; i < a->value.list.len; i++) { @@ -271,18 +259,12 @@ ystr_build(&tmp, "EXCEPTION<%s>", ystr_str(&expr->value.str)); break; case YEXPR_FUNC: - ystr_build(&tmp, "CLOSURE"); - yvalue_t* funcval = yvalue_get(expr->value.closure->id); - if (funcval == NULL) { - ystr_build(&tmp, "<INVALID-REF>"); - break; + ystr_build(&tmp, "CLOSURE<"); + yclosure_t* cl = expr->value.closure; + for (size_t i = 0; i < cl->args.len; i++) { + ystr_build(&tmp, "%s ", yref_debug(YVEC_AT(&cl->args, i, yref_t))); } - if (funcval->typ != YVALUE_FUNC) { - ystr_build(&tmp, "<INVALID-VALUE-REF>"); - break; - } - yfunc_t* func = &funcval->value.func; - ystr_build(&tmp, "<%s>", ystr_str(&func->name)); + ystr_build(&tmp, ">"); break; default: assert(false /* Unhandled YEXPR_TYPE */);
--- a/src/expr.h Sat Sep 28 19:13:26 2019 +0200 +++ b/src/expr.h Sun Sep 29 14:14:04 2019 +0200 @@ -87,7 +87,7 @@ * function) with `captured` values (vector of yexpr_t in order of the * `captured` field of the function). */ -void yexpr_set_closure(yexpr_t* v, yref_t funcref, yvec_t captured); +void yexpr_set_closure(yexpr_t* v, yclosure_t* cl); /** * Initialize a yexpr_t value if not yet done and append another value to it.
--- a/src/expr_test.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/expr_test.c Sun Sep 29 14:14:04 2019 +0200 @@ -20,13 +20,8 @@ yexpr_destroy(&exprref); - ystr_t str2 = ystr_new("abc"); - ref = yref_new_id(yvalue_resolve_or_create_symbol(&str2)); - assert(YREF_ID == yref_type(&ref)); - ystr_destroy(&str2); - ref0 = ref; - - ref = yref_new(); + ref0 = yref_new_anon(); + ref = yref_new_anon(); assert(YREF_ID == yref_type(&ref)); assert(ref.ref.id == ref0.ref.id + 1); } @@ -96,8 +91,7 @@ // Test refs, symbolic and numeric. yexpr_set_ref(&expr, yref_new_c("my-ref")); yexpr_debug(&expr); - yvalue_resolve_or_create_ref(&expr.value.ref); - assert(YREF_ID == yref_type(&expr.value.ref)); + assert(YREF_SYM == yref_type(&expr.value.ref)); yexpr_debug(&expr); yexpr_destroy(&expr); expr = yexpr_new();
--- a/src/preprocess_test.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/preprocess_test.c Sun Sep 29 14:14:04 2019 +0200 @@ -12,7 +12,6 @@ int main(void) { fprintf(stderr, "global destroy ===========\n"); - yvalue_free_all(); yatom_free_all(); return 0; }
--- a/src/sizes_test.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/sizes_test.c Sun Sep 29 14:14:04 2019 +0200 @@ -34,7 +34,7 @@ assert(24 == sizeof(yexpr_t)); assert(16 == sizeof(yref_t)); assert(48 == sizeof(yfunc_t)); - assert(32 == sizeof(yclosure_t)); + assert(40 == sizeof(yclosure_t)); } /**
--- a/src/types.h Sat Sep 28 19:13:26 2019 +0200 +++ b/src/types.h Sun Sep 29 14:14:04 2019 +0200 @@ -22,13 +22,10 @@ typedef enum { /// An uninitialized reference. YREF_UNDEF = 0, - /// A reference into the global value table - deprecated. + /// A numeric reference. YREF_ID = 1, - /// An unresolved reference consisting of a string in the `ref.sym` field. + /// A reference consisting of a string in the `ref.sym` field. YREF_SYM = 2, - /// A stack-relative reference - YREF_STACK = 3, - YREF_LOCAL = 4, } YREF_TYPE; /** @@ -42,10 +39,8 @@ union { /// A symbol, i.e. name, of a variable or function. ystr_t sym; - /// An ID pointing to a slot in the values table (see value.c) + /// A numeric ID. yvalue_id_t id; - /// An ID pointing to a position on the call stack. - yvalue_id_t sid; } ref; } yref_t; @@ -82,8 +77,6 @@ YBUILTIN_RAISE = 15, YBUILTIN_RECOVER = 16, - - YBUILTIN_INTERNAL_MKCLSR = 17, } YBUILTIN_TYPE; /** @@ -114,11 +107,16 @@ YEXPR_EXCEPTION = 8, } YEXPR_TYPE; +struct yexpr_t; +typedef struct yexpr_t yexpr_t; + typedef struct { /// Reference to function code. - yref_t id; - /// List of ycaptured_t. - yvec_t captured; + yexpr_t* body; + /// Arguments. + yvec_t /* <yref_t> */ args; + /// Captured variables. + yvec_t /* <yeval_map_t> */ captured; } yclosure_t; /**
--- a/src/value.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/value.c Sun Sep 29 14:14:04 2019 +0200 @@ -4,4 +4,84 @@ #include <search.h> #include <string.h> +const yref_t YREF_NULL = { .ref.sym.inner.big = {.base = NULL, .cap = 0, .len = 0, .size = 0} }; +const yvalue_id_t YREF_ID_MARKER = 0xff00000000000000; + +YREF_TYPE yref_type(yref_t* ref) { + if ((YREF_ID_MARKER & ref->ref.id) == YREF_ID_MARKER) + return YREF_ID; + return YREF_SYM; +} + +ystr_t yref_debug(yref_t* ref) { + ystr_t tmp = ystr_new(NULL); + + if (yref_is_null(ref)) { + ystr_build(&tmp, "r:NULL"); + return tmp; + } + + switch (yref_type(ref)) { + case YREF_ID: + ystr_build(&tmp, "r:%llu", ref->ref.id); + return tmp; + case YREF_SYM: + ystr_build(&tmp, "r:%s", ystr_str(&ref->ref.sym)); + return tmp; + default: + ystr_build(&tmp, "r:INVALID"); + return tmp; + } +} + +yref_t yref_new_null(void) { + return YREF_NULL; +} + +bool yref_is_null(yref_t* ref) { + return 0 == (ref->ref.id & YREF_ID_MARKER) && ystr_len(&ref->ref.sym) == 0; +} + +yref_t yref_new_anon(void) { + static yvalue_id_t anon_counter = YREF_ID_MARKER | 0x0010101010101010; + yref_t ref; + ref.ref.id = anon_counter++; + return ref; +} + +yref_t yref_new_str(ystr_t* sym) { + yref_t ref; + ref.ref.sym = *sym; + return ref; +} + +yref_t yref_new_c(const char* sym) { + yref_t ref; + ref.ref.sym = ystr_new(sym); + return ref; +} + +bool yref_eq(yref_t* a, yref_t* b) { + if (yref_type(a) == YREF_ID && yref_type(b) == YREF_ID) { + return a->ref.id == b->ref.id; + } else if (yref_type(a) == YREF_SYM && yref_type(b) == YREF_SYM) { + return ystr_cmp(&a->ref.sym, &b->ref.sym) == 0; + } + return false; +} + +yref_t yref_clone(yref_t* ref) { + if (yref_type(ref) == YREF_SYM) { + yref_t new = *ref; + new.ref.sym = ystr_clone(&ref->ref.sym); + return new; + } + return *ref; +} + +void yref_destroy(yref_t* ref) { + if (yref_type(ref) == YREF_SYM) + ystr_destroy(&ref->ref.sym); + return; +}
--- a/src/value.h Sat Sep 28 19:13:26 2019 +0200 +++ b/src/value.h Sun Sep 29 14:14:04 2019 +0200 @@ -15,11 +15,64 @@ /** * @file value.h - * @brief Types and functions for describing values and references to values. + * @brief Types and functions for describing references to values. * @addtogroup language * @{ */ +/// A void reference. +extern const yref_t YREF_NULL; + +/** + * @brief Retrieve the type of a reference. + */ +YREF_TYPE yref_type(yref_t* ref); + +/** + * @brief Return a null reference. + */ +yref_t yref_new_null(void); + +/** + * @brief Return a debug representation of a reference. The caller owns the returned string. + */ +ystr_t yref_debug(yref_t* ref); + +/** + * @brief Returns true if ref is void. + */ +bool yref_is_null(yref_t* ref); + +/** + * @brief Returns an anonymous reference. + */ +yref_t yref_new_anon(void); + +/** + * @brief Return a named reference. Ownership of `sym` is transferred to the new reference. + */ +yref_t yref_new_str(ystr_t* sym); + +/** + * @brief Return a named reference from the C string. + */ +yref_t yref_new_c(const char* sym); + +/** + * @brief Returns true if the two references are identical. + */ +bool yref_eq(yref_t* a, yref_t* b); + +/** + * @brief Clone a reference. + */ +yref_t yref_clone(yref_t* ref); + +/** + * @brief Free a reference. + */ +void yref_destroy(yref_t* ref); + /** * @} */
--- a/src/value_test.c Sat Sep 28 19:13:26 2019 +0200 +++ b/src/value_test.c Sun Sep 29 14:14:04 2019 +0200 @@ -2,133 +2,7 @@ #include <assert.h> -void test_value_create_resolve(void) { - ystr_t sym; - ystr_init(&sym, "vcr_symbol"); - - assert(YVALUE_INVALID_ID == yvalue_resolve_symbol(&sym)); - - yvalue_id_t id = yvalue_resolve_or_create_symbol(&sym); - assert(YVALUE_INVALID_ID != id); - assert(id == yvalue_resolve_symbol(&sym)); - - yvalue_id_t id2 = yvalue_create(); - assert(id2 != id); - assert(id2 == id+1); - - ystr_destroy(&sym); -} - -void test_ref_create_resolve(void) { - ystr_t symbol = ystr_new("rcr_symbol_long_long"); - ystr_t symbol2 = ystr_new(NULL); - ystr_copy(&symbol, &symbol2); - yref_t ref = yref_new_str(&symbol); // symbol is now owned by ref. - - yref_t cloned = yref_clone(&ref); - assert(cloned.ref.sym.inner.big.base != ref.ref.sym.inner.big.base); - - yvalue_resolve_or_create_ref(&ref); - assert(YVALUE_INVALID_ID != yvalue_resolve_symbol(&symbol2)); - assert(YREF_ID == yref_type(&ref)); - assert(ref.ref.id == yref_clone(&ref).ref.id); - assert(YREF_SYM == yref_type(&cloned)); - assert(ref.ref.id == yvalue_resolve_symbol(&symbol2)); - - ystr_destroy(&symbol2); - yref_destroy(&ref); - yref_destroy(&cloned); -} - -void test_value_clone(void) { - yvalue_t first_value = { .typ = YVALUE_EXPR, .value.expr = yexpr_new() }; - yexpr_set_str(&first_value.value.expr, ystr_new("a long external test string")); - yref_t first = yvalue_set(YVALUE_INSERT, &first_value); - yref_t second = yvalue_set(YVALUE_INSERT, &first_value); - yref_t cloned = yvalue_clone(&first); - assert(first.ref.id != cloned.ref.id); - - *ystr_at(&yvalue_get(first)->value.expr.value.str, 0) = 'A'; - assert('A' == *ystr_at(&yvalue_get(first)->value.expr.value.str, 0)); - assert('A' == *ystr_at(&yvalue_get(second)->value.expr.value.str, 0)); - assert('A' == *ystr_at(&yvalue_get(yref_clone(&first))->value.expr.value.str, 0)); - assert('a' == *ystr_at(&yvalue_get(cloned)->value.expr.value.str, 0)); - - // Double-free expected! -} - -void test_value_set(void) { - yexpr_t expr; - expr.typ = YEXPR_NUMBER; - expr.value.n = 12345; - yvalue_t val; - val.typ = YVALUE_EXPR; - val.value.expr = expr; - - // Allocate unnamed ref. - yref_t ref = yvalue_set(YVALUE_INSERT, &val); - assert(YREF_ID == yref_type(&ref)); - yvalue_t* got = yvalue_get(ref); - assert(YVALUE_EXPR == got->typ); - assert(YEXPR_NUMBER == got->value.expr.typ); - assert(12345 == got->value.expr.value.n); - - // Use named refs. - ystr_t symbol = ystr_new("vs_symbol"); - ystr_t symbol2 = ystr_new("vs_symbol"); - - val.value.expr.value.n = 12346; - yvalue_id_t id = yvalue_resolve_or_create_symbol(&symbol); - yref_t ref2 = yref_new_id(id); - yref_t ref2b = yvalue_set(ref2, &val); - assert(ref2b.ref.id == ref2.ref.id); - yvalue_t* got2 = yvalue_get(ref2); - assert(12346 == got2->value.expr.value.n); - - // Get symbolically. - yref_t ref3 = yref_new_str(&symbol2); - yvalue_t* got3 = yvalue_get(ref3); - assert(12346 == got3->value.expr.value.n); - - // Get invalid entry. - yref_t ref_invalid = yref_new_c("invalid_entry"); - assert(NULL == yvalue_get(ref_invalid)); - assert(NULL != yvalue_get(ref3)); - - yvalue_destroy(&val); - yref_destroy(&ref); - yref_destroy(&ref2); - yref_destroy(&ref2b); - yref_destroy(&ref3); - yref_destroy(&ref_invalid); -} - -void test_value_refs(void) { - yref_t ref = yref_new_c("hello"); - assert(YREF_SYM == yref_type(&ref)); - assert(yref_eq(&ref, &ref)); - yref_destroy(&ref); - ref = yref_new(); - assert(YREF_ID == yref_type(&ref)); - assert(yref_eq(&ref, &ref)); - yref_destroy(&ref); - ref = yref_new_cix(2); - assert(YREF_STACK == yref_type(&ref)); - assert(2 == yref_cix(&ref)); - assert(yref_eq(&ref, &ref)); - ystr_t dbg = yref_debug(&ref); - assert(0 == ystr_cmp_str(&dbg, "id:<stackrel:-2>")); - ystr_destroy(&dbg); - yref_destroy(&ref); -} - int main(void) { - test_value_create_resolve(); - test_ref_create_resolve(); - test_value_clone(); - test_value_set(); - test_value_refs(); - //yvalue_free_all(); test_value_clone does some weird thigns, so we can't free all values. yatom_free_all(); return 0; }