JavaScript のコメントを除去
JSのコメント削除って簡単に書けないかなあ。文字列リテラルとか正規表現リテラルまで考えるとなあ。
http://twitter.com/miya2000/status/11177757376
とつぶやいたところ、有志が実装を投げてくれました。
- -
まずわたし。最初は「正規表現一発でいけるんじゃない?」とか考えていたのですが早々にあきらめて頭からループすることに。この実装は後に述べる問題があります。
https://gist.github.com/346593/f1880d17e377b07b99944ed6c2a0ceb524d314c2
miya2000 (@miya2000) | Twitter
id:Constellation さんはかなり構文解析の領域まで踏み込んでいます。コードを書くのが楽しくってしょうがないという感じでステキです。
小倉唯 (@Constellation) | Twitter
xulapp さんは私が早々にあきらめた正規表現一つでの方法を教えてくれました。すごいですね。
xulapp (@xulapp) | Twitter
mooz さんは私のと似ているようで設計思想の違いがはっきりわかる実装になっています*1
mooz (@stillpedant) | Twitter
昨日はひとつの問題を通して多くの話が聞けた、とても楽しい一日でした。ありがとうございます。
- -
さて、問題というのはなにかというと 正規表現リテラルの開始の「/」と割り算の「/」の区別がつかない ということです。
たとえばこんなコードを
var i = 100 / 10; // コメント
「/」を区切りとして頭から切り取りながら進めていくと
/ 10; // コメント
こうなるわけですね。単純に「/」の後が「/」でも「*」でも無いなら正規表現、としてしまうと
/ 10; /
を正規表現としてあつかってしまい、「// コメント」の部分がコメント扱いされなくなってしまうのです。
じゃあということで「スラッシュの直前の文字がXXXなら」というアプローチにみんなで向かったわけですが、
var i = {} / 10; // -> NaN { i = "aaa" } /aaa/.test(i);
のように直前が「}」となってもその後の「/」がどちらなのか確定できず、これ以上はもう構文解析しないとむずかしいよね、ということに。
- -
id:Constellation さんは構文解析の方針で時間があるときにやってみたいとのこと。
私のほうはなかばあきらめつつ、処理時間もきにせず eval とかその辺でやれないかなと、そんなかんじです。
- -
(3/30 追記)
判別できるように修正しました。
まず、与えられたコードには構文エラーが無いものとします。
var i = 100 / 10; /[/*]/.test(i); // */
構文エラーが無いことは以下のようにして確認できます。
Function('(' + 'var i = 100 / 10; /[/*]/.test(i); // */' + ')');
最初のスラッシュが割り算か正規表現リテラルかを判別するために、スラッシュの次に「^」を置いてみます。
Function('(' + 'var i = 100 /^ 10; /[/*]/.test(i); // */' + ')');
構文エラーになりました。このスラッシュは割り算ということになります。では次のスラッシュでやってみましょう。
Function('(' + 'var i = 100 / 10; /^[/*]/.test(i); // */' + ')');
正規表現リテラルのスラッシュの場合は「^」が入っても構文エラーとはならないため、このスラッシュは正規表現リテラルの開始文字となります。
このままだと当然「/」がくるたびに全体を evaluate してしまうので、与えられたコードによってはかなり時間がかかってしまうでしょう。
用途によってはこのままでも大丈夫でしょうが、チューニングしたい場合は、直前の文字で判別できないときだけこのやり方で判別するということができそうです。
*1:「有限個の状態と遷移と動作の組み合わせ」