DVX_GUI/apps/dvxbasic/compiler/symtab.c

226 lines
6.2 KiB
C

// symtab.c -- DVX BASIC symbol table implementation
#include "symtab.h"
#include "thirdparty/stb_ds_wrap.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
// ============================================================
// Case-insensitive name comparison
// ============================================================
static bool namesEqual(const char *a, const char *b) {
while (*a && *b) {
char ca = *a >= 'a' && *a <= 'z' ? *a - 32 : *a;
char cb = *b >= 'a' && *b <= 'z' ? *b - 32 : *b;
if (ca != cb) {
return false;
}
a++;
b++;
}
return *a == *b;
}
// ============================================================
// basSymTabAdd
// ============================================================
BasSymbolT *basSymTabAdd(BasSymTabT *tab, const char *name, BasSymKindE kind, uint8_t dataType) {
// Determine scope: local > form > global.
// Only variables get SCOPE_FORM; SUBs/FUNCTIONs/CONSTs remain global.
BasScopeE scope;
if (tab->inLocalScope) {
scope = SCOPE_LOCAL;
} else if (tab->inFormScope && kind == SYM_VARIABLE) {
scope = SCOPE_FORM;
} else {
scope = SCOPE_GLOBAL;
}
// Check for duplicate in current scope (skip ended form symbols)
for (int32_t i = 0; i < tab->count; i++) {
if (tab->symbols[i].formScopeEnded) {
continue;
}
if (tab->symbols[i].scope == scope && namesEqual(tab->symbols[i].name, name)) {
return NULL; // duplicate
}
}
BasSymbolT entry;
memset(&entry, 0, sizeof(entry));
arrput(tab->symbols, entry);
tab->count = (int32_t)arrlen(tab->symbols);
BasSymbolT *sym = &tab->symbols[tab->count - 1];
strncpy(sym->name, name, BAS_MAX_SYMBOL_NAME - 1);
sym->name[BAS_MAX_SYMBOL_NAME - 1] = '\0';
sym->kind = kind;
sym->scope = scope;
sym->dataType = dataType;
sym->isDefined = true;
if (scope == SCOPE_FORM && tab->formScopeName[0]) {
strncpy(sym->formName, tab->formScopeName, BAS_MAX_SYMBOL_NAME - 1);
sym->formName[BAS_MAX_SYMBOL_NAME - 1] = '\0';
}
return sym;
}
// ============================================================
// basSymTabAllocSlot
// ============================================================
int32_t basSymTabAllocSlot(BasSymTabT *tab) {
if (tab->inLocalScope) {
return tab->nextLocalIdx++;
}
if (tab->inFormScope) {
return tab->nextFormVarIdx++;
}
return tab->nextGlobalIdx++;
}
// ============================================================
// basSymTabEnterLocal
// ============================================================
void basSymTabEnterLocal(BasSymTabT *tab) {
tab->inLocalScope = true;
tab->nextLocalIdx = 0;
}
// ============================================================
// basSymTabFind
// ============================================================
BasSymbolT *basSymTabFind(BasSymTabT *tab, const char *name) {
// Search local scope first
if (tab->inLocalScope) {
for (int32_t i = tab->count - 1; i >= 0; i--) {
if (tab->symbols[i].scope == SCOPE_LOCAL && namesEqual(tab->symbols[i].name, name)) {
return &tab->symbols[i];
}
}
}
// Search form scope and global scope
for (int32_t i = tab->count - 1; i >= 0; i--) {
if (tab->symbols[i].formScopeEnded) {
continue;
}
if ((tab->symbols[i].scope == SCOPE_FORM || tab->symbols[i].scope == SCOPE_GLOBAL) &&
namesEqual(tab->symbols[i].name, name)) {
return &tab->symbols[i];
}
}
return NULL;
}
// ============================================================
// basSymTabFindGlobal
// ============================================================
BasSymbolT *basSymTabFindGlobal(BasSymTabT *tab, const char *name) {
for (int32_t i = 0; i < tab->count; i++) {
if (tab->symbols[i].formScopeEnded) {
continue;
}
if (tab->symbols[i].scope == SCOPE_GLOBAL && namesEqual(tab->symbols[i].name, name)) {
return &tab->symbols[i];
}
}
return NULL;
}
// ============================================================
// basSymTabInit
// ============================================================
void basSymTabInit(BasSymTabT *tab) {
memset(tab, 0, sizeof(*tab));
}
// ============================================================
// basSymTabLeaveLocal
// ============================================================
void basSymTabLeaveLocal(BasSymTabT *tab) {
// Remove all local symbols, freeing their dynamic arrays
int32_t newCount = 0;
for (int32_t i = 0; i < tab->count; i++) {
if (tab->symbols[i].scope == SCOPE_LOCAL) {
arrfree(tab->symbols[i].patchAddrs);
arrfree(tab->symbols[i].fields);
} else {
if (i != newCount) {
tab->symbols[newCount] = tab->symbols[i];
}
newCount++;
}
}
arrsetlen(tab->symbols, newCount);
tab->count = newCount;
tab->inLocalScope = false;
tab->nextLocalIdx = 0;
}
// ============================================================
// basSymTabEnterFormScope
// ============================================================
void basSymTabEnterFormScope(BasSymTabT *tab, const char *formName) {
tab->inFormScope = true;
strncpy(tab->formScopeName, formName, BAS_MAX_SYMBOL_NAME - 1);
tab->formScopeName[BAS_MAX_SYMBOL_NAME - 1] = '\0';
tab->nextFormVarIdx = 0;
tab->formScopeSymStart = tab->count;
}
// ============================================================
// basSymTabLeaveFormScope
// ============================================================
int32_t basSymTabLeaveFormScope(BasSymTabT *tab) {
int32_t varCount = tab->nextFormVarIdx;
// Mark all form-scope symbols added since BEGINFORM as ended
for (int32_t i = tab->formScopeSymStart; i < tab->count; i++) {
if (tab->symbols[i].scope == SCOPE_FORM) {
tab->symbols[i].formScopeEnded = true;
}
}
tab->inFormScope = false;
tab->formScopeName[0] = '\0';
tab->nextFormVarIdx = 0;
tab->formScopeSymStart = 0;
return varCount;
}