Algorithm Search⌘K

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 t1⋯tnt_1 \cdots t_n and tn+1=$t_{n+1} = \$. Every infix and postfix operator has lbp≥1\text{lbp} \ge 1, every rbp is at least 0, and lbp(t)=0\text{lbp}(t) = 0 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 ( e )(\,e\,) 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 pp of pos when it is made and stops at qq if pos is qq 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 n+1n + 1 calls and nn rounds in all, and PARSE stops after O(n)O(n) steps.

Lemma 1: the parser builds valid trees

Lemma 1. If EXPR(ρ) starts at pp and returns a tree TT, stopping at qq, then

  1. TT is a tree of the grammar whose leaves are tp⋯tq−1t_p \cdots t_{q-1}, and TT is valid;
  2. every node on the left spine of TT has lbp>ρ\text{lbp} > \rho;
  3. lbp(tq)≤ρ\text{lbp}(t_q) \le \rho, and lbp(tq)≤rbp(X)\text{lbp}(t_q) \le \text{rbp}(X) for every node XX on the right spine of TT.

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 uu with operand RR returned by EXPR(rbp(u)) that stopped at the current position mm. In the last case, part 2 of the induction gives condition (B) at uu, and part 3 gives lbp(tm)≤rbp(X)\text{lbp}(t_m) \le \text{rbp}(X) for every XX on the right spine of uu, which is uu followed by the right spine of RR.

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 tp⋯tm−1t_p \cdots t_{m-1}; every node on its left spine has lbp>ρ\text{lbp} > \rho; and lbp(tm)≤rbp(X)\text{lbp}(t_m) \le \text{rbp}(X) for every XX on its right spine. A round takes o=tmo = t_m with lbp(o)>ρ\text{lbp}(o) > \rho.

  • If oo is postfix, the new node satisfies (A) by the third fact, its left spine is oo followed by the old left spine, and its right spine is empty.
  • If oo is infix, line L12 calls EXPR(rbp(o)), which returns RR and stops at some m′m'. The new node satisfies (A) by the third fact and (B) by part 2 for RR. Its left spine is oo followed by the old left spine, all with lbp>ρ\text{lbp} > \rho. Its right spine is oo followed by the right spine of RR, and part 3 for RR gives lbp(tm′)≤rbp(o)\text{lbp}(t_{m'}) \le \text{rbp}(o) and lbp(tm′)≤rbp(X)\text{lbp}(t_{m'}) \le \text{rbp}(X) for XX on the right spine of RR.

The loop ends at qq with lbp(tq)≤ρ\text{lbp}(t_q) \le \rho, and the three facts give parts 1, 2 and 3. □\square

Lemma 2: a valid tree is what the parser builds

Lemma 2. Let TT be a valid tree whose leaves are tp⋯tq−1t_p \cdots t_{q-1}. Suppose every node on the left spine of TT has lbp>ρ\text{lbp} > \rho, lbp(tq)≤ρ\text{lbp}(t_q) \le \rho, and lbp(tq)≤rbp(X)\text{lbp}(t_q) \le \text{rbp}(X) for every node XX on the right spine of TT. Then a call EXPR(ρ) that starts at pp returns TT and stops at qq.

Proof. By induction on the size of TT. Write TT along its left spine: from the bottom, a node N0N_0 that is an operand, a group or a prefix node, then the nodes o1,…,oko_1, \ldots, o_k of the left spine, from the bottom up, where an infix oso_s has the right operand RsR_s. The first leaf of TT is the first token of N0N_0, which is tpt_p, and the call reads it at line L4.

  • If N0N_0 is an operand, line L5 makes the leaf.
  • If N0=( e )N_0 = (\,e\,), the tree ee is valid, its left spine has lbp≥1>0\text{lbp} \ge 1 > 0, and the token after it is ), with lbp=0≤0\text{lbp} = 0 \le 0 and 0≤0 \le every rbp. By induction EXPR(0) returns ee and stops at the ), and line L6 reads it.
  • If N0N_0 is a prefix node uu with operand RR, condition (B) at uu says that the left spine of RR has lbp>rbp(u)\text{lbp} > \text{rbp}(u). The token after RR is o1o_1 if k≥1k \ge 1, and tqt_q if k=0k = 0. In the first case condition (A) at o1o_1 bounds its lbp by the rbp of every node on the right spine of N0N_0, which is uu followed by the right spine of RR; in the second case the hypothesis on tqt_q gives the same bound, since then N0=TN_0 = T. By induction, line L7 gets RR from EXPR(rbp(u)).

Now the loop. Before round ss, the token at pos is oso_s, and it is on the left spine of TT, so lbp(os)>ρ\text{lbp}(o_s) > \rho and line L9 takes it. If oso_s is postfix, line L11 builds its node. If oso_s is infix, condition (B) at oso_s bounds the left spine of RsR_s from below by rbp(os)\text{rbp}(o_s). The token after RsR_s is os+1o_{s+1}, whose lbp is at most the rbp of every node on the right spine of its left operand, by (A); that spine is oso_s followed by the right spine of RsR_s. For s=ks = k the token after RkR_k is tqt_q, and the right spine of TT is oko_k followed by the right spine of RkR_k, so the hypothesis gives the same bound. By induction, line L12 gets RsR_s. After round kk the token is tqt_q with lbp(tq)≤ρ\text{lbp}(t_q) \le \rho, so the loop ends and the call returns TT at qq. □\square

Lemma 3: the parser accepts every well-formed input

A well-formed sequence EE 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 EE). From the grammar, EE is a sequence U∗PQ∗(O U∗PQ∗)∗U^* P Q^* (O\, U^* P Q^*)^* with prefix operators UU, primaries PP (an operand or ( E′ )(\,E'\,) with E′E' well formed), postfix operators QQ and infix operators OO. 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 E′E', and by induction it stops in state Y of E′E' at a token with lbp≤0\text{lbp} \le 0, which is not an infix or postfix operator; in state Y the only other possibility is the end of E′E', 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 lbp>ρ≥0\text{lbp} > \rho \ge 0 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. □\square

Conclusion

Soundness. If PARSE returns a tree TT, then by Lemma 1, with p=1p = 1 and line L2, TT 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 lbp≤0\text{lbp} \le 0. That token is not an infix or postfix operator, so it is the end of the input, and line L2 passes.

Uniqueness. Let TT be any valid tree of a well-formed input. Its left spine has lbp≥1>0\text{lbp} \ge 1 > 0, and the token after it is $\$, with lbp($)=0≤0\text{lbp}(\$) = 0 \le 0 and 0≤rbp(X)0 \le \text{rbp}(X) for every XX. By Lemma 2 the call EXPR(0) of line L1 returns TT. So every valid tree is the tree that PARSE returns, there is exactly one valid tree, and PARSE returns it.

Back to Pratt parsing