#15 / 23
B振る舞いDIFF
インタプリタ
Interpreter
ミニ言語の通訳係。検索フィルタやルール式を、文法のクラス階層に翻訳して評価する。
Intent · 目的
あるドメイン特化のミニ言語の文法を、クラス階層として表現する。各文法ルールが1クラスになり、構文木を組み立てて `interpret()` を呼ぶと評価が進む。
Motivation · 動機
「アクセス権限を `role==admin && plan==pro` みたいな式で書きたい」「検索フィルタに `tag:react -tag:legacy` を許したい」── 専用の言語を組み込みたい場面はある。文字列を毎回パーサで解析して `eval` してたら危険だし、構造化もされない。文法をクラス(数値、変数、AND、OR、NOT…)として表現すれば、AST を組み立てて木を辿るだけで評価できる。レゴでミニ言語を組み立てるイメージ。本格的な言語処理にはパーサジェネレータの方がいいが、軽い DSL なら自前で十分。
適用場面
- 1規則がそれほど多くない自前 DSL(検索式、権限式、簡易計算式)。
- 2文法が安定していて、頻繁にコロコロ変わらない場合。
- 3実行効率より拡張性・可読性を優先したい場合。
概念図
サンプルコード
interface Expr { interpret(ctx: Record<string, number>): number; }
class Num implements Expr {
constructor(private v: number) {}
interpret(){ return this.v; }
}
class Var implements Expr {
constructor(private n: string) {}
interpret(ctx: Record<string, number>){ return ctx[this.n] ?? 0; }
}
class Add implements Expr {
constructor(private l: Expr, private r: Expr) {}
interpret(ctx: Record<string, number>){ return this.l.interpret(ctx) + this.r.interpret(ctx); }
}
const e: Expr = new Add(new Var("x"), new Num(3));
console.log(e.interpret({ x: 10 })); // 13Pros · メリット
- +新しい文法ルールはクラス1個追加すれば済む。拡張は局所的。
- +AST 操作が自然に書ける。文法の構造がそのままコードに現れる。
- +ルールが少なければ、自前パーサ+専用ライブラリより読みやすいことも。
Cons · デメリット
- −規則が多くなるとクラス爆発する。30個ルールがあるDSLは、もう本パターン向きじゃない。
- −実行効率は良くない。本格的な言語処理にはパーサジェネレータ+VMの方が桁違いに速い。
- −AST 設計の負荷が高く、メンテが特定の人しかできなくなる現象が起こりがち。
関連パターン
⚔ ready to test
理解度を確かめる時間だ
3 問のクイズで、 このパターンを身体に染み込ませよう。