changeset 42:9bead1335e42

value: Add test
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 21 Aug 2019 13:09:17 +0200
parents a3eac54f2112
children 0efbcd20df2c
files src/CMakeLists.txt src/value.c src/value.h src/value_test.c
diffstat 4 files changed, 114 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/CMakeLists.txt	Wed Aug 21 13:08:52 2019 +0200
+++ b/src/CMakeLists.txt	Wed Aug 21 13:09:17 2019 +0200
@@ -17,3 +17,8 @@
 ADD_EXECUTABLE(atom_test atom_test.c)
 TARGET_LINK_LIBRARIES(atom_test core gcov)
 YADD_TEST(atom_test)
+
+# Value test.
+ADD_EXECUTABLE(value_test value_test.c)
+TARGET_LINK_LIBRARIES(value_test core gcov)
+YADD_TEST(value_test)
--- a/src/value.c	Wed Aug 21 13:08:52 2019 +0200
+++ b/src/value.c	Wed Aug 21 13:09:17 2019 +0200
@@ -3,6 +3,12 @@
 #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 = {.typ = YREF_ID, .ref.id = UINT64_MAX - 2};
+
 // TODO: Adjust when programs are bigger.
 static const size_t YVALUE_INITIAL_VALUES = 256, YVALUE_MAX_VALUES = 1024;
 static const yvalue_id_t YVALUE_COUNTER_OFFSET = 0xb0b0b0b0b0;
@@ -17,6 +23,10 @@
 //  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.
 
 static bool yvalue_table_initialized = false;
 static struct hsearch_data yvalue_name_table;
@@ -25,7 +35,7 @@
 // reference-counting in different table)
 static yvalue_id_t yvalue_counter = YVALUE_COUNTER_OFFSET;
 
