design·lab

Behavioral pattern

Interpreter

Model each rule of a tiny grammar as a class that knows how to interpret() itself — an expression becomes a tree of objects, not a tangle of if-else.

✗ The problem

Ad-hoc parsing turns into spaghetti

Evaluating "5 + 3 - 2" by hand-walking tokens with nested if-else works — until the grammar grows a third operator, then parentheses, then precedence…

function evaluate(expr) {
  const tokens = expr.split(' ');
  let result = Number(tokens[0]);
  for (let i = 1; i < tokens.length; i += 2) {
    if (tokens[i] === '+') result += Number(tokens[i+1]);
    else if (tokens[i] === '-') result -= Number(tokens[i+1]);
    // new operator? edit this function again…
  }
  return result;
}
No structure — every new rule tangles the parser further.
✓ The pattern

Every grammar rule becomes a class

Terminal rules (numbers) and non-terminal rules (operators) all implement the same interpret() contract. An expression is just a tree of them.

class NumberExpr {
  constructor(n) { this.n = n; }
  interpret() { return this.n; }
}
class AddExpr {
  constructor(left, right) { this.left = left; this.right = right; }
  interpret() { return this.left.interpret() + this.right.interpret(); }
}
// SubtractExpr mirrors AddExpr, with a "-"

Tree for (5 + 3) - 2

SubtractExpr (non-terminal)
+
AddExpr (non-terminal)
5
NumberExpr
3
NumberExpr
2
NumberExpr
✓ See it live

Build the tree, then interpret() it bottom-up

Pick an expression. It builds real NumberExpr / AddExpr / SubtractExpr objects, then interpret() resolves leaves first, then parents.

Pick an expression above ↑

?
interpret()
?
interpret()
?
NumberExpr
?
NumberExpr
?
NumberExpr

Result: —

✓ Takeaway

A tree of self-evaluating rules

  • Each grammar rule is one class implementing interpret() — the expression itself is a tree of these objects.
  • Great fit for small DSLs: config rules, query filters, feature-flag expressions.
  • Caution: it does not scale to complex grammars — reach for a real parser / AST tool once rules multiply.
🎯 Principle applied: each grammar rule owns its own evaluation (SRP) and new rules are new classes (Open/Closed); the tree of expressions is itself a Composite.