Ad-hoc string parsing the agent must read sequentially to understand evaluation semantics. Operator precedence is implicit; bugs in the parser cascade silently across every rule. Static analysis of 'what variables does this rule depend on' requires re-implementing the parser in the analysis tool.
One class per grammar rule, each with interpret(env). The agent reads the AST shape directly from the construction expression; static traversal returns complete answers; per-rule edits scope to one class file.
Before the pattern
function evaluateRule(ruleString, context) {if (ruleString.includes(' AND ')) {const [left, right] = ruleString.split(' AND ');return evaluateRule(left, context) && evaluateRule(right, context);}if (ruleString.includes(' OR ')) {const [left, right] = ruleString.split(' OR ');return evaluateRule(left, context) || evaluateRule(right, context);}if (ruleString.startsWith('NOT ')) {return !evaluateRule(ruleString.slice(4), context);}return Boolean(context[ruleString]);}const pass = evaluateRule('verified AND NOT banned', user);// Strings as DSL, ad-hoc parser, operator precedence implicit in eval order,// no caching, no static analysis, every consumer re-implements its own dialect.
After the pattern
class Variable {constructor(name) {this.name = name;}interpret(env) {return Boolean(env[this.name]);}}class And {constructor(left, right) {this.left = left;this.right = right;}interpret(env) {return this.left.interpret(env) && this.right.interpret(env);}}class Or {constructor(left, right) {this.left = left;this.right = right;}interpret(env) {return this.left.interpret(env) || this.right.interpret(env);}}class Not {constructor(operand) {this.operand = operand;}interpret(env) {return !this.operand.interpret(env);}}const rule = new And(new Variable('verified'), new Not(new Variable('banned')));const pass = rule.interpret(user);
String DSLs are opaque to the type system. The agent reasoning about rule correctness must trace through the parser's logic at every consumer; partial verification produces misleading confidence. New operators add cascading edits the agent must verify across every parsing path.
AST construction in code is verbose and harder to read than the string DSL. The agent's reading cost on the construction site is higher; tracing a runtime error in an AND.interpret requires walking the recursive call chain through the tree, which can be deep.
Per-rule edits are one-file; static traversal of 'rules using variable X' is a complete enumeration; type system enforces interpret-method existence on every rule class. The agent's verification budget on grammar changes is bounded and structurally provable.
Grammars growing past ~10 operators turn into class proliferation the agent navigates as a graph rather than a hierarchy. Visitor or AST-walker tooling becomes mandatory at that scale; without it, every traversal-style analysis requires editing every rule class — the cross-cutting problem the pattern was supposed to avoid.