デザインパターン-Interpreter

Interpreterパターンとは

Interpreterパターンとは、クラスで表現した文法規則を用いて構文を解析し、その結果得られた手順(命令)に基づき処理を実行していくというパターンです。

「Interpreter」とは「通訳」を意味します。


文法で定義されている規則をクラスとして表し、それに対する振る舞いを併せて定義します。

この規則は構文木における節や葉に相当し、インスタンスを繋げることで構文木そのものを表現しつつ、構文木を処理します。


Interpreterパターンの利点

  • 規則の追加や変更が容易となる。
  • Interpreterパターンでは、1つの規則を1つのクラスで表します。
    • 新しい規則を追加する場合はAbstractExpressionクラスのサブクラスを追加します。

構成要素

  • AbstractExpression(命令・抽象的な表現)
    • 共通のインタフェースを定義します。
  • TerminalExpression(終端となる表現)
    • 終端を表現するクラスです。
    • 階層関係の末端(構造木の葉)を表します。
  • NonterminalExpression(終端以外の表現)
    • 非終端を表現するクラスです。
    • 階層関係のノード(構造木の節)を表します。
  • Context(文脈・状況)
    • インタプリタが構文解析を行うための情報を提供します。
  • Client(利用者)

rubyによるInterpreterパターンの実装

「begin-end」に囲まれたスクリプトを解析して実行する例となります。


#!/usr/bin/env ruby

# 命令・抽象的な表現(AbstractExpression)
class Parser
  def initialize(lexer, exec)
    @lexer    = lexer
    @executer = exec
  end
end

# 終端となる表現(構造木の葉) (TerminalExpression)
class Program < Parser
  def parse
    if(@lexer.current_token == "begin")
      @lexer.next_token
      @cmdlist = CommandList.new(@lexer, @executer)
      @cmdlist.parse
    end
  end
end

# 終端となる表現(構造木の葉) (TerminalExpression)
class CommandList < Parser
  def parse
    while(@lexer.current_token != nil)
      break if(@lexer.current_token == "end")
      cmd = Command.new(@lexer, @executer)
      cmd.parse
      @lexer.next_token
    end
  end
end

# 終端以外の表現(構造木の節) NonterminalExpression
class Command < Parser
  def parse
    @name = @lexer.current_token
    @executer.execute(@name)
  end
end

# 終端以外の表現(構造木の節) NonterminalExpression
class CommandExecuter
  def execute(cmd)
    # 対応するメソッドを実装してゆく
    puts(cmd)
  end
end

# 文脈 Context
class Lexer
  def initialize(text)
    @current_token = nil
    @tokenizer = text.split(" ")
    next_token
  end

  def next_token
    @current_token = @tokenizer.shift
    return(@current_token)
  end

  def current_token
    return(@current_token)
  end
end

# Client
class Interpreter
  def self.execute(str)
    p = Program.new(Lexer.new(str), CommandExecuter.new)
    p.parse
  end
end

script = "begin ps df end"
Interpreter.execute(script)


関連ページ