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 string in core.hash (D4 put hashing there and explicitly de-exported the standalone copy). Keys are hashed with name.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