Proofs: LR parse table
These are the human proofs of LR parse table, at full length.
A conflict-free LR(0), SLR or LR(1) table makes LR-DRIVE a correct parser with the valid prefix property full
This proof shows: correctness.
Setting. Let be a grammar in which every nonterminal derives some terminal string (the implementation first drops the productions that use a nonterminal deriving none), augmented with , where is new. For a string , write for the state reached from the start state by the automaton's transitions on the symbols of , if all these transitions exist. The automaton is the LR(0) automaton for the kinds LR(0) and SLR, and the canonical LR(1) automaton for LR(1). A viable prefix is a prefix of a right-sentential form with no longer than , where . An LR(0) item is valid for if with and , and the LR(1) item is valid if moreover is the first symbol of .
We use Knuth's theorem on the LR item page: is defined if and only if is a viable prefix, and then it is the set of items valid for (LR(0) items for the LR(0) automaton, LR(1) items for the LR(1) automaton).
Statement. Suppose FILL, run on the automaton with the lookahead sets of the chosen kind, records no conflict. Let and . Then:
- (Soundness.) If LR-DRIVE accepts , then the tree it returns is a parse tree of , so .
- (Completeness.) If , LR-DRIVE accepts and reports no error.
- (Valid prefix.) LR-DRIVE shifts only if is a prefix of a sentence, and when it reports an error at , the string (for , the string itself) is not a prefix of any sentence.
Termination on inputs that are not sentences is not part of this statement; it is proved for LR(0) tables on the LR(0) parsing page and by Knuth (1965) for LR(k) in general.
Proof.
Step 1 (cells). Since there is no conflict, L7 assigns each cell at most one action, and by L1 to L5 the action is in cell exactly when some item of calls for it: shift to the successor of on when has an item with the dot before (then this transition exists), accept when and , and reduce when has a complete item with in its lookahead set. The lookahead set is for LR(0), for SLR, and for the LR(1) item .
Step 2 (dots and stack). For every item of , is a suffix of , say , and is in . By induction on : the items of have the dot at the start; an item of either comes from closure, with the dot at the start, or is from . Moreover, an item with is in a state only through closure, so that state also has an item with the dot before and the transition on exists. The item is in only for , since is on no right side and is only in .
Let be the string of grammar symbols on the stack. We claim that LR-DRIVE keeps the invariant: the states on the stack are of the prefixes of , from to on top; the trees on the stack have the roots in order; and the yields of the trees, concatenated, are the tokens shifted so far. A shift of (L12) pushes and the leaf . A reduction by (L13) happens only when (Step 1), so by Step 2; it pops states and trees, exposes , which contains , so the GOTO entry for exists, and pushes and the node whose children are the popped trees, a correct node of a parse tree since .
Step 3 (soundness). Accept (L14) happens only on , which comes only after all of has been shifted (L11 rejects a token inside the input), and only when , that is, by Step 2. By the invariant, the stack then holds one tree with root and yield . This proves part 1.
Step 4 (completeness). Let and fix a rightmost derivation . Read backwards, it defines the canonical run of a shift-reduce parser: in configuration (stack , unread input ) with , let where ; if , reduce by , reaching the configuration with ; otherwise shift. The canonical run ends with stack and empty input, and then accepts.
We show that in every configuration of the canonical run, is a prefix of and with . At the start , and the handle of is a terminal string. Shifts keep the property as long as . After a reduction, the stack is and is the rightmost nonterminal of . The next handle comes from the step . If contains a nonterminal, it contains the rightmost one, , so with and . Otherwise is terminal and lies to the right of , inside , so with . Both cases give the claim. So every stack of the canonical run is a viable prefix, and so is whenever the canonical move is to shift .
Now we show by induction on the steps that LR-DRIVE makes the same moves as the canonical run. Both start in the same configuration, and by Step 2 the states of LR-DRIVE are determined by , so it is enough to show that the cell , with the next token, contains the canonical move: since the cell holds at most one action, it then holds exactly that move.
- Shift . is a viable prefix, so by Knuth's theorem is defined, so has an item with the dot before and the cell holds a shift.
- Reduce , with , and the first symbol of . The LR(0) item and the LR(1) item are valid for , so by Knuth's theorem they are in . For LR(0), every column gets the reduction. For SLR, shows . For LR(1), is the lookahead of the item. In all three cases the cell holds the reduction.
- Accept. The stack is , the state contains , and the next token is , so the cell holds accept.
So LR-DRIVE follows the canonical run, never reports an error, and accepts. This proves part 2.
Step 5 (valid prefix). If LR-DRIVE shifts from stack , the state is defined, so is a viable prefix by Knuth's theorem, a prefix of a right-sentential form . Every symbol of derives some terminal string , and by the invariant , so .
Suppose LR-DRIVE reports an error at but is a prefix of a sentence (for , suppose , which part 2 excludes). Each move of LR-DRIVE depends only on its stack and the next token, so its runs on and on coincide up to the first time it looks at token and moves on. By part 2 the run on never reports an error and shifts its -th token after finitely many moves, so the run on also shifts and does not report an error at it, a contradiction. This proves part 3.