view src/expr.c @ 148:1620bde57dcb

expr: Implement yexpr_equal
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 03 Sep 2019 12:16:44 +0200
parents 63650268d006
children 88fec15abdbc
line wrap: on
line source

#include "expr.h"

#include "built-ins.h"
#include "value.h"

#include "base/str.h"
#include "base/vec.h"

#include <assert.h>
#include <stdio.h>

void yexpr_init(yexpr_t* v) {
    v->typ = YEXPR_UNDEF;
    memset(&v->value, 0, sizeof(v->value));
}

yexpr_t yexpr_new(void) {
    yexpr_t e;
    yexpr_init(&e);
    return e;
}

void yexpr_destroy(yexpr_t* v) {
    if (v == NULL) return;
    switch (v->typ) {
        // Cases where the value is internal to the yexpr_t value:
        case YEXPR_UNDEF:
        case YEXPR_NUMBER:
        case YEXPR_ATOM:
        case YEXPR_BUILTIN:
            break;
        // External values:
        case YEXPR_EXCEPTION:
        case YEXPR_STRING:
            ystr_destroy(&v->value.str);
            break;
        case YEXPR_REF:
            // TODO: maybe reference-count the reference here.
            yref_destroy(&v->value.ref);
            break;
        case YEXPR_LIST:
#define list (&v->value.list)
            for (size_t i = 0; i < list->len; i++)
                yexpr_destroy(YVEC_AT(list, i, yexpr_t));
            yvec_destroy(list);
#undef list
            break;
        default:
            assert(false /* handle all cases!! */);
    }
    memset(v, 0, sizeof(yexpr_t));
}

yexpr_t yexpr_copy(yexpr_t* orig) {
    yexpr_t copy = yexpr_new();
    copy.typ = orig->typ;
    switch (orig->typ) {
        // Cases where the value is internal to the yexpr_t value:
        case YEXPR_UNDEF:
        case YEXPR_NUMBER:
        case YEXPR_ATOM:
        case YEXPR_BUILTIN:
            copy = *orig;
            break;
        // External values:
        case YEXPR_EXCEPTION:
        case YEXPR_STRING:
            copy = *orig;
            ystr_t strcopy = ystr_new(ystr_str(&orig->value.str));
            copy.value.str = strcopy;
            break;
        case YEXPR_REF:
            // TODO: maybe reference-count the references?
            copy = *orig;
            copy.value.ref = yref_clone(&orig->value.ref);
            break;
        case YEXPR_LIST:
            // Recursively copy list...
            yvec_init(&copy.value.list, orig->value.list.size,
                      orig->value.list.len);
            yvec_resize(&copy.value.list, orig->value.list.len);
#define _list (&orig->value.list)
#define _newlist (&copy.value.list)
            for (size_t i = 0; i < _list->len; i++)
                *YVEC_AT(_newlist, i, yexpr_t) =
                    yexpr_copy(YVEC_AT(_list, i, yexpr_t));
#undef _list
#undef _newlist
            break;
        default:
            assert(false /* handle all cases!! */);
    }

    return copy;
}

static const size_t INITIAL_LIST_SIZE = 4;

void yexpr_set_number(yexpr_t* v, long long n) {
    yexpr_destroy(v);
    v->typ = YEXPR_NUMBER;
    v->value.n = n;
}

void yexpr_set_builtin(yexpr_t* v, YBUILTIN_TYPE b) {
    yexpr_destroy(v);
    v->typ = YEXPR_BUILTIN;
    v->value.builtin = b;
}

void yexpr_set_str(yexpr_t* v, ystr_t str) {
    yexpr_destroy(v);
    v->typ = YEXPR_STRING;
    v->value.str = str;
}

void yexpr_set_atom(yexpr_t* v, yatom_t atom) {
    yexpr_destroy(v);
    v->typ = YEXPR_ATOM;
    v->value.atom = atom;
}

void yexpr_set_atom_name(yexpr_t* v, const char* atom_name) {
    yexpr_destroy(v);
    v->typ = YEXPR_ATOM;
    v->value.atom = yatom_get_or_add(atom_name);
}

void yexpr_set_ref(yexpr_t* v, yref_t ref) {
    yexpr_destroy(v);
    v->typ = YEXPR_REF;
    v->value.ref = ref;
}

void yexpr_set_list(yexpr_t* v, yvec_t vec) {
    yexpr_destroy(v);
    v->typ = YEXPR_LIST;
    v->value.list = vec;
}

