Proofs: Pratt parsing
These are the human proofs of Pratt parsing, at full length.
A Pratt parser accepts exactly the well-formed expressions and returns their unique valid tree full
This proof shows: correctness, termination.
We use the pseudocode, the binding powers and the conditions (A) and (B) of the page. The tokens are and . Every infix and postfix operator has , every rbp is at least 0, and for every other token, including ) and . A token is never both infix and postfix.
Spines. The left spine of a tree is the chain of nodes reached from its root by repeatedly going to the left operand of an infix or postfix node. The right spine is the chain reached by repeatedly going to the right operand of an infix or prefix node. A group or an operand ends both chains. In the conditions, the left spine of the right operand counts only nodes that take a left operand, and the right spine of the left operand counts only nodes that have a right operand, which by definition are the nodes of the spines.
Say that a call EXPR(ρ) starts at the value of pos when it is made and stops at if pos is when it returns.
Termination and running time
Each call reads a token at line L4 and each round of the loop of line L9 reads a token at line L10, and pos only grows. So there are at most calls and rounds in all, and PARSE stops after steps.
Lemma 1: the parser builds valid trees
Lemma 1. If EXPR(ρ) starts at and returns a tree , stopping at , then
- is a tree of the grammar whose leaves are , and is valid;
- every node on the left spine of has ;
- , and for every node on the right spine of .
Proof. By induction on the order of returns. Consider the tree left after lines L4 to L8. It is an operand (both spines empty), or a group whose inside is valid by induction (both spines end at the group), or a prefix node with operand returned by EXPR(rbp(u)) that stopped at the current position . In the last case, part 2 of the induction gives condition (B) at , and part 3 gives for every on the right spine of , which is followed by the right spine of .
We show that each round of the loop keeps three facts, which hold before the first round by what we just said: left is valid with leaves ; every node on its left spine has ; and for every on its right spine. A round takes with .
- If is postfix, the new node satisfies (A) by the third fact, its left spine is followed by the old left spine, and its right spine is empty.
- If is infix, line L12 calls EXPR(rbp(o)), which returns and stops at some . The new node satisfies (A) by the third fact and (B) by part 2 for . Its left spine is followed by the old left spine, all with . Its right spine is followed by the right spine of , and part 3 for gives and for on the right spine of .
The loop ends at with , and the three facts give parts 1, 2 and 3.
Lemma 2: a valid tree is what the parser builds
Lemma 2. Let be a valid tree whose leaves are . Suppose every node on the left spine of has , , and for every node on the right spine of . Then a call EXPR(ρ) that starts at returns and stops at .
Proof. By induction on the size of . Write along its left spine: from the bottom, a node that is an operand, a group or a prefix node, then the nodes of the left spine, from the bottom up, where an infix has the right operand . The first leaf of is the first token of , which is , and the call reads it at line L4.
- If is an operand, line L5 makes the leaf.
- If , the tree is valid, its left spine has , and the token after it is
), with and every rbp. By induction EXPR(0) returns and stops at the), and line L6 reads it. - If is a prefix node with operand , condition (B) at says that the left spine of has . The token after is if , and if . In the first case condition (A) at bounds its lbp by the rbp of every node on the right spine of , which is followed by the right spine of ; in the second case the hypothesis on gives the same bound, since then . By induction, line L7 gets from EXPR(rbp(u)).
Now the loop. Before round , the token at pos is , and it is on the left spine of , so and line L9 takes it. If is postfix, line L11 builds its node. If is infix, condition (B) at bounds the left spine of from below by . The token after is , whose lbp is at most the rbp of every node on the right spine of its left operand, by (A); that spine is followed by the right spine of . For the token after is , and the right spine of is followed by the right spine of , so the hypothesis gives the same bound. By induction, line L12 gets . After round the token is with , so the loop ends and the call returns at .
Lemma 3: the parser accepts every well-formed input
A well-formed sequence can be read from left to right in two states: in state X an operand is expected (a prefix operator, an operand or (), and in state Y an operand has just ended (next comes a postfix or infix operator, or the end of ). From the grammar, is a sequence with prefix operators , primaries (an operand or with well formed), postfix operators and infix operators . Every tree yields such a sequence, and every such sequence has a tree, for example the one that groups all operators to the left.
Lemma 3. If EXPR(ρ) starts at a position in state X of a well-formed sequence at the current level of parentheses, it returns without error and stops at a position in state Y of that sequence.
Proof. By induction on the number of tokens the call reads. In state X, line L4 reads an operand, a ( or a prefix operator. After an operand the state is Y. After (, the call EXPR(0) starts in state X of the inner sequence , and by induction it stops in state Y of at a token with , which is not an infix or postfix operator; in state Y the only other possibility is the end of , so the token is the ) and line L6 reads it, reaching state Y. After a prefix operator the state is X again, and by induction the call of line L7 stops in state Y. In the loop, a token with is infix or postfix. A postfix operator leads from Y to Y. An infix operator leads to X, and by induction the call of line L12 stops in Y. So the call stops in state Y.
Conclusion
Soundness. If PARSE returns a tree , then by Lemma 1, with and line L2, is a valid tree of the grammar whose leaves are the whole input. So the input is well formed.
Completeness. If the input is well formed, then by Lemma 3 the call EXPR(0) at line L1 stops in state Y of the whole input, at a token with . That token is not an infix or postfix operator, so it is the end of the input, and line L2 passes.
Uniqueness. Let be any valid tree of a well-formed input. Its left spine has , and the token after it is , with and for every . By Lemma 2 the call EXPR(0) of line L1 returns . So every valid tree is the tree that PARSE returns, there is exactly one valid tree, and PARSE returns it.