The Abstract Syntax Tree
The representation every later phase is written against: node design, traversal, the visitor as the standard shape of a pass, and why the AST outlives the parser.
The representation every later phase is written against: `let x = 1 + 2;` stops being ten tokens and becomes a declaration holding an addition holding two literals. The punctuation is gone; the containment is everything.
Tagged union or class hierarchy, parent pointers or not, pointers or arena indices, forty node kinds or four hundred, and whether `a += 1` is its own node or sugar the parser desugars on the spot. Five decisions, each of which shapes every pass written afterwards.
Every frontend pass is a depth-first search over a tree, and which analyses are correct depends on *when* the node is processed: scopes open on the way down, types are computed on the way up, and getting that backwards produces a compiler that is confidently wrong.
A pass is an object with one method per node kind — `visitBinaryExpression`, `visitFunctionDeclaration`, `visitCallExpression` — and the tree calls it. It is the standard shape of a compiler pass because it makes new passes free, and it is the standard complaint about compiler frontends because it makes new node kinds expensive.
Two things a pass can do to an AST: annotate it, or rewrite it. Annotation is cheap and reversible; rewriting has a legality condition and destroys what was there. Whether the rewrite happens in place or produces a new tree decides whether the frontend can ever serve an editor.
The compiler is no longer the only thing that parses your code. The formatter, the linter, the language server, the refactoring engine and the documentation generator all need the same tree — which is why a modern frontend is built as a library and why "just parse it again" is the wrong answer six times over.