226 lines
6.2 KiB
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;
|
|
}
|