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);
-
 /**
  * @}
  */