Algorithm Search⌘K

Proofs: LR(0) parsing

These are the human proofs of LR(0) parsing, at full length.

LR0-PARSE accepts exactly the sentences, reports errors at the earliest token, and runs in linear time full

This proof shows: correctness.

Statement. Let GG be a grammar, let G†G^\dagger be GG without the productions that use a nonterminal deriving no terminal string, augmented with S′→S $S' \to S \, \$, and suppose BUILD-LR0 finds no conflict for G†G^\dagger. Then for every input w=a1⋯anw = a_1 \cdots a_n, with an+1=$a_{n+1} = \$:

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

GG and G†G^\dagger have the same parse trees of sentences, because a tree of a terminal string uses only nonterminals that derive terminal strings. So it suffices to argue about G†G^\dagger. We write γ\gamma for the string of symbols on the stack (the labels of the roots of the stack trees, from the bottom), q0,…,qmq_0, \ldots, q_m for the stack of states, GOTO(I0,γ)\mathrm{GOTO}(I_0, \gamma) as on the LR item page, and uu for the tokens shifted so far. A run coincides with another run up to a point if both made the same steps until then.

Step 1 (invariant). At all times (a) qj=GOTO(I0,X1⋯Xj)q_j = \mathrm{GOTO}(I_0, X_1 \cdots X_j) for γ=X1⋯Xm\gamma = X_1 \cdots X_m and every jj, and (b) the jj-th stack tree is a parse tree of G†G^\dagger with root XjX_j, and the yields of the stack trees, in order, spell uu. Both hold at the start. A shift of aa pushes GOTO(qm,a)\mathrm{GOTO}(q_m, a) and the leaf aa. A reduction by A→βA \to \beta happens when qmq_m contains the complete item A→β⋅A \to \beta \cdot. By Knuth's theorem on the LR item page, qmq_m is the set of items valid for γ\gamma, so there is a rightmost derivation S′⇒rm∗δAx⇒rmδβxS' \Rightarrow_{rm}^* \delta A x \Rightarrow_{rm} \delta \beta x with γ=δβ\gamma = \delta \beta. So the top ∣β∣|\beta| symbols are β\beta, the popped trees have the roots β\beta, and the new node satisfies the definition of a parse tree. Moreover δA\delta A is a prefix of the right-sentential form δAx\delta A x, so it has a valid item, GOTO(qm−∣β∣,A)\mathrm{GOTO}(q_{m - |\beta|}, A) is defined, and it equals GOTO(I0,δA)\mathrm{GOTO}(I_0, \delta A).

Step 2 (soundness). If the parser accepts, the top state contains S′→S$⋅S' \to S \$ \cdot, which is valid for γ\gamma; as S′S' occurs on no right side, the only rightmost derivation that can make it valid is S′⇒S$S' \Rightarrow S \$ itself, so γ=S$\gamma = S \$. By Step 1(b) the first stack tree has root SS and yield uu without its last token $\$. The parser returns it only if $\$ was the last token, that is, u=w$u = w \$, so it returns a parse tree of ww, and w∈L(G)w \in L(G).

Step 3 (shifts stay inside sentences). Suppose the parser shifts aa with stack γ\gamma after shifting uu. Then GOTO(qm,a)\mathrm{GOTO}(q_m, a) is defined, so γa\gamma a has a valid item and is a prefix of a right-sentential form γaζ\gamma a \zeta of G†G^\dagger. Every symbol of G†G^\dagger derives a terminal string, so ζ⇒∗x\zeta \Rightarrow^* x for some terminal string xx, and γ⇒∗u\gamma \Rightarrow^* u by Step 1(b). Hence S′⇒∗uaxS' \Rightarrow^* u a x. Every terminal string derived from S′S' is v$v \$ with v∈L(G)v \in L(G), so uau a is a prefix of v$v \$ for some sentence vv.

Step 4 (completeness). Let v∈L(G)v \in L(G) and let S′=θ0⇒rmθ1⇒rm⋯⇒rmθM=v$S' = \theta_0 \Rightarrow_{rm} \theta_1 \Rightarrow_{rm} \cdots \Rightarrow_{rm} \theta_M = v \$ be a rightmost derivation in G†G^\dagger, with θ1=S$\theta_1 = S \$. Write θj=αjβjxj\theta_j = \alpha_j \beta_j x_j for j≥1j \ge 1, where βj\beta_j is the handle reduced in the step from θj−1\theta_{j-1}, by Aj→βjA_j \to \beta_j, and xjx_j is a terminal string. We claim that the run on vv passes through configurations with stack γ\gamma and unread input zz such that γz=θj\gamma z = \theta_j and γ\gamma is a prefix of αjβj\alpha_j \beta_j, for j=M,M−1,…,1j = M, M-1, \ldots, 1. At the start, γ=ε\gamma = \varepsilon and z=v$=θMz = v \$ = \theta_M. Consider such a configuration.

  • If γ=αjβj\gamma = \alpha_j \beta_j, the item Aj→βj⋅A_j \to \beta_j \cdot is valid for γ\gamma, so the top state has a complete item. Since there is no conflict, it is the only complete item and the state has no shift item, so the parser reduces by Aj→βjA_j \to \beta_j, or accepts if j=1j = 1. For j=1j = 1 the input is used up, so the parser accepts vv. For j>1j > 1 the new stack is αjAj\alpha_j A_j with input xjx_j, and αjAjxj=θj−1\alpha_j A_j x_j = \theta_{j-1}. The handle of θj−1\theta_{j-1} ends at or after this AjA_j, because xjx_j consists of terminals and the step from θj−2\theta_{j-2} rewrote the rightmost nonterminal; so αjAj\alpha_j A_j is a prefix of αj−1βj−1\alpha_{j-1} \beta_{j-1}.
  • If γ\gamma is a proper prefix of αjβj\alpha_j \beta_j, the next symbol of αjβj\alpha_j \beta_j is the first symbol aa of zz, a terminal. Then γa\gamma a is a prefix of αjβj\alpha_j \beta_j and has a valid item, so the top state has an item with the dot before aa. Since there is no conflict, it has no complete item, its action is shift, and GOTO(qm,a)\mathrm{GOTO}(q_m, a) is defined. The parser shifts aa, and the claim holds for the same jj with a longer γ\gamma.

