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の部分をまとめたトークンを使うように変えておきます。
これで無事にパースできるようになりました。