Proofs: LL(1) table-driven parsing
These are the human proofs of LL(1) table-driven parsing, at full length.
LL1-PARSE stops in linear time and accepts exactly the sentences of an LL(1) grammar, with their parse trees full
This proof shows: correctness.
Statement. Let be a grammar in which every nonterminal derives some terminal string, and let be the table built from as on the LL parse table page, with no cell holding two productions. Then for every input :
- LL1-PARSE stops after at most steps, where depends only on ;
- it accepts if and only if , and then it returns a parse tree of with root ;
- if it reports an error at (with ), then is a prefix of a sentence of , and is not when .
Setup. Write , and for the sets as defined, and , for the sets that BUILD computes. The LL parse table page proves that the computed nullable set and FIRST sets are exact and that for every . So for every production , and the cell contains every production of with . Since no cell has two productions, two different productions of one nonterminal have disjoint PREDICT sets.
The partial tree at a moment of the run is the tree below the dummy root built so far: each expanded nonterminal is an inner node, each matched token is a leaf, and each stack entry other than is a slot, a leaf that will receive the subtree of its symbol. The start index of an inner node is the number of tokens matched before it was expanded.
Step 1: the invariant. At each test of line L4, after tokens have been matched, let be the string of the symbols on the stack above , read from the top. Then the partial tree has the root , its leaves from left to right are followed by the slots of (and marks for empty right sides), every inner node uses a production of , and so .
At the start the tree is the slot and the claim holds. A match (lines L6 to L8) turns the top slot, a terminal equal to , into a matched leaf; the matched prefix grows by and loses its first symbol. An expansion (lines L10 to L13) by turns the top slot into an inner node with the slots , and replaces the first symbol of by . Every slot to the left of the top slot was already a matched leaf, so the step rewrites the leftmost nonterminal of and is a leftmost step.
Step 2: acceptance gives a parse tree. The parser accepts at line L5 only when is on top, so , and . By Step 1 the tree has root , uses only productions of , has no slots left, and its leaves spell . So it is a parse tree of , and .
Step 3: sentences are accepted. Let and fix a parse tree of . Claim: as long as the parser runs, the partial tree is the part of made of the nodes that have been expanded or matched, together with slots for the children of these nodes that have not, and the next node of in preorder is the top slot. This holds at the start. Suppose it holds and the top slot is the node of , with the label , and tokens matched.
- If is a terminal, it is the leaf of after in preorder, so and the parser matches it.
- If is a nonterminal, let be the production of in , and let be the yield of the subtree of ; it starts at because every leaf before in preorder is one of . If , then and . If , then , and expanding every node of except the subtree of gives , so , where . In both cases , so and the parser expands with the children it has in .
- If the top is , all nodes of have been processed, all leaves matched, and the parser accepts.
Each step processes a new node of the finite tree , so the parser accepts after one step for each node of and one final step.
Step 4: an LL(1) grammar is not left-recursive. Claim: no nonterminal that is reachable from has a derivation with .
Suppose one does. Following the path from the root to the inner in the tree of this derivation gives nonterminals with and productions with , indices taken modulo . Each is reachable from . Since , we have , and around the cycle all the sets are equal to one set . Moreover . Going once around the cycle from gives , where is a string that contains .
Among all complete trees whose root is one of , choose one of least height; say its root is , its root production is , and its yield is . If , the child of the root would be the root of a complete tree of smaller height; so .
- If , its first token is in , and in of the right side of , so in . Two productions of share , a contradiction.
- If , the right side of is nullable and . Since is reachable, for some , and derives a terminal string because every symbol is productive, so is not empty. If is not nullable, it derives a nonempty terminal string; let be its first token. Then , because , and , because . So , a contradiction. If is nullable, then is nullable, for a part of , and is nullable; so the right side of is nullable and , which meets , a contradiction.
Step 5: the partial tree is small. Let be at least the length of every right side, and let be the number of matched tokens at some moment. Two facts about a path from an inner node down to an inner node of the partial tree with the same start index:
- every node to the left of the path inside the subtree of has been completed without matching a token, so its yield is ; reading the productions along the path gives with ;
- so by Step 4, the labels on such a path are distinct, and the path has at most inner nodes.
Call an inner node heavy if its subtree contains a matched token, and light otherwise. Consider the tree formed by the heavy nodes and the matched leaves. A heavy node with one heavy or matched child has all its other children to the left of that child light and complete with yield , so it has the same start index as that child. Hence each maximal chain of heavy nodes with one such child has at most nodes. There are at most heavy nodes with two or more such children, and at most chains, one above each matched leaf or branching node. So there are at most heavy nodes. A light node has no matched leaf below it, so all inner nodes of a maximal light subtree have its start index, and by the second fact the subtree has height at most and at most inner nodes. The roots of maximal light subtrees are the root of the tree or children of heavy nodes, so there are at most of them. In total the partial tree has at most inner nodes, with .
Step 6: termination and the bound. Each step of the parser is a match, of which there are at most , an expansion, which creates an inner node, or the final step. By Step 5 there are at most expansions, so the parser stops after at most steps with . This proves part 1.
Step 7: rejection. If , the parser stops by Step 6 and cannot accept by Step 2, so it reports an error. With Step 3 this proves part 2.
Step 8: the valid prefix property. Suppose the parser reports an error at . By Step 1, , and derives some terminal string because every symbol is productive; so . Now let and suppose for some . Until it matches , the parser looks only at the tokens , so it makes the same moves on and on . On the second input it never reports an error by Step 3, so it matches on both inputs, and it cannot report an error at on . This proves part 3.