Each step shortens zz or decreases jj, so the run reaches j=1j = 1 and accepts. With Step 2 this proves part 1. It also shows that the run on vv follows the rightmost derivation of any tree of vv; since the run is unique, vv has only one parse tree in G†G^\dagger.

Step 5 (earliest error). The actions before the (i+1)(i+1)-th shift depend only on a1⋯aia_1 \cdots a_i, because the parser looks at a token only when it shifts it. An error at line L5 at token ii means GOTO(qm,ai)\mathrm{GOTO}(q_m, a_i) is undefined after u=a1⋯ai−1u = a_1 \cdots a_{i-1} has been shifted. If i>1i > 1, Step 3 for the last shift shows that uu is a prefix of v$v \$ for a sentence vv, and as the parser has not accepted, $\$ is not in uu, so uu is a prefix of vv; if i=1i = 1, u=εu = \varepsilon is a prefix of any sentence. If a1⋯aia_1 \cdots a_i were a prefix of v′$v' \$ for some sentence v′v', the run on v′v' would coincide with the run on ww up to this point and then fail, contradicting Step 4. For i≤ni \le n this means that a1⋯aia_1 \cdots a_i is no prefix of a sentence; for i=n+1i = n + 1, ai=$a_i = \$ and it means w∉L(G)w \notin L(G). At line L7, an error at token ii means that the parser shifted $\$ as the token aia_i with i≤ni \le n. Then a1⋯ai−1∈L(G)a_1 \cdots a_{i-1} \in L(G) by Step 2's argument, and no sentence contains $\$. Finally, a state with no action can only be state 0 when SS has no production in G†G^\dagger, that is, when L(G)=∅L(G) = \emptyset. This proves part 2.

Step 6 (linear time). Assume L(G)≠∅L(G) \ne \emptyset; otherwise the parser stops at once. Consider any moment of the run before it stops, with uu shifted. By Step 3, or trivially if u=εu = \varepsilon, uu is a prefix of v$v \$ for a sentence vv, and the run so far coincides with the run on vv, which by Step 4 is finite and builds the unique tree tvt_v. Every node built so far lies in a stack tree, and every stack tree is a subtree of tvt_v.

No repeated node. In tvt_v no node has a proper descendant with the same label and the same yield span: otherwise replacing the subtree of the upper one by the subtree of the lower one gives a second tree of vv, against uniqueness. Let pp be the number of nonterminals of G†G^\dagger and r≥1r \ge 1 the length of its longest right side.

Size of a stack tree. Let TT be a stack tree with yield of length ℓ\ell. The nonempty yield spans of the nodes of TT form a laminar family of intervals of ℓ\ell positions, so there are at most 2ℓ−12\ell - 1 distinct ones. Nodes with the same nonempty span lie on one path, because disjoint subtrees with nonempty yields have disjoint spans, so by the previous paragraph at most p+1p + 1 nodes share a span. A maximal subtree with empty yield has all its nodes at the same empty span, so no label repeats on a path, its height is at most pp, and its size is at most cε=rp+1c_\varepsilon = r^{p+1}. Each such subtree is TT itself or a child of a node with nonempty yield. So ∣T∣≤c1(ℓ+1)|T| \le c_1 (\ell + 1) with c1=(p+1)(1+r cε)+cεc_1 = (p + 1)(1 + r \, c_\varepsilon) + c_\varepsilon.

Number of stack trees. At most ∣u∣|u| stack trees have a nonempty yield. Let EcE_c and EdE_d, c<dc < d, be two stack entries with empty yield and only entries with empty yield between them. Entries higher on the stack were pushed later, and EcE_c has not been popped since it was pushed. If a token had been shifted between the pushes of EcE_c and EdE_d, it would lie in an entry above EcE_c and below EdE_d, which would then have a nonempty yield; so no token was shifted in between. In that interval the parser never looked below EcE_c, so its steps depend only on the state of EcE_c. If EcE_c and EdE_d had the same state, then from the push of EdE_d on the same steps would repeat forever without a shift, but the run on vv is finite and ends by shifting $\$. So the entries of one block of consecutive empty-yield entries have distinct states, and each block has at most NN entries, where NN is the number of states. There are at most ∣u∣+1|u| + 1 blocks, so there are at most ∣u∣+N(∣u∣+1)|u| + N (|u| + 1) stack trees.

Total. Each step builds one node, so the number of steps so far is at most the total size of the stack trees, which is at most c1(∣u∣+∣u∣+N(∣u∣+1))≤c (∣u∣+1)c_1 (|u| + |u| + N(|u| + 1)) \le c \, (|u| + 1) with c=c1(N+2)c = c_1 (N + 2). Since ∣u∣≤n+1|u| \le n + 1, the run has at most c (n+2)c \, (n + 2) steps before it stops, which proves part 3. ■\blacksquare

Back to LR(0) parsing