skip to content

When is a precedence-climbing binding-power table worth adopting for a formula evaluator whose users request a new operator every quarter, and what does it cost?

level: principalimportance: should knowfreq 33%

answer

  1. data versus the shape of code
  2. count the edits per new operator
  3. depth paid on every operand
  4. precedence stops being legible
  5. differential-test over stored formulas

basics

~20 s

Adopt it when the operator set keeps growing or precedence must be data: each addition becomes one table row instead of a new rule layer and an edit to its neighbour. The cost is that precedence stops being readable from the code and becomes numbers you must document and regression-test.

solid answer

~50 s

The two designs accept exactly the same language when the numbers reproduce the layering, so this is an economics decision, not a correctness one. A layered expression grammar makes precedence legible — the call chain is the precedence order — but every new level is a new function plus an edit to the one above it, and every operand pays one call per level. A binding-power table makes each addition a data row, lets the precedence data be built at run time, and costs one call per operand. What you give up is legibility: precedence now lives in numbers that need a published table, gaps between levels so insertions do not renumber everything, and tests that pin the shape. The migration risk is the sharp edge — a wrong number does not fail to parse, it silently reassociates stored formulas, so you differential-test the old and new parsers over the real corpus before switching.

go deeper

for a junior

Know that there is more than one way to encode operator precedence, and that one of them is a table of numbers rather than the structure of the parser's functions.

for a middle

Be able to state the cost of adding an operator under each design, and explain why a wrong binding power produces a wrong result rather than a parse error.

for a senior

Plan the migration: derive the numbers from the existing levels, differential-test both parsers over the stored corpus, and treat any tree difference as a defect until it is deliberately accepted.

for a principal

Frame it as who changes precedence and how often. If change is frequent or comes from outside the team, precedence is already data, and the table makes that explicit at the price of a documentation obligation you then own.

## The two shapes, priced Both designs parse the same expressions when the binding powers reproduce the layering, so the decision is about cost of change, not capability. | dimension | layered expression rules | binding-power table | |---|---|---| | adding an operator at an existing level | one line in one function | one row | | adding a new precedence level | a new function plus an edit to its neighbour | one row, using a gap in the numbering | | calls per operand parsed | one per level | one | | where precedence is readable | the shape of the code | a table of numbers | | precedence decided at run time | not without regenerating the parser | possible, since it is data | | review surface of a change | control flow | data | The row that usually decides it is the last one. A change to a table row is reviewable by someone who does not know the parser; a change to a chain of mutually calling functions is not. ## When the table earns its place - **The operator set grows on someone else's schedule.** A formula evaluator behind a reporting tool's calculated columns is the archetype: every quarter a user wants one more operator, and each addition to a layered parser touches working code that already has behaviour people depend on. - **Precedence must be configurable or extensible.** If operators can be contributed rather than hard-coded, the layered design cannot express that at all without rebuilding the parser, because precedence is the shape of the code. - **The level count is large.** Fifteen to twenty levels means fifteen to twenty frames on every operand, which shows up in parse-heavy work such as re-parsing a large saved workbook of formulas on load. - **Several surfaces share one expression language.** One loop plus one table is easier to keep in step across a validator, an evaluator and a formatter than one chain of functions copied into each. ## When the layers are fine - **The operator set is small and closed.** Four or five levels that have not changed in years cost nothing to maintain, and the explicitness is a genuine asset for an occasional maintainer. - **The grammar is the published artefact.** If the rule set is what you hand to users or to another team as the specification, keeping the code shaped like it removes a translation step and a class of drift. - **Nobody on the team has met this technique.** A parser that the team cannot confidently modify is a worse outcome than a few redundant functions, and this argument is weaker than it sounds only because the loop is small enough to learn in an afternoon. ## What you give up 1. **Legibility.** The precedence order stops being visible in the code. It must be written down deliberately, in a table ordered by power with associativity noted per row, and that document has to be kept truthful. 2. **Slack in the numbering.** Because left-associative operators consume one unit above their level, consecutive numbering leaves no room to insert a level later. Gaps of ten are the usual convention. 3. **A specific failure mode.** Nearly every mistake in this design is a wrong number, and a wrong number yields an accepted parse with a different tree. The parser does not complain; the values do. ## Migrating without breaking stored formulas 1. **Derive the numbers from the existing layers.** Number the levels from the loosest upward with gaps of ten, give each left-associative operator a right power one above its left power, and equal powers to the right-associative ones. 2. **Differential-test over the real corpus.** Run both parsers across every stored formula and compare trees, not just values — equal values on today's data can hide a reassociation that differs on tomorrow's. 3. **Treat any difference as a defect in the numbers**, not as an improvement, until someone decides otherwise deliberately. A change in how an existing formula groups is a change in what users' saved work computes. 4. **Publish the precedence table** at the point of cutover, since it is now the only place the answer lives. ## The judgment The question a lead actually answers is not which parser is better but **how often precedence changes and who changes it**. If the answer is rarely and only us, layered rules are honest and cheap. If it is often, or by people outside the team, precedence is data in everything but representation, and the table simply admits that. The cost of admitting it is one afternoon of migration plus a permanent documentation obligation; the cost of not admitting it is paid a little at a time, on every request for one more operator.

  • What do you publish to users once precedence lives in numbers?
    A precedence table ordered from loosest to tightest, with the associativity of each row. The numbers themselves are internal and should not be exposed, since they carry gaps and the plus-one convention; what users need is the order and the grouping direction, which is exactly what they previously read off the rule set.
  • What is the main risk when swapping one design for the other?
    Silent reassociation. A wrong binding power does not reject input — it builds a different tree, so a stored formula keeps parsing and starts returning a different number. The defence is a differential run of both parsers over the existing corpus, comparing tree shape rather than only the values today's data happens to produce.
  • Does the flat design make the parser faster in any way that matters?
    Only for parse-heavy workloads. It removes one call per precedence level per operand, so with fifteen levels a bare literal costs one frame instead of fifteen. That is measurable when thousands of stored formulas are parsed at load, and irrelevant when a single expression is parsed per user action.

saying these in an interview costs you the question

  • Claims the table parser accepts a different language than the layered one
  • Assumes precedence stops needing documentation once it is numbers
  • Argues the switch is right regardless of how the operator set grows
  • Thinks a migration is safe without comparing trees on existing formulas
  • Believes consecutive numbering leaves room for later insertions