On 2026-11-01, vinipsmaker's "Pratt parsing is a black box" topped Lobsters' compilers tag (40 points, 11 comments, with Andy Chu of Oils, pervognsen bringing up Lean, and matklad joining in). Starting from that post, this piece re-learns Pratt parsing not as an algorithm but as a way to organize a parser, and reads the real code in Rust (rust-analyzer) and TypeScript (typescript-go).

Why this, why now

The problem

When you write an expression grammar in BNF, you have to stack layers such as Expr → Term → Factor → Atom to express precedence and associativity. As matklad put it, the shape of expressions is lost in that formulation. And if you code Expr '+' Expr directly as recursive descent, you hit the textbook wall of left recursion and loop forever.

Meanwhile, expressions in real languages come in many shapes:

Writing all of this as a layered grammar is hard work, and handing it to a parser generator makes error recovery and IDE-specific tuning hard instead.

How it works

1. The core loop (treat it as a part)

fn expr_bp(lexer: &mut Lexer, min_bp: u8) -> S {
    let mut lhs = nud(lexer.next());           // a token that expects nothing to its left
    loop {
        let op = lexer.peek();
        let Some((l_bp, r_bp)) = infix_bp(op) else { break };
        if l_bp < min_bp { break; }             // a weaker operator arrived: return to the caller
        lexer.next();
        let rhs = expr_bp(lexer, r_bp);         // the right side collects only things at least r_bp strong
        lhs = S::Cons(op, vec![lhs, rhs]);
    }
    lhs
}

min_bp is the strength of the operator to the left. The recursive call collects only operators that bind tighter than the current position and returns to its caller when a weaker one shows up. Associativity is expressed as the difference between left and right binding power. matklad gives + the pair (5, 6), an odd left value with the right value one higher, and flips it to (2, 1) for the right-associative =. In vinipsmaker's single-value tables, left-associative operators recurse with bp and right-associative ones with bp - 1. Both express the same thing.

2. The shift in thinking: nud and led

The point of Pratt is not the loop. It is that semantic actions are registered on terminals (tokens) instead of on production rules. At GOTO 2013, Crockford said Pratt reduces parsing to one question: what do we expect to see to the left of a token?