Module: compiler.symbol_table
Source: ./compiler/symbol_table.reef
Overview
compiler/symbol_table — scoped name -> value bindings, generic in the value.
ONE generic table, not three. This module used to export IntSymbolTable, StringSymbolTable and PtrSymbolTable with a parallel copy of every operation (scope_push_int/_str/_ptr, define_int/_str/_ptr, ...), because generics could not carry it when the module was written. They can now: collections ships HashMap[K, V], Trie[V] and Stack[T], and the collections 0.9.2 redesign (D9/D13) deleted exactly this triplication for IntOrderedMap / StringOrderedMap / PtrOrderedMap. This was the last instance in the tree.
Collapsing it also removed two things the copies were hiding:
- lookup_ptr returned a BARE pointer with null meaning "not found", so the Ptr copy never got the C16 treatment its Int and Str siblings did. One generic lookup returns Option[V] for every V, so the sentinel has nowhere left to live.
- a private DJB2 hash_string, byte-identical in intent to
impl Hash for stringin core.hash (D4 put hashing there and explicitly de-exported the standalone copy). Keys are hashed withname.hash()now.
The default_value field is gone too. It was a per-table constant nothing
could set, and it was what made the old lookup return an indistinguishable
0 for "bound to 0" and "not bound".
Structure is unchanged: a fixed stack of scopes, each an open-addressed table with linear probing. Capacity is fixed, so a scope that fills silently drops the insert -- pre-existing, noted in examples/katas/stdlib, and NOT addressed here.
Usage: import compiler.symbol_table as symtab let st = symtab.create_table:int symtab.define(st, "x", 1) symtab.scope_push(st) symtab.define(st, "x", 2) // shadows the outer binding symtab.lookup(st, "x") // Some(2) symtab.scope_pop(st) symtab.lookup(st, "x") // Some(1)
Types
Scope
One lexical scope: open addressing with linear probing. states: 0 = empty, 1 = occupied.
Fields:
| Name | Type |
|---|---|
keys |
[string] |
values |
[V] |
states |
[int] |
size |
int |
SymbolTable
Fields:
| Name | Type |
|---|---|
scopes |
[Scope[V]] |
depth |
int |
Functions
fn max_scope_depth(): int
Maximum nesting depth of scopes
fn scope_capacity(): int
Slots per scope. Fixed: an insert past this is dropped.
fn slot_of(name: string): int
Slot index for a key. Masking off the sign bit before the modulo matters: hash() can return a negative int, and a negative index is an out-of-bounds read. Same guard collections/hashmap uses.
fn create_table(): SymbolTable[V]
fn scope_depth(st: SymbolTable[V]): int
fn slot_in_scope(scope: Scope[V], name: string): int
Probe one scope. Returns the slot index, or -1 if the key is not present. Private and index-only: the -1 never escapes as a VALUE, which is what made the old per-type lookup helpers sentinel-shaped.
fn lookup(st: SymbolTable[V], name: string): option.Option[V]
Innermost binding of name, or None if it is bound in no scope.
fn lookup_or(st: SymbolTable[V], name: string, default: V): V
A miss here is ordinary -- an unbound name is the normal question a symbol
table answers -- so unlike the refusals in 035_ERROR_HANDLING.md this one
does get an _or twin.
fn has(st: SymbolTable[V], name: string): bool
fn has_current(st: SymbolTable[V], name: string): bool
Bound in the CURRENT scope only -- what a redefinition check needs.
Procedures
proc init_scope(st: SymbolTable[V], depth: int)
proc scope_push(st: SymbolTable[V])
proc scope_pop(st: SymbolTable[V])
proc define(st: SymbolTable[V], name: string, value: V)
Generated by reefc doc