【要望】JavaScriptの正規表現リテラル

  1. おはようございます。
    遅ればせながら、Mery 2 正式版の公開お疲れ様です。
    早速アップグレードいたしました。

    サロゲートペア・結合文字にも対応し、以前要望しておりました
    「ファイルを開いた時の既定エンコード利用」にも対応していただき、大変満足です。
    PowerShell用のエディタもMeryに変更しました。

    ところで、バージョン1で色分けされていたように、
    JavaScriptの正規表現リテラルに対応することはできないでしょうか。
    ご検討いただけると幸いです。

     |  100の人  |  返信
  2. こんばんは、早速ダウンロードしてくださりありがとうございます。

    > サロゲートペア・結合文字にも対応し、以前要望しておりました
    > 「ファイルを開いた時の既定エンコード利用」にも対応していただき、大変満足です。
    > PowerShell用のエディタもMeryに変更しました。

    おほめの言葉、ありがとうございます。
    地味な機能アップですが、気づいてくださるとうれしいものですね。

    > ところで、バージョン1で色分けされていたように、
    > JavaScriptの正規表現リテラルに対応することはできないでしょうか。
    > ご検討いただけると幸いです。

    そう言われてみるとバージョン 1 の時は色分けされておりましたね。
    バージョン 2 からは編集モードの定義を自分で追加することも可能ですので、正規表現リテラルを色分けすることも可能だとは思いますが、標準で対応していても良さそうですね。

    一応、JavaScript の編集モードの設定→強調文字列に追加で

    /.*?/

    正規表現のみチェック

    でそれっぽく色分けできるとは思うのですが、正しいかどうかは怪しいところです。

     |  Kuro  |  返信
  3. こんにちは。
    ご返信有り難うございます。

    教えていただいた手順で強調文字列に「/.*?/」を追加してみました。
    しかし同じ行に別のスラッシュがあると、
    正規表現リテラルとスラッシュの間にもマッチしてしまい、うまく色分けできません。

    編集モードの設定→構文→引用符に囲まれた文字列 において
    任意の約物を設定できるようにし、
    正規表現リテラルに対応するってことは出来ないでしょうか。

    ただそれだと、/[^/]+/のような正規表現リテラルが認識できないと思うので、
    JavaScriptの正規表現リテラルに対応するには
    正規表現専用の構文解釈が必要な気もしますがどうなんでしょう。

     |  100の人  |  返信
  4. こんにちは。

    > 教えていただいた手順で強調文字列に「/.*?/」を追加してみました。
    > しかし同じ行に別のスラッシュがあると、
    > 正規表現リテラルとスラッシュの間にもマッチしてしまい、うまく色分けできません。

    そう言われてみるとそうですね。
    正規表現リテラルを認識させるのはちょっとやっかいでしたね。すみません。

    > ただそれだと、/[^/]+/のような正規表現リテラルが認識できないと思うので、
    > JavaScriptの正規表現リテラルに対応するには
    > 正規表現専用の構文解釈が必要な気もしますがどうなんでしょう。

    ですね。バージョン 1 の時は正規表現リテラル専用の構文解析を実装しておりました。
    単純に正規表現で解析しようとすると少しややこしいことになりそうです。

    あと、編集モードの設定は汎用性を持たせたものにしておりますので、一部の言語に
    特化させるのも避けたいと考えております。

    ちょっと調べてみましたところ、以下のような正規表現で認識できるようです。
    むちゃむちゃ長いですが・・・

    (?:\/(?!\*)(?:\\.|[^\/\\])+\/(?:[gim]{0,3}|\b)(?=[^\/\\a-hj-ln-zA-Z0-9<>\*+%'"$_-]|[;:({\s,?})\[\]&|]|$))

    参考サイト: http://yurameki.s10.xrea.com/feel.happy.nu/program.php?program_id=33&program_menu=4&page=%E8%A8%80%E8%AA%9E%E5%88%A5

    これを強調文字列として登録してやると、一応認識してくれるようです。

    あと、Mery の正規表現ライブラリは鬼車を使用していますので、戻り読みなどを使用すればもっと単純化できる気がしますが、正規表現に疎いものでちょっと分りかねます・・・。

     |  Kuro  |  返信
  5. こんばんは。
    お早いご返信有り難うございます。

    > ちょっと調べてみましたところ、以下のような正規表現で認識できるようです。
    > むちゃむちゃ長いですが・・・
    > (?:\/(?!\*)(?:\\.|[^\/\\])+\/(?:[gim]{0,3}|\b)(?=[^\/\\a-hj-ln-zA-Z0-9<>\*+%'"$_-]|[;:({\s,?})\[\]&|]|$))

    調べていただいた正規表現ですが、/.*?/ と同じで、
    同じ行にスラッシュがあるとうまくいかないようです。

    > あと、Mery の正規表現ライブラリは鬼車を使用していますので、
    > 戻り読みなどを使用すればもっと単純化できる気がしますが、

    たしかに戻り読みなどを活用すれば、
    正規表現リテラルだけを認識できる正規表現が書けそうですね。
    正規表現に対する認識がちょっと甘かったです。

    しかしながら、以下のような正規表現リテラルがあった場合、
    正規表現リテラル以降がまるごとコメントになってしまいます。
    /^\/foo\/bar\//
    /\/*/

    「引用符に囲まれた文字列」の前後の約物を自由に設定できるようにし、
    正規表現リテラルを文字列リテラルの一種として解釈してもらえると、
    これらの問題が解消できると思うので、助かるのですが……
    無理なお願いをしていたら、申し訳ございません。

    > あと、編集モードの設定は汎用性を持たせたものにしておりますので、
    > 一部の言語に特化させるのも避けたいと考えております。

    文字列リテラルの前後の文字列を個別に設定できるようになれば、
    ・Rubyの正規表現リテラル
    ・PHPやPerlの任意のヒアドキュメント
    ・PHPやPerlの実行演算子(`ls -al`)
    ・Rubyの文字列リテラル(%!foobar!)
    ・MySQLの識別子(`foo--bar`)
    などにも一部対応できるようになると思いますが、どうでしょうか。

    よろしくお願いいたします。

     |  100の人  |  返信
  6. こんばんは。

    > 調べていただいた正規表現ですが、/.*?/ と同じで、
    > 同じ行にスラッシュがあるとうまくいかないようです。

    むむ、確かにある程度はうまくいくようですが、後続にスラッシュ単品が出現するとダメっぽいかもしれませんね。こりゃ難しいです。

    > しかしながら、以下のような正規表現リテラルがあった場合、
    > 正規表現リテラル以降がまるごとコメントになってしまいます。
    > /^\/foo\/bar\//

    そうですね、この辺に対応するのは現状の仕様だと無理だと思います。先にコメントの方を色分けしちゃいますので。

    > 「引用符に囲まれた文字列」の前後の約物を自由に設定できるようにし、
    > 正規表現リテラルを文字列リテラルの一種として解釈してもらえると、
    > これらの問題が解消できると思うので、助かるのですが……
    > 無理なお願いをしていたら、申し訳ございません。

    引用符に囲まれた文字列を正規表現で定義すること自体が構造上できません。
    ご不便をおかけして申し訳ございません。

    > 文字列リテラルの前後の文字列を個別に設定できるようになれば、
    > ・Rubyの正規表現リテラル
    > ・PHPやPerlの任意のヒアドキュメント
    > ・PHPやPerlの実行演算子(`ls -al`)
    > ・Rubyの文字列リテラル(%!foobar!)
    > ・MySQLの識別子(`foo--bar`)
    > などにも一部対応できるようになると思いますが、どうでしょうか。

    そうですね、現在の Mery では対応することは困難ですが、今後の課題として、ご要望として頂いておきますね。

    構文解析をユーザ任せにするのも色々と不都合が発生するようで、大変ですね。。。勉強になりました。貴重なご意見ありがとうございます。

     |  Kuro  |  返信
  7. > 引用符に囲まれた文字列を正規表現で定義すること自体が構造上できません。

    引用符に囲まれた文字列の設定で現在「1重引用符」「2重引用符」が選択できますが、これに「スラッシュ」「バッククォート」等を追加することも、やっぱり難しいのでしょうか……

    とりあえず、正規表現リテラルのみを強調できる正規表現を考えてみたいと思います。

    お忙しい中いろいろとご説明いただき、どうも有り難う御座いました。

     |  100の人  |  返信
  8. 横からお邪魔します。JavaScriptの正規表現リテラルはエスケープされたスラッシュと一行コメント、複数行コメントの開始への対応はまだしも、割り算と複数行コメントの終了を除外するのが難しいです。
    Kuroさんが引用してきたパターンはスラッシュのエスケープに厳密に対応しているみたいなので、後方(行頭に近い方)にスラッシュがある場合はともかく、前方に余計な(ただし文法的に正しい)スラッシュがあるだけでは間違えないのではないですか?
    間違って正規表現リテラルとみなさないためにパターンの最後に大きな先読みがあります。この部分によってマッチングに失敗した場合、リテラル終端のスラッシュから前方の余計なスラッシュまでが正規表現リテラルとしてマッチすることはありそうです。
    この先読み部分は自分のスタイルに合わせてチューニングが可能な部分ですし、リテラルを開始するスラッシュの直後にスペースは絶対に書かない(現れない)、などの自分ルールを加えることで誤認識をさらに減らすこともできますが、単体の正規表現ではそれくらいしかできないということでもあります。

    手元に bregonig.dllの正規表現を使った Ruby用のパターンがありましたので、参考にするもしないも自由にして下さい。本物のRubyインタープリタが解釈するものとの違いは以下の通りです。
    *改行文字を含むことができない。(※Mery+鬼車ならできるかも)
    *区切り文字に改行文字を使用できない。(※Mery+鬼車なら対応できるかも)
    *かっこを使ったリテラルで区切り文字のエスケープに対応していない。(エスケープするくらいなら違う種類のかっこを使ってよ)

    # %!string!, %Q!string!, %q!string!, %s!Symbol!
    /%[Qqs]?([ -'*-\/:;=?@\\^_`|~])(?:[^\\]|\\.)*?\1/

    # %q(string), %q<string>, %q[striing], %q{string}, %Q, %s...
    /%[Qqs]?(?<parenth>\([^\()]*(?:\g<parenth>[^\()]*)*\))/
    /%[Qqs]?(?<bracket3><[^<>]*(?:\g<bracket3>[^<>]*)*>)/
    /%[Qqs]?(?<bracket4>\[[^\[\]]*(?:\g<bracket4>[^\[\]]*)*])/
    /%[Qqs]?(?<brace>\{[^\{}]*(?:\g<brace>[^\{}]*)*})/

    # %r!regexp!i
    /%r([ -'*-\/:;=?@\\^_`|~])(?:[^\\]|\\.)*?\1[eimnosux]*(?!\s*[A-Za-z0-9\[{])/

    # %r(regexp)i, %r<regexp>i, %r[regexp]i, %r{regexp}i
    /%r(?<parenth>\([^\()]*(?:\g<parenth>[^\()]*)*\))[eimnosux]*(?!\s*[A-Za-z0-9\[{])/
    /%r(?<bracket3><[^<>]*(?:\g<bracket3>[^<>]*)*>)[eimnosux]*(?!\s*[A-Za-z0-9\[{])/
    /%r(?<bracket4>\[[^\[\]]*(?:\g<bracket4>[^\[\]]*)*])[eimnosux]*(?!\s*[A-Za-z0-9\[{])/
    /%r(?<brace>\{[^\{}]*(?:\g<brace>[^\{}]*)*})[eimnosux]*(?!\s*[A-Za-z0-9\[{])/

    長々と失礼しました。

     |  AC  |  返信
  9. 割り算等のことを失念しておりました。
    無茶をしつこく要求し、大変申し訳ございませんでした。

    正規表現に関するご説明有り難うございます。
    参考にさせていただきたいと思います。

    失礼いたしました。

     |  100の人  |  返信
  10. こんにちは。
    私は以下のように設定しています。
    同一行に除算が複数回あると誤判定しますけれど……。(T T)

    正規表現:(?<!/|\\)/(?!\*)([^/\n\[]|(?<!\\)(\\\\)*\\[/\[]|(?<!\\)(\\\\)*(?=\\\\)|((?<!\\)\[([^\]\n]|(?<!\\)(\\\\)*\\\\])*(?<!\\)(\\\\)*\]))+(?<!\\)(\\\\)*/
    行コメント:(?<!\\)(\\\\)*//.*(?=\n)

    それでは。

     |  手石  |  返信
  11. > こんにちは。
    > 私は以下のように設定しています。
    > 同一行に除算が複数回あると誤判定しますけれど……。(T T)

    > 正規表現:(?<!/|\\)/(?!\*)([^/\n\[]|(?<!\\)(\\\\)*\\[/\[]|(?<!\\)(\\\\)*(?=\\\\)|((?<!\\)\[([^\]\n]|(?<!\\)(\\\\)*\\\\])*(?<!\\)(\\\\)*\]))+(?<!\\)(\\\\)*/
    > 行コメント:(?<!\\)(\\\\)*//.*(?=\n)

    > それでは。

    (?<!/|\\)/(?!\*)([^/\n\[]|(?<!\\)(\\\\)*\\[/\[]|(?<!\\)(\\\\)*(?=\\\\)|((?<!\\)\[([^\]\n]|(?<!\\)(\\\\)*\\\])*(?<!\\)(\\\\)*\]))+(?<!\\)(\\\\)*/

     |  teisi  |  返信
  12. ゴメンナサイ。
    間違えたり途中で投稿したり……。

     正規表現は、
    > (?<!/|\\)/(?!\*)([^/\n\[]|(?<!\\)(\\\\)*\\[/\[]|(?<!\\)(\\\\)*(?=\\\\)|((?<!\\)\[([^\]\n]|(?<!\\)(\\\\)*\\\])*(?<!\\)(\\\\)*\]))+(?<!\\)(\\\\)*/
    でした。

    それでは。

     |  手石  |  返信
  13. 100の人 さん、Kuro さん、AC さん 手石さん、貴重なお話をありがとうございます。
    脇から拝見していて、とても参考になりました。

    このままだともったいない流れなので、最後にひとつ。
    お礼のご挨拶かわりに、めまいを抑えて考えたことを書かせてください。

    100の人 さん
    ------------
    > JavaScriptの正規表現リテラルに対応することはできないでしょうか。

    > 編集モードの設定→構文→引用符に囲まれた文字列 において
    > 任意の約物を設定できるようにし、
    > 正規表現リテラルに対応するってことは出来ないでしょうか。

    > 引用符に囲まれた文字列の設定で現在「1重引用符」「2重引用符」が選択できますが、
    > これに「スラッシュ」「バッククォート」等を追加することも、やっぱり難しいのでしょうか……

      この「JavaScriptの」という点が大切です。編集モードは、言語ごとに対応が異なる
      ので、どんな場面でも通用する強調文字列を求めるはたいへんなこと。

      Mery はプログラム開発専用のツールではなく、いろいろな文章や文字データを加工
      する幅広い用途の道具、ということもあって、IDE(統合開発環境)のエディタや
      lint(構文チェッカー)と同等の機能を求めるものではないはずです。

      どうかそれを念頭において、以下をご覧ください。

      Mery 1.1.2.2840 の JavaScript 編集モードでの表示は、文字リテラルと同じく
      正規表現リテラルは、赤い字に表示されます。
      / と / に挟まれた間を正規表現リテラルと判断しますが、その中に /* があれば、
      複数行コメントの開始と見なして、以降をコメントの緑色で表示します。

      でも正規表現リテラルの前にカッコの ( があるとき、( ) に囲まれてた式の中にある
      ときは、正規表現リテラルの中に /* があっても複数行コメントの開始と見なしません。
      
      この動作は Mery 2 にはありません。Kuro さんがおっしゃるように、Mery 1 と同様に
      複数行コメントの判断を先にするので、正規表現リテラルが括弧に囲まれていても /* 
      以降は */ が出現するまで、複数行コメントと見なされます。
      
      これについては、Mery 2 の編集モードでは操作できません。

      しかし、これは、コードを編集するのがご自身であれば、\/* の \/ を \x{002f} など
      のコードポイントや内部コードなどで書くか、その行に行コメントで、// */ のように
      ダミー文字列を書くことで実用上の支障は減らせるのではないかと思います。

      開始と終了が同じ文字となる引用符の組のような対応関係を判断するのは、おっしゃる
      ほど簡単ではありません。エスケープの条件がそれぞれに異なるからです。引用符ですら
      2つ重ねるとエスケープとする言語があります。
      
      構文の知識がなければ、その言語の文脈を確実には把握できません。
      エディタ作者がそれをすべて用意するのではなく、その言語を熟知しているユーザが
      その知識で編集モード(構文ファイル)を作れるようにして下さったのですから、それ
      をできるだけ活用して、自分で使い易い道具に仕立ててゆけばいいですね。

      JavaScript の正規表現でも、100の人 さんのご指摘通り、文字集合 [ ] の中では特別な
      扱いをしなければなりません。引用符の代わりに / スラッシュを使うとしても、もっと
      工夫が要るように思います。
      
      強調文字列は、改行を超えての表示ができませんし、複数行コメントや引用符の指定では
      正規表現が使えません。

      また、Rubyの正規表現リテラルは、簡単には表現できないほど多様な記述ができます。
      開始・終了文字の括弧や任意の文字が使えること、内部に式展開があること、など難問が
      目白押しです。

      さて、ずいぶん時間がかかってしまいましたが、Mery 1.1.2.2840 と見比べながら、なんとか
      似たところまではたどり着きました。
      でも、例にあげているような日本語の文章の中ではなくて、あくまでも JavaScript の
      文脈の中で見てください。構文の判断材料がないとうまく動作できません。

    ○ JavaScript の正規表現リテラル 強調文字列
      開始と終了は / の1字。
      開始側の \ 1字の前置 を除外。
      // と /* は除外。
      終了側 / で JavaScript構文を考慮。 任意の空白に続く、 ) . , ; = } 改行 または // を後置。
      開始側 / で JavaScript構文を考慮。 = ( 空白 改行 を前置。
      内側 [ ] の中は文字集合。内部では [ ] のエスケープ と 改行 を除外。
      終了側に後置のオプション文字は選択しない。

    (?<!^\\|[^\\]\\|/)(?<=[\(=\s]|^)(?:(?<a>\/)(?![*/])(?:(?:(?!\k<a>)[^\\\n])|(?:\\[^\n])|(?<!^\\|[^\\]\\)(?:(?:[\[])(?:(?:(?!\])[^\\\n])|(?:\\[^\n]))*?\]))*?\k<a>)(?=[gimy]{,4}\x{0020}*(?:[\),.;=\}\n]|\/\/))

    AC さん
    -------
    > この先読み部分は自分のスタイルに合わせてチューニングが可能な部分ですし、リテラルを開始する
    > スラッシュの直後にスペースは絶対に書かない(現れない)、などの自分ルールを加えることで誤認識をさらに
    > 減らすこともできますが、単体の正規表現ではそれくらいしかできないということでもあります。

    まったく同感です。パーソナルな用途に柔軟に対応してくれることだけで、十分うれしいので、
    状況に応じて、できるところまで手伝ってくれる、という Mery の方向性がいいですね。

    > *改行文字を含むことができない。(※Mery+鬼車ならできるかも)
    > *区切り文字に改行文字を使用できない。(※Mery+鬼車なら対応できるかも)

    検索(選択)ではできますが、強調の表示は Mery が許してくれませんのでできません。

    > *かっこを使ったリテラルで区切り文字のエスケープに対応していない。(エスケープするくらいなら違う
    > 種類のかっこを使ってよ)

    作ってみました。長~い正規表現でもよければ、お試しください。Ruby をよく知らないで作っている
    ので、怪しい挙動です。

    ○ Ruby の %r 正規表現リテラル 強調文字列
      %r( )
      開始 %r( と終了は ) の文字。
      開始側の \ 1字の前置 を除外。
      終了側 ) で Ruby構文を考慮。 任意の空白に続く、 ) . , ; = } 改行 または // を後置。
      開始側 %r( で Buby構文を考慮。 = ( 空白 改行 を前置。
      内側 [ ] の中は文字集合。内部では [ ] のエスケープ と 改行 を除外。
      終了側に後置のオプション文字は選択しない。

    (?<!^\\|[^\\]\\)(?<=[=\(=\s]|^)(?:(?<a>%r(?<b>[\(]))(?:(?:(?!\k<a>)[^\\\n])|(?:\\[^\n])|(?<!^\\|[^\\]\\)(?:(?:[\[])(?:(?:(?!\])[^\\\n])|(?:\\[^\n]))*?\]))*?\))(?=[eimnosux]{,5}\x{0020}*(?:[\),.;=\}\n]|\/\/))

    ○ Ruby の %r 正規表現リテラル 検索用文字列  ※長すぎて (260字超) 強調文字列には使えません
      %r( )
      上記のほかに…
      式展開 #{ } の中は任意式、内部では { } の入れ子、組になっていない括弧を許容
      外側の %r( ) と包含する #{ } で二重の再帰。

    (?<!^\\|[^\\]\\)(?<=[=\(=\s]|^)(?<!^\\|[^\\]\\)(?<a>%r)(?<c>(?:(?<b>\()(?:(?:(?:[^\(\)\\\[\]#])|(?:\\[\s\S])|(?:(?<!^\\|[^\\]\\)(?:(?:[\[])(?:(?:(?!\])[^\\\n])|(?:\\[^\n]))*?\]))|(?:(?<!^\\|[^\\]\\)(?:#)(?<d>(?:\{(?:(?:(?:[^\{\}\\])|(?:\\[\s\S]))*\g<d>?(?:(?:[^\{\}\\])|(?:\\[\s\S]))*)+(?:\})?)))|[#\[\]])*\g<c>?(?:(?:[^\(\)\\\[\]])|(?:\\[\s\S])(?:(?<!^\\|[^\\]\\)(?:(?:[\[])(?:(?:(?!\])[^\\\n])|(?:\\[^\n]))*?\]))|[\[\]])*)+(?:\))?))(?=[eimnosux]{,8}\x{0020}*(?:[\),.;=\}\n]|\/\/))

    いつもながら長文ですみません。  もいすん === inuuik  _o_

     |  もいすん  |  返信
  14. もいすんさん、おはようございます。

    > Mery 1.1.2.2840 の JavaScript 編集モードでの表示は、

    (/    /)の中に、" があって、(/ "  /) を
    きちんと色分けできたのが、特徴でした。
    (真魚、Mery1のみできました。)
    (やっぱり、真魚の作者さまもすばらしい方だと思ってます。)

    (EmEditorのフリーはだめだったので、EmEditorを購入しようとは思いませんでした。)

    > さて、ずいぶん時間がかかってしまいましたが、Mery 1.1.2.2840 と見比べながら、
    > なんとか似たところまではたどり着きました。
    > ○ JavaScript の正規表現リテラル 強調文字列

    自分の書いたスクリプトで確認させて頂きました。
    (/ "  /)を除けば、バッチリです。

    (というより、" については、別途考慮すべき事もあるとは思いますけれど。)

    ご提示ありがとうございます。

     |  いっち  |  返信
スポンサーリンク