void yexpr_set_exception(yexpr_t* v, ystr_t message) {
    yexpr_destroy(v);
    v->typ = YEXPR_EXCEPTION;
    v->value.str = message;
}

void yexpr_append_value(yexpr_t* v, yexpr_t new_val) {
    assert(v->typ == YEXPR_UNDEF || v->typ == YEXPR_LIST);

    if (v->typ == YEXPR_UNDEF) {
        v->typ = YEXPR_LIST;
        yvec_init(&v->value.list, sizeof(yexpr_t), INITIAL_LIST_SIZE);
    }
    YVEC_PUSH(&v->value.list, &new_val);
}

bool yexpr_equal(yexpr_t* a, yexpr_t* b) {
    if (a->typ != b->typ) return false;
    switch (a->typ) {
        case YEXPR_UNDEF:
            return true;
        case YEXPR_ATOM:
            return a->value.atom == b->value.atom;
        case YEXPR_BUILTIN:
            return a->value.builtin == b->value.builtin;
        case YEXPR_NUMBER:
            return a->value.n == b->value.n;
        case YEXPR_STRING:
        case YEXPR_EXCEPTION:
            return 0 == ystr_cmp(&a->value.str, &b->value.str);
        case YEXPR_REF:
            if (yref_eq(&a->value.ref, &b->value.ref)) return true;
            yvalue_t* aexpr = yvalue_get(a->value.ref);
            yvalue_t* bexpr = yvalue_get(b->value.ref);
            if (aexpr->typ != bexpr->typ) return false;
            return yexpr_equal(&aexpr->value.expr, &bexpr->value.expr);
        case YEXPR_LIST:
            if (a->value.list.len != b->value.list.len) return false;
            for (size_t i = 0; i < a->value.list.len; i++) {
                if (!yexpr_equal(YVEC_AT(&a->value.list, i, yexpr_t),
                                 YVEC_AT(&b->value.list, i, yexpr_t)))
                    return false;
            }
            return true;
        default:
            assert(false /* missing expression type! */);
    }
}

void yexpr_init_or_extend(yexpr_t* expr, yexpr_t arg) {
    if (expr->typ == YEXPR_UNDEF) {
        yexpr_append_value(expr, arg);
    } else if (expr->typ == YEXPR_LIST) {
        yexpr_append_value(expr, arg);
    } else {
        yexpr_t list;
        yexpr_init(&list);
        yexpr_append_value(&list, *expr);
        yexpr_append_value(&list, arg);
        *expr = list;
    }
}

ystr_t yexpr_debug_str(yexpr_t* expr) {
    ystr_t tmp = ystr_new(NULL), tmp2;
    if (expr == NULL) {
        ystr_set(&tmp, "NULL");
        return tmp;
    }
    switch (expr->typ) {
        case YEXPR_UNDEF:
            ystr_build(&tmp, "<undef>");
            break;
        case YEXPR_NUMBER:
            ystr_build(&tmp, "%lld", expr->value.n);
            break;
        case YEXPR_STRING:
            ystr_append(&tmp, &expr->value.str);
            break;
        case YEXPR_ATOM:
            ystr_build(&tmp, "%s", yatom_name(expr->value.atom));
            break;
        case YEXPR_REF:
            tmp2 = yref_debug(&expr->value.ref);
            ystr_append(&tmp, &tmp2);
            ystr_destroy(&tmp2);
            break;
        case YEXPR_BUILTIN:
            ystr_build(&tmp, "%s", ybuiltin_name(expr->value.builtin));
            break;
        case YEXPR_LIST:
            ystr_build(&tmp, "(");
            yexpr_t* var = YVEC_AT(&expr->value.list, 0, yexpr_t);
            for (int i = 0; i < expr->value.list.len;
                 var = YVEC_AT(&expr->value.list, ++i, yexpr_t)) {
                ystr_t sub = yexpr_debug_str(var);
                ystr_append(&tmp, &sub);
                ystr_build(&tmp, " ");
                ystr_destroy(&sub);
            }
            ystr_build(&tmp, ")");
            break;
        case YEXPR_EXCEPTION:
            ystr_build(&tmp, "EXCEPTION<%s>", ystr_str(&expr->value.str));
            break;
        default:
            assert(false /* Unhandled YEXPR_TYPE */);
    }
    return tmp;
}

void yexpr_debug(yexpr_t* expr) {
    ystr_t repr = yexpr_debug_str(expr);
    fputs(ystr_str(&repr), stderr);
    fputs("\n", stderr);
    ystr_destroy(&repr);
}