Mercurial > lbo > hg > ylisp
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