Small Group Tutorials

Here to help students catch up, keep up, and move ahead. Book a consultation here.

How to Learn Parsing Algorithms: Grammars, Recursive Descent, LL/LR Parsing and Abstract Syntax Trees

Wait, What?

A computer can read every character in a program correctly and still have no idea what the program means.

Parsing is the bridge between a stream of tokens and a structured representation. Beginners often meet it as “checking grammar.” Professional systems use parsing to turn source code, query languages, data formats and domain-specific languages into trees that later stages can analyse, transform or execute. The real learning goal is to understand how a grammar constrains possible structure, how an algorithm decides which structure fits the input, and how ambiguity or bad input is handled.

Quick Answer

Learn parsing through the route characters → tokens → grammar → parse tree → abstract syntax tree → recursive descent → lookahead → LL parsing → bottom-up shift/reduce reasoning → LR parsing → ambiguity → precedence and associativity → error recovery → parser generators → performance and maintainability. Trace a tiny expression grammar by hand before writing a parser.

1. Separate Lexing From Parsing

Consider the expression 12 + 3 * 4. A lexer may recognise the tokens NUMBER, PLUS, NUMBER, STAR, NUMBER. That tells us what the pieces are. It does not yet tell us whether addition or multiplication groups first, whether parentheses are legal, or what larger language construct the expression belongs to.

A parser consumes the token sequence using grammatical rules. Its output is usually a tree or tree-like representation that records structure.

2. A Grammar Is a Generative Contract

A context-free grammar describes how larger syntactic categories can be formed from smaller ones. A tiny arithmetic grammar might define an expression in terms of terms, and a term in terms of factors. The grammar is not merely documentation; it determines which token sequences are valid and what structural relationships they can have.

  • Terminals: tokens that appear in the input.
  • Nonterminals: named syntactic categories.
  • Productions: rules that expand one nonterminal into a sequence.
  • Start symbol: the category representing a complete input.

3. Parse Trees and Abstract Syntax Trees Are Different Jobs

A parse tree mirrors the grammar closely. An abstract syntax tree, or AST, usually removes punctuation and grammar scaffolding that later stages do not need. For 12 + 3 * 4, an AST may contain a plus node whose right child is a multiplication node.

The current LLVM Kaleidoscope tutorial demonstrates this transition directly: a lexer feeds a parser that builds an AST, using recursive descent and operator-precedence parsing. See LLVM: Implementing a Parser and AST.

4. Begin With Recursive Descent Because the Control Flow Is Visible

In recursive descent, grammar structure is reflected in ordinary functions. A function for expressions calls functions for lower-level components, and those functions consume tokens as they recognise valid forms. This makes the correspondence between grammar and code unusually clear for learners.

Trace one function call at a time. Mark the current token, the rule being attempted, what input is consumed, and what AST node is returned. Do not hide this state behind code until the learner can narrate it.

5. Left Recursion Shows That Grammars and Algorithms Must Fit Each Other

A grammar such as Expr → Expr + Term | Term is natural mathematically but causes a naive recursive-descent parser to call itself immediately without consuming input. The parser can recurse forever.

This is a powerful algorithm lesson: the same language can often be expressed by multiple grammars, but not every grammar is equally suitable for every parsing strategy. Grammar transformation is therefore part of algorithm design, not cosmetic rewriting.

6. Lookahead Is a Decision Resource

When the parser has several possible productions, it may inspect one or more upcoming tokens to decide which production can succeed. Predictive top-down parsers organise these decisions systematically.

For an LL-style parser, learners should understand the meaning before the table machinery: read input from left to right, construct a leftmost derivation, and use lookahead to choose the production.

7. FIRST and FOLLOW Sets Turn Intuition Into a Procedure

FIRST asks which terminals can begin strings derived from a grammar symbol. FOLLOW asks which terminals may appear immediately after a nonterminal in a valid sentential form. These sets help determine whether a predictive parser can choose productions without backtracking.

Do not begin by memorising set equations. Give learners several productions and ask which token could appear first after each choice. Then formalise the observation into FIRST and FOLLOW.

8. Bottom-Up Parsing Reverses the Perspective

Top-down parsing starts from the grammar’s start symbol and predicts how it could produce the input. Bottom-up parsing starts from the input and repeatedly recognises pieces that can be reduced to larger grammatical categories.

The core mental model is shift or reduce. Shift means move another input token onto the working stack. Reduce means recognise that the top of the stack matches the right-hand side of a production and replace it with the corresponding nonterminal.

9. LR Parsing Is State-Based Recognition of Viable Prefixes

LR parsers use an automaton and stack to remember enough grammatical context to decide when to shift and when to reduce. Variants such as SLR, LALR and canonical LR trade table size, construction complexity and expressive power.

Cornell’s compiler course sequence places grammars, top-down parsing, LL tables, bottom-up parsing, and LR/SLR/LALR parsing in direct progression. See Cornell CS 412/413 Compiler Lectures. The material is older, but the algorithmic progression remains foundational.

10. Ambiguity Is a Property of the Grammar, Not a Parser Bug

