構文解析のオハナシは、変なことを書いてしまったですね。これ以上追記をするとゴチャゴチャし混乱しそうだから、このエントリーに補足と注意をまとめます。
構文規則の曖昧性と演算子の優先順位
ことの発端は、演算子を含む式を「式 ::= 式 '+' 式」のように“なにげなく”定義すると、曖昧な構文規則(文法)になるよ、ってことね。再帰下降法のパーザーを前提にするんなら、優先順位の高さに応じて式の構成を階層化するってこと(これが、aiue3=ショー君のメモ)。例えば:
[優先順位 低] 式 ::= 加数 ('+' 式)*
[優先順位 中] 加数 ::= 因数 ('*' 加数)*
[優先順位 高] 因数 ::= 数 | '-' 因数 | '(' 式 ')'
優先順位ごとに構文規則があり、それぞれ、'+', '*', '-'(単項) という演算子が導入されています。これを、最初に僕が間違ったように「加数 ::= 因数 ('*' 加数)* | '-' 加数」とかすると、'*'と'-'の優先順位がハッキリせずに曖昧性が解消されません。
なお、「演算子順位文法に基づくパーザージェネレータの実例を知らない」と書きましたが、老舗定番ジェネレータyacc(解析アルゴリズムはLR、C言語ベース)でも、%left, %right という指令で演算子優先順位を指定できるようです。
手書きサンプル・パーザー
だいたいうまく動くようです。見直してみると、可読性もそれほどヒドイわけでもないし。
実はアサーションが間違っていました(本末転倒じゃ)、オプション -ea (enable assertions) を付けずに実行していたので気が付きませんでした。これだけは直しました。
コンパイル/実行すると次のような感じ:
C>javac ArithExp.java
C>java -cp . -ea ArithExp
3+-5*--3*5+23+-5*--3*5+2 ==>
+(
3,
+(
*(
-(
5
),
*(
-(
-(
3
)
),
5
)
),
2
)
)