Algorithm Search⌘K

Proofs: LL(1) table-driven parsing

These are the human proofs of LL(1) table-driven parsing, at full length.

LL1-PARSE stops in linear time and accepts exactly the sentences of an LL(1) grammar, with their parse trees full

This proof shows: correctness.

Statement. Let G=(V,Σ,R,S)G = (V, \Sigma, R, S) be a grammar in which every nonterminal derives some terminal string, and let MM be the table built from GG as on the LL parse table page, with no cell holding two productions. Then for every input w=a1⋯anw = a_1 \cdots a_n:

  1. LL1-PARSE stops after at most c⋅(n+1)c \cdot (n + 1) steps, where cc depends only on GG;
  2. it accepts if and only if w∈L(G)w \in L(G), and then it returns a parse tree of ww with root SS;
  3. if it reports an error at aia_i (with an+1=$a_{n+1} = \$), then a1⋯ai−1a_1 \cdots a_{i-1} is a prefix of a sentence of L(G)L(G), and a1⋯aia_1 \cdots a_i is not when i≤ni \le n.

Setup. Write FIRST\mathrm{FIRST}, FOLLOW\mathrm{FOLLOW} and PREDICT\mathrm{PREDICT} for the sets as defined, and FOLLOWM\mathrm{FOLLOW}_M, PREDICTM\mathrm{PREDICT}_M for the sets that BUILD computes. The LL parse table page proves that the computed nullable set and FIRST sets are exact and that FOLLOWM(A)⊇FOLLOW(A)\mathrm{FOLLOW}_M(A) \supseteq \mathrm{FOLLOW}(A) for every AA. So PREDICTM(p)⊇PREDICT(p)\mathrm{PREDICT}_M(p) \supseteq \mathrm{PREDICT}(p) for every production pp, and the cell M[A,a]M[A, a] contains every production pp of AA with a∈PREDICT(p)a \in \mathrm{PREDICT}(p). Since no cell has two productions, two different productions of one nonterminal have disjoint PREDICT sets.

The partial tree at a moment of the run is the tree below the dummy root built so far: each expanded nonterminal is an inner node, each matched token is a leaf, and each stack entry other than $\$ is a slot, a leaf that will receive the subtree of its symbol. The start index of an inner node is the number of tokens matched before it was expanded.

Step 1: the invariant. At each test of line L4, after i−1i - 1 tokens have been matched, let γ\gamma be the string of the symbols on the stack above $\$, read from the top. Then the partial tree has the root SS, its leaves from left to right are a1,…,ai−1a_1, \ldots, a_{i-1} followed by the slots of γ\gamma (and ε\varepsilon marks for empty right sides), every inner node uses a production of RR, and so S⇒lm∗a1⋯ai−1γS \Rightarrow_{lm}^* a_1 \cdots a_{i-1} \gamma.

At the start the tree is the slot SS and the claim holds. A match (lines L6 to L8) turns the top slot, a terminal equal to aia_i, into a matched leaf; the matched prefix grows by aia_i and γ\gamma loses its first symbol. An expansion (lines L10 to L13) by X→Y1⋯YkX \to Y_1 \cdots Y_k turns the top slot XX into an inner node with the slots Y1,…,YkY_1, \ldots, Y_k, and replaces the first symbol XX of γ\gamma by Y1⋯YkY_1 \cdots Y_k. Every slot to the left of the top slot was already a matched leaf, so the step rewrites the leftmost nonterminal of a1⋯ai−1γa_1 \cdots a_{i-1} \gamma and is a leftmost step.

Step 2: acceptance gives a parse tree. The parser accepts at line L5 only when $\$ is on top, so γ=ε\gamma = \varepsilon, and i=n+1i = n + 1. By Step 1 the tree has root SS, uses only productions of RR, has no slots left, and its leaves spell a1⋯an=wa_1 \cdots a_n = w. So it is a parse tree of ww, and w∈L(G)w \in L(G).

