Semantic Analysis: Names, Scopes, and Rules

  • Trace lexical scope and declared-before-use lookup.
  • Register a function before its own body without hoisting later declarations.
  • Classify name/arity errors separately from dynamic runtime types.
1 / 6

Readiness

  • Build v2 Program/Stmt/Expr trees.
  • Use a stack of maps and distinguish declaration from assignment.
2 / 6

Worked reconstruction

Analyze let x=1; { let x=2; x=3; } x=4;:

Visit Scope stack, outer first Lookup/update target
outer declaration {x:var} Introduce outer x
enter block {x:var}, {} New current scope
inner declaration {x:var}, {x:var} Shadowing is legal
inner assignment same Nearest x is inner
exit block {x:var} Inner binding disappears
final assignment same Nearest x is outer

Duplicates are checked only in the current scope; lookup searches outward. An initializer is analyzed before its new name is inserted. Therefore let x=x; fails when no outer x exists, while an inner let x=x; can refer to an already declared outer x.

3 / 6

Diagnose before implementing

An implementation registers every function before visiting any statements. It accepts later(); fn later(){ return 1; }. Explain why this is wrong for this course even though another language could permit it.

4 / 6

Independent transfer

Construct one legal shadowing example, one same-scope duplicate, and one recursive function. Write the scope snapshot at the first disputed identifier. Add a wrong-arity call without changing its otherwise valid function body.

5 / 6

Carry forward

Carry a semantically accepted Program into compilation; do not assume acceptance means every runtime arithmetic operation will succeed.

6 / 6
←/→ or click edges to navigate · ? help · N notes · F fullscreen