Proofs: SLR(1) parsing
These are the human proofs of SLR(1) parsing, at full length.
An SLR(1) parser without conflicts parses exactly the sentences, detects errors at the first bad token, and stops in linear time full
This proof shows: correctness, termination.
We use the notation of the context-free grammar page and the pseudocode BUILD-SLR and SLR-PARSE of the page. Line B1 removes the useless productions; this changes neither nor the parse trees of sentences, since no parse tree of a sentence uses a useless production. So we assume that is reduced: every nonterminal is reachable from and derives a terminal string. is augmented with , the end marker is , and FOLLOW is computed for the augmented grammar. We assume that the table has no conflict. The input is and .
An item is valid for a string of grammar symbols if there is a rightmost derivation with and a terminal string. is a viable prefix if some item is valid for it. Write for the state reached from state 0 by the GOTO transitions over the symbols of , if it exists.
Lemma 1: the states are the sets of valid items
Claim. For every , exists if and only if is viable, and then is the set of items valid for .
Items in the state are valid. By induction on the construction. is valid for (the derivation with no step, ). GOTO keeps validity: if is valid for , the same derivation shows that is valid for . CLOSURE keeps validity: if is valid for , by , then derives a terminal string because is reduced, and is a rightmost derivation for every production . So is valid for .
Valid items are in the state. Let be valid for , by a derivation of steps. We show by induction on that , where ; then GOTO over the symbols of gives . If , then , , and is in state 0. Otherwise, look at the step of the derivation that created this occurrence of , by a production : . After this step the derivation rewrites only symbols to the right of , until itself is rewritten, because it is rightmost. So and derives a terminal prefix of . Then is valid for by a shorter derivation, so by induction it is in , and CLOSURE adds .
Finally, a state built by GOTO from a nonempty kernel is nonempty, and every item in it is valid, so is viable when exists; conversely, if some item is valid for , it lies in , which therefore exists.
Lemma 2: the stack invariant, and soundness
A configuration is a stack of trees with roots , a stack of states, and the position of the next token. Invariant: the states are ; the leaves of the trees, left to right, are ; and each tree is a parse tree with its root.
It holds after line L1. A shift of pushes and the leaf . A reduction by is made in a state that contains , so by Lemma 1 the item is valid for , which therefore ends with , say . The same derivation shows that is valid for , so , the state uncovered by line L8, has a GOTO on , and line L9 pushes and a node over the trees of . So the invariant holds, and line L9 never finds GOTO undefined.
Soundness. The accept action is only in a state containing , which by Lemma 1 is valid only for , and only on , that is, at . So an accepted result is a single parse tree with root and leaves . Reading the reductions backwards gives a rightmost derivation of , by Claim 1 of the shift-reduce parsing proof, which holds for every sequence of legal moves.
Lemma 3: every move toward acceptance is in the table
A configuration is good if some sequence of legal shift-reduce moves (shift the next token, or reduce a right side that forms the top of the stack) leads from it to the stack at position . By Claim 1 of the shift-reduce proof applied to such a continuation, its reductions read backwards give a rightmost derivation .
Claim. Let a configuration with the invariant of Lemma 2 be good, and fix an accepting continuation. Its next move is an action in .
Proof. Reduce by . Then , and the continuation after the move gives . With in front and the step , the item is valid for , so it is in by Lemma 1. The same derivation shows (or when ). So line B5 put "reduce " into the entry.
Shift . The leaf ends inside the final tree, so some later reduction of the continuation pops it; take the first one, by . Until then nothing below is popped, so at that moment the stack is with , and as in the first case the continuation gives with terminal. So is valid for , it is in , and line B4 put the shift into the entry.
Accept. Then and ; is in and line B6 put the accept action on .
Corollary. Since the table has no conflict, from a good configuration the parser makes the next move of every accepting continuation, and the new configuration is good again. So all accepting continuations from a good configuration are the same sequence, and the parser follows it to acceptance in finitely many moves. In particular the grammar is unambiguous: two parse trees of would give two different reverse rightmost derivations, that is, two different accepting continuations from the start.
Theorem 4: the parser accepts exactly
If , the start configuration is good (any rightmost derivation of , read backwards, is an accepting continuation), so by the corollary the parser accepts, with the parse tree of . If , it never accepts, by soundness.
Lemma 5: errors are found at the first bad token
Claim. The parser shifts (for ) only if is a prefix of a sentence, and it accepts only if is a sentence. Conversely, if is a prefix of a sentence, the parser shifts , or accepts when and .
Proof. A shift of happens in a state with an item , which by Lemma 1 is valid for : with . The trees on the stack show , and derives a terminal string because is reduced, so followed by that string and is a sentence. For the converse, let start with . Each move depends only on the stack and the token being examined, and until is shifted the parser examines only . So the runs on and agree until is shifted, and on that happens, by Theorem 4.
So if and the parser stops, it reports the error at the least such that is not a prefix of a sentence, where means that itself is a proper prefix of a sentence. It remains to show that it stops.
Lemma 6: no cycle of empty symbols in the automaton
Claim. There are no states , , and nullable symbols with .
Proof. Suppose there are. Every state is reached by some string, so for a viable , and for every . By Lemma 1 these strings are viable, so for each there is a valid item with , ; since is reduced, derives a terminal string , and the 's derive , so
Put trees for the symbols of , with some terminal yield , on the stack with the states of its prefixes, and the input . Read backwards, the derivation above is an accepting continuation from this configuration, which first builds the empty trees one after the other, without shifting. By the corollary of Lemma 3, the parser follows it. But before its first shift, the parser's moves from stack depend only on the first token of . Some is the first token for infinitely many , and for all of them the parser makes the same sequence of moves before its first shift. That sequence is finite (it is part of an accepting run for one of them) and yet contains a stack of height for infinitely many . This is impossible.
Theorem 7: the parser stops after moves
Between two shifts there are finitely many reductions. Suppose that from some configuration the parser reduces forever. No new leaf is shifted, so every tree created from then on, on top of the stack, has the empty yield, and every stack is viable by Lemma 2 and Lemma 1.
Case 1: some stack of states occurs twice. The states determine the symbols (each state other than 0 is entered by one symbol), so the two configurations have the same roots , and the second forest is obtained from the first by at least one reduction, so it has more nodes and the same leaves. Since is viable and is reduced, there is a rightmost derivation with terminal; hanging the first forest or the second forest under its tree gives two different parse trees of the same sentence. This contradicts the unambiguity in the corollary of Lemma 3.
Case 2: no stack of states repeats. Let be the lowest stack level that is popped infinitely often, if there is one. Then after some time the levels below never change, and each time level is popped, the next push gives a stack of states of height that consists of the fixed lower part and one state: finitely many stacks, occurring infinitely often, which is Case 1. So every level is eventually never popped. The heights are then unbounded (otherwise some stack would repeat), so there are infinitely many entries that are never popped, above the height at the start; their trees have empty yields. Two of them, at levels , carry the same state, and the entries between them are consecutive, with states linked by GOTO over their nullable root symbols. This is a cycle as in Lemma 6, a contradiction.
So between two shifts the parser makes finitely many reductions, there are at most shifts, and the parser stops. By Theorem 4 and Lemma 5 it then accepts exactly the sentences, with their parse tree, and reports any other input at the first token that no sentence allows there.
The number of moves is linear. Every move creates a node, a leaf for a shift and an inner node for a reduction, and no node is ever destroyed, so the number of moves is at most the number of nodes in the forest on the stack when the parser stops, plus one. Let that forest have the roots and the leaves , with . Since is viable, as in Case 1 the forest is part of a parse tree of a sentence, and is unambiguous. Then in no node has a proper descendant with the same label and the same span of leaves, since replacing the upper node's subtree by the lower one gives another parse tree of the same sentence. Let be the longest right side and the number of states. In the forest:
- the nodes with a nonempty span have at most different spans, since the spans of a tree are nested or disjoint, and at most nodes have each span, because nodes with the same nonempty span lie on one path and have different labels;
- each maximal subtree with an empty yield has no repeated label on a path, so it has at most nodes;
- such a subtree is either a child of a node with a nonempty span, at most per node, or a root of the forest. The roots with an empty yield come in runs of consecutive stack entries, one run after each root with a nonempty yield and one at the bottom; a run has at most entries, since two entries with the same state in a run would give a cycle as in Lemma 6.
So the forest has at most nodes, which is for a fixed grammar, and the parser makes moves.