changeset 36:97ebaf1119ba

value: Add initial implementation
author Lewin Bormann <lbo@spheniscida.de>
date Wed, 21 Aug 2019 10:52:11 +0200
parents 8ac9f39db10f
children 8f662c20a3ec
files src/value.c src/value.h
diffstat 2 files changed, 182 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/value.c	Wed Aug 21 10:52:11 2019 +0200
@@ -0,0 +1,100 @@
+#include "value.h"
+
+#include <search.h>
+#include <string.h>
+
+// 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;
+
+// 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)
+
+static bool yvalue_table_initialized = false;
+static struct hsearch_data yvalue_name_table;
+static yvec_t yvalue_table;
+// 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 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);
+}
+
+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;
+    return (yvalue_id_t)result->data;
+}
+
+yvalue_id_t yvalue_resolve_or_create_symbol(ystr_t* sym) {
+    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;
+    ENTRY new = {.key = strdup(ystr_str(sym)), .data = (void*)new_id};
+    ENTRY* result;
+    hsearch_r(new, ENTER, &result, &yvalue_name_table);
+    return new_id;
+}
+
+bool yvalue_resolve_or_create_ref(yref_t* ref) {
+    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;
+        return true;
+    }
+    assert(ref->typ == YREF_ID || ref->typ == YREF_SYM);
+}
+
+yref_t yvalue_set(yref_t ref, yvalue_t* val) {
+    // ref == YVALUE_INSERT
+    if (ref.typ == YVALUE_INSERT.typ && ref.ref.id == YVALUE_INSERT.ref.id) {
+        ref.ref.id = yvalue_counter++;
+        size_t new_index = YVEC_PUSH(&yvalue_table, val);
+        return ref;
+    } else {
+        yvalue_t* valp = yvalue_get(ref);
+        assert(valp != NULL);
+        *valp = *val;
+        return ref;
+    }
+}
+
+yvalue_t* yvalue_get(yref_t ref) {
+    if (ref.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 (ref.typ == YREF_SYM) {
+        yvalue_id_t id = yvalue_resolve_symbol(&ref.ref.sym);
+        if (id == YVALUE_INVALID_ID) {
+            return NULL;
+        }
+        ref.typ = YREF_ID;
+        ref.ref.id = id;
+        return yvalue_get(ref);
+    } else {
+        assert(ref.typ == YREF_ID || ref.typ == YREF_SYM);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/value.h	Wed Aug 21 10:52:11 2019 +0200
@@ -0,0 +1,82 @@
+#ifndef value_h
+#define value_h
+
+#define _GNU_SOURCE
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <src/expr.h>
+#include <src/func.h>
+
+#include <src/base/str.h>
+#include <src/base/vec.h>
+
+/**
+ * @file value.h
+ * @brief Types and functions for describing values and references to values.
+ * @addtogroup language
+ * @{
+ */
+
+// 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;
+
+enum YVALUE_TYPE {
+    YVALUE_UNDEF = 0,
+    YVALUE_FUNC = 1,
+    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).
+ */
+typedef struct {
+    union {
+        yfunc_t func;
+        yexpr_t expr;
+    } value;
+    enum YVALUE_TYPE typ;
+} yvalue_t;
+
+/**
+ * @brief Resolve a symbol to an ID.
+ */
+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.
+ */
+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.
+ */
+bool yvalue_resolve_or_create_ref(yref_t* ref);
+
+/**
+ * @brief Set a value referenced by `ref`. If a new value 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)
+ */
+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);
+
+/**
+ * @}
+ */
+
+#endif