-static void yvalue_init_tables(void) {
+static inline void yvalue_init_tables(void) {
     if (yvalue_table_initialized) return;
     yvalue_table_initialized = true;
     hcreate_r(YVALUE_MAX_VALUES, &yvalue_name_table);
@@ -35,9 +45,11 @@
 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 = sym_s;  // Valid, key is not modified.
     hsearch_r(want, FIND, &result, &yvalue_name_table);
     if (result == NULL) return YVALUE_INVALID_ID;
@@ -45,9 +57,17 @@
 }
 
 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++ - YVALUE_COUNTER_OFFSET;
+    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);
@@ -55,19 +75,25 @@
 }
 
 bool yvalue_resolve_or_create_ref(yref_t* ref) {
+    yvalue_init_tables();
+
     if (ref->typ == YREF_ID) return true;
     if (ref->typ == YREF_SYM) {
         yvalue_id_t id = yvalue_resolve_or_create_symbol(&ref->ref.sym);
         ystr_destroy(&ref->ref.sym);
         ref->ref.id = id;
+        ref->typ = YREF_ID;
         return true;
     }
     assert(ref->typ == YREF_ID || ref->typ == YREF_SYM);
 }
 
 yref_t yvalue_set(yref_t ref, yvalue_t* val) {
+    yvalue_init_tables();
+
     // ref == YVALUE_INSERT
     if (ref.typ == YVALUE_INSERT.typ && ref.ref.id == YVALUE_INSERT.ref.id) {
+        // Unnamed value.
         ref.ref.id = yvalue_counter++;
         size_t new_index = YVEC_PUSH(&yvalue_table, val);
         return ref;
@@ -80,6 +106,8 @@
 }
 
 yvalue_t* yvalue_get(yref_t ref) {
+    yvalue_init_tables();
+
     if (ref.typ == YREF_ID) {
         assert(ref.ref.id <= yvalue_counter);
         yvalue_t* vp = YVEC_AT(&yvalue_table,
--- a/src/value.h	Wed Aug 21 13:08:52 2019 +0200
+++ b/src/value.h	Wed Aug 21 13:09:17 2019 +0200
@@ -22,7 +22,10 @@
 // See expr.h for definitions of yref_t, yvalue_id_t, YREF_TYPE.
 
 /// An invalid value ID.
-const yvalue_id_t YVALUE_INVALID_ID = UINT64_MAX - 1;
+const yvalue_id_t YVALUE_INVALID_ID;
+
+/// Use YVALUE_INSERT in `yvalue_set()` to insert a new value.
+const yref_t YVALUE_INSERT;
 
 enum YVALUE_TYPE {
     YVALUE_UNDEF = 0,
@@ -30,9 +33,6 @@
     YVALUE_EXPR = 2,
 };
 
-/// Use YVALUE_INSERT in `yvalue_set()` to insert a new value.
-const yref_t YVALUE_INSERT = {.typ = YREF_ID, .ref.id = UINT64_MAX - 2};
-
 /**
  * @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).
@@ -52,7 +52,7 @@
 
 /**
  * @brief Resolve a symbol to an ID or create a new ID if one doesn't exist
- * already.
+ * already. Also allocates a place in the value table.
  */
 yvalue_id_t yvalue_resolve_or_create_symbol(ystr_t* sym);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/value_test.c	Wed Aug 21 13:09:17 2019 +0200
@@ -0,0 +1,74 @@
+#include "value.h"
+
+#include <assert.h>
+
+#include <src/base/str.h>
+#include <src/expr.h>
+
+void test_value_create_resolve(void) {
+    ystr_t sym;
+    ystr_init(&sym, "vcr_symbol");
+    assert(YVALUE_INVALID_ID == yvalue_resolve_symbol(&sym));
+    yvalue_id_t id = yvalue_resolve_or_create_symbol(&sym);
+    assert(YVALUE_INVALID_ID != id);
+    assert(id == yvalue_resolve_symbol(&sym));
+
+    ystr_destroy(&sym);
+}
+
+void test_ref_create_resolve(void) {
+    ystr_t symbol = ystr_new("rcr_symbol");
+    ystr_t symbol2;
+    ystr_copy(&symbol, &symbol2);
+    yref_t ref = yref_new_str(&symbol);  // symbol is now owned by ref.
+
+    yvalue_resolve_or_create_ref(&ref);
+    assert(YVALUE_INVALID_ID != yvalue_resolve_symbol(&symbol2));
+    assert(YREF_ID == ref.typ);
+    assert(ref.ref.id == yvalue_resolve_symbol(&symbol2));
+}
+
+void test_value_set(void) {
+    yexpr_t expr;
+    expr.typ = YEXPR_NUMBER;
+    expr.value.n = 12345;
+    yvalue_t val;
+    val.typ = YVALUE_EXPR;
+    val.value.expr = expr;
+
+    // Allocate unnamed ref.
+    yref_t ref = yvalue_set(YVALUE_INSERT, &val);
+    assert(YREF_ID == ref.typ);
+    yvalue_t* got = yvalue_get(ref);
+    assert(YVALUE_EXPR == got->typ);
+    assert(YEXPR_NUMBER == got->value.expr.typ);
+    assert(12345 == got->value.expr.value.n);
+
+    // Use named refs.
+    ystr_t symbol = ystr_new("vs_symbol");
+    ystr_t symbol2 = ystr_new("vs_symbol");
+
+    val.value.expr.value.n = 12346;
+    yvalue_id_t id = yvalue_resolve_or_create_symbol(&symbol);
+    yref_t ref2 = yref_new_id(id);
+    yref_t ref2b = yvalue_set(ref2, &val);
+    assert(ref2b.ref.id == ref2.ref.id);
+    yvalue_t* got2 = yvalue_get(ref2);
+    assert(12346 == got2->value.expr.value.n);
+
+    // Get symbolically.
+    yref_t ref3 = yref_new_str(&symbol2);
+    yvalue_t* got3 = yvalue_get(ref3);
+    assert(12346 == got2->value.expr.value.n);
+
+    // Get invalid entry.
+    yref_t ref_invalid = yref_new_c("invalid_entry");
+    assert(NULL == yvalue_get(ref_invalid));
+    assert(NULL != yvalue_get(ref3));
+}
+
+int main(int argc, char** argv) {
+    test_value_create_resolve();
+    test_ref_create_resolve();
+    test_value_set();
+}