数式を解くプログラム その5 - 構文解析2

id:yone-ken:20090220:p1 の続きです。再度、拡張BNFによる構文定義を確認します。

   ::=  ('+' )* |  ('-' )*
         ::=  ('*' )* |  ('/' )*
       ::= '('  ')' | '+'  | '-'  | 

構文解析を実装するに当たり、どんなクラスが必要でしょうか?また、そのクラスにはどんなメンバが必要でしょうか?私の考えた方針は次の通りです。

  • 構文定義の要素である各非終端記号が自分の担当箇所の解析を行えばよい。
  • 今回は構文解析で、解析+計算まで行うインタープリターを実装する。


この方針により、Expressionクラス、Termクラス、Factorクラスを作成することにします。また、これらが解析+計算を行うメソッドをEvaluateメソッドとし、戻り値はなしで、引数には字句解析処理を渡すことにします。計算結果はValueプロパティを用意してそこから取得することにします。(今考えるとValueプロパティは不要で、単純にEvaluateメソッドの戻り値で返すので十分な気もします。)これら3つのクラスは共通のインタフェースを持つのでこれらの抽象クラスとしてNodeクラスを用意します。

また、与えられる数式は正しい式ばかりとは限りませんし、問題なく計算できるとは限りませんので、Evaluateメソッドで解析エラー、もしくは、計算エラーがあった場合には、EvaluateExceptionを発生させることにします。

NodeクラスとEvaluateExceptionクラスを以下に掲載します。

abstract class Node
{
    public decimal Value { get; protected set; }
    public abstract void Evaluate(Context<Token> context);
}
class EvaluateException : Exception
{
    private Token token;
    public Token Token
    {
        get { return this.token; }
    }
    public EvaluateException(string messgae, Token token)
        : base(messgae)
    {
        this.token = token;
    }
}

次回はExpressionクラス、Termクラス、Factorクラスと外向きのインターフェースとしてのExpressionEvaluatorクラスを紹介します。