view src/built-ins_test.c @ 151:fe5d01fa28ee

value, expr: Test "==" methods and enable freeing all symbols
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 03 Sep 2019 12:57:53 +0200
parents aa551e42de49
children f91caf9fe066
line wrap: on
line source

#include "built-ins.h"

#include "atom.h"
#include "parse.h"
#include "preprocess.h"
#include "value.h"

void test_builtin_translate(void) {
    fprintf(stderr, "test_builtin_translate ============\n");

    yexpr_t expr = yexpr_new();

    yexpr_set_atom(&expr, yatom_get_or_add("atom"));
    assert(!ybuiltin_translate(&expr));
    assert(YEXPR_ATOM == expr.typ);

    yexpr_set_ref(&expr, yref_new());
    assert(!ybuiltin_translate(&expr));
    assert(YEXPR_REF == expr.typ);

    yexpr_set_ref(&expr, yref_new_c("my-ref"));
    assert(!ybuiltin_translate(&expr));
    assert(YEXPR_REF == expr.typ);
    yexpr_destroy(&expr);

    yexpr_set_ref(&expr, yref_new_c("let"));
    assert(ybuiltin_translate(&expr));
    assert(YEXPR_BUILTIN == expr.typ);
    assert(YBUILTIN_LET == expr.value.builtin);
}

void test_builtin_for(void) {
    fprintf(stderr, "test_builtin_for ============\n");

    ystr_t input = ystr_new("(for loopvar (1 2 3) loopvar)"),
           error = ystr_new(NULL);
    yexpr_t program = yexpr_new();
    assert(yparse_str(&input, &program, &error));
    yexpr_t* for_expr = YVEC_AT(&program.value.list, 0, yexpr_t);
    assert(for_expr->typ == YEXPR_LIST);

    yexpr_debug(&program);
    ypreprocess_resolve_builtins(&program);
    ypreprocess_refs(&program);
    yexpr_debug(&program);

    yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)};
    YVEC_PUSH(&state.call_stack, for_expr);

    yexpr_t result = ybuiltin_run(YBUILTIN_FOR, &state);
    assert(result.typ == YEXPR_LIST);

    yexpr_debug(&result);

    yvec_destroy(&state.call_stack);
    yexpr_destroy(&program);
    yexpr_destroy(&result);
    ystr_destroy(&input);
}

void test_builtin_for_complex(void) {
    fprintf(stderr, "test_builtin_for_complex ============\n");

    ystr_t input = ystr_new("(for loopvar (1 2 3) (+ loopvar 1 2))"),
           error = ystr_new(NULL);
    yexpr_t program = yexpr_new();
    assert(yparse_str(&input, &program, &error));
    yexpr_t* for_expr = YVEC_AT(&program.value.list, 0, yexpr_t);
    assert(for_expr->typ == YEXPR_LIST);

    ypreprocess(&program);
    yexpr_debug(for_expr);

    yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)};
    YVEC_PUSH(&state.call_stack, for_expr);

    yexpr_t result = ybuiltin_run(YBUILTIN_FOR, &state);
    assert(result.typ == YEXPR_LIST);

    // (1 2 3) should each be increased by 3.
    yexpr_debug(&result);
    assert(4 == YVEC_AT(&result.value.list, 0, yexpr_t)->value.n);

    yvec_destroy(&state.call_stack);
    yexpr_destroy(&program);
    yexpr_destroy(&result);
    ystr_destroy(&input);
}

void test_builtin_for_noresult(void) {
    fprintf(stderr, "test_builtin_for_noresult ============\n");

    ystr_t input = ystr_new("(for loopvar (1 2 3) (undef))"),
           error = ystr_new(NULL);
    yexpr_t program = yexpr_new();
    assert(yparse_str(&input, &program, &error));
    yexpr_t* for_expr = YVEC_AT(&program.value.list, 0, yexpr_t);
    assert(for_expr->typ == YEXPR_LIST);

    ypreprocess_resolve_builtins(&program);
    ypreprocess_refs(&program);
    yexpr_debug(for_expr);

    yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)};
    YVEC_PUSH(&state.call_stack, for_expr);

    yexpr_t result = ybuiltin_run(YBUILTIN_FOR, &state);
    assert(result.typ == YEXPR_LIST);
    assert(result.value.list.len == 0);

    yvec_destroy(&state.call_stack);
    yexpr_destroy(&program);
    ystr_destroy(&input);
    yexpr_destroy(&result);
}

void test_builtin_let(void) {
    fprintf(stderr, "test_builtin_let ============\n");

    yexpr_t ref_expr = yexpr_new();
    yexpr_t let_expr = yexpr_new();
    yref_t ref = yref_new();
    yexpr_t val_expr = yexpr_new();
    yexpr_t let_call = yexpr_new();

    // (let <ref> 'my-atom)
    yexpr_set_ref(&ref_expr, ref);
    yexpr_set_builtin(&let_expr, YBUILTIN_LET);
    yexpr_set_atom(&val_expr, yatom_get_or_add("my-atom"));

    yvec_t let_args;
    YVEC_INIT(&let_args, 4, yexpr_t);
    YVEC_PUSH(&let_args, &let_expr);
    YVEC_PUSH(&let_args, &ref_expr);
    YVEC_PUSH(&let_args, &val_expr);

    yexpr_set_list(&let_call, let_args);

    // NOTE: The expressions should be copied, but for this test we can store
    // them directly.
    yeval_state_t state;
    yvec_t* stack = &state.call_stack;
    YVEC_INIT(stack, 4, yexpr_t);
    YVEC_PUSH(stack, &let_call);

    yexpr_t result = ybuiltin_run(YBUILTIN_LET, &state);
    yexpr_debug(&result);
    assert(result.typ == YEXPR_UNDEF);
    assert(stack->len == 0);

    yvalue_t* stored = yvalue_get(ref);
    assert(stored->typ == YVALUE_EXPR);
    assert(stored->value.expr.typ == YEXPR_ATOM);
    assert(stored->value.expr.value.atom == val_expr.value.atom);

    // Type mismatch exception.
    yexpr_set_number(YVEC_AT(&let_call.value.list, 1, yexpr_t), 124);
    YVEC_PUSH(stack, &let_call);
    result = ybuiltin_run(YBUILTIN_LET, &state);
    yexpr_debug(&result);
    assert(result.typ == YEXPR_EXCEPTION);
    assert(stack->len == 0);
    yexpr_destroy(&result);

    // Not enough arguments on stack.
    result = ybuiltin_run(YBUILTIN_LET, &state);
    assert(result.typ == YEXPR_EXCEPTION);
    fputs(ystr_str(&result.value.str), stderr);
    fputs("\n", stderr);
    assert(0 ==
           ystr_cmp_str(
               &result.value.str,
               "BUILTIN:LET: BUG: not enough arguments on call stack: NULL"));

    yexpr_destroy(&result);
    yexpr_destroy(&ref_expr);
    yexpr_destroy(&val_expr);
    yexpr_destroy(&let_call);
    yvec_destroy(stack);
};

