Proofs: Recursive descent parsing
These are the human proofs of Recursive descent parsing, at full length.
Predictive recursive descent stops and parses exactly the strings of an LL(1) grammar full
This proof shows: correctness, termination.
We use the pseudocode PARSE and PROC of the page and the notation of the context-free grammar page. The grammar is , the input is and . We assume:
- LL(1). Two different productions of one nonterminal have disjoint PREDICT sets, where , together with when .
- No left recursion. There is no with .
A call PROC(A) starts at the value of pos when it is made and returns at if pos is when it returns. It reads .
Soundness
Lemma 1. If a call PROC(A) starts at and returns at , the tree it returns is a parse tree with root whose leaves, from left to right, are .
Proof. By induction on the order in which calls return. Line L5 fixes a production . The loop of lines L7 to L11 handles in order, and pos never decreases. Let be the values of pos after each symbol. If is a terminal, line L9 checked and line L10 set , so the child is the leaf . If is a nonterminal, the call PROC() started at and returned at before the call of returned, so by induction its tree has root and leaves . The node with these children uses the production , and its leaves are the concatenation .
Corollary. If PARSE returns a tree, it is a parse tree of with root : the call PROC(S) starts at 1, and line L3 checked that it returned at . So PARSE never accepts a string outside .
Completeness
Lemma 2. Let be a parse tree of with root . Let be a node of with label and production , whose leaves are (empty if ). Then .
Proof. If , then , so . If , then . Cutting at shows where is the sequence of labels of the nodes to the right of the path from the root to , and . Hence , and the symbol after is (it is when ). So , and in both cases .
Lemma 3. In the setting of Lemma 2, a call PROC(A) that starts at returns at and returns the subtree of at .
Proof. By induction on the height of . By Lemma 2 the token is in , and by the LL(1) condition it is in no other PREDICT set of , so line L5 chooses , with . Let be the boundaries of the children of in . Suppose pos is when the loop reaches . If is a terminal, the child of is the leaf , so line L9 does not fail and line L10 moves pos to . If is a nonterminal, its child is a lower node of , so by induction PROC() returns at with that child. After the loop pos is , and line L12 returns the node with the children of .
Corollary. If with parse tree , then PROC(S) starts at 1 and returns at with (Lemma 3 at the root), line L3 passes, and PARSE returns . Since this holds for every parse tree of and PARSE is deterministic, has only one parse tree: an LL(1) grammar is unambiguous.
Termination
Draw an edge when has a production with and .
Lemma 4. Without left recursion, the edge graph has no cycle.
Proof. An edge from gives . Following the edges of a cycle composes these derivations into for some , which is left recursion.
Lemma 5. At any moment, the active calls (made and not yet returned) that start at the same position are at most .
Proof. The active calls form a chain , each made by the previous one at line L11, and their start positions are nondecreasing. Let be PROC(A) and be PROC(B), both starting at . Then chose and made for , after handling without moving pos. None of these is a terminal, since a matched terminal moves pos. Each is a nonterminal whose call returned at the position where it started, so by Lemma 1 it derives . So is an edge. The calls that start at are consecutive in the chain, so their nonterminals follow a path of edges, which by Lemma 4 has no repeated vertex. So there are at most of them.
Theorem. PARSE stops on every input, and the call stack never holds more than calls.
Proof. Start positions lie in , so by Lemma 5 the chain of active calls has length at most . The calls of a run form a tree, the call tree, in which each call has at most children, where is the length of the longest right side, and each path from the root is a chain of active calls at some moment, of length at most . A tree with bounded branching and bounded depth is finite, and each call does finitely many steps besides its calls. So the run is finite, and it ends by returning a tree or reporting an error.
Conclusion
By the termination theorem PARSE always stops. By the corollary of Lemma 3 it returns the parse tree of every . By the corollary of Lemma 1 it never returns a tree for , so on such input it stops with an error, at line L3, L5 or L9.