Every "math evaluator in N lines" article either uses shunting-yard (Dijkstra's stack-based RPN converter) or hand-waves over the hard cases (-2^2, right associativity, error positions). This one uses recursive descent because the grammar and the code map 1-to-1, which makes the whole thing easier to read and write about. ~250 lines of JS, 36 unit tests, supports functions, variables, constants, parentheses, and reports errors with a column number and a caret.
Why recursive descent (not shunting-yard)
Both work. Shunting-yard is more compact (one for loop, two stacks). But for explaining what's happening, recursive descent wins:
Grammar ↔ code is 1-to-1. You can read the BNF and the implementation in parallel and they look the same.
Stack traces show the AST shape. When you're debugging "why is -2^2 returning 4 instead of -4", the call stack literally is the parse tree.










