Algorithm Search⌘K

Proofs: Shift-reduce parsing

These are the human proofs of Shift-reduce parsing, at full length.

The shift-reduce driver builds a reverse rightmost derivation, and the exact decision procedure parses every grammar full

This proof shows: correctness, termination.

We use the pseudocode SHIFT-REDUCE of the page and the notation of the context-free grammar page. The input is w=a1⋯anw = a_1 \cdots a_n. A configuration is the stack, a sequence of trees t1,…,tmt_1, \ldots, t_m with roots X1,…,XmX_1, \ldots, X_m, together with pos. A legal move is a shift (if pos≤npos \le n) or a reduction by a production A→βA \to \beta whose right side β\beta is the sequence of the top ∣β∣|\beta| roots. The augmented grammar adds a new start symbol S′S' and the production S′→SS' \to S.

Claim 1: whatever DECIDE does, an accepted tree is right

Invariant. After every move, (i) the leaves of t1,…,tmt_1, \ldots, t_m, left to right, are a1⋯apos−1a_1 \cdots a_{pos-1}; (ii) each tit_i is a parse tree with root XiX_i; (iii) if p1,…,prp_1, \ldots, p_r are the productions of the reductions so far, then applying pr,pr−1,…,p1p_r, p_{r-1}, \ldots, p_1 to X1⋯Xm apos⋯anX_1 \cdots X_m\, a_{pos} \cdots a_n, each to the rightmost nonterminal, gives a rightmost derivation X1⋯Xm apos⋯an⇒rm∗wX_1 \cdots X_m\, a_{pos} \cdots a_n \Rightarrow^*_{rm} w.

Proof. At the start the stack is empty, pos is 1, and the derivation of ww from ww has no step. A shift appends the leaf aposa_{pos} and increases pos, which changes neither the string X1⋯Xm apos⋯anX_1 \cdots X_m\, a_{pos} \cdots a_n nor the derivation. A reduction by A→βA \to \beta replaces the top trees, whose roots spell β\beta, by a node AA over them, which keeps (i) and (ii). The string becomes αA apos⋯an\alpha A\, a_{pos} \cdots a_n, where α\alpha is the rest of the stack, and αA apos⋯an⇒αβ apos⋯an\alpha A\, a_{pos} \cdots a_n \Rightarrow \alpha \beta\, a_{pos} \cdots a_n rewrites AA, which is the rightmost nonterminal because everything after it is terminal. So this step, followed by the old derivation, is a rightmost derivation, which is (iii). □\square

When the driver accepts, the stack is the single tree with root SS and pos is n+1n + 1, so by (i) and (ii) it is a parse tree of ww, and by (iii) the reductions read backwards are a rightmost derivation S⇒rm∗wS \Rightarrow^*_{rm} w.

Goodness and item chains

A configuration is good if some sequence of legal moves leads from it to the stack SS with pos =n+1= n + 1. Legal moves depend only on the roots, so goodness depends only on X1⋯XmX_1 \cdots X_m and pos.

Lemma 2. The configuration with roots γ=X1⋯Xm\gamma = X_1 \cdots X_m at position pos is good if and only if there are productions S′→β1A2δ1S' \to \beta_1 A_2 \delta_1, A2→β2A3δ2A_2 \to \beta_2 A_3 \delta_2, ..., Ak−1→βk−1Akδk−1A_{k-1} \to \beta_{k-1} A_k \delta_{k-1} and Ak→βkδkA_k \to \beta_k \delta_k of the augmented grammar such that γ=β1β2⋯βk\gamma = \beta_1 \beta_2 \cdots \beta_k and δkδk−1⋯δ1⇒∗apos⋯an\delta_k \delta_{k-1} \cdots \delta_1 \Rightarrow^* a_{pos} \cdots a_n.

