changeset 44:6183b0c133bf

expr: Extend yexpr_t interface and add comprehensive tests
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 21 Aug 2019 22:43:55 +0200
parents 0efbcd20df2c
children 828d3b58f2cf
files gen/y.yy src/CMakeLists.txt src/expr.c src/expr.h src/expr_test.c src/value.c
diffstat 6 files changed, 185 insertions(+), 30 deletions(-) [+]
line wrap: on
line diff
--- a/gen/y.yy	Wed Aug 21 21:59:11 2019 +0200
+++ b/gen/y.yy	Wed Aug 21 22:43:55 2019 +0200
@@ -83,7 +83,7 @@
 inner_sexpr: inner_sexpr TOK_ATOM {
                 printf("found atom %s\n", $2);
                 yexpr_t atom;
-                yexpr_set_atom(&atom, strdup($2));
+                yexpr_set_atom_name(&atom, strdup($2));
                 yexpr_init_or_extend(&$1, atom);
                 $$ = $1;
            }
--- a/src/CMakeLists.txt	Wed Aug 21 21:59:11 2019 +0200
+++ b/src/CMakeLists.txt	Wed Aug 21 22:43:55 2019 +0200
@@ -22,3 +22,8 @@
 ADD_EXECUTABLE(value_test value_test.c)
 TARGET_LINK_LIBRARIES(value_test core gcov)
 YADD_TEST(value_test)
+
+# Expr test.
+ADD_EXECUTABLE(expr_test expr_test.c)
+TARGET_LINK_LIBRARIES(expr_test core gcov)
+YADD_TEST(expr_test)
--- a/src/expr.c	Wed Aug 21 21:59:11 2019 +0200
+++ b/src/expr.c	Wed Aug 21 22:43:55 2019 +0200
@@ -8,15 +8,15 @@
 #include <stdio.h>
 
 yref_t yref_new_id(yvalue_id_t id) {
-    yref_t ref = { .typ = YREF_ID, .ref.id = id };
+    yref_t ref = {.typ = YREF_ID, .ref.id = id};
     return ref;
 }
 yref_t yref_new_str(ystr_t* sym) {
-    yref_t ref = { .typ = YREF_SYM, .ref.sym = *sym };
+    yref_t ref = {.typ = YREF_SYM, .ref.sym = *sym};
     return ref;
 }
 yref_t yref_new_c(const char* sym) {
-    yref_t ref = { .typ = YREF_SYM, .ref.sym = ystr_new(sym) };
+    yref_t ref = {.typ = YREF_SYM, .ref.sym = ystr_new(sym)};
     return ref;
 }
 
@@ -25,55 +25,83 @@
     if (ref->typ == YREF_SYM) ystr_destroy(&ref->ref.sym);
 }
 
-void yexpr_set_number(yexpr_t* v, long long n) {
-    v->typ = YEXPR_NUMBER;
-    v->value.n = n;
-}
-
-void yexpr_set_str(yexpr_t* v, ystr_t str) {
-    v->typ = YEXPR_STRING;
-    v->value.str = str;
-}
-
 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) {
         case YEXPR_UNDEF:
         case YEXPR_NUMBER:
         case YEXPR_ATOM:
-            return;
+            break;
         case YEXPR_STRING:
             ystr_destroy(&v->value.str);
-            return;
+            break;
         case YEXPR_REF:
             // TODO: maybe reference-count the reference here.
             yref_destroy(&v->value.ref);
-            return;
+            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
-            return;
+            break;
         default:
             assert(false /* handle all cases!! */);
     }
+    memset(v, 0, sizeof(yexpr_t));
+}
+
+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_atom(yexpr_t* v, const char* atom_name) {
+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);
 }
 
-static const size_t INITIAL_LIST_SIZE = 4;
+void yexpr_set_ref(yexpr_t* v, yref_t ref) {
+    yexpr_destroy(v);
+    v->typ = YEXPR_REF;
+    v->value.ref = ref;
+}
 
