Names and their bindings

The same spelling can denote different bindings in different scopes. A symbol table records what a name means at a relevant location, rather than merging every matching string in a source file.

Python builds its symbol tables after the syntax tree and before bytecode generation. Its inspection interface distinguishes parameters, locals, globals, free variables and imported names.

Name classification can depend on an entire function rather than only lines already executed. In Python, assigning x in a function without a relevant global or nonlocal declaration makes its uses local. Reading it before assignment raises UnboundLocalError even if an outer x exists. Scanning binding operations supplies this scope information before execution; the existence of an outer value does not override the local classification.

References: [1] [4]

Resolving names during code generation

LLVM’s tutorial compiler uses NamedValues to map names in the current scope to existing IR values. Variable-expression generation looks there first; an unknown name cannot simply become a valid new value.

That teaching example supports limited binding forms. Real compilers must also account for nested scopes, shadowing, modules and type information; one map illustrates the responsibility rather than every implementation.

References: [2]

Symbols in object files

Object files can retain symbols for linking. GNU nm displays their values and kinds, helping distinguish definitions, unresolved references, code and data during cross-file diagnosis.

Such output is not a complete list of source-language identifiers. Compilation and optimization change what survives, and file formats have their own categories; diagnosis must identify which compilation stage a symbol table describes.

References: [3]