Proof. (Only if.) Take a sequence of moves that ends in acceptance, and let TT be the final tree, with a root S′S' added above SS. The current trees t1,…,tmt_1, \ldots, t_m are subtrees of TT, because later moves only put new nodes above them; call them old, and call the nodes made by later reductions new. A new node is pushed on top of the stack, to the right of every old tree still there, so in the order of children, old trees always come before new nodes that contain no old tree. Hence the children of a new node are: some old trees, then at most one new node that contains old trees, then new nodes and future leaves that contain no old tree. Start at the root S′S' and walk down: at a new node with production Aj→βjAj+1δjA_j \to \beta_j A_{j+1} \delta_j, βj\beta_j are the roots of its old children, Aj+1A_{j+1} is the child that contains old trees, and δj\delta_j are the rest; stop at the first node that has no child containing old trees, with production Ak→βkδkA_k \to \beta_k \delta_k. The β\beta's are all the old trees, in order, so their concatenation is γ\gamma. The trees of the δ\delta's cover the leaves apos⋯ana_{pos} \cdots a_n, from the deepest node up, so δk⋯δ1⇒∗apos⋯an\delta_k \cdots \delta_1 \Rightarrow^* a_{pos} \cdots a_n. (If the stack is already SS at pos=n+1pos = n + 1, take k=1k = 1, β1=S\beta_1 = S, δ1=ε\delta_1 = \varepsilon.)

(If.) Split apos⋯ana_{pos} \cdots a_n into parts derived by the symbols of δk,…,δ1\delta_k, \ldots, \delta_1 and choose a parse tree for each part. Build TT from the bottom: a node AkA_k over the old trees of βk\beta_k and the trees of δk\delta_k, then a node Ak−1A_{k-1} over the old trees of βk−1\beta_{k-1}, the node AkA_k and the trees of δk−1\delta_{k-1}, and so on up to S′S'. Then TT is a parse tree of ww that contains t1,…,tmt_1, \ldots, t_m, and its new nodes are the nodes on the chain and the nodes of the δ\delta trees. Do the moves of the postorder of the new nodes and future leaves: shift each future leaf when it comes, and reduce each new node when it comes. By induction along the postorder, when a node comes, all its children are finished and are the top entries of the stack in order, since everything later in the postorder is to their right. So each move is legal, and the last one makes the node SS over everything, at pos n+1n + 1. □\square

The implementation tests the condition of Lemma 2 by running the items of the chains over γ\gamma. It keeps pairs (item [A→β⋅δ][A \to \beta \cdot \delta], ee), where ee is the position where the continuations of the ancestors, δj−1⋯δ1\delta_{j-1} \cdots \delta_1, must start to derive ae⋯ana_e \cdots a_n. Reading a symbol XX moves the dot over XX; at an item [A→β⋅Bδ][A \to \beta \cdot B \delta] with end ee, the pairs ([B→⋅η],e′)([B \to \cdot \eta], e') are added for every e′e' with δ⇒∗ae′⋯ae−1\delta \Rightarrow^* a_{e'} \cdots a_{e-1}. The configuration is good when some pair ([A→β⋅δ],e)([A \to \beta \cdot \delta], e) after γ\gamma has δ⇒∗apos⋯ae−1\delta \Rightarrow^* a_{pos} \cdots a_{e-1}. The relation ⇒∗\Rightarrow^* on substrings is computed once by a least fixpoint.

Claim 2: the exact procedure accepts exactly L(G)L(G) and stops

Lemma 3. From a good configuration that is not an acceptance, the procedure returns a legal move to a good configuration, and the number of moves before the next shift or acceptance is finite.

Proof. Fix an accepting continuation c0,c1,…c_0, c_1, \ldots from the current configuration c0c_0. Every cic_i is good. Let cjc_j be the configuration just before its first shift, or its final acceptance; the moves c0→⋯→cjc_0 \to \cdots \to c_j are good reductions, and cjc_j is a target: shifting from it is good, or it is the acceptance. If c0c_0 is a target the procedure shifts or accepts. Otherwise the breadth-first search over good reductions from c0c_0 explores level by level, each stack has at most ∣P∣|P| reductions, and a target lies at depth at most jj; so the search stops, and returns the first move of a shortest path to a target, which leads to a good configuration. Let d(c)d(c) be the length of a shortest path of good reductions from cc to a target. The move goes along a shortest path, so dd decreases by one at each reduction. When d=0d = 0 the procedure shifts or accepts. □\square

Theorem. With the exact procedure, the driver accepts if and only if w∈L(G)w \in L(G), and it always stops.

Proof. The start configuration, empty stack at pos 1, is good exactly when w∈L(G)w \in L(G): by Lemma 2 with γ=ε\gamma = \varepsilon, the condition reads S⇒∗wS \Rightarrow^* w. If it is not good, the procedure reports an error at once. If it is good, by Lemma 3 every move keeps the configuration good, there are at most nn shifts, and between two shifts there are finitely many reductions, so the run reaches the acceptance; by Claim 1 the returned tree is a parse tree of ww. □\square

Back to Shift-reduce parsing