tana_ash's diary

プログラミングや電子工作など。やってみたこと、わかったことをまとめておく場所。

if文をパースするときの混乱と解決法

はじめに

今、私はJavascriptにコンパイルされる新しい言語を作っています。
そこで、この言語のパーサをJavascript版のyaccのようなツール Jison (http://zaach.github.com/jison/ )を使って作っています。
このツールは基本的にyaccと似たようなものらしいのでyaccの解説を読みながら作っています。

現れた問題

この言語は最終的にはインデントを使ったPythonのような表記など、複雑なものになる予定ですが、
現在はシンプルなCやJavascriptのような「{}」を使った書き方になっています。
ところで、

if 1 {
  print(a + b + 10)
}
12

のようなソースコードをパースするときに問題が発生しました。
「}」の後には「else」が来るはずなのに数字が来るのはおかしい、といったような構文エラーが出てしまいます。
その理由は、定義ファイルで

| IF expr optnl '{' optnl optexprs '}'
    { $$ = ["if", $2, $6]; }
| IF expr optnl '{' optnl optexprs '}' ELSE optnl '{' optnl optexprs '}'
    { $$ = ["ifelse", $2, $6, $13]; }
| IF expr optnl '{' optnl optexprs '}' NEWLINE ELSE optnl '{' optnl optexprs '}'
    { $$ = ["ifelse", $2, $6, $13]; }

このような定義があったからです。「optnl」は「改行か何もないか」、「optexprs」は、「式の連続があるかないか」です。
そのため、「}」の後に改行を入れてしまったためelseにつながるのだと認識されてしまったのかこのエラーが出てしまいます。

解決

このエラーを解決するためには悩みましたが、結局「パーサではなくレクサ部分を変更する」という対処法を取りました。

"}"         return '}'

としてカッコが定義されているよりも上に

\}[ \t]*\n?[ \t]*else    return 'CLOSEELSE'

というように、閉じカッコとelseをまとめたトークンを作ってしまいます。
そして、if-elseの定義では閉じカッコとelseの部分をまとめたトークンを使うように変えておきます。
これで無事にパースできるようになりました。