// symtab.c -- DVX BASIC symbol table implementation #include "symtab.h" #include "thirdparty/stb_ds_wrap.h" #include #include #include // ============================================================ // 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; }