// Design Patterns · 15
Interpreter
Represent a small language as a tree of objects that evaluate.
How it fits together
- DiscountEngine uses / calls RuleParser (rule text)
- RuleParser creates RuleExpression (builds tree)
- DiscountEngine uses / calls RuleExpression (Evaluate(ctx))
- DiscountEngine creates RuleContext
- Compare implements RuleExpression
- And implements RuleExpression
- And wraps / holds RuleExpression (children)
- Compare uses / calls RuleContext (reads)
Spot it
- Rules stored as strings and parsed ad hoc
- A growing set of combinable conditions
- Same rule language evaluated in many places
It costs
Fine for small grammars; big ones get slow and hard to maintain.
In code
public sealed record RuleContext(decimal Total, string Country);public interface IRuleExpression{ bool Evaluate(RuleContext context);}public sealed record Compare(string Field, string Op, string Value) : IRuleExpression{ public bool Evaluate(RuleContext context) => (Field, Op) switch { ("total", ">") => context.Total > decimal.Parse(Value, CultureInfo.InvariantCulture), ("country", "=") => context.Country == Value, _ => throw new NotSupportedException($"Unknown rule: {Field} {Op}"), };}public sealed record And(IRuleExpression Left, IRuleExpression Right) : IRuleExpression{ public bool Evaluate(RuleContext context) => Left.Evaluate(context) && Right.Evaluate(context);}Go deeperThe full explanation and what to ask for · 1 min readExpandCollapse
Interpreter models each grammar rule as a class. Terminal expressions, such as a comparison or a literal, evaluate directly; non-terminal ones, such as and, or and not, hold child expressions and combine their results. A parser turns text into this tree once, and evaluation walks it against a context.
It suits small, stable languages: filter rules, feature flag conditions, simple formulas. For anything bigger, reach for a parser generator or an existing expression or rules engine instead of growing hand-written classes, and never run untrusted rule text as real code.
The expression tree is a Composite, and extra operations over it, such as printing or validating, are a job for Visitor. Many platforms build expression trees that can be compiled or translated to SQL: the same idea, with a compiler in place of the interpreter.
Ask for it
- grammar
- abstract syntax tree
- terminal expression
- non-terminal expression
- context
- parser
Discount rules like "total > 100 and country = DE" are checked with string matching. Define a RuleExpression interface with Evaluate(context), terminal Compare nodes and And/Or/Not nodes, plus a small RuleParser that builds the tree once. Add tests for precedence and unknown fields.