-void yexpr_add_value(yexpr_t* v, yexpr_t new_val) {
+void yexpr_set_list(yexpr_t* v, yvec_t vec) {
+    yexpr_destroy(v);
+    v->typ = YEXPR_LIST;
+    v->value.list = vec;
+}
+
+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) {
@@ -85,14 +113,14 @@
 
 void yexpr_init_or_extend(yexpr_t* expr, yexpr_t arg) {
     if (expr->typ == YEXPR_UNDEF) {
-        yexpr_add_value(expr, arg);
+        yexpr_append_value(expr, arg);
     } else if (expr->typ == YEXPR_LIST) {
-        yexpr_add_value(expr, arg);
+        yexpr_append_value(expr, arg);
     } else {
         yexpr_t list;
         yexpr_init(&list);
-        yexpr_add_value(&list, *expr);
-        yexpr_add_value(&list, arg);
+        yexpr_append_value(&list, *expr);
+        yexpr_append_value(&list, arg);
         *expr = list;
     }
 }
@@ -111,6 +139,16 @@
         case YEXPR_ATOM:
             fprintf(stderr, "%s", yatom_name(expr->value.atom));
             break;
+        case YEXPR_REF:
+            if (expr->value.ref.typ == YREF_ID) {
+                fprintf(stderr, "id<%llu>", expr->value.ref.ref.id);
+            } else if (expr->value.ref.typ == YREF_SYM) {
+                fprintf(stderr, "id<\"%s\">",
+                        ystr_str(&expr->value.ref.ref.sym));
+            } else {
+                fprintf(stderr, "id<UNINITIALIZED>");
+            }
+            break;
         case YEXPR_LIST:
             fprintf(stderr, "(");
             yexpr_t* var = YVEC_AT(&expr->value.list, 0, yexpr_t);
--- a/src/expr.h	Wed Aug 21 21:59:11 2019 +0200
+++ b/src/expr.h	Wed Aug 21 22:43:55 2019 +0200
@@ -41,7 +41,8 @@
 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.
+ * 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);
 
@@ -94,6 +95,11 @@
 void yexpr_init(yexpr_t* v);
 
 /**
+ * Return an initialized yexpr_t.
+ */
+yexpr_t yexpr_new(void);
+
+/**
  * Deallocate all memory occupied by expr and any subexpressions.
  */
 void yexpr_destroy(yexpr_t* v);
@@ -111,19 +117,35 @@
 void yexpr_set_str(yexpr_t* v, ystr_t str);
 
 /**
- * Initialize a yexpr_t value with an atom.
+ * Initialize a yexpr_t with an atom.
+ */
+void yexpr_set_atom(yexpr_t* v, yatom_t atom);
+
+/**
+ * Initialize a yexpr_t value with an atom, referenced by its name.
  *
  * Causes a new atom to be created in the atoms table, or an existing one to be
  * referenced.
  */
-void yexpr_set_atom(yexpr_t* v, const char* atom_name);
+void yexpr_set_atom_name(yexpr_t* v, const char* atom_name);
+
+/**
+ * Set the value of `v` to be a reference `ref`. Ownership of `ref` goes to `v`,
+ * it may not be freed after the call.
+ */
+void yexpr_set_ref(yexpr_t* v, yref_t ref);
+
+/**
+ * Set the value of `v` to the list/vector `vec`. Ownership of `vec` is transferred to `v`; do not destroy `vec`.
+ */
+void yexpr_set_list(yexpr_t* v, yvec_t vec);
 
 /**
  * Initialize a yexpr_t value if not yet done and append another value to it.
  *
  * Invariants: `v` must be of type `YEXPR_LIST` or `YEXPR_UNDEF`.
  */
