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 be a grammar, let be without the productions that use a nonterminal deriving no terminal string, augmented with , and suppose BUILD-LR0 finds no conflict for . Then for every input , with :
- LR0-PARSE accepts if and only if , and then it returns a parse tree of with root ;
- if and it reports an error at token , then is a prefix of a sentence, and is not a prefix of any sentence when , while when ;
- it stops after at most steps, where depends only on .
and 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 . We write for the string of symbols on the stack (the labels of the roots of the stack trees, from the bottom), for the stack of states, as on the LR item page, and 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) for and every , and (b) the -th stack tree is a parse tree of with root , and the yields of the stack trees, in order, spell . Both hold at the start. A shift of pushes and the leaf . A reduction by happens when contains the complete item . By Knuth's theorem on the LR item page, is the set of items valid for , so there is a rightmost derivation with . So the top symbols are , the popped trees have the roots , and the new node satisfies the definition of a parse tree. Moreover is a prefix of the right-sentential form , so it has a valid item, is defined, and it equals .
Step 2 (soundness). If the parser accepts, the top state contains , which is valid for ; as occurs on no right side, the only rightmost derivation that can make it valid is itself, so . By Step 1(b) the first stack tree has root and yield without its last token . The parser returns it only if was the last token, that is, , so it returns a parse tree of , and .
Step 3 (shifts stay inside sentences). Suppose the parser shifts with stack after shifting . Then is defined, so has a valid item and is a prefix of a right-sentential form of . Every symbol of derives a terminal string, so for some terminal string , and by Step 1(b). Hence . Every terminal string derived from is with , so is a prefix of for some sentence .
Step 4 (completeness). Let and let be a rightmost derivation in , with . Write for , where is the handle reduced in the step from , by , and is a terminal string. We claim that the run on passes through configurations with stack and unread input such that and is a prefix of , for . At the start, and . Consider such a configuration.
- If , the item is valid for , 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 , or accepts if . For the input is used up, so the parser accepts . For the new stack is with input , and . The handle of ends at or after this , because consists of terminals and the step from rewrote the rightmost nonterminal; so is a prefix of .
- If is a proper prefix of , the next symbol of is the first symbol of , a terminal. Then is a prefix of and has a valid item, so the top state has an item with the dot before . Since there is no conflict, it has no complete item, its action is shift, and is defined. The parser shifts , and the claim holds for the same with a longer .
Each step shortens or decreases , so the run reaches and accepts. With Step 2 this proves part 1. It also shows that the run on follows the rightmost derivation of any tree of ; since the run is unique, has only one parse tree in .
Step 5 (earliest error). The actions before the -th shift depend only on , because the parser looks at a token only when it shifts it. An error at line L5 at token means is undefined after has been shifted. If , Step 3 for the last shift shows that is a prefix of for a sentence , and as the parser has not accepted, is not in , so is a prefix of ; if , is a prefix of any sentence. If were a prefix of for some sentence , the run on would coincide with the run on up to this point and then fail, contradicting Step 4. For this means that is no prefix of a sentence; for , and it means . At line L7, an error at token means that the parser shifted as the token with . Then by Step 2's argument, and no sentence contains . Finally, a state with no action can only be state 0 when has no production in , that is, when . This proves part 2.
Step 6 (linear time). Assume ; otherwise the parser stops at once. Consider any moment of the run before it stops, with shifted. By Step 3, or trivially if , is a prefix of for a sentence , and the run so far coincides with the run on , which by Step 4 is finite and builds the unique tree . Every node built so far lies in a stack tree, and every stack tree is a subtree of .
No repeated node. In 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 , against uniqueness. Let be the number of nonterminals of and the length of its longest right side.
Size of a stack tree. Let be a stack tree with yield of length . The nonempty yield spans of the nodes of form a laminar family of intervals of positions, so there are at most 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 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 , and its size is at most . Each such subtree is itself or a child of a node with nonempty yield. So with .
Number of stack trees. At most stack trees have a nonempty yield. Let and , , be two stack entries with empty yield and only entries with empty yield between them. Entries higher on the stack were pushed later, and has not been popped since it was pushed. If a token had been shifted between the pushes of and , it would lie in an entry above and below , which would then have a nonempty yield; so no token was shifted in between. In that interval the parser never looked below , so its steps depend only on the state of . If and had the same state, then from the push of on the same steps would repeat forever without a shift, but the run on 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 entries, where is the number of states. There are at most blocks, so there are at most 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 with . Since , the run has at most steps before it stops, which proves part 3.