Proofs: Shunting-yard algorithm
These are the human proofs of Shunting-yard algorithm, at full length.
The shunting-yard algorithm outputs the postfix form of the precedence parse tree full
This proof shows: correctness, termination.
We use the pseudocode and the relation of the page. The precedence levels are , and all operators of one level have the same associativity. A primary is an operand or a group where is an expression. A flat sequence is with , primaries and binary operators . Write for the postfix form of a tree, and for the postfix form of the tree of a primary (the operand itself, or of the tree of for ).
The tree of a flat sequence
Definition. The tree is defined by induction on . If , is the tree of . If , let be the least precedence among . The root is , where is the largest index with if level is left associative, and the smallest such index if it is right associative. The left subtree is and the right subtree is .
This is the parse tree of the precedence grammar, whose level is when it is left associative and when it is right associative, with . A string derived from contains, outside parentheses, only operators of level at least . So in a derivation from of a flat sequence whose least level is , the topmost production of level splits at the last operator of level (left associative: its right part comes from and has no operator of level ) or at the first one (right associative: its left part comes from ). By induction on this split is forced at every node, so the parse tree exists, is unique, and equals .
Right spine. The right spine of a tree is its root, the root of its right subtree, and so on, down to the last leaf. In the spine operators are from the root down, with , and the last primary is . Let be the left subtree of . Applying the definition of postfix form times gives
The same formula holds for the subtree rooted at any spine node , with .
Lemma 1 (spine order). For consecutive spine operators and , not .
Proof. Let be the root of the subsequence whose tree is rooted at , with least level , and let be the root of the right part of . If level is left associative, is the last operator of level in , so every operator of the right part, among them, has ; then not . If level is right associative, ; if we are done, and if then is right associative, so again not .
Lemma 2 (suffix). Let be an operator. If , then for every .
Proof. It suffices to take and then repeat. Write and . By Lemma 1, , or and the level of is right associative. If , then and . Otherwise and is left associative, so the level of , which is the level of , is left associative; then the second case of Lemma 1 is impossible, so and .
So the spine operators that pop are exactly for some with (the set is empty when ). Define the graft of and a primary onto : take the spine node , or the last leaf when , and replace the subtree rooted there by the node .
Lemma 3 (graft). $\tau(\sigma, t, y) = $ the graft of and onto .
Proof. Induction on . If , then , the spine has no operator, , and the graft at the leaf gives the same tree. Let , let be the least level in , the root of , . Four cases.
- . In the operator is the only one of least level, so the root is with the subtrees and . Every spine operator has level at least , so it pops ; then , and the graft at the root gives the same tree.
- and level is left associative. The root of is the last operator of level , which is , with left part . The root pops (equal precedence, left associative), so by Lemma 2, and the graft gives the same tree.
- and level is right associative. The root of is the first operator of level , which is , with left part and right part . The root does not pop . So the graft point is on the spine of , and it is the same node as the graft point of in , because the spine of is followed by the spine of . By the induction hypothesis for , which has fewer operators, is the graft onto , and so is the graft onto .
- . The least level of is still , and is not of level , so the root is still (the last or the first operator of level does not change), with left part and right part . The root does not pop , since . The rest is as in case 3.
The invariant
Fix a flat sequence that the algorithm reads with a stack whose top part, before , is a base: either the bottom of the stack or a (. We assume the claim of the theorem below for every group among the (induction on the nesting depth, see the end), so reading a primary appends to the output and leaves the stack as it was.
Invariant (S). After the algorithm reads , the stack above the base holds exactly the spine operators of , with at the bottom and on top, and the output written since the base is .
Initialization. After , the stack above the base is empty, the tree is the tree of with no spine operator, and the output is .
Maintenance. Let (S) hold after , and let and . The loop of lines L9 and L10 pops operators from the top while the top is an operator that pops ; it stops at the base, since a ( is not an operator and the bottom ends the loop. By Lemma 2 the operators popped are exactly , in this order. The output becomes
and by (1) applied to the subtree at , the bracket is ; if the bracket is and is the leaf . Line L11 pushes , and reading appends . By Lemma 3, the new tree is the graft: its spine is , the left subtree of is , and the last primary is . So the stack is the new spine and the output is , which is (S) for .
End of the sequence. A flat sequence ends at a ) (line L6) or at the end of the input (line L12). Both pop all operators above the base, from the top: line L6 stops at the (, and line L14 empties the stack. They append , so by (1) the output written since the base is . Line L7 then removes the (, so the stack is as it was before the group.
The theorem
Theorem. If the input is an expression of the precedence grammar, SHUNTING-YARD returns of the parse tree of .
Proof. By induction on the nesting depth of parentheses. An expression is a flat sequence of primaries, and the groups among them have smaller depth, so by the induction hypothesis reading a group pushes (, appends of the tree of (by the argument above with the ( as base), and pops the (, which is the assumption used for (S). The whole input is a flat sequence with the bottom of the stack as base, so the output is , which is of the parse tree by the definition section. Line L13 never fires, because each ( was removed by its ).
Rejection. The implementation keeps a flag that is true exactly when the next token must start a primary. It accepts a token only if the token sequence so far is a prefix of the form with matched parentheses at each ), and at the end it requires the flag to be false and no ( on the stack. Every expression of the grammar passes these checks, and every token sequence that passes them is a flat sequence of primaries with matched parentheses, which is an expression of the grammar. So the implementation raises an error exactly on the inputs that are not expressions.
Termination. Each token is read once. Each loop of lines L6, L9 and L12 pops one token at each run, and each token is pushed at most once, so the loops run at most times in total.