void test_builtin_let_parsed(void) {
    fprintf(stderr, "test_builtin_let_parsed ============\n");

    ystr_t input = ystr_new("(let my-var (1 2 3))"), error = ystr_new(NULL);
    yexpr_t program = yexpr_new(), *let_expr;
    assert(yparse_str(&input, &program, &error));
    ypreprocess_resolve_builtins(&program);

    let_expr = YVEC_AT(&program.value.list, 0, yexpr_t);

    yeval_state_t state;
    YVEC_INIT(&state.call_stack, 4, yexpr_t);
    YVEC_PUSH(&state.call_stack, let_expr);
    yexpr_t result = ybuiltin_run(YBUILTIN_LET, &state);
    assert(result.typ == YEXPR_UNDEF);
    assert(state.call_stack.len == 0);

    // Retrieve stored value.
    yref_t my_var_ref = yref_new_c("my-var");
    yvalue_t* stored = yvalue_get(my_var_ref);
    assert(stored->typ == YVALUE_EXPR);
    assert(stored->value.expr.typ == YEXPR_LIST);
    assert(stored->value.expr.value.list.len == 3);
    assert(YVEC_AT(&stored->value.expr.value.list, 1, yexpr_t)->value.n == 2);

    yexpr_destroy(&result);
    yexpr_destroy(&program);
    yref_destroy(&my_var_ref);
    yvec_destroy(&state.call_stack);
    ystr_destroy(&input);
    ystr_destroy(&error);
}

void test_builtin_let_recall(void) {
    fprintf(stderr, "test_builtin_let_recall ============\n");

    ystr_t input = ystr_new(
        "(let my-var ('a 'b) \"cd\")"
        "(my-var)");
    ystr_t error = ystr_new(NULL);
    yexpr_t program = yexpr_new();
    assert(yparse_str(&input, &program, &error));
    assert(YEXPR_LIST == program.typ && 2 == program.value.list.len);

    ypreprocess_resolve_builtins(&program);

    yeval_state_t state = {.call_stack = YVEC_NEW(NULL, 0, yexpr_t)};
    // Evaluate whole program
    yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0);

    // Check that result is "cd"
    assert(YEXPR_LIST == result.typ && 1 == result.value.list.len);
    yexpr_t* inner = YVEC_AT(&result.value.list, 0, yexpr_t);
    assert(YEXPR_STRING == inner->typ);
    yexpr_debug(inner);
    assert(0 == ystr_cmp_str(&inner->value.str, "cd"));

    yvec_destroy(&state.call_stack);
    yexpr_destroy(&result);
    ystr_destroy(&error);
    ystr_destroy(&input);
    yexpr_destroy(&program);
}

void test_builtin_eq(void) {
    fprintf(stderr, "test_builtin_eq ============\n");

    ystr_t input = ystr_new(
        "(let var-a 1)"
        "(let var-b 'atom)"
        "(let var-c \"string\")"
        "((== var-a 1) (== 1 var-a) (== var-a var-a) (== for for) (== (1 2 3) "
        "(var-a 2 3)) (== var-b 'atom) (== "
        "'atom2 'atom2) (== "
        "var-c \"string\"))");
    ystr_t error = ystr_new(NULL);
    yexpr_t program = yexpr_new();
    assert(yparse_str(&input, &program, &error));

    yexpr_debug(&program);
    ypreprocess(&program);
    yexpr_debug(&program);

    yeval_state_t state;
    YVEC_INIT(&state.call_stack, 4, yexpr_t);
    yexpr_t result = yeval_list_return_last(&state, &program.value.list, 0);
    assert(result.typ == YEXPR_LIST);
    yexpr_debug(&result);
    for (size_t i = 0; i < result.value.list.len; i++) {
        yexpr_t* elem = YVEC_AT(&result.value.list, i, yexpr_t);
        assert(YEXPR_ATOM == elem->typ &&
               yatom_get_or_add("true") == elem->value.atom);
    }

    yexpr_destroy(&result);
    yexpr_destroy(&program);
    ystr_destroy(&input);
    ystr_destroy(&error);
    yvec_destroy(&state.call_stack);
}

int main(void) {
    test_builtin_translate();
    test_builtin_for();
    test_builtin_for_complex();
    test_builtin_for_noresult();
    test_builtin_let();
    test_builtin_let_parsed();
    test_builtin_let_recall();
    test_builtin_eq();
    yvalue_free_all();
    yatom_free_all();
    return 0;
}