GoF本 Interpreter

GoFデザインパターン勉強メモ

ねらい

  • 言語の文法をクラスの木構造で表現する
  • 抽象構文木(AST: Abstract Syntax Tree)であらわされる文を翻訳する

モチベーション

  • 同じような類の問題が頻出するので、それに特化した言語をつくりたくなることがある

    • BNFで表されるような言語

つかいどころ

  • 文法が簡潔であること

    • 複雑な文法には不向き

      • 文法を表現するクラス構造が巨大で管理不能になる
      • ASTを作らずに翻訳を行うような別のしくみを適用したほうがいい

        • 例) パーサジェネレータ … パーサを生成するプログラム
  • 実行効率があまり重要でない

    • 高効率な実装は、ふつう直接ASTを処理せず、中間形式を経由する
    • ASTから中間形式への変換にはInterpreter Patternが利用できる

登場人物

  • AbstractExpression

    • ASTのすべてのノード共通のインタフェースを定義

      • Interpret()処理を定義
  • TerminalExpression

    • ASTの葉ノード

      • 翻訳対象の言語でいうとリテラルとか
  • NonterminalExpression

    • ASTの中間ノード
    • BNFの左辺に来るようなヤツ

      • 翻訳対象の言語でいうと、演算子とか
    • Interpret()の中では、子ノードのInterpret()を再帰的に呼び出したりする
  • Context

    • 翻訳時にグローバルな状態を保持する

      • ノードからextrinsicな状態を外出し
      • 【補】プリペアドステートメントのバインディングとか
  • Client

    • TerminalExpression/NonterminalExpressionオブジェクトからなるASTに対し、Interpret()を呼び出す

クライアントコードからの利用

  • contextを初期化し、ASTのInterpret(context)を実行

結果

  • 文法の変更・拡張が容易

    • 継承・override
  • 複雑な文法はメンテ困難

    • BNFの1式ごとにクラスが必要
  • Interpret()以外にもいろいろできる

    • ASTのpretty printing
    • 型チェック

実装にあたり考えるべきこと

  • ASTの生成

    • パース処理についてはInterpreterパターンの守備範囲外

      • Clientがパースする?
      • Clientはパースされたものを受け取る?
      • Clientの中でASTを組み立てる?
  • Interpret()の実装

    • Expressionに実装すると、処理が散らばる
    • Visitorパターンを併用し、visitorオブジェクトに実装してまとめる方法もある

      • 各Expression派生は、Visitorのどの処理でInterpretされればよいかを知っている
  • TerminalExpressionオブジェクトの共用

    • Flyweightパターン
    • extrinsicな状態はContextに外出しする

Compositeとどうちがうの

  • 見方の問題

    • Composite: 再帰構造
    • Interpret: CompositeでASTを表現し、Contextにextrinsicな状態を外出しし、再帰的に翻訳する一連のふるまい

関連するパターン

  • Composite

    • ASTがCompositeパターン
  • Flyweight

    • TerminalExpressionに適用可能
  • Iterator

    • 木の走査に適用可能

      • 先行順 (preorder)
      • 中間順 (inorder)
      • 後行順 (postorder)
  • Visitor

    • 翻訳処理を各Expression派生クラスに散らばらせず、知識をひとまとめに

よくわかんなかったやつ

  • parser generator
  • compiler generator
  • table-driven parser