Compiler Foundations: Language, Tokens, Grammar

  • Identify the representation and failure boundary for each compiler stage.
  • Plan six cumulative artifacts and distinguish version-specific language rules.
  • Use a failing example to choose the next diagnostic trace.
1 / 6

Readiness

  • Trace a Go slice and map; read a recursive function and a stack trace.
2 / 6

Worked reconstruction

Consider the expression 8 - 2 * 3:

Stage Output What it has decided
Lex NUMBER 8, MINUS, NUMBER 2, STAR, NUMBER 3, EOF Character boundaries and positions
Parse Sub(8, Mul(2,3)) Multiplication binds more tightly
Lower PUSH 8; PUSH 2; PUSH 3; MUL; SUB Evaluation order and stack protocol
Hand execution [8,2,3] → [8,6] → [2] Numeric result under runtime rules

The expression lexer can tokenize missing + 1; it does not know whether missing has a binding. Likewise a parser can construct a well-formed tree for a name that semantic analysis will reject. Later let x = ; fails parsing before names can be checked. In v2, let x = "a" - 1; has valid syntax and names but fails runtime numeric checks; v6 can reject that mismatch earlier.

3 / 6

Diagnose before implementing

A student fixes 8 - 2 * 3 by changing the VM so every subtraction multiplies the right operand by three. The parser produced Mul(Sub(8,2),3). Identify the first wrong representation and a second input that exposes the proposed patch.

4 / 6

Independent transfer

Build a failure-stage table for @, 1 +, unknown + 1, and "s" - 1 in a v2 program context. Write the earliest stage that can reject each and what evidence that stage needs. Revisit the table after v6 to identify which failures move earlier.

5 / 6

Carry forward

Carry a version table and one minimal counterexample per stage into the lexer. Do not import a later feature until its module explicitly changes the contract.

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