Mercurial > lbo > hg > ylisp
changeset 182:8cd4314a144f
big cleanup for rewrite of execution logic
author | Lewin Bormann <lbo@spheniscida.de> |
---|---|
date | Fri, 27 Sep 2019 14:33:38 +0200 |
parents | 3ec2b2edb977 |
children | 37b01b114b50 |
files | src/built-ins.c src/eval.c src/eval.h src/eval_test.c src/preprocess.c src/preprocess.h src/preprocess_test.c src/value.c src/value.h |
diffstat | 9 files changed, 0 insertions(+), 1450 deletions(-) [+] |
line wrap: on
line diff
--- a/src/built-ins.c Fri Sep 27 14:30:38 2019 +0200 +++ b/src/built-ins.c Fri Sep 27 14:33:38 2019 +0200 @@ -196,53 +196,6 @@ /// wrong. yexpr_t ybuiltin_fn_let(yeval_state_t* state) { #define _pattern "(let ref [expr ...] expr)" - yexpr_t let, *ref; - size_t counter = 0; - yvec_t* letlist = &let.value.list; - - if (!yvec_pop(&state->call_stack, &let)) NOTENOUGHFAIL(LET); - if (let.typ != YEXPR_LIST) BADEXPRFAIL(LET, let); - if (let.value.list.len < 3) BADEXPRFAIL(LET, let); - - yexpr_t let_id = *YVEC_AT(letlist, counter++, yexpr_t); - if (let_id.typ != YEXPR_BUILTIN || let_id.value.builtin != YBUILTIN_LET) - BADEXPRFAIL(LET, let); - - ref = YVEC_AT(letlist, counter++, yexpr_t); - if (ref->typ != YEXPR_REF) TYPEFAIL(LET, let, _pattern); - - assert(counter == 2); - - // Add or update reference in local scope. - - // Check if ref refers to a captured variable. - for (size_t i = 0; i < state->current_closure->captured.len; i++) { - ycaptured_t* candidate = YVEC_AT(&state->current_closure->captured, i, ycaptured_t); - if (yref_eq(&ref->value.ref, &candidate->ref)) { - yexpr_destroy(&candidate->expr); - candidate->expr = yeval_list_return_last(state, letlist, counter); - return yexpr_new(); - } - } - - // Else, find local slot created by previous `let`. - for (size_t i = state->locals.len-1; i > 0; i--) { - yeval_local_def_t* candidate = YVEC_AT(&state->locals, i, yeval_local_def_t); - if (yref_eq(&ref->value.ref, &candidate->ref)) { - // Found, update. - yexpr_destroy(&candidate->val); - candidate->val = yeval_list_return_last(state, letlist, counter); - return yexpr_new(); - } - if (yref_type(&candidate->ref) == YREF_UNDEF) - break; - } - - // Not found, push onto locals stack. - yeval_local_def_t new = {.ref = ref->value.ref, .val = yeval_list_return_last(state, letlist, counter)}; - YVEC_PUSH(&state->locals, &new); - return yexpr_new(); - #undef _pattern } @@ -435,25 +388,6 @@ // yexpr_t of type // YEXPR_FUNC, with // captured values. - yexpr_t mkclsr; - yexpr_t closure = yexpr_new(); - - if (!yvec_pop(&state->call_stack, &mkclsr)) NOTENOUGHFAIL(INTERNAL_MKCLSR); - if (mkclsr.typ != YEXPR_LIST) BADEXPRFAIL(INTERNAL_MKCLSR, mkclsr); - if (mkclsr.value.list.len != 2) BADEXPRFAIL(INTERNAL_MKCLSR, mkclsr); - - yexpr_t *head = YVEC_AT(&mkclsr.value.list, 0, yexpr_t), - *tail = YVEC_AT(&mkclsr.value.list, 1, yexpr_t); - if (head->typ != YEXPR_BUILTIN || - head->value.builtin != YBUILTIN_INTERNAL_MKCLSR) - BADEXPRFAIL(INTERNAL_MKCLSR, mkclsr); - if (tail->typ != YEXPR_REF) BADEXPRFAIL(INTERNAL_MKCLSR, mkclsr); - - yvalue_t* funcval = yvalue_get(tail->value.ref); - if (funcval->typ != YVALUE_FUNC) - TYPEFAIL(INTERNAL_MKCLSR, mkclsr, _pattern); - yfunc_t* func = &funcval->value.func; - // TODO: continue implementation #undef _pattern }
--- a/src/eval.c Fri Sep 27 14:30:38 2019 +0200 +++ b/src/eval.c Fri Sep 27 14:33:38 2019 +0200 @@ -5,193 +5,3 @@ #include "expr.h" #include "value.h" -static yexpr_t yeval_undef_ref(yref_t* ref) { - yexpr_t exception = yexpr_new(); - ystr_t msg = ystr_new(NULL); - ystr_t dbg = yref_debug(ref); - ystr_build(&msg, "undefined reference: %s", ystr_str(&dbg)); - ystr_destroy(&dbg); - yexpr_set_exception(&exception, msg); - return exception; -} - -yexpr_t yeval_call_func(yeval_state_t* state, yfunc_t* ref, yexpr_t* call) { - assert(call->typ == YEXPR_LIST); - if (ref->args.len != call->value.list.len - 1) { - yexpr_t exc = yexpr_new(); - ystr_t msg = ystr_new(NULL); - ystr_build(&msg, - "Unexpected number of arguments in call to function %s: " - "Want %u, got %u", - ystr_str(&ref->name), ref->args.len, - call->value.list.len - 1); - yexpr_set_exception(&exc, msg); - return exc; - } - yvec_t* args = &ref->args; - yexpr_t evald_args[32]; - assert(args->len <= 32); - for (size_t i = 0; i < args->len; i++) { - evald_args[i] = - yeval(state, YVEC_AT(&call->value.list, i + 1, yexpr_t), false); - } - for (size_t i = 0; i < args->len; i++) { - YVEC_PUSH(&state->call_stack, &evald_args[i]); - } - yexpr_t result = yeval_list_return_last(state, &ref->body, 0); - for (size_t i = 0; i < args->len; i++) { - yexpr_t old; - yvec_pop(&state->call_stack, &old); - yexpr_destroy(&old); - } - return result; -} - -yexpr_t yeval_list_return_last(yeval_state_t* state, yvec_t* list, size_t off) { - if (list->len - off == 0) return yexpr_new(); - assert(list->size == sizeof(yexpr_t)); - for (size_t i = off; i < (list->len - (unsigned int)1); i++) { - yeval(state, YVEC_AT(list, i, yexpr_t), true); - } - return yeval(state, YVEC_AT(list, list->len - 1, yexpr_t), false); -} - -yexpr_t yeval(yeval_state_t* state, yexpr_t* expr, bool in_place) { - yexpr_t result; - if (in_place) result = yexpr_new(); - switch (expr->typ) { - case YEXPR_UNDEF: - // UNDEFs are often the result of removing defns from the parsed - // tree. - case YEXPR_ATOM: - case YEXPR_NUMBER: - case YEXPR_EXCEPTION: - case YEXPR_BUILTIN: - if (in_place) break; // values can't have side effects - // Flat copy is enough. - return *expr; - case YEXPR_STRING: - if (in_place) break; - result = *expr; - result.value.str = ystr_clone(&expr->value.str); - return result; - case YEXPR_REF: - // Reference is resolved if expr-ref. func-refs are not - // resolved. NOTE: Can this result in infinite recursion? - if (in_place) break; // values can't have side effects - // YREF_STACK references a value pushed on the stack. - if (yref_type(&expr->value.ref) == YREF_STACK) { - result = yexpr_copy(YVEC_AT( - &state->call_stack, - state->call_stack.len - 1 - yref_cix(&expr->value.ref), - yexpr_t)); - return result; - } else if (yref_type(&expr->value.ref) == YREF_LOCAL) { - // First look for locally defined variables, then for captured ones. - for (size_t i = state->locals.len - 1; i > 0; i--) { - yeval_local_def_t* candidate = YVEC_AT(&state->locals, i, yeval_local_def_t); - if (yref_type(&candidate->ref) == YREF_UNDEF) - break; - if (yref_eq(&expr->value.ref, &candidate->ref)) - return candidate->val; - } - // Look for captured variables. - for (size_t i = 0; i < state->current_closure->captured.len; i++) { - ycaptured_t* candidate = YVEC_AT(&state->current_closure->captured, i, ycaptured_t); - if (yref_eq(&expr->value.ref, &candidate->ref)) - return candidate->expr; - } - } - yvalue_t* val = yvalue_get(expr->value.ref); - if (val == NULL) return yeval_undef_ref(&expr->value.ref); - if (val->typ == YVALUE_EXPR) { - result = yexpr_copy(&val->value.expr); - return result; - } else if (val->typ == YVALUE_FUNC) { - return *expr; - } - // Undefined reference! - return yeval_undef_ref(&expr->value.ref); - case YEXPR_LIST: - // Is empty list? - if (expr->value.list.len == 0) { - if (in_place) break; - result = *expr; - result.value.list = yvec_clone(&expr->value.list); - return result; - } - // Is builtin call? - yexpr_t* first = YVEC_AT(&expr->value.list, 0, yexpr_t); - if (first->typ == YEXPR_BUILTIN) { - // both must be yexpr_t vecs. - assert(expr->value.list.size == state->call_stack.size); - // Builtins expect their call list on the stack. - YVEC_PUSH(&state->call_stack, expr); - return ybuiltin_run(first->value.builtin, state); - } - - // Push "separator" frame onto locals stack. - yeval_local_def_t sep = {.ref = yref_new_undef() }; - YVEC_PUSH(&state->locals, &sep); - - // Is function call? - if (first->typ == YEXPR_REF) { - yexpr_t ref = yeval(state, first, false); - // If resolved reference is still possibly a function - if (ref.typ == YEXPR_REF) { - yvalue_t* val = yvalue_get(ref.value.ref); - if (val == NULL) { - result = yeval_undef_ref(&expr->value.ref); - goto clean_up; - } - // Call function. - if (val->typ == YVALUE_FUNC) { - result = yeval_call_func(state, &val->value.func, expr); - if (in_place) { - yexpr_destroy(&result); - } - goto clean_up; - } else if (val->typ == YVALUE_EXPR) { - assert(val->typ == YVALUE_FUNC /* yeval() should have returned a copy of the expression if first element was a reference to an expression */); - } - assert(false /* yeval() on a reference should have prevented any other case */); - } - yexpr_destroy(&ref); - } - - result = *expr; - - // Don't store results - if (in_place) { - for (size_t i = 0; i < result.value.list.len; i++) { - yexpr_t* elem = YVEC_AT(&result.value.list, i, yexpr_t); - yexpr_t elem_result = - yeval(state, elem, /* in_place= */ true); - yexpr_destroy(&elem_result); - } - } else { - // Store results; input list is copied shallowly, and assigned - // with copied values from yeval. - result.value.list = yvec_clone(&expr->value.list); - for (size_t i = 0; i < result.value.list.len; i++) { - yexpr_t* elem = YVEC_AT(&result.value.list, i, yexpr_t); - yexpr_t elem_result = yeval(state, elem, /* in_place= */ false); - *elem = elem_result; - } - } - -clean_up: - // Clean up locals. - for (size_t i = state->locals.len - 1; i > 0; i--) { - yeval_local_def_t* def = YVEC_AT(&state->locals, i, yeval_local_def_t); - if (yref_type(&def->ref) == YREF_UNDEF) - break; - yexpr_destroy(&def->val); - yvec_pop(&state->locals, NULL); - } - return result; - default: - assert(false /* yeval is not yet fully implemented */); - } - return result; -}
--- a/src/eval.h Fri Sep 27 14:30:38 2019 +0200 +++ b/src/eval.h Fri Sep 27 14:33:38 2019 +0200 @@ -11,49 +11,6 @@ * @{ */ -typedef struct { - // if ref's type is YREF_UNDEF, this element marks a scope border. - yref_t ref; - yexpr_t val; -} yeval_local_def_t; - -/// Evaluation state passed to invocations of `yeval`. There only exists one -/// instance of this value per program, so per-eval-call information should be -/// passed directly to yeval. -typedef struct { - /// A vec of expressions that can be used for storing intermediate values in - /// a quick way (push/pop is fast on pre-allocated memory). - yvec_t call_stack; - - /// Values captured by the closure we are currently evaluating. - yclosure_t* current_closure; - /// Values defined in functions. Grows; every scope has a contiguous sequence and different scopes are separated by YREF_UNDEF entries. - yvec_t /* <yeval_local_def> */ locals; -} yeval_state_t; - -/** - * @brief Evaluate a list of expressions starting at off, but only return the - * last one. Used to run functions or other constructs that evaluate expressions - * for their side-effects. - */ -yexpr_t yeval_list_return_last(yeval_state_t* state, yvec_t* list, size_t off); - -/** - * @brief The core of ylisp: Evaluate an expression, resulting in a new - * expression. - * - * The caller takes ownership of the returned expression. `yeval` does not take - * ownership of the supplied expression and does not modify or destroy any - * children of it (this is important for recursive evaluation). - * - * `yeval` calls itself recursively during program execution. - * - * If in_place is set, yeval will evaluate the given expression without - * returning its result. This reduces memory allocations to where they are - * needed, namely at the end of function calls, where an expression is returned. - * If set, the return value should be ignored. - */ -yexpr_t yeval(yeval_state_t* state, yexpr_t* expr, bool in_place); /** * @}
--- a/src/eval_test.c Fri Sep 27 14:30:38 2019 +0200 +++ b/src/eval_test.c Fri Sep 27 14:33:38 2019 +0200 @@ -6,152 +6,8 @@ #include "preprocess.h" #include "value.h" -void test_eval_edge_cases(void) { - fprintf(stderr, "test_eval_edge_cases ===========\n"); - - ystr_t input = ystr_new("() -- empty list\n"); - yexpr_t program = yexpr_new(); - ystr_t error = ystr_new(NULL); - assert(yparse_str(&input, &program, &error)); - - yeval_state_t state; - state.call_stack = YVEC_NEW(NULL, 4, yexpr_t); - state.locals = YVEC_NEW(NULL, 4, yeval_local_def_t); - yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0); - assert(result.typ == YEXPR_LIST); - assert(result.value.list.len == 0); - - yexpr_destroy(&program); - ystr_destroy(&input); - ystr_destroy(&error); - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - yexpr_destroy(&result); -} - -void test_eval_func_wrong_call(void) { - fprintf(stderr, "test_eval_func_wrong_call ===========\n"); - - ystr_t input = ystr_new( - "(defn my-func (first-arg) first-arg)" - "(my-func 'correct-number-of-args)" - "(my-func 'too 'many 'args)"); - yexpr_t program = yexpr_new(); - ystr_t error = ystr_new(NULL); - assert(yparse_str(&input, &program, &error)); - yexpr_debug(&program); - - ypreprocess(&program); - - yeval_state_t state; - state.call_stack = YVEC_NEW(NULL, 4, yexpr_t); - state.locals = YVEC_NEW(NULL, 4, yeval_local_def_t); - yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0); - assert(result.typ == YEXPR_EXCEPTION); - assert(0 == ystr_cmp_str(&result.value.str, - "Unexpected number of arguments in call to " - "function my-func: Want 1, got 3")); - yexpr_debug(&result); - - yexpr_destroy(&program); - ystr_destroy(&input); - ystr_destroy(&error); - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - yexpr_destroy(&result); -} - -void test_eval_recursion(void) { - fprintf(stderr, "test_eval_recursion ===========\n"); - - // Calculate faculty values from 1 to 10. - ystr_t input = ystr_new( - "(defn fac (n) (if (== n 0) 1 ((* n (fac (- n 1))))))" - "(for i (1 2 3 4 5 6 7 8 9 10) (fac i))"); - yexpr_t program = yexpr_new(); - ystr_t error = ystr_new(NULL); - assert(yparse_str(&input, &program, &error)); - yexpr_debug(&program); - ypreprocess(&program); - yexpr_debug(&program); - - yeval_state_t state; - state.call_stack = YVEC_NEW(NULL, 4, yexpr_t); - state.locals = YVEC_NEW(NULL, 4, yeval_local_def_t); - yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0); - yexpr_debug(&result); - assert(YEXPR_LIST == result.typ); - assert(362880 == YVEC_AT(&result.value.list, 8, yexpr_t)->value.n); - - yexpr_destroy(&program); - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - ystr_destroy(&error); - yexpr_destroy(&result); - ystr_destroy(&input); -} - -void test_eval_higher_order(void) { - fprintf(stderr, "test_eval_higher_order ===========\n"); - - ystr_t input = ystr_new( - "(defn f (a) (+ a 1))" - "(defn g (fn) (fn 3))" - "(g f)"); - - yexpr_t program = yexpr_new(); - ystr_t error = ystr_new(NULL); - assert(yparse_str(&input, &program, &error)); - ypreprocess(&program); - - yeval_state_t state; - state.call_stack = YVEC_NEW(NULL, 4, yexpr_t); - state.locals = YVEC_NEW(NULL, 4, yeval_local_def_t); - yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0); - assert(YEXPR_NUMBER == result.typ && 4 == result.value.n); - - yexpr_destroy(&program); - yvec_destroy(&state.locals); - yvec_destroy(&state.call_stack); - ystr_destroy(&error); - yexpr_destroy(&result); - ystr_destroy(&input); -} - -void test_eval_undefined_ref(void) { - fprintf(stderr, "test_eval_undefined_ref ===========\n"); - - ystr_t input = ystr_new( - "(defn f (a) (+ a 1))" - "(let x 4)" - "(f g)"); - - yexpr_t program = yexpr_new(); - ystr_t error = ystr_new(NULL); - assert(yparse_str(&input, &program, &error)); - ypreprocess(&program); - - yeval_state_t state; - state.call_stack = YVEC_NEW(NULL, 4, yexpr_t); - yvec_destroy(&state.locals); - state.locals = YVEC_NEW(NULL, 4, yeval_local_def_t); - yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0); - assert(YEXPR_EXCEPTION == result.typ); - - yexpr_destroy(&program); - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - ystr_destroy(&error); - yexpr_destroy(&result); - ystr_destroy(&input); -} int main(void) { - test_eval_edge_cases(); - test_eval_func_wrong_call(); - test_eval_recursion(); - test_eval_higher_order(); - test_eval_undefined_ref(); yvalue_free_all(); yatom_free_all(); return 0;
--- a/src/preprocess.c Fri Sep 27 14:30:38 2019 +0200 +++ b/src/preprocess.c Fri Sep 27 14:33:38 2019 +0200 @@ -6,373 +6,3 @@ #include "func.h" #include "value.h" -void ypreprocess(yexpr_t* expr) { - ypreprocess_resolve_builtins(expr); - ypreprocess_refs(expr); -} - -void ypreprocess_resolve_builtins(yexpr_t* expr) { - switch (expr->typ) { - case YEXPR_NUMBER: - case YEXPR_STRING: - case YEXPR_ATOM: - case YEXPR_EXCEPTION: - case YEXPR_UNDEF: - break; - case YEXPR_BUILTIN: - assert(expr->typ != YEXPR_BUILTIN /* We shouldn't traverse the same expression twice */); - case YEXPR_REF: - if (yref_type(&expr->value.ref) == YREF_SYM) - ybuiltin_translate(expr); - break; - case YEXPR_LIST: - if (expr->value.list.len == 0) break; - yvec_t* list = &expr->value.list; - for (size_t i = 0; i < list->len; i++) { - ypreprocess_resolve_builtins(YVEC_AT(list, i, yexpr_t)); - } - break; - default: - assert(false /* unexpected expression type! */); - } -} - -/** - * @brief Resolve references to arguments in the function body. - */ -static void ypreprocess_resolve_inner_refs(yexpr_t* body, yvec_t* arg_descs) { - switch (body->typ) { - case YEXPR_REF: - if (yref_type(&body->value.ref) != YREF_SYM) break; - // Check if reference is to known argument. - for (size_t i = 0; i < arg_descs->len; i++) { - yarg_desc_t* arg = YVEC_AT(arg_descs, i, yarg_desc_t); - if (0 == ystr_cmp(&body->value.ref.ref.sym, &arg->argname)) { - yexpr_set_ref(body, yref_clone(&arg->argref)); - break; - } - } - break; - case YEXPR_LIST: - for (size_t i = 0; i < body->value.list.len; i++) { - yexpr_t* expr = YVEC_AT(&body->value.list, i, yexpr_t); - ypreprocess_resolve_inner_refs(expr, arg_descs); - } - break; - default: - break; - } -} - -/** - * @brief Store a `(defn ...)` as function and replace it with a YEXPR_UNDEF - * definition. - * - * `expr` must be a list starting with the `defn` built-in. - * - * Returns false if syntax is incorrect. The reference to the function is stored - * into `newref`. - */ -static bool ypreprocess_compile_defn(yexpr_t* expr, yref_t* newref) { - yvec_t* list = &expr->value.list; - assert(4 <= list->len); - yexpr_t* defn = YVEC_AT(list, 0, yexpr_t); - yexpr_t* name = YVEC_AT(list, 1, yexpr_t); - yexpr_t* args = YVEC_AT(list, 2, yexpr_t); - if (defn->typ != YEXPR_BUILTIN || defn->value.builtin != YBUILTIN_DEFN) - return false; - if (name->typ != YEXPR_REF) return false; - assert(yref_type(&name->value.ref) == YREF_SYM); - if (args->typ != YEXPR_LIST) return false; - size_t n_args = args->value.list.len; - - yfunc_t func; - yvec_t body_exprs = YVEC_NEW(yvec_at(list, 3), list->len - 3, yexpr_t); - yvec_t arg_descs = YVEC_NEW(NULL, args->value.list.len, yarg_desc_t); - for (size_t i = 0; i < args->value.list.len; i++) { - yarg_desc_t desc; - yexpr_t* arg = YVEC_AT(&args->value.list, i, yexpr_t); - assert(arg->typ == YEXPR_REF && yref_type(&arg->value.ref) == YREF_SYM); - desc.argname = ystr_clone(&arg->value.ref.ref.sym); - desc.argref = yref_new_cix(n_args - 1 - i); - YVEC_PUSH(&arg_descs, &desc); - } - - func.name = name->value.ref.ref.sym; - func.args = arg_descs; - yexpr_t body = yexpr_new(); - yexpr_set_list(&body, body_exprs); // no destroy! - // Resolve references to arguments in body. - ypreprocess_resolve_inner_refs(&body, &arg_descs); - func.body = body_exprs; - - // Store function as value at the named reference. - yvalue_t func_value; - func_value.typ = YVALUE_FUNC; - func_value.value.func = func; - - *newref = yvalue_set(YVALUE_INSERT, &func_value); - - yexpr_destroy(defn); - yexpr_destroy(args); - yvec_destroy(list); - - *expr = yexpr_new(); - return true; -} - -typedef struct { - ystr_t sym; - yref_t ref; -} ypreprocess_existing_t; - -/// References in a scope. -typedef struct { - /// Vector of ypreprocess_existing_t - yvec_t refs; -} ypreprocess_scope_t; - -static yref_t ypreprocess_find_or_create_ref(ystr_t* symbol, - ypreprocess_state_t* state) { - // Search for symbol in stack, starting at top-most scope (enclosing list) - // and working upwards. - for (size_t i = 0; i < state->scopes.len; i++) { - size_t index = state->scopes.len - 1 - i; - ypreprocess_scope_t* scope = - *YVEC_AT(&state->scopes, index, ypreprocess_scope_t*); - - // If found: Add reference to closure captured list - for (size_t ref = 0; ref < scope->refs.len; ref++) { - ypreprocess_existing_t* candidate = - YVEC_AT(&scope->refs, ref, ypreprocess_existing_t); - if (0 == ystr_cmp(symbol, &candidate->sym)) { - return candidate->ref; - } - } - } - - // No existing reference found! Create one in the current scope. - yref_t ref = yref_new_local(); - // For all new references, we define them in the scope above the current - // list (thus "- 2"), e.g. in let, defn - ypreprocess_scope_t* topscope = - *YVEC_AT(&state->scopes, state->scopes.len - 2, ypreprocess_scope_t*); - ypreprocess_existing_t newref = {.sym = ystr_clone(symbol), .ref = ref}; - YVEC_PUSH(&topscope->refs, &newref); - return ref; -} - -/// Mark the given reference for capture in all function descriptions up to the -/// function defining it. Returns true if a definition of the reference was -/// found somewhere in the lexical stack. -/// TODO: Merge with ypreprocess_find_or_create_ref(). -static bool ypreprocess_set_capture(yref_t ref, ypreprocess_state_t* state) { - // If we are at the top level definition, we don't need to look further up. - if (state->defn_scopes.len < 1) return false; - - // Find innermost function defining the reference, and add it to the capture - // list of all functions inside it. - for (size_t i = state->defn_scopes.len - 1; i > 0; i--) { - size_t scopeix = *YVEC_AT(&state->defn_scopes, i, size_t); - yvec_t* defined_refs = - &YVEC_AT(&state->scopes, scopeix, ypreprocess_scope_t)->refs; - - // Check if function defines this reference. - for (size_t j = 0; j < defined_refs->len; j++) { - ypreprocess_existing_t* candidate = - YVEC_AT(defined_refs, j, ypreprocess_existing_t); - if (yref_eq(&candidate->ref, &ref)) { - // Found function defining it! We are done here. - return true; - } - } - // This is not the defn defining the reference. Mark the reference to be - // captured by it. - assert(yref_type(&ref) == YREF_LOCAL || yref_type(&ref) == YREF_STACK); - yvec_t* captured = YVEC_AT(&state->capture, i, yvec_t); - YVEC_PUSH(captured, &ref); - } - return false; -} - -/** Assign unique IDs to every reference. */ -static void ypreprocess_refs_recursive(ypreprocess_state_t* state, - yexpr_t* expr) { - // Abort cheaply for types we don't want to resolve anyway. - switch (expr->typ) { - case YEXPR_UNDEF: - case YEXPR_BUILTIN: - case YEXPR_NUMBER: - case YEXPR_STRING: - case YEXPR_ATOM: - case YEXPR_EXCEPTION: - return; - default: - break; - } - - ypreprocess_scope_t scope; - - switch (expr->typ) { - case YEXPR_REF: - if (yref_type(&expr->value.ref) == YREF_SYM) { - ystr_t* sym = &expr->value.ref.ref.sym; - yref_t resolved = ypreprocess_find_or_create_ref(sym, state); - bool found = ypreprocess_set_capture(resolved, state); - yexpr_set_ref(expr, resolved); - } else if (yref_type(&expr->value.ref) == YREF_STACK) { - bool found = ypreprocess_set_capture(expr->value.ref, state); - } - break; - case YEXPR_LIST: - // Create new scope for list. - scope.refs = YVEC_NEW(NULL, 4, ypreprocess_existing_t); - - ypreprocess_scope_t* scopep = &scope; - yvec_t* scope_stack = &state->scopes; - YVEC_PUSH(&state->scopes, &scopep); - - // Handle functions. - if (expr->value.list.len >= 4) { - yexpr_t* head = YVEC_AT(&expr->value.list, 0, yexpr_t); - if (head->typ == YEXPR_BUILTIN && - head->value.builtin == YBUILTIN_DEFN) { - // Compile and store function. Push symbol->reference - // mapping onto scope stack. - - // Set up closure support: - // List of references that this function depends on. - yvec_t captured; - size_t stackix = scope_stack->len - 1; - YVEC_INIT(&captured, 4, yref_t); - - YVEC_PUSH(&state->defn_scopes, &stackix); - YVEC_PUSH(&state->capture, &captured); - - yref_t new_ref; - // TODO: handle errors better - bool compile_defn_ok = - ypreprocess_compile_defn(expr, &new_ref); - assert(compile_defn_ok); - yvalue_t* func = yvalue_get(new_ref); - assert(func != NULL); - - ypreprocess_existing_t funcref = { - .sym = ystr_clone(&func->value.func.name), - .ref = new_ref}; - YVEC_PUSH(&(*YVEC_AT(scope_stack, scope_stack->len - 2, - ypreprocess_scope_t*)) - ->refs, - &funcref); - - yvec_t* func_body = &func->value.func.body; - for (size_t i = 0; i < func_body->len; i++) { - ypreprocess_refs_recursive( - state, YVEC_AT(func_body, i, yexpr_t)); - } - - // Now we know which references are used by inner functions. - // We can replace the `defn` list with a `(let name (_mkclsr ...))` expression. - - ypreprocess_find_or_create_ref(&func->value.func.name, state); - - yvec_t* to_capture = YVEC_AT( - &state->capture, state->capture.len - 1, yvec_t); - - yvec_t mkclsr_list; - YVEC_INIT(&mkclsr_list, 2 + to_capture->len, yexpr_t); - yexpr_t mkclsr_bi = yexpr_new(); - yexpr_set_builtin(&mkclsr_bi, YBUILTIN_INTERNAL_MKCLSR); - yexpr_t funcrefex = yexpr_new(); - yexpr_set_ref(&funcrefex, new_ref); - - YVEC_PUSH(&mkclsr_list, &mkclsr_bi); - YVEC_PUSH(&mkclsr_list, &funcrefex); - for (size_t i = 0; i < to_capture->len; i++) { - yexpr_t refex = yexpr_new(); - yexpr_set_ref(&refex, *YVEC_AT(to_capture, i, yref_t)); - YVEC_PUSH(&mkclsr_list, &refex); - } - yexpr_t mkclsr = yexpr_new(); - yexpr_set_list(&mkclsr, mkclsr_list); - - yvec_t let_list; - YVEC_INIT(&let_list, 3, yexpr_t); - yexpr_t let_bi = yexpr_new(); - yexpr_set_builtin(&let_bi, YBUILTIN_LET); - - YVEC_PUSH(&let_list, &let_bi); - yexpr_t let_expr = yexpr_new(); - yexpr_set_list(&let_expr, &let_list); - - *expr = mkclsr; - - yvec_pop(&state->defn_scopes, NULL); - yvec_t capt; - yvec_pop(&state->capture, &capt); - yvec_destroy(&capt); - - fprintf(stderr, "defined function %s: ", - ystr_str(&func->value.func.name)); - yexpr_debug(expr); - goto cleanup; - } - } - - // Not a function definition. - - // Resolve list elements recursively. - for (size_t i = 0; i < expr->value.list.len; i++) { - yexpr_t* elem = YVEC_AT(&expr->value.list, i, yexpr_t); - ypreprocess_refs_recursive(state, elem); - } - - cleanup: - // Clean up. - yvec_pop(&state->scopes, NULL); - - for (size_t i = 0; i < scope.refs.len; i++) { - ypreprocess_existing_t* ex = - YVEC_AT(&scope.refs, i, ypreprocess_existing_t); - ystr_destroy(&ex->sym); - yref_destroy(&ex->ref); - } - yvec_destroy(&scope.refs); - break; - default: - break; - } -} - -// Initial invocation must be of type YEXPR_LIST and represent the top-level -// program. -void ypreprocess_refs(yexpr_t* expr) { - ypreprocess_state_t state; - yvec_t scope_stack = YVEC_NEW(NULL, 16, ypreprocess_scope_t*); - state.scopes = scope_stack; - state.capture = YVEC_NEW(NULL, 16, yvec_t); - state.defn_scopes = YVEC_NEW(NULL, 16, size_t); - - ypreprocess_refs_recursive(&state, expr); - assert(scope_stack.len == 0); - yvec_destroy(&scope_stack); -} - -void ypreprocess_refs_repl(yexpr_t* repl_expr, ypreprocess_state_t** state) { - if (*state == NULL) { - *state = malloc(sizeof(ypreprocess_state_t)); - yvec_t scope_stack = YVEC_NEW(NULL, 16, ypreprocess_scope_t*); - (*state)->scopes = scope_stack; - (*state)->capture = YVEC_NEW(NULL, 16, yvec_t); - (*state)->defn_scopes = YVEC_NEW(NULL, 16, size_t); - - ypreprocess_scope_t* root = malloc(sizeof(ypreprocess_scope_t)); - root->refs = YVEC_NEW(NULL, 4, ypreprocess_existing_t); - YVEC_PUSH(&(*state)->scopes, &root); - } - assert(YEXPR_LIST == repl_expr->typ); - for (size_t i = 0; i < repl_expr->value.list.len; i++) { - yexpr_t* elem = YVEC_AT(&repl_expr->value.list, i, yexpr_t); - ypreprocess_refs_recursive(*state, elem); - } -}
--- a/src/preprocess.h Fri Sep 27 14:30:38 2019 +0200 +++ b/src/preprocess.h Fri Sep 27 14:33:38 2019 +0200 @@ -9,42 +9,6 @@ * @addtogroup frontend * @{ */ - -typedef struct { - yvec_t /* <ypreprocess_scope_t> */ scopes; - - // The following two vecs are in "lock-step", each element corresponds to - // the same index in the other. - - /// Each `defn` pushes a new vector onto this stack. It contains the index - /// of the scope frame belonging to that function. - yvec_t /* <size_t> */ defn_scopes; - /// Each `defn` pushes a new vector onto this stack. Every time a reference - /// is encountered, it is pushed onto all stacks down to the function - /// defining it. yref_ts must be of type YREF_LOCAL. - yvec_t /* <yvec_t<yref_t>> */ capture; -} ypreprocess_state_t; - -/** - * @brief Apply preprocessing stages, listed as individual functions below. - */ -void ypreprocess(yexpr_t* expr); - -/** - * @brief Replace IDs that reference a built-in by built-in expressions. - * - * Run order: 1 - */ -void ypreprocess_resolve_builtins(yexpr_t* expr); - -/** - * @brief Replace symbolic IDs with per-scope-unique numeric IDs. - * - * Run order: 2 - */ -void ypreprocess_refs(yexpr_t* expr); - -void ypreprocess_refs_repl(yexpr_t* repl_expr, ypreprocess_state_t** state); /** * @} */
--- a/src/preprocess_test.c Fri Sep 27 14:30:38 2019 +0200 +++ b/src/preprocess_test.c Fri Sep 27 14:33:38 2019 +0200 @@ -9,112 +9,8 @@ #include <stdio.h> -// The very first successfully executable program! -void test_preprocess_defn(void) { - fprintf(stderr, "test_preprocess_defn ===========\n"); - - // my-func adds the two arguments plus 42. - ystr_t input = ystr_new( - "(defn my-func (arg1 arg2) (+ arg1 arg2) (undef) (+ arg1 arg2 41))" - "(my-func 42 (my-func 43 (my-func 44 0)))"); // (((44 + 0 + 41) + 43 + - // 41) + 42 + 41) == 252 - yexpr_t program = yexpr_new(); - ystr_t error = ystr_new(NULL); - assert(yparse_str(&input, &program, &error)); - - ypreprocess_resolve_builtins(&program); - yexpr_debug(&program); - ypreprocess_refs(&program); - yexpr_debug(&program); - - /// We can't do these anymore, as functions are now anonymous: - /* - fprintf(stderr, "arg ref 0: %lu\n", - YVEC_AT(&func->value.func.args, 0, yarg_desc_t)->argref.ref.id); - fprintf(stderr, "arg ref 1: %lu\n", - YVEC_AT(&func->value.func.args, 1, yarg_desc_t)->argref.ref.id); - fprintf( - stderr, "arg name 0: %s\n", - ystr_str(&YVEC_AT(&func->value.func.args, 0, yarg_desc_t)->argname)); - fprintf( - stderr, "arg name 1: %s\n", - ystr_str(&YVEC_AT(&func->value.func.args, 1, yarg_desc_t)->argname)); - */ - - yeval_state_t state; - state.call_stack = YVEC_NEW(NULL, 0, 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_NUMBER); - assert(252 == result.value.n); - yexpr_debug(&result); - - ystr_destroy(&input); - yexpr_destroy(&program); - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); - yexpr_destroy(&result); -} - -void test_preprocess_refs(void) { - fprintf(stderr, "test_preprocess_refs ===========\n"); - - // my-func adds the two arguments plus 42. - ystr_t input = ystr_new( - "(defn my-func (arg) (+ arg 1))" - "(let a 33)" - "(let b 44)" - "(+ a b)" - "(let a b)" - "(my-func a)"); - yexpr_t program = yexpr_new(); - ystr_t error = ystr_new(NULL); - assert(yparse_str(&input, &program, &error)); - - yexpr_debug(&program); - ypreprocess_resolve_builtins(&program); - yexpr_debug(&program); - ypreprocess_refs(&program); - yexpr_debug(&program); - - yeval_state_t state; - state.call_stack = YVEC_NEW(NULL, 0, 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_NUMBER); - assert(45 == result.value.n); - yexpr_debug(&result); - - yexpr_destroy(&program); - ystr_destroy(&input); - ystr_destroy(&error); - yvec_destroy(&state.call_stack); - yvec_destroy(&state.locals); -} - -void test_preprocess_closure(void) { - fprintf(stderr, "test_preprocess_closure ==============\n"); - - ystr_t input = ystr_new("(defn outer (x) (defn inner () (+ x 1)))"); - yexpr_t program = yexpr_new(); - ystr_t error = ystr_new(NULL); - assert(yparse_str(&input, &program, &error)); - - yexpr_debug(&program); - ypreprocess_resolve_builtins(&program); - yexpr_debug(&program); - ypreprocess_refs(&program); - yexpr_debug(&program); - - yexpr_destroy(&program); - ystr_destroy(&input); - ystr_destroy(&error); -} int main(void) { - test_preprocess_defn(); - test_preprocess_refs(); - test_preprocess_closure(); fprintf(stderr, "global destroy ===========\n"); yvalue_free_all(); yatom_free_all();
--- a/src/value.c Fri Sep 27 14:30:38 2019 +0200 +++ b/src/value.c Fri Sep 27 14:33:38 2019 +0200 @@ -4,329 +4,4 @@ #include <search.h> #include <string.h> -/// An invalid value ID. -const yvalue_id_t YVALUE_INVALID_ID = UINT64_MAX - 1; -/// Use YVALUE_INSERT in `yvalue_set()` to insert a new value. -const yref_t YVALUE_INSERT = {.ref.id = (0xff00000000000000 | UINT64_MAX) - 2}; - -// TODO: Adjust when programs are bigger. -static const size_t YVALUE_INITIAL_VALUES = 256, YVALUE_MAX_VALUES = 1024; -// ff marks IDs as such. -static const yvalue_id_t YVALUE_COUNTER_OFFSET = 0xff10101010101010; - -// ylisp keeps tables containing all named values. Unnamed values are -// represented as yexpr_t, whereas named values are referenced by their name -// (symbol) or ID (numerical ID corresponding to it). -// For best performance, symbols should be translated to IDs once and then used -// as IDs later. -// -// The tables are: -// 1. Name (char*, heap, owned by hashtable) -> ID (yvalue_id_t) (Hashtable - -// slow) -// 2. ID (yvalue_id_t) -> Value (yvalue_t) (yvec_t - fast) -// -// Invariants: -// 1. Every symbol has a corresponding place in the value table once it has -// been created. - -// Marker bytes to distinguish yref_t without an extra field. -static const yvalue_id_t YVALUE_ID_MARKER = 0xff00000000000000; -static const yvalue_id_t YVALUE_CALL_STACK_MARKER = 0xfe00000000000000; -static const yvalue_id_t YVALUE_LOCAL_MARKER = 0xfd00000000000000; -static const yvalue_id_t YVALUE_INVALID_MARKER = 0xee00000000000000; - -static bool yvalue_table_initialized = false; -static struct hsearch_data yvalue_name_table; -static yvec_t yvalue_table; // vector of yvalue_t -static yvec_t yvalue_names; // vector of char* -// TODO: Enable garbage collection/reuse instead of monotonic counter. (e.g. -// reference-counting in different table) -static yvalue_id_t yvalue_counter = YVALUE_COUNTER_OFFSET; -static yvalue_id_t yvalue_local_counter = YVALUE_LOCAL_MARKER; - -static inline void yvalue_init_tables(void) { - if (yvalue_table_initialized) return; - yvalue_table_initialized = true; - hcreate_r(YVALUE_MAX_VALUES, &yvalue_name_table); - YVEC_INIT(&yvalue_table, YVALUE_INITIAL_VALUES, yvalue_t); - YVEC_INIT(&yvalue_names, YVALUE_INITIAL_VALUES, char*); -} - -bool yvalue_is_valid(yvalue_id_t id) { - return (id & YVALUE_ID_MARKER) == YVALUE_ID_MARKER && id < yvalue_counter; -} - -YREF_TYPE yref_type(yref_t* ref) { - if ((ref->ref.id & YVALUE_ID_MARKER) == YVALUE_ID_MARKER) { - return YREF_ID; - } - if ((ref->ref.id & YVALUE_CALL_STACK_MARKER) == YVALUE_CALL_STACK_MARKER) { - return YREF_STACK; - } - if ((ref->ref.id & YVALUE_LOCAL_MARKER) == YVALUE_LOCAL_MARKER) { - return YREF_LOCAL; - } - if ((ref->ref.id & YVALUE_INVALID_MARKER) == YVALUE_INVALID_MARKER) { - return YREF_UNDEF; - } - return YREF_SYM; -} - -yvalue_id_t yvalue_create(void); - -yref_t yref_new(void) { - yref_t ref = {.ref.id = yvalue_create()}; - return ref; -} - -yref_t yref_new_id(yvalue_id_t id) { - assert(yvalue_is_valid(id)); - yref_t ref = {.ref.id = id}; - return ref; -} - -yref_t yref_new_str(ystr_t* sym) { - yref_t ref = {.ref.sym = *sym}; - return ref; -} - -yref_t yref_new_c(const char* sym) { - yref_t ref = {.ref.sym = ystr_new(sym)}; - return ref; -} - -yref_t yref_new_cix(size_t ix) { - yref_t ref = {.ref.id = ix | YVALUE_CALL_STACK_MARKER}; - return ref; -} - -yref_t yref_new_local(void) { - yref_t ref = {.ref.id = yvalue_local_counter++}; - return ref; -} - -yref_t yref_new_local_from_id(yvalue_id_t id) { - yref_t ref = {.ref.id = id}; - assert(yref_type(&ref) == YREF_LOCAL); - return ref; -} - -yref_t yref_new_undef(void) { - yref_t ref = {.ref.id = YVALUE_INVALID_MARKER}; - return ref; -} - -bool yref_eq(yref_t* a, yref_t* b) { - if (yref_type(a) != yref_type(b)) return false; - YREF_TYPE t = yref_type(a); - switch (t) { - case YREF_SYM: - return 0 == ystr_cmp(&a->ref.sym, &b->ref.sym); - case YREF_ID: - case YREF_LOCAL: - return a->ref.id == b->ref.id; - case YREF_STACK: - return a->ref.sid == b->ref.sid; - default: - return false; - } -} - -uint64_t yref_cix(yref_t* ref) { return ref->ref.id ^ YVALUE_CALL_STACK_MARKER; } - -yref_t yref_clone(yref_t* ref) { - switch (yref_type(ref)) { - case YREF_SYM: - // Copy string. - return yref_new_c(ystr_str(&ref->ref.sym)); - case YREF_ID: - case YREF_STACK: - case YREF_LOCAL: - case YREF_UNDEF: - return *ref; - default: - assert(yref_type(ref) == YREF_SYM || yref_type(ref) == YREF_ID || - yref_type(ref) == YREF_STACK); - return yref_new(); - } -} - -yref_t yvalue_clone(yref_t* ref) { - yvalue_t* orig = yvalue_get(*ref); - assert(orig->typ == YVALUE_EXPR || orig->typ == YVALUE_FUNC); - if (orig->typ == YVALUE_FUNC) { - // Functions are immutable for now. - return *ref; - } else if (orig->typ == YVALUE_EXPR) { - yvalue_t new = {.typ = YVALUE_EXPR, - .value.expr = yexpr_copy(&orig->value.expr)}; - return yvalue_set(YVALUE_INSERT, &new); - } - return yref_new_id(YVALUE_INVALID_ID); -} - -ystr_t yref_debug(yref_t* ref) { - YREF_TYPE typ = yref_type(ref); - if (typ == YREF_ID) { - ystr_t s = ystr_new(NULL); - ystr_build(&s, "id:<%llu>", ref->ref.id); - return s; - } else if (typ == YREF_SYM) { - ystr_t s = ystr_new(NULL); - ystr_build(&s, "id:<\"%s\">", ystr_str(&ref->ref.sym)); - return s; - } else if (typ == YREF_STACK) { - ystr_t s = ystr_new(NULL); - ystr_build(&s, "id:<stackrel:%lld>", -(int64_t)yref_cix(ref)); - return s; - } else if (typ == YREF_LOCAL) { - ystr_t s = ystr_new(NULL); - ystr_build(&s, "id:<local:%lld>", ref->ref.sid); - return s; - } - return ystr_new("id:INVALID"); -} - -void yref_destroy(yref_t* ref) { - if (ref == NULL) return; - if (yref_type(ref) == YREF_SYM) { - ystr_destroy(&ref->ref.sym); - } -} - -void yvalue_destroy(yvalue_t* val) { - switch (val->typ) { - case YVALUE_EXPR: - yexpr_destroy(&val->value.expr); - break; - default: - break; - } - memset(val, 0, sizeof(yvalue_t)); -} - -yvalue_id_t yvalue_resolve_symbol(ystr_t* sym) { - if (ystr_len(sym) == 0) return YVALUE_INVALID_ID; - yvalue_init_tables(); - - const char* sym_s = ystr_str(sym); - ENTRY want = {NULL, NULL}; - ENTRY* result; - - want.key = (char*)sym_s; // Valid, key is not modified. - hsearch_r(want, FIND, &result, &yvalue_name_table); - if (result == NULL) return YVALUE_INVALID_ID; - return (yvalue_id_t)result->data; -} - -yvalue_id_t yvalue_create(void) { - yvalue_init_tables(); - - // Create new value entry. - yvalue_t empty; - memset(&empty, 0, sizeof(yvalue_t)); - YVEC_PUSH(&yvalue_table, &empty); - return yvalue_counter++; -} - -yvalue_id_t yvalue_resolve_or_create_symbol(ystr_t* sym) { - yvalue_init_tables(); - - yvalue_id_t id = yvalue_resolve_symbol(sym); - if (id != YVALUE_INVALID_ID) return id; - yvalue_id_t new_id = yvalue_counter++; - - // Create new value entry. - yvalue_t empty; - memset(&empty, 0, sizeof(yvalue_t)); - YVEC_PUSH(&yvalue_table, &empty); - - ENTRY new = {.key = strdup(ystr_str(sym)), .data = (void*)new_id}; - ENTRY* result; - hsearch_r(new, ENTER, &result, &yvalue_name_table); - YVEC_PUSH(&yvalue_names, &new.key); - return new_id; -} - -void yvalue_resolve_or_create_ref(yref_t* ref) { - yvalue_init_tables(); - YREF_TYPE typ = yref_type(ref); - - if (typ == YREF_SYM) { - yvalue_id_t id = yvalue_resolve_or_create_symbol(&ref->ref.sym); - // CAREFUL: This may cause double-frees if `ref` is copied from another - // ref. - ystr_destroy(&ref->ref.sym); - ref->ref.id = id; - } -} - -yref_t yvalue_set(yref_t ref, yvalue_t* val) { - yvalue_init_tables(); - YREF_TYPE typ = yref_type(&ref); - - // ref == YVALUE_INSERT - if (typ == YREF_ID && ref.ref.id == YVALUE_INSERT.ref.id) { - // Unnamed value. - ref = yref_new(); - yvalue_t* valp = yvalue_get(ref); - assert(valp != NULL); - *valp = *val; - return ref; - } else { - // Resolve reference. - yvalue_id_t id; - if (typ == YREF_ID) { - id = ref.ref.id; - } else { - id = yvalue_resolve_or_create_symbol(&ref.ref.sym); - assert(yvalue_is_valid(id)); - ref = yref_new_id(id); - } - yvalue_t* valp = yvalue_get(ref); - assert(valp != NULL); - if (valp->typ == YVALUE_EXPR) yexpr_destroy(&valp->value.expr); - *valp = *val; - return ref; - } -} - -yvalue_t* yvalue_get(yref_t ref) { - yvalue_init_tables(); - YREF_TYPE typ = yref_type(&ref); - - if (typ == YREF_ID) { - assert(ref.ref.id <= yvalue_counter); - yvalue_t* vp = YVEC_AT(&yvalue_table, - ref.ref.id - YVALUE_COUNTER_OFFSET, yvalue_t); - assert(vp != NULL); - return vp; - } else if (typ == YREF_SYM) { - yvalue_id_t id = yvalue_resolve_symbol(&ref.ref.sym); - if (id == YVALUE_INVALID_ID) { - return NULL; - } - ref.ref.id = id; - return yvalue_get(ref); - } else { - return NULL; - } -} - -void yvalue_free_all(void) { - size_t freed = 0; - for (yvalue_id_t i = 0; i < yvalue_counter - YVALUE_COUNTER_OFFSET; i++) { - yvalue_t* val = YVEC_AT(&yvalue_table, i, yvalue_t); - if (val->typ == YVALUE_EXPR) yexpr_destroy(&val->value.expr); - if (val->typ == YVALUE_FUNC) yfunc_destroy(&val->value.func); - freed += 1; - } - for (size_t i = 0; i < yvalue_names.len; i++) { - free(*YVEC_AT(&yvalue_names, i, char*)); - } - yvec_destroy(&yvalue_names); - hdestroy_r(&yvalue_name_table); - yvec_destroy(&yvalue_table); -} -
--- a/src/value.h Fri Sep 27 14:30:38 2019 +0200 +++ b/src/value.h Fri Sep 27 14:33:38 2019 +0200 @@ -20,178 +20,6 @@ * @{ */ -// See expr.h for definitions of yref_t, yvalue_id_t, YREF_TYPE. - -/// An invalid value ID. -const yvalue_id_t YVALUE_INVALID_ID; - -/// Use YVALUE_INSERT in `yvalue_set()` to insert a new value. -const yref_t YVALUE_INSERT; - -/// Type of a `yvalue_t` value. -enum YVALUE_TYPE { - YVALUE_UNDEF = 0, - YVALUE_FUNC = 1, - YVALUE_EXPR = 2, -}; - -/** - * @brief Checks if the id is a valid ID. - */ -bool yvalue_is_valid(yvalue_id_t id); - -/** - * @brief Return type of a reference. - */ -YREF_TYPE yref_type(yref_t* ref); - -/** - * Create a new reference pointing to the same value as `ref`. The caller - * assumes ownership of the returned reference and needs to destroy it later. - */ -yref_t yref_clone(yref_t* ref); - -/** - * Create a new unnamed reference. - */ -yref_t yref_new(void); - -/** - * Create a new reference with the given ID. - */ -yref_t yref_new_id(yvalue_id_t id); - -/** - * Create a new reference to the given symbol. Takes ownership of `sym`, it must - * not be freed afterwards. - */ -yref_t yref_new_str(ystr_t* sym); - -/** - * Create a new reference to the given symbol. - */ -yref_t yref_new_c(const char* sym); - -/** - * Create a new reference to function argument. - */ -yref_t yref_new_cix(size_t ix); - -/** - * Create a new unique "local" reference. - */ -yref_t yref_new_local(void); - -/** - * Create a local reference from a value. `id` must have been generated by yref_new_local(). - */ -yref_t yref_new_local_from_id(yvalue_id_t id); - -/** - * Create a reference of type YREF_UNDEF. - */ -yref_t yref_new_undef(void); - -/** - * Return the stack-relative index of ref. - */ -uint64_t yref_cix(yref_t* ref); - -/** - * Return true if the references are identical. - */ -bool yref_eq(yref_t* a, yref_t* b); - -/** - * Create a debug description of ref. - */ -ystr_t yref_debug(yref_t* ref); - -/** - * @brief Deallocate memory used by the reference itself. - */ -void yref_destroy(yref_t* ref); - -/** - * @brief Copies the contents of a reference to a new slot and returns a new - * reference. (not for functions, which are immutable for now) - */ -yref_t yvalue_clone(yref_t* ref); - -/** - * @brief A value with a name, stored in the global value table to be referenced - * by name or ID. To be used as pointer (not value). - * - * Expressions or functions assigned to a yvalue_t are owned by it; do not - * destroy them. Use `yvalue_destroy()` instead. - */ -typedef struct { - union { - /// A function to be called. Functions are immutable, they can not be - // updated once set. - yfunc_t func; - /// An expression (often a scalar number/string/atom) - yexpr_t expr; - } value; - /// Type of a value. - enum YVALUE_TYPE typ; -} yvalue_t; - -/** - * @brief Create an unnamed value ID. - */ -yvalue_id_t yvalue_create(void); - -/** - * @brief Free all memory used by a value. - */ -void yvalue_destroy(yvalue_t* val); - -/** - * @brief Resolve a symbol to an ID. Caller retains ownership of `sym`. - */ -yvalue_id_t yvalue_resolve_symbol(ystr_t* sym); - -/** - * @brief Resolve a symbol to an ID or create a new ID if one doesn't exist - * already. Also allocates a place in the value table. Caller retains ownership - * of `sym`. - */ -yvalue_id_t yvalue_resolve_or_create_symbol(ystr_t* sym); - -/** - * @brief Translate a symbol in `ref` to an ID, or create a new ID. `ref` is - * updated to be an ID ref. Caller retains ownership of `ref`. - * - * NOTE: If `ref` is symbolic, this function frees the symbol string. This - * means that it is an error to supply a pointer to a reference that is copied - * from another reference that is freed later (double-free). - */ -void yvalue_resolve_or_create_ref(yref_t* ref); - -/** - * @brief Set a value referenced by `ref`. If a new anonymous reference should - * be created, use the special ref value `YVALUE_INSERT`. The value pointed to - * by val may not be destroyed afterwards (the variable itself can be - * deallocated though). If the reference is a symbolic reference that does not - * exist yet, it is resolved and created first. - * - * The reference at which the value was inserted at is returned (this is only - * useful if YVALUE_INSERT is used). - */ -yref_t yvalue_set(yref_t ref, yvalue_t* val); - -/** - * @brief Return a pointer to the value referenced by `ref`. The pointed-to - * value may be modified to update the value. - */ -yvalue_t* yvalue_get(yref_t ref); - -/** - * @brief Free all stored values. - */ -void yvalue_free_all(void); - /** * @} */