このブログの更新は Twitterアカウント @m_hiyama で通知されます。
Follow @m_hiyama

メールでのご連絡は hiyama{at}chimaira{dot}org まで。

はじめてのメールはスパムと判定されることがあります。最初は、信頼されているドメインから差し障りのない文面を送っていただけると、スパムと判定されにくいと思います。

参照用 記事

構文解析のオハナシ:訂正

昨日のエントリーのどこが変かというと:


加数 ::= 因数 ('*' 因数)* | '-' 加数

これだと、積('*')と符号反転('-')の優先順位が相変わらず曖昧で、「一意的にパーズ(解析)できます。」なんてセリフが嘘になります。例えば、2+-3*5 が、2+(-(3*5)) か 2+((-3)*5) かハッキリしません。この例では計算結果は変わりませんが、今は構文解析ツリーを作るのが目的なので、2+(-(3*5)) と 2+((-3)*5) は別物とみなすのです。

昨日エントリーの後半では、なぜか積('*')が符号反転('-')より優先される前提で書かれています。そういう約束があってもいいですけど、常識的には符号反転のほうが積より強い(優先順位が高い)演算子ですよね。つまり、'+' < '*' < '-' という順序が自然でしょう。

ということで、構文定義のBNFは次のようにすべきでした。


式 ::= 加数 ('+' 加数)*
加数 ::= 因数 ('*' 因数)*
因数 ::= 数 | '-' 因数 | '(' 式 ')'

あるいは:


式 ::= 加数 | 加数 '+' 式
加数 ::= 因数 | 因数 '*' 加数
因数 ::= 数 | '-' 因数 | '(' 式 ')'

さらにあるいは:


式 ::= 加数 ('+' 式)?
加数 ::= 因数 ('*' 加数)?
因数 ::= 数 | '-' 因数 | '(' 式 ')'

実際に再帰下降法のパーザーを生成(手書き、または自動生成)するときは、1番目(whileループ使用)か3番目(再帰呼び出し使用)に基づいてコーディングします。念のため手書きしてみました。注意事項:

  1. 面倒だったので1ファイルに全部突っこんである。
  2. 構文エラーはまともにハンドリングしてない(nullを返すだけ)。
  3. その他、いたるところ手抜き、dirtyコーディング、行き当たりバッタリ。
  4. 何か変な挙動があれば、デバッグレベル3で実行のこと。

構文定義規則(BNF)に対応するメソッドを眺めると、機械的な手続きで「構文定義→プログラム」と変換できることが分かると思いますよ。