notesonly.in

One notebook for every subject — open it anywhere.

Log in

Semantic Analysis and Symbol Tables With easy examples.

Semantic analysis is the compiler phase that checks whether a syntactically valid program actually makes sense. The parser accepts "colorless green ideas sleep furiously" as a grammatical English sentence; the semantic analyzer is the part that objects. In a compiler, it catches errors like using a variable that was never declared or adding a string to a number.

Grammar alone cannot express meaning. Rules like "every variable must be declared" and "both sides of + must be numbers" are not context-free, so they need a dedicated pass. Semantic analysis turns "it parses" into "it is a valid program", and its central data structure — the symbol table — appears in every serious language tool.

Key Concepts

  • Symbol table: maps each declared name to its type, scope, memory address, and declaration line.
  • Scope: the region where a name is visible; nested blocks nest scopes, and inner declarations shadow outer ones.
  • Type checking: verifying operations get compatible operands, e.g. both sides of arithmetic are numeric.
  • Type coercion: automatic safe conversion, like promoting an integer to float in a mixed expression.
  • Declaration before use: a name must be declared before it is referenced — enforced here.

How It Works

  1. The analyzer walks the abstract syntax tree, keeping a stack of symbol tables — one per open scope.
  2. On a declaration like "int x = 5;", it inserts x with type int, erroring if x is already declared in that scope.
  3. On a use of a name, it searches outward through enclosing scopes; finding nothing, it reports "undeclared identifier".
  4. On an operation, it checks operand types against the language's rules — coercing where allowed, erroring where not.

Worked Example

Analyze: int x = 5; float y = x + 2.5;

  1. "int x = 5;": insert { name: x, type: int, value: 5 }. Table size: 1.
  2. "float y = x + 2.5;": look up x — found, type int. Operands of + are int and float.
  3. Type rule: mixed int/float allowed with coercion — 5 becomes 5.0; 5.0 + 2.5 = 7.5.
  4. Result float matches y's declared type: insert { name: y, type: float, value: 7.5 }. Table size: 2. No errors.

Had the second line used an undeclared z, the lookup would fail in every scope and report "undeclared identifier z" — an error the parser cannot catch, since "z + 2.5" is perfectly grammatical.

Common Misconceptions

  • The parser catches type errors. It cannot — "3 + true" parses fine; type errors are semantic.
  • The symbol table is one flat list. Tables nest by scope so a local can shadow a global safely.
  • Only compiled languages need this. Interpreted languages like Python run the same checks at run time.

Real-World Applications

  • Compilers: every production compiler runs semantic analysis before code generation.
  • IDEs: red squiggles, go-to-definition, and rename-refactoring query symbol-table-like structures.
  • Static analyzers: security and bug-finding tools extend semantic checks with deeper rules.
  • Linters: reuse scope and type info to flag suspicious code.

Semantic analysis gives a program meaning: it tracks every name in scoped symbol tables, enforces declaration and type rules, and rejects grammatical nonsense. It turns a well-formed token tree into a program the compiler can trust.