ToDo:
Eclipseでコードを書いていて、単なる文字列パターンではなく、Java言語の式や文のパターンに対して、検索・置換ができたら便利なのにと思うことがある。正規表現検索でも使えなくは無いのだが、括弧がネストしている場合などは、正規表現では手に負えないので、Java言語の文法のパターンを記述できる必要がある。ここまで考えて、ふとどこかでそんなことを聞いたな、と思い返してみると、NetBeansのJackpotがまさにそのような用途のためのツールな気がしてきた。Jackpotでは、専用のルール言語を用いて、マッチさせるJava言語のソースのパターンと、それをどのように置き換えるかを記述できる。まさに、自分の目的にうってつけだ。惜しむらくは、これがNetBeans用であって、Eclipse用には使えないことか。
_ qmoci mulcnsjo [hfxbcjwk pjhqkrfi lcor cjwzg xgrdsjak liofxgrek imzcb ]
_ bhqsaly ftbocpwrm [tsyxmqi fkmt dvlnxgi oqphmyv ldimk qgxctrsju gjtras ]
_ vguw ydafnzqps [lhwfm mboicsavk azwcfj dqzye gjwbmqcea qjzdr bnwk ]
_ wjnztfgvy avbwpsg [dskxqjmn lhcsetnz nijv mreslfkvg iyhx zkrfblut abpgfy ]
_ yhxluoap lurneoabk [xkjhvszab dzjbmrc bxkoyth nlygue aeyi xiaghod rbgv ]
構文解析法に関する情報を調べていたところ、LL(*)(LL starと呼ぶらしい)という構文解析アルゴリズムがあることを知った。LL(*)に関する説明スライドを斜め読みした感じでは、LL(k)で先読みできるトークン数は固定長だったのに対して、LL(*)では、無限長のトークンを先読みできるため、LL(*) >>>>>越えられない壁>>> LL(k)ということのようだ。
例えば、次のような文法があったとき、argは任意回繰り返すため、LL(k)による固定長の先読みでは駄目だが、LL(*)なら解析することができる。
fun : type ID "(" (arg)* ")" ;
fun : type ID "(" (arg)* ")" "{" body "}"
ただ、LL(*)では、正規言語で表現できる範囲でしか先読みができないため、次のように再帰的な構造を先読みする必要がある場合、LL(*)では正しく解析できないという弱点もあるようだ。
a : b X | b Y ;
b : A | A b ;
Onionでは、foreach文が次のように感じで記述できる。
foreach x :String << ["A", "B", "C"] {
System::out.println(x);
}
ただ、"<<"を使うのは不本意で、本来は他の言語でよく使われるキーワードである"in"にしたいと考えていた。しかし、これを単純に実装すると大きな問題があるため、今まではやむなく"<<"を使っていた。というのは、"in"を使えるようにするには、Lexerに予約語"in"を加える必要があるのだが、そうしてしまうと、識別子として"in"が使えなくなってしまうためだ。
もちろん、これだけなら大した問題は無い。inという変数名を使わなければいいだけのことだからだ。しかし、Javaの標準ライブラリであるSystemクラスには、標準入力を表すinというstaticフィールドがあるため、Onionのプログラム中で、System::inが使えなくなってしまう。これは、Javaのライブラリに簡単にアクセスできることが重要であるOnionとしては許容できない。
しかし、JavaCCにあるSemantic Lookaheadという機能を使うことで、この問題を解決できることに最近気がついたので、実装してみることにした。
Semantic Lookaheadとは簡単に言うと、ユーザ定義メソッドが真を返すかどうかによって、ある構文規則を選択するかどうかを決定できる機能である。これを使うことによって、予約語では無いものを、特定の文脈でのみ、予約語であるかのように扱うことなどが簡単にできる。
この機能を使うことで、foreach ... in ...のような文を表現するJavaCCの構文規則は次のように記述することができた。
/* sが次のトークンの文字列と一致するかどうかを調べる */
boolean la(String s) {
return getToken(1).image.equals(s);
}
...
/* foreach文の構文規則 */
foreach_stmt() :{}{
"foreach" var_decl (LOOKAHEAD({la("in")}) <ID>) expr block
}
ポイントは、LOOKAHEAD({la("in")} <ID>という部分である。ここで、<ID>だけなら、識別子ならどれでもマッチしてしまうのだが、LOOKAHEAD({la("in")}というのを前に置くことで、la("in")が真を返す場合にのみ、解析が成功する。そして、laは次のトークンの文字列が引数と一致する場合にのみ真を返すメソッドとして定義してあるため、次のトークンが"in"である場合にのみ、解析が成功するようになる。
早朝、ループ道路を散歩していたところ、真ん中ぐらいに差し掛かったところで、急に激しい動悸に襲われた。立っていられなくなり、しばらくうずくまっていたところ、数分後にはとりあえず回復した。その後、家に向かって歩き続けていたところ、再び激しい動悸が。今度は、すぐに収まりそうに無く、やばそうだと感じたため、震えながら救急車を呼んだ。
数分後には、救急車が到着して、大学病院に向かって搬送されることになったが、その途中で、手足や顔がどんどんしびれて来て、マジで死ぬのではないかという強い不安感に襲われた。病院の人に、死ぬような症状ではないから大丈夫大丈夫、不安になればなるほど症状が激しくなるので、落ち着いてゆっくり呼吸するように、と言われたが、そう言われてもそうすぐに気持ちが切り替えられるわけもなく、荒い呼吸を繰り返すばかり。思い返してみると、このときの自分は、本当に情けなかった。
しばらくして、病院に到着。採血や心電図検査、血圧の検査が行われたが、その間もなかなか不安が治まらず、手足のしびれが取れなかった。ビニール袋を口にあてて(もちろん密封はしないで)、そこに向かってゆっくり呼吸するように言われて、その通りにしていると、しばらくして大分症状が治まってきた。あとで知ったのだが、この方法は、過換気症候群と呼ばれる症状が出たときの対処法で、ペーパーバッグ法と言うらしい。
それからしばらくして、検査の結果が出たものの、カリウムが若干不足気味である以外は、特に異常は無いということと、症状もある程度収まってきたため、翌日の循環器内科の予約を取って、一度家に帰ることになった。(続く)
_ stjyax ljytez [wnisg thneoz yijor faxlmrcu ujhdmk wpzmth nreyv ]