Algorithm Search⌘K

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 G=(V,Σ,P,S)G = (V, \Sigma, P, S), the input is w=a1⋯anw = a_1 \cdots a_n and an+1=$a_{n+1} = \$. We assume:

  1. LL(1). Two different productions of one nonterminal have disjoint PREDICT sets, where PREDICT(A→α)=FIRST(α)\text{PREDICT}(A \to \alpha) = \text{FIRST}(\alpha), together with FOLLOW(A)\text{FOLLOW}(A) when α⇒∗ε\alpha \Rightarrow^* \varepsilon.
  2. No left recursion. There is no A∈VA \in V with A⇒+AβA \Rightarrow^+ A\beta.

A call PROC(A) starts at the value pp of pos when it is made and returns at qq if pos is qq when it returns. It reads ap⋯aq−1a_p \cdots a_{q-1}.

Soundness

Lemma 1. If a call PROC(A) starts at pp and returns at qq, the tree it returns is a parse tree with root AA whose leaves, from left to right, are ap⋯aq−1a_p \cdots a_{q-1}.

Proof. By induction on the order in which calls return. Line L5 fixes a production A→X1⋯XkA \to X_1 \cdots X_k. The loop of lines L7 to L11 handles X1,…,XkX_1, \ldots, X_k in order, and pos never decreases. Let p=p0≤p1≤⋯≤pk=qp = p_0 \le p_1 \le \cdots \le p_k = q be the values of pos after each symbol. If XiX_i is a terminal, line L9 checked api−1=Xia_{p_{i-1}} = X_i and line L10 set pi=pi−1+1p_i = p_{i-1} + 1, so the child is the leaf api−1a_{p_{i-1}}. If XiX_i is a nonterminal, the call PROC(XiX_i) started at pi−1p_{i-1} and returned at pip_i before the call of AA returned, so by induction its tree has root XiX_i and leaves api−1⋯api−1a_{p_{i-1}} \cdots a_{p_i - 1}. The node AA with these children uses the production A→X1⋯XkA \to X_1 \cdots X_k, and its leaves are the concatenation ap⋯aq−1a_p \cdots a_{q-1}. □\square

Corollary. If PARSE returns a tree, it is a parse tree of ww with root SS: the call PROC(S) starts at 1, and line L3 checked that it returned at n+1n + 1. So PARSE never accepts a string outside L(G)L(G).

Completeness

Lemma 2. Let TT be a parse tree of ww with root SS. Let vv be a node of TT with label AA and production A→αA \to \alpha, whose leaves are ap⋯aq−1a_p \cdots a_{q-1} (empty if q=pq = p). Then ap∈PREDICT(A→α)a_p \in \text{PREDICT}(A \to \alpha).

Proof. If q>pq > p, then α⇒∗ap⋯aq−1\alpha \Rightarrow^* a_p \cdots a_{q-1}, so ap∈FIRST(α)a_p \in \text{FIRST}(\alpha). If q=pq = p, then α⇒∗ε\alpha \Rightarrow^* \varepsilon. Cutting TT at vv shows S⇒∗a1⋯ap−1 A δS \Rightarrow^* a_1 \cdots a_{p-1}\, A\, \delta where δ\delta is the sequence of labels of the nodes to the right of the path from the root to vv, and δ⇒∗ap⋯an\delta \Rightarrow^* a_p \cdots a_n. Hence S$⇒∗a1⋯ap−1 A ap⋯an$S\$ \Rightarrow^* a_1 \cdots a_{p-1}\, A\, a_p \cdots a_n \$, and the symbol after AA is apa_p (it is $\$ when p=n+1p = n + 1). So ap∈FOLLOW(A)a_p \in \text{FOLLOW}(A), and in both cases ap∈PREDICT(A→α)a_p \in \text{PREDICT}(A \to \alpha). □\square

Lemma 3. In the setting of Lemma 2, a call PROC(A) that starts at pp returns at qq and returns the subtree of TT at vv.

Proof. By induction on the height of vv. By Lemma 2 the token apa_p is in PREDICT(A→α)\text{PREDICT}(A \to \alpha), and by the LL(1) condition it is in no other PREDICT set of AA, so line L5 chooses A→αA \to \alpha, with α=X1⋯Xk\alpha = X_1 \cdots X_k. Let p=p0≤⋯≤pk=qp = p_0 \le \cdots \le p_k = q be the boundaries of the children of vv in TT. Suppose pos is pi−1p_{i-1} when the loop reaches XiX_i. If XiX_i is a terminal, the child of vv is the leaf api−1=Xia_{p_{i-1}} = X_i, so line L9 does not fail and line L10 moves pos to pip_i. If XiX_i is a nonterminal, its child is a lower node of TT, so by induction PROC(XiX_i) returns at pip_i with that child. After the loop pos is qq, and line L12 returns the node AA with the children of vv. □\square

Corollary. If w∈L(G)w \in L(G) with parse tree TT, then PROC(S) starts at 1 and returns at n+1n + 1 with TT (Lemma 3 at the root), line L3 passes, and PARSE returns TT. Since this holds for every parse tree TT of ww and PARSE is deterministic, ww has only one parse tree: an LL(1) grammar is unambiguous.

Termination

Draw an edge A→BA \to B when GG has a production A→βBγA \to \beta B \gamma with B∈VB \in V and β⇒∗ε\beta \Rightarrow^* \varepsilon.

Lemma 4. Without left recursion, the edge graph has no cycle.

Proof. An edge A→BA \to B from A→βBγA \to \beta B \gamma gives A⇒βBγ⇒∗BγA \Rightarrow \beta B \gamma \Rightarrow^* B\gamma. Following the edges of a cycle A→⋯→AA \to \cdots \to A composes these derivations into A⇒+AδA \Rightarrow^+ A\delta for some δ\delta, which is left recursion. □\square

Lemma 5. At any moment, the active calls (made and not yet returned) that start at the same position pp are at most ∣V∣|V|.

Proof. The active calls form a chain C1,C2,…,CdC_1, C_2, \ldots, C_d, each made by the previous one at line L11, and their start positions are nondecreasing. Let CjC_j be PROC(A) and Cj+1C_{j+1} be PROC(B), both starting at pp. Then CjC_j chose A→X1⋯XkA \to X_1 \cdots X_k and made Cj+1C_{j+1} for Xi=BX_i = B, after handling X1,…,Xi−1X_1, \ldots, X_{i-1} 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 ε\varepsilon. So A→BA \to B is an edge. The calls that start at pp 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 ∣V∣|V| of them. □\square

Theorem. PARSE stops on every input, and the call stack never holds more than (n+1)∣V∣(n + 1)|V| calls.

Proof. Start positions lie in 1,…,n+11, \ldots, n + 1, so by Lemma 5 the chain of active calls has length at most (n+1)∣V∣(n + 1)|V|. The calls of a run form a tree, the call tree, in which each call has at most rr children, where rr 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 (n+1)∣V∣(n + 1)|V|. 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. □\square

Conclusion

By the termination theorem PARSE always stops. By the corollary of Lemma 3 it returns the parse tree of every w∈L(G)w \in L(G). By the corollary of Lemma 1 it never returns a tree for w∉L(G)w \notin L(G), so on such input it stops with an error, at line L3, L5 or L9.

Back to Recursive descent parsing