180 lines
7.2 KiB
C
180 lines
7.2 KiB
C
// The MIT License (MIT)
|
|
//
|
|
// Copyright (C) 2026 Scott Duensing
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
|
|
// symtab.h -- DVX BASIC symbol table
|
|
//
|
|
// Tracks variables, constants, subroutines, functions, and labels
|
|
// during compilation. Supports nested scopes (global + one local
|
|
// scope per SUB/FUNCTION).
|
|
//
|
|
// Embeddable: no DVX dependencies, pure C.
|
|
|
|
#ifndef DVXBASIC_SYMTAB_H
|
|
#define DVXBASIC_SYMTAB_H
|
|
|
|
#include "../compiler/opcodes.h"
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
|
|
// ============================================================
|
|
// Symbol kinds
|
|
// ============================================================
|
|
|
|
typedef enum {
|
|
SYM_VARIABLE,
|
|
SYM_CONST,
|
|
SYM_SUB,
|
|
SYM_FUNCTION,
|
|
SYM_LABEL,
|
|
SYM_TYPE_DEF // user-defined TYPE
|
|
} BasSymKindE;
|
|
|
|
// BasScopeE moved to opcodes.h (shared with runtime).
|
|
|
|
// ============================================================
|
|
// Symbol entry
|
|
// ============================================================
|
|
|
|
#define BAS_MAX_SYMBOL_NAME 64
|
|
#define BAS_MAX_PARAMS 16
|
|
#define BAS_MAX_CONST_STR 256 // max CONST string-literal length
|
|
|
|
// UDT field definition
|
|
typedef struct {
|
|
char name[BAS_MAX_SYMBOL_NAME];
|
|
uint8_t dataType; // BAS_TYPE_*
|
|
int32_t udtTypeId; // if dataType == BAS_TYPE_UDT, index of the TYPE_DEF symbol
|
|
} BasFieldDefT;
|
|
|
|
typedef struct {
|
|
// Fields are ordered by alignment (4-byte, then 2-byte, then 1-byte) to
|
|
// minimize struct padding on the 32-bit target.
|
|
uint32_t nameHash; // FNV-1a over uppercase(name); fast-reject during lookup
|
|
BasSymKindE kind;
|
|
BasScopeE scope;
|
|
int32_t index; // slot index (local or global)
|
|
int32_t codeAddr; // PC address for SUB/FUNCTION/LABEL
|
|
int32_t localCount; // number of local variables (for SUB/FUNCTION, set on leave)
|
|
int32_t udtTypeId; // for variables of BAS_TYPE_UDT: index of TYPE_DEF symbol
|
|
int32_t fixedLen; // for STRING * n: fixed length (0 = variable-length)
|
|
|
|
// For SUB/FUNCTION: parameter info
|
|
int32_t paramCount;
|
|
int32_t requiredParams; // count of non-optional params
|
|
|
|
// Forward-reference backpatch list (code addresses to patch when defined)
|
|
int32_t *patchAddrs; // stb_ds dynamic array
|
|
int32_t patchCount;
|
|
|
|
// For CONST: the constant value
|
|
union {
|
|
int32_t constInt;
|
|
double constDbl;
|
|
};
|
|
|
|
// For TYPE_DEF: field definitions
|
|
BasFieldDefT *fields; // stb_ds dynamic array
|
|
int32_t fieldCount;
|
|
|
|
uint16_t externLibIdx; // constant pool index for library name (if isExtern)
|
|
uint16_t externFuncIdx; // constant pool index for function name (if isExtern)
|
|
|
|
uint8_t dataType; // BAS_TYPE_* for variables/functions
|
|
bool isDefined; // false = forward-declared
|
|
bool isArray;
|
|
bool isShared;
|
|
bool isExtern; // true = external library function (DECLARE LIBRARY)
|
|
bool formScopeEnded; // true = form scope ended, invisible to lookups
|
|
|
|
char name[BAS_MAX_SYMBOL_NAME];
|
|
char formName[BAS_MAX_SYMBOL_NAME]; // form name for SCOPE_FORM vars
|
|
uint8_t paramTypes[BAS_MAX_PARAMS];
|
|
bool paramByVal[BAS_MAX_PARAMS];
|
|
bool paramOptional[BAS_MAX_PARAMS]; // true = OPTIONAL parameter
|
|
char constStr[BAS_MAX_CONST_STR];
|
|
} BasSymbolT;
|
|
|
|
// ============================================================
|
|
// Symbol table
|
|
// ============================================================
|
|
|
|
typedef struct {
|
|
// Array of POINTERS to heap-allocated symbols (stb_ds array of pointers).
|
|
// Pointers-of-pointers rather than array-of-structs because callers
|
|
// routinely hold a BasSymbolT * across parsing operations that may
|
|
// trigger basSymTabAdd (which grows this array). An array of structs
|
|
// would be reallocated on growth, invalidating any held pointer.
|
|
// Indirection via a stable heap pointer per symbol avoids that.
|
|
BasSymbolT **symbols;
|
|
int32_t count;
|
|
int32_t nextGlobalIdx; // next global variable slot
|
|
int32_t nextLocalIdx; // next local variable slot (reset per SUB/FUNCTION)
|
|
bool inLocalScope; // true when inside SUB/FUNCTION
|
|
bool inFormScope; // true inside BEGINFORM...ENDFORM
|
|
char formScopeName[BAS_MAX_SYMBOL_NAME]; // current form name
|
|
int32_t nextFormVarIdx; // next form-level variable slot
|
|
int32_t formScopeSymStart; // symbol count at BEGINFORM (for marking ended)
|
|
} BasSymTabT;
|
|
|
|
// ============================================================
|
|
// API
|
|
// ============================================================
|
|
|
|
void basSymTabInit(BasSymTabT *tab);
|
|
|
|
// Free all symbols and the symbol array (called at parser teardown).
|
|
void basSymTabFree(BasSymTabT *tab);
|
|
|
|
// Add a symbol. Returns the symbol pointer, or NULL if the name already
|
|
// exists in the current scope or memory allocation fails.
|
|
BasSymbolT *basSymTabAdd(BasSymTabT *tab, const char *name, BasSymKindE kind, uint8_t dataType);
|
|
|
|
// Look up a symbol by name. Searches local scope first, then global.
|
|
// Case-insensitive.
|
|
BasSymbolT *basSymTabFind(BasSymTabT *tab, const char *name);
|
|
|
|
// Look up a symbol in the global scope only.
|
|
BasSymbolT *basSymTabFindGlobal(BasSymTabT *tab, const char *name);
|
|
|
|
// Enter local scope (called at SUB/FUNCTION start).
|
|
void basSymTabEnterLocal(BasSymTabT *tab);
|
|
|
|
// Leave local scope (called at END SUB/FUNCTION). Removes local symbols.
|
|
void basSymTabLeaveLocal(BasSymTabT *tab);
|
|
|
|
// Allocate the next variable slot (global, local, or form depending on scope).
|
|
int32_t basSymTabAllocSlot(BasSymTabT *tab);
|
|
|
|
// Allocate the next global variable slot unconditionally, ignoring the
|
|
// current local/form scope flags. Used when a symbol's final scope is
|
|
// SCOPE_GLOBAL but the table is currently inside a SUB/FUNCTION or form.
|
|
int32_t basSymTabAllocGlobalSlot(BasSymTabT *tab);
|
|
|
|
// Enter form scope (called at BEGINFORM). Form-level DIMs create SCOPE_FORM variables.
|
|
void basSymTabEnterFormScope(BasSymTabT *tab, const char *formName);
|
|
|
|
// Leave form scope (called at ENDFORM). Marks form-scope symbols as ended.
|
|
// Returns the number of form variables allocated.
|
|
int32_t basSymTabLeaveFormScope(BasSymTabT *tab);
|
|
|
|
#endif // DVXBASIC_SYMTAB_H
|