A grammar is ambiguous when one valid input can have more than one parse tree. Arithmetic expressions provide the classic example if precedence and associativity are not encoded. The string a + b * c could group as addition-first or multiplication-first.

Professional parser design must decide whether ambiguity is eliminated in the grammar, resolved by declared precedence rules, or intentionally preserved for later disambiguation.

11. Precedence and Associativity Are Structural Constraints

  • Precedence: determines which operator binds more tightly.
  • Associativity: determines how operators at the same precedence group.

The difference becomes visible in the AST. A correct parser is not merely accepting the token sequence; it is constructing the intended hierarchy.

12. Error Recovery Is Part of the User Experience

A parser that stops at the first unexpected token is easy to implement but unpleasant to use. Editors, compilers and interactive tools often need to report multiple useful errors from one input. Recovery strategies may skip tokens to a synchronising symbol, insert or delete likely missing tokens, or use grammar-aware recovery.

Professional quality is therefore not measured only by whether valid programs parse. It also includes whether invalid input produces accurate, local and understandable diagnostics.

13. Parser Generators Change the Engineering Boundary

Parser generators take a grammar or grammar-like specification and produce recogniser code. Modern tools may use more sophisticated prediction than the textbook LL(1) or LALR examples. ANTLR 4, for example, implements adaptive LL(*) prediction and builds decision DFAs from ATN simulation. See ANTLR ParserATNSimulator documentation.

The engineering lesson is to understand the abstraction boundary. A generator can remove boilerplate, but the programmer still needs to understand grammar design, ambiguity, precedence, error handling and the shape of the produced tree.

14. Complexity Depends on the Parsing Family and Grammar

Many practical deterministic parsers process input in linear time under the restrictions of their grammar and parsing method. More general context-free parsing algorithms, such as CYK or Earley-style methods, can handle broader grammar classes but may require higher worst-case time.

This is another workload lesson: “most general” is not automatically “best.” A compiler grammar designed for deterministic parsing can benefit from simpler, faster machinery than an arbitrary natural-language grammar.

15. Common Learning Failure States

  • Confusing tokens with characters.
  • Confusing parse trees with ASTs.
  • Writing recursive descent directly from a left-recursive grammar.
  • Memorising FIRST/FOLLOW rules without understanding prediction.
  • Treating shift/reduce conflicts as random parser-generator errors.
  • Ignoring associativity when encoding operator precedence.
  • Accepting valid input but producing the wrong tree structure.
  • Testing only valid programs and neglecting error recovery.

16. A Scaffold-Fade Learning Ladder

  • Level 1: tokenise a tiny arithmetic expression by hand.
  • Level 2: build a parse tree from a given grammar.
  • Level 3: compress the parse tree into an AST.
  • Level 4: trace a recursive-descent parser with explicit token position.
  • Level 5: remove left recursion and compute simple FIRST/FOLLOW sets.
  • Level 6: simulate an LL table.
  • Level 7: perform shift/reduce traces.
  • Level 8: diagnose conflicts and ambiguity.
  • Level 9: build a small language parser and evaluate error messages, maintainability and performance.

Recent computing-education research on faded Parsons problems supports progressively restoring generative work while retaining scaffolding for advanced concepts. See Caraco, Lojo and Fox (2025). Parsing exercises fit this well: first order a correct token-consumption trace, then fill missing grammar decisions, then write the parser independently.

17. Read, Trace and Explain Before Writing

The Raspberry Pi Foundation recommends code-reading, tracing and explanation before code writing and uses PRIMM—Predict, Run, Investigate, Modify, Make—as a structured route into programming. See Computing pedagogy at the Raspberry Pi Foundation. Parsing makes internal state visible enough to exploit this approach: predict the next token consumption or reduction, run the parser, then explain the discrepancy.

18. Immediate, Delayed and Transfer Checks

  • Immediate: identify terminals, nonterminals and productions in a tiny grammar.
  • Structural: build an AST for an expression with precedence.
  • Top-down: trace recursive-descent function calls and lookahead decisions.
  • Bottom-up: explain each shift and reduction in a short trace.
  • Delayed: reconstruct the parser logic without seeing the implementation.
  • Transfer: choose a parsing approach for a configuration language, calculator, compiler front end and ambiguous research grammar.

19. AI Assistance Boundary

AI can generate tiny grammars, produce example strings, create parse traces and suggest tests for invalid input. The learner should still be able to explain why a production is chosen, identify ambiguity, predict the AST and diagnose a parser conflict without outsourcing the reasoning.

Professional Direction

Advanced work includes GLR and Earley parsing, scannerless parsing, incremental parsing for editors, error-correcting parsing, parser combinators, PEGs, syntax-preserving transformations, concrete-syntax trees, language-server integration and performance engineering for very large code bases. The durable principle is that a parser is a controlled mapping from a linear stream into hierarchical meaning.

Algorithm-learning rule: do not ask only “Can this string be accepted?” Ask “What structure does this string imply, how does the parser know, and what evidence will reveal that it built the wrong structure?”