Files
akbasic/src/symtab.c
Tachikoma c5d13f00f6
Some checks failed
akbasic CI Build / cmake_build (push) Failing after 4m7s
akbasic CI Build / coverage (push) Failing after 3m33s
akbasic CI Build / akgl_build (push) Failing after 4m39s
akbasic CI Build / mutation_test (push) Failing after 3m32s
akbasic CI Build / sanitizers (push) Failing after 14m32s
Repoint the TODO.md citations that no longer resolve
Its own SS0-SS9 structure survived the move to the tracker, so 133 citations
still resolve and are left alone. Thirteen did not.

Six named another repository's TODO.md by a section number: libakstdlib 1.6,
2.2.2 and 2.3, and libakgl's file. Those repositories dropped their numbering
entirely, so each now names UPGRADING.md, an issue, or the tracker.

Three cited 'TODO.md section 12', which has never existed here -- the defect
list is SS6. TODO.md itself caught two others of that class earlier. The
CMakeLists comment carried a stale premise with it: eleven defects
'deliberately reproduced and not yet fixed' stopped being the rule when SS0.1
retired the fidelity constraint.

Four told a reader to record work in TODO.md; the scanner defect is issue #4,
the audio_tables mutation gap is #25, and the UI gaps are libakgl #79 and #80.

Verified: cmake --build build && ctest --test-dir build, 112/112.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Andrew Kesterson <andrew@aklabs.net>
2026-08-02 22:01:28 -04:00

134 lines
4.0 KiB
C

/**
* @file symtab.c
* @brief Implements the fixed-capacity open-addressed symbol table.
*/
#include <string.h>
#include <akerror.h>
#include <akstdlib.h>
#include <akbasic/error.h>
#include <akbasic/symtab.h>
/*
* Probe for `key`. On success *slot points at either the slot holding the key or
* the first free slot it could occupy; *found says which. Uses
* aksl_strhash_djb2 rather than a private hash. That wrapper sign-extends char,
* so a high-bit byte hashes wrong (libakstdlib's UPGRADING.md) -- harmless
* here because BASIC identifiers are 7-bit ASCII, and it would only ever cost
* probe efficiency, never correctness, since the key comparison is a strcmp.
*/
static akerr_ErrorContext *probe(akbasic_SymbolTable *obj, const char *key, int *slot, bool *found)
{
PREPARE_ERROR(errctx);
uint32_t hashval = 0;
int index = 0;
int i = 0;
PASS(errctx, aksl_strhash_djb2((char *)key, strlen(key), &hashval));
*found = false;
index = (int)(hashval % (uint32_t)obj->capacity);
for ( i = 0; i < obj->capacity; i++ ) {
int probeidx = (index + i) % obj->capacity;
if ( !obj->slots[probeidx].used ) {
*slot = probeidx;
SUCCEED_RETURN(errctx);
}
if ( strcmp(obj->slots[probeidx].key, key) == 0 ) {
*slot = probeidx;
*found = true;
SUCCEED_RETURN(errctx);
}
}
FAIL_RETURN(errctx, AKBASIC_ERR_BOUNDS,
"Symbol table is full (%d entries), cannot place '%s'",
obj->capacity, key);
}
akerr_ErrorContext *akbasic_symtab_init(akbasic_SymbolTable *obj, int capacity)
{
PREPARE_ERROR(errctx);
FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER,
"NULL symbol table in init");
FAIL_ZERO_RETURN(errctx, (capacity > 0 && capacity <= AKBASIC_SYMTAB_MAX_SLOTS),
AKBASIC_ERR_BOUNDS,
"Symbol table capacity %d out of range 1..%d",
capacity, AKBASIC_SYMTAB_MAX_SLOTS);
memset(obj, 0, sizeof(*obj));
obj->capacity = capacity;
obj->count = 0;
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akbasic_symtab_set(akbasic_SymbolTable *obj, const char *key, void *value, int64_t ivalue)
{
PREPARE_ERROR(errctx);
int slot = 0;
bool found = false;
FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER,
"NULL symbol table in set");
FAIL_ZERO_RETURN(errctx, (key != NULL), AKERR_NULLPOINTER,
"NULL key in symbol table set");
FAIL_ZERO_RETURN(errctx, (strlen(key) < AKBASIC_SYMTAB_MAX_KEY),
AKBASIC_ERR_BOUNDS,
"Symbol name '%s' exceeds %d characters",
key, AKBASIC_SYMTAB_MAX_KEY - 1);
PASS(errctx, probe(obj, key, &slot, &found));
if ( !found ) {
strncpy(obj->slots[slot].key, key, AKBASIC_SYMTAB_MAX_KEY - 1);
obj->slots[slot].key[AKBASIC_SYMTAB_MAX_KEY - 1] = '\0';
obj->slots[slot].used = true;
obj->count += 1;
}
obj->slots[slot].value = value;
obj->slots[slot].ivalue = ivalue;
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akbasic_symtab_get(akbasic_SymbolTable *obj, const char *key, void **value, int64_t *ivalue)
{
PREPARE_ERROR(errctx);
int slot = 0;
bool found = false;
FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER,
"NULL symbol table in get");
FAIL_ZERO_RETURN(errctx, (key != NULL), AKERR_NULLPOINTER,
"NULL key in symbol table get");
FAIL_ZERO_RETURN(errctx, (strlen(key) < AKBASIC_SYMTAB_MAX_KEY),
AKERR_KEY,
"Symbol '%s' is not present", key);
PASS(errctx, probe(obj, key, &slot, &found));
FAIL_ZERO_RETURN(errctx, found, AKERR_KEY, "Symbol '%s' is not present", key);
if ( value != NULL ) {
*value = obj->slots[slot].value;
}
if ( ivalue != NULL ) {
*ivalue = obj->slots[slot].ivalue;
}
SUCCEED_RETURN(errctx);
}
akerr_ErrorContext *akbasic_symtab_clear(akbasic_SymbolTable *obj)
{
PREPARE_ERROR(errctx);
int capacity = 0;
FAIL_ZERO_RETURN(errctx, (obj != NULL), AKERR_NULLPOINTER,
"NULL symbol table in clear");
capacity = obj->capacity;
memset(obj, 0, sizeof(*obj));
obj->capacity = capacity;
SUCCEED_RETURN(errctx);
}