-void yexpr_add_value(yexpr_t* v, yexpr_t new_val);
+void yexpr_append_value(yexpr_t* v, yexpr_t new_val);
 
 /****** helpers for parsing ******/
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/expr_test.c	Wed Aug 21 22:43:55 2019 +0200
@@ -0,0 +1,89 @@
+#include "expr.h"
+
+#include <src/value.h>
+
+void test_expr_set(void) {
+    yexpr_t expr = yexpr_new();
+
+    // Test atoms.
+    yexpr_set_atom_name(&expr, "myatom");
+    assert(0 == strcmp("myatom", yatom_name(expr.value.atom)));
+    yexpr_set_atom(&expr, yatom_get_or_add("myatom2"));
+    assert(0 == strcmp("myatom2", yatom_name(expr.value.atom)));
+    yexpr_debug(&expr);
+    fputs("\n", stderr);
+    yexpr_destroy(&expr);
+    expr = yexpr_new();
+
+    // Test number.
+    yexpr_set_number(&expr, 12345);
+    assert(12345 == expr.value.n);
+    yexpr_debug(&expr);
+    fputs("\n", stderr);
+    yexpr_destroy(&expr);
+    expr = yexpr_new();
+
+    // Test string.
+    yexpr_set_str(&expr, ystr_new("hello"));
+    assert(0 == ystr_cmp_str(&expr.value.str, "hello"));
+    yexpr_debug(&expr);
+    fputs("\n", stderr);
+    yexpr_destroy(&expr);
+    expr = yexpr_new();
+
+    // Test refs, symbolic and numeric.
+    yexpr_set_ref(&expr, yref_new_c("my-ref"));
+    yexpr_debug(&expr);
+    fputs("\n", stderr);
+    yvalue_resolve_or_create_ref(&expr.value.ref);
+    assert(YREF_ID == expr.value.ref.typ);
+    yexpr_debug(&expr);
+    fputs("\n", stderr);
+    yexpr_destroy(&expr);
+    expr = yexpr_new();
+
+    // Test list.
+    yexpr_t src[3] = {yexpr_new(), yexpr_new(), yexpr_new()};
+    yexpr_set_number(&src[0], 123);
+    yexpr_set_number(&src[1], 124);
+    yexpr_set_number(&src[2], 125);
+    yvec_t vec = YVEC_NEW(src, 3, yexpr_t);
+    yexpr_set_list(&expr, vec);
+    assert(124 == (YVEC_AT(&expr.value.list, 1, yexpr_t))->value.n);
+
+    // Test append (first value to end).
+    yexpr_append_value(&expr, src[0]);
+    yexpr_append_value(&expr, src[2]);
+    assert(123 == (YVEC_AT(&expr.value.list, 3, yexpr_t))->value.n);
+    assert(125 == (YVEC_AT(&expr.value.list, 4, yexpr_t))->value.n);
+
+    yexpr_destroy(&expr);
+    expr = yexpr_new();
+
+    // Test init_or_extend.
+    yexpr_set_number(&expr, 12345);
+
+    yexpr_t src2[3] = {yexpr_new(), yexpr_new(), yexpr_new()};
+    yexpr_set_atom_name(&src2[0], "atom1");
+    yexpr_set_atom_name(&src2[1], "atom2");
+    yexpr_set_atom_name(&src2[2], "atom3");
+    yexpr_init_or_extend(&expr, src2[0]);
+    yexpr_init_or_extend(&expr, src2[2]);
+    // Expr now contains list of exprs: 12345, atom1, atom3.
+    yvec_t list = expr.value.list;
+    assert(12345 == YVEC_AT(&list, 0, yexpr_t)->value.n);
+    yatom_t got = YVEC_AT(&list, 1, yexpr_t)->value.atom;
+    assert(yatom_get_or_add("atom1") == got);
+    got = YVEC_AT(&list, 2, yexpr_t)->value.atom;
+    assert(yatom_get_or_add("atom3") == got);
+
+    yexpr_debug(&expr);
+    fputs("\n", stderr);
+
+    yexpr_destroy(&expr);
+}
+
+int main(int argc, char** argv) {
+    test_expr_set();
+    return 0;
+}
--- a/src/value.c	Wed Aug 21 21:59:11 2019 +0200
+++ b/src/value.c	Wed Aug 21 22:43:55 2019 +0200
@@ -48,8 +48,9 @@
             yexpr_destroy(&val->value.expr);
             break;
         default:
-            return;
+            break;
     }
+    memset(val, 0, sizeof(yvalue_t));
 }
 
 yvalue_id_t yvalue_resolve_symbol(ystr_t* sym) {