Step 3: sentences are accepted. Let w∈L(G)w \in L(G) and fix a parse tree tt of ww. Claim: as long as the parser runs, the partial tree is the part of tt made of the nodes that have been expanded or matched, together with slots for the children of these nodes that have not, and the next node of tt in preorder is the top slot. This holds at the start. Suppose it holds and the top slot is the node xx of tt, with the label XX, and i−1i - 1 tokens matched.

  • If XX is a terminal, it is the leaf of tt after a1⋯ai−1a_1 \cdots a_{i-1} in preorder, so X=aiX = a_i and the parser matches it.
  • If XX is a nonterminal, let p=X→αp = X \to \alpha be the production of xx in tt, and let ai⋯aj−1a_i \cdots a_{j-1} be the yield of the subtree of xx; it starts at aia_i because every leaf before xx in preorder is one of a1,…,ai−1a_1, \ldots, a_{i-1}. If j>ij > i, then α⇒∗ai⋯aj−1\alpha \Rightarrow^* a_i \cdots a_{j-1} and ai∈FIRST(α)a_i \in \mathrm{FIRST}(\alpha). If j=ij = i, then α⇒∗ε\alpha \Rightarrow^* \varepsilon, and expanding every node of tt except the subtree of xx gives S$⇒∗a1⋯ai−1Xai⋯an$S \$ \Rightarrow^* a_1 \cdots a_{i-1} X a_i \cdots a_n \$, so ai∈FOLLOW(X)a_i \in \mathrm{FOLLOW}(X), where an+1=$a_{n+1} = \$. In both cases ai∈PREDICT(p)a_i \in \mathrm{PREDICT}(p), so M[X,ai]=pM[X, a_i] = p and the parser expands xx with the children it has in tt.
  • If the top is $\$, all nodes of tt have been processed, all nn leaves matched, and the parser accepts.

Each step processes a new node of the finite tree tt, so the parser accepts ww after one step for each node of tt and one final step.

Step 4: an LL(1) grammar is not left-recursive. Claim: no nonterminal AA that is reachable from SS has a derivation A⇒+βAδA \Rightarrow^+ \beta A \delta with β⇒∗ε\beta \Rightarrow^* \varepsilon.

Suppose one does. Following the path from the root to the inner AA in the tree of this derivation gives nonterminals A0=A,A1,…,Ak=AA_0 = A, A_1, \ldots, A_k = A with k≥1k \ge 1 and productions pj=Aj→βjAj+1δjp_j = A_j \to \beta_j A_{j+1} \delta_j with βj⇒∗ε\beta_j \Rightarrow^* \varepsilon, indices taken modulo kk. Each AjA_j is reachable from SS. Since Aj⇒∗Aj+1δjA_j \Rightarrow^* A_{j+1} \delta_j, we have FIRST(Aj+1)⊆FIRST(Aj)\mathrm{FIRST}(A_{j+1}) \subseteq \mathrm{FIRST}(A_j), and around the cycle all the sets FIRST(Aj)\mathrm{FIRST}(A_j) are equal to one set FF. Moreover F⊆FIRST(βjAj+1δj)⊆PREDICT(pj)F \subseteq \mathrm{FIRST}(\beta_j A_{j+1} \delta_j) \subseteq \mathrm{PREDICT}(p_j). Going once around the cycle from AjA_j gives Aj⇒+AjζjA_j \Rightarrow^+ A_j \zeta_j, where ζj\zeta_j is a string that contains δj\delta_j.

Among all complete trees whose root is one of A0,…,Ak−1A_0, \ldots, A_{k-1}, choose one of least height; say its root is AjA_j, its root production is qq, and its yield is x∈Σ∗x \in \Sigma^*. If q=pjq = p_j, the child Aj+1A_{j+1} of the root would be the root of a complete tree of smaller height; so q≠pjq \ne p_j.

  • If x≠εx \ne \varepsilon, its first token aa is in FIRST(Aj)=F⊆PREDICT(pj)\mathrm{FIRST}(A_j) = F \subseteq \mathrm{PREDICT}(p_j), and in FIRST\mathrm{FIRST} of the right side of qq, so in PREDICT(q)\mathrm{PREDICT}(q). Two productions of AjA_j share aa, a contradiction.
  • If x=εx = \varepsilon, the right side of qq is nullable and FOLLOW(Aj)⊆PREDICT(q)\mathrm{FOLLOW}(A_j) \subseteq \mathrm{PREDICT}(q). Since AjA_j is reachable, S$⇒∗uAjρ$S \$ \Rightarrow^* u A_j \rho \$ for some u,ρu, \rho, and ρ$\rho \$ derives a terminal string because every symbol is productive, so FOLLOW(Aj)\mathrm{FOLLOW}(A_j) is not empty. If ζj\zeta_j is not nullable, it derives a nonempty terminal string; let bb be its first token. Then b∈FIRST(Aj)b \in \mathrm{FIRST}(A_j), because Aj⇒+Ajζj⇒∗ζjA_j \Rightarrow^+ A_j \zeta_j \Rightarrow^* \zeta_j, and b∈FOLLOW(Aj)b \in \mathrm{FOLLOW}(A_j), because S$⇒∗uAjζjρ$S \$ \Rightarrow^* u A_j \zeta_j \rho \$. So b∈PREDICT(pj)∩PREDICT(q)b \in \mathrm{PREDICT}(p_j) \cap \mathrm{PREDICT}(q), a contradiction. If ζj\zeta_j is nullable, then δj\delta_j is nullable, Aj+1⇒∗Ajζ′⇒∗εA_{j+1} \Rightarrow^* A_j \zeta' \Rightarrow^* \varepsilon for a part ζ′\zeta' of ζj\zeta_j, and βj\beta_j is nullable; so the right side of pjp_j is nullable and PREDICT(pj)⊇FOLLOW(Aj)\mathrm{PREDICT}(p_j) \supseteq \mathrm{FOLLOW}(A_j), which meets PREDICT(q)\mathrm{PREDICT}(q), a contradiction.

