hogashi.*

日記から何から

cpanfileをなんとなくパースする正規表現書いた

github.com

 Module::CPANfile のテスト (t/parse.t) を見ながら JavaScript でつくりました。まだ完全ではなさそう (他にもテストあるし) だけど、それなりになったのでオ〜という気持ちで、うまく使って何かやりたい。文字種とかかなり雑なので、読む用がよさそう、バリデーションとかには向きません。
 README に書いてるけどこういう感じになる。

[
  [
    '\n' +
      '# https://github.com/miyagawa/cpanfile/blob/5e89c54bb388402db3a0bb61a44de875860df3d1/cpanfile\n'
  ],
  [
    "requires 'CPAN::Meta', 2.12091;",
    "requires 'CPAN::Meta', 2.12091;"
  ],
  [
    "\nrequires 'CPAN::Meta::Prereqs', 2.12091;",
    "requires 'CPAN::Meta::Prereqs', 2.12091;"
  ],
  [
    "\nrequires 'parent';",
    "requires 'parent';"
  ],
  [
    "\n\nrecommends 'Pod::Usage';",
    "recommends 'Pod::Usage';"
  ],
  [
    '\n' +
      '\n' +
      'on test => sub {\n' +
      "    requires 'Test::More', 0.88;\n" +
      "    requires 'File::pushd';\n" +
      '};',
    "requires 'File::pushd';"
  ]
]

 ちなみに実装にあたって、正規表現をバリバリ組み合わせられる便利関数をつくった。

  • '/abc\n/' みたいな RegExp オブジェクトは
  • .toString() すると '/abc\\n/' と (必要に応じてエスケープされた) スラッシュ付きの文字列になるので、
  • 前後のスラッシュを取り除いてから
  • 文字列結合して new RegExp() し直すことで

組み合わせる作戦。
 その結果の正規表現なので、実体を見ると激ヤバとなっていて、これを直に書くのは無理ですねという気持ちになった (Perl だったら正規表現で改行とインデントしまくれるので書けるのかもしれない) 。
 追記: その後ちょっと長めの cpanfile で試したら 15分経っても終わらなくて、色々見直したら 1秒かからずにすっと行けるようになったし、正規表現が 1607文字 → 496文字と大幅に減った。規模としては、もし直で正規表現を作り上げるとなっても「大変だけど面白い」と思えそうなくらい。

/(?:\s*(?:((?:(?:requires|author_requires|configureRequires|test_requires|conflicts|recommends)\s*(?:(?:[^'"]+|['"][^'"]+['"])(?:(?:\s*(?:,|=>)\s*)(?:[0-9.]+|(?:['"][^'"]+['"])))?)(?:\s*;)))|(?:on\s*(?:[^'"]+|['"][^'"]+['"])(?:\s*(?:,|=>)\s*)(?:sub\s*\{(?:\s*(?:((?:(?:requires|author_requires|configureRequires|test_requires|conflicts|recommends)\s*(?:(?:[^'"]+|['"][^'"]+['"])(?:(?:\s*(?:,|=>)\s*)(?:[0-9.]+|(?:['"][^'"]+['"])))?)(?:\s*;)))|#[^\n]*(?:\n|$)))*\s*\})(?:\s*;))|#[^\n]*(?:\n|$)))/g
$ pbpaste | wc -c
     496


 Module::CPANfile とはこれ。 Perl のモジュールで、パースするのに cpanfile をエイッと eval するつくりになっていて、 JavaScript にそのまま持ってくることは不可能だった (Node 版 PPI がもし存在すればできるのかもしれない)。 metacpan.org

 ちなみに npm にある ppi は画像の EXIF からピクセルパーインチをゲットするやつっぽい。そもそも「node PPI perl」とか検索すると木としてのノードということで普通に PerlPPI が登場してしまって本当に存在するのか確かめるのが難しかった。