Step 5: the partial tree is small. Let r≥2r \ge 2 be at least the length of every right side, and let m≤nm \le n be the number of matched tokens at some moment. Two facts about a path from an inner node uu down to an inner node vv of the partial tree with the same start index:

  • every node to the left of the path inside the subtree of uu has been completed without matching a token, so its yield is ε\varepsilon; reading the productions along the path gives Xu⇒+βXvδX_u \Rightarrow^+ \beta X_v \delta with β⇒∗ε\beta \Rightarrow^* \varepsilon;
  • so by Step 4, the labels on such a path are distinct, and the path has at most ∣V∣|V| inner nodes.

Call an inner node heavy if its subtree contains a matched token, and light otherwise. Consider the tree formed by the heavy nodes and the mm matched leaves. A heavy node with one heavy or matched child has all its other children to the left of that child light and complete with yield ε\varepsilon, so it has the same start index as that child. Hence each maximal chain of heavy nodes with one such child has at most ∣V∣|V| nodes. There are at most m−1m - 1 heavy nodes with two or more such children, and at most 2m−12m - 1 chains, one above each matched leaf or branching node. So there are at most (m−1)+∣V∣(2m−1)≤(2∣V∣+1)m(m - 1) + |V| (2m - 1) \le (2|V| + 1) m heavy nodes. A light node has no matched leaf below it, so all inner nodes of a maximal light subtree have its start index, and by the second fact the subtree has height at most ∣V∣|V| and at most r∣V∣r^{|V|} inner nodes. The roots of maximal light subtrees are the root of the tree or children of heavy nodes, so there are at most r(2∣V∣+1)m+1r (2|V| + 1) m + 1 of them. In total the partial tree has at most c0(m+1)c_0 (m + 1) inner nodes, with c0=(2∣V∣+1)(1+r∣V∣+1)c_0 = (2|V| + 1)(1 + r^{|V| + 1}).

Step 6: termination and the bound. Each step of the parser is a match, of which there are at most nn, an expansion, which creates an inner node, or the final step. By Step 5 there are at most c0(n+1)c_0 (n + 1) expansions, so the parser stops after at most c⋅(n+1)c \cdot (n + 1) steps with c=c0+2c = c_0 + 2. This proves part 1.

Step 7: rejection. If w∉L(G)w \notin L(G), the parser stops by Step 6 and cannot accept by Step 2, so it reports an error. With Step 3 this proves part 2.

Step 8: the valid prefix property. Suppose the parser reports an error at aia_i. By Step 1, S⇒∗a1⋯ai−1γS \Rightarrow^* a_1 \cdots a_{i-1} \gamma, and γ\gamma derives some terminal string zz because every symbol is productive; so a1⋯ai−1z∈L(G)a_1 \cdots a_{i-1} z \in L(G). Now let i≤ni \le n and suppose a1⋯aiz′∈L(G)a_1 \cdots a_i z' \in L(G) for some z′z'. Until it matches aia_i, the parser looks only at the tokens a1,…,aia_1, \ldots, a_i, so it makes the same moves on ww and on a1⋯aiz′a_1 \cdots a_i z'. On the second input it never reports an error by Step 3, so it matches aia_i on both inputs, and it cannot report an error at aia_i on ww. This proves part 3. ■\blacksquare

Back to LL(1) table-driven parsing