Write Good Parser in Perl
        Jiro Nishiguchi(西口次郎)
              id:spiritloose
              jiro@cpan.org
      Oct...
BEGIN {}
1. $self introduction
2. パーサとは
3. Perlの代表的なパーサ
4. How to write parsers
5. まとめ
$self
●   PAUSE ID: JIRO
    http://search.cpan.org/~jiro/
●   フリーランスのエンジニア
●   Image::ObjectDetect
●   Text::Migemo
●   h...
Parserとは?
●   なんらかの意味を持ったテキストを、その後の処理に適した
    形にする。
●   例(YAML):foo: bar → { foo => 'bar’ }
●   「正しく」「動作」して「あたりまえ」という期待
● ...
Kind of parser?
●   HTML / XML
●   CSV / TSV
●   JSON
●   YAML
●   Program (Perl, Ruby, Template-Toolkit, etc)
●   Protoco...
Perlでよく使うパーサ
●   HTML::Parser
●   XML::LibXML
●   JSON, JSON::XS
●   HTTP::Parser::XS
●   Template-Toolkit
●   Cache::Memc...
HTML::Parser
●   Since 1996
●   Depended on by 408 modules
    ●   ex. HTML::FIllInForm
●   Written in XS
●   手書き(自前で1バイトず...
XML::LibXML
●   Perl binding for libxml2
●   XMLの代表的なパーサ
●   Depended on by 267 modules
    ●   eg. Plagger
JSON(::PP)
●   Standard JSON module
●   Wrapper of JSON::PP and JSON::XS
●   Pure Perlで手書き
JSON::XS
●   高速なJSONパーサ
●   手書き
HTTP::Parser::XS
●   Plackで使われている
●   Written in XS
●   手書き
Template-Toolkit
●   Pure Perl
●   Based on Parse::Yapp
Cache::Memcached
●   get コマンドのパース
    ●   ::GetParser
    ●   ::GetParserXS (別ディストリビューション)
●   手書き
Why XS?
●   Perlのテキスト処理は遅い(Cに比べて)
●   1バイトずつ読み進める処理はCが高速
How to write
1.既存のモジュールを使う
2.正規表現
3.Parser Generater をつかう
4.手書き
手書き?
●   自由度は最も高い
●   プログラマの腕によっては最高速になることも
●   一方で…
    ●
        バッファオーバーラン
    ●
        漏れ
●   (私のような) 怠惰なプログラマには向かない
既存のモジュールを使う
●   重要!
●   既知のフォーマットで、ライブラリもそろっているのに自作す
    るといいことがあまりない
●   バグを生みやすい
●   可能な限り新しいフォーマットを作らないのが重要
正規表現ベース
●   書き捨て
●   規則が小さい、シンプル
●   誰にでも(Perlをかける人なら)分かりやすい
●   Perlの正規表現は十分に速い
●   規則が大きくなってくるとメンテナンスが大変
Regexp::Assemble
my $ra = Regexp::Assemble->new;
$ra->add('^(incr|decr) ([^ ]+) (d+)( noreply)?$');
$ra->add('^(delete) ([...
Parser generator とは?
●   文法などの定義情報からパーサを生成する
●   怠惰なプログラマにうってつけ
●   yacc(bison)
●   Parse::Yapp
●   Perse::Eyapp (Extended...
Ragel
●   State Machine Compiler
●   C, C++, Objective-C, D, Java and Ruby(no Perl?)
●   BNF/Regexp に似た文法
●   RubyのMongrel...
Ragel + XS
●   ステートマシンの定義を書く
●   パースする関数をCセクションに書く
●   XSセクションではその関数を呼び出すだけ
Ragel + XS
#include “xsutil.h” /* Module::Install::XSUtil */

%%{
   # ステートマシン定義部
}%%

static SV *parse(pTHX_ SV *text) {
...
XSいやなんですけど…
●   たいしたことしないので大丈夫です
●   データ構造を作って返すだけ
●   文字列結合や、配列、ハッシュが触れればOK
●   Perlでデータを加工したい場合は中間表現を返したり
例:ログ解析(正規表現)
our $RE = qr/([^ ]+) ([^ ])+ ([^ ]+) [([^]]+)] "([^"]+)"
([^ ]+) ([^ ]+)/;
our @COLS = qw(host logname user t...
例:ログ解析(Ragel)
word       =   [^ ]+;
host       =   word                >begin_host      %end_directive;
logname    =   wor...
Benchmark

   Ragel




Pure Perl




            0   50000   100000      150000     200000   250000   300000

           ...
例:Whitespace
●   スペースとタブと改行だけで構成される言語
●   シンプルなスタックマシン
●   実は教育用によい?
Whitespace(Hello world)
Whitespace(Hello world)
[S][S][S][T][S][S][T][S][S][S][LF]         [S][S][S][S][S][T][T][S][T][T][T][T][LF]
[T][LF]       ...
Whitespace(disasm)
PUSH 72    PUSH 111
PUTC       PUTC
PUSH 101   PUSH 114
PUTC       PUTC
PUSH 108   PUSH 108
PUTC       ...
Whitespace
add   =   tb   sp   sp   sp   >{   op   =   ADD;   }   %end_op;
sub   =   tb   sp   sp   tb   >{   op   =   SUB...
Graphviz
%%{
      machine test_parser;
      main := 'a' ('b' | 'c') 'd'+;
}%%

$ ragel -Vp test.rl | dot -Tpng > test.png
Perl6
●   パーサのための専用構文が用意された
    ●   grammer
●   前述のRagelの例のようなことが built-inでできる
●   Perl6のパーサも Perl6 の grammer で書かれている
●   ...
まとめ
●   よく使われるPerlのパーサライブラリを紹介した
●   パーサを書く場合の手法をいくつか紹介した
●   速度が求められる場合はCのパーサジェネレータ+XSを検
    討してもよい
●
    保守性と速度のトレードオフ
【未承諾広告】求人
●   コミュニティサイト 2001年~
●   5億pv / month
●   100 servers
●   mod_perl + Sledge + MySQL
●   Subversion + Redmine + C...
END { thank_you(); }
●   References
    ●   http://www.slideshare.net/spiritloose
    ●   http://github.com/spiritloose/
 ...
Upcoming SlideShare
Loading in...5
×

Write good parser in perl

3,849

Published on

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,849
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
8
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Write good parser in perl

  1. 1. Write Good Parser in Perl Jiro Nishiguchi(西口次郎) id:spiritloose jiro@cpan.org Oct 15, 2010 YAPC::Asia Tokyo
  2. 2. BEGIN {} 1. $self introduction 2. パーサとは 3. Perlの代表的なパーサ 4. How to write parsers 5. まとめ
  3. 3. $self ● PAUSE ID: JIRO http://search.cpan.org/~jiro/ ● フリーランスのエンジニア ● Image::ObjectDetect ● Text::Migemo ● http://d.hatena.ne.jp/spiritloose/
  4. 4. Parserとは? ● なんらかの意味を持ったテキストを、その後の処理に適した 形にする。 ● 例(YAML):foo: bar → { foo => 'bar’ } ● 「正しく」「動作」して「あたりまえ」という期待 ● ソフトウェアの中での重要度は非常に高いはずだが… ● 空気のような存在 ● でもベンチはとられまくり ● かわいそうな子
  5. 5. Kind of parser? ● HTML / XML ● CSV / TSV ● JSON ● YAML ● Program (Perl, Ruby, Template-Toolkit, etc) ● Protocol (HTTP, SMTP, Memcached, etc) ● 業務データ
  6. 6. Perlでよく使うパーサ ● HTML::Parser ● XML::LibXML ● JSON, JSON::XS ● HTTP::Parser::XS ● Template-Toolkit ● Cache::Memcached
  7. 7. HTML::Parser ● Since 1996 ● Depended on by 408 modules ● ex. HTML::FIllInForm ● Written in XS ● 手書き(自前で1バイトずつ読み進める)
  8. 8. XML::LibXML ● Perl binding for libxml2 ● XMLの代表的なパーサ ● Depended on by 267 modules ● eg. Plagger
  9. 9. JSON(::PP) ● Standard JSON module ● Wrapper of JSON::PP and JSON::XS ● Pure Perlで手書き
  10. 10. JSON::XS ● 高速なJSONパーサ ● 手書き
  11. 11. HTTP::Parser::XS ● Plackで使われている ● Written in XS ● 手書き
  12. 12. Template-Toolkit ● Pure Perl ● Based on Parse::Yapp
  13. 13. Cache::Memcached ● get コマンドのパース ● ::GetParser ● ::GetParserXS (別ディストリビューション) ● 手書き
  14. 14. Why XS? ● Perlのテキスト処理は遅い(Cに比べて) ● 1バイトずつ読み進める処理はCが高速
  15. 15. How to write 1.既存のモジュールを使う 2.正規表現 3.Parser Generater をつかう 4.手書き
  16. 16. 手書き? ● 自由度は最も高い ● プログラマの腕によっては最高速になることも ● 一方で… ● バッファオーバーラン ● 漏れ ● (私のような) 怠惰なプログラマには向かない
  17. 17. 既存のモジュールを使う ● 重要! ● 既知のフォーマットで、ライブラリもそろっているのに自作す るといいことがあまりない ● バグを生みやすい ● 可能な限り新しいフォーマットを作らないのが重要
  18. 18. 正規表現ベース ● 書き捨て ● 規則が小さい、シンプル ● 誰にでも(Perlをかける人なら)分かりやすい ● Perlの正規表現は十分に速い ● 規則が大きくなってくるとメンテナンスが大変
  19. 19. Regexp::Assemble my $ra = Regexp::Assemble->new; $ra->add('^(incr|decr) ([^ ]+) (d+)( noreply)?$'); $ra->add('^(delete) ([^ ]+)( noreply)?$'); $ra->add('^(gets?) (.+)$'); $ra->re; # Optimized Regexp
  20. 20. Parser generator とは? ● 文法などの定義情報からパーサを生成する ● 怠惰なプログラマにうってつけ ● yacc(bison) ● Parse::Yapp ● Perse::Eyapp (Extended yapp) ● Perse::RecDecent ● Pegex ● Ragel
  21. 21. Ragel ● State Machine Compiler ● C, C++, Objective-C, D, Java and Ruby(no Perl?) ● BNF/Regexp に似た文法 ● RubyのMongrel(HTTP Server), Hprecot(HTML Parser) ● Graphvizでグラフを出力可能 ● ロバストなパーサを作りやすい ● ランタイムライブラリ不要 ● http://www.complang.org/ragel/
  22. 22. Ragel + XS ● ステートマシンの定義を書く ● パースする関数をCセクションに書く ● XSセクションではその関数を呼び出すだけ
  23. 23. Ragel + XS #include “xsutil.h” /* Module::Install::XSUtil */ %%{ # ステートマシン定義部 }%% static SV *parse(pTHX_ SV *text) { /* パーサに必要なデータ宣言 */ %% write init; %% write exec; return res; } MODULE = MyParser PACKAGE = MyParser SV *parse(SV *klass, SV *text) CODE: RETVAL = parse(aTHX_ text); OUTPUT: RETVAL
  24. 24. XSいやなんですけど… ● たいしたことしないので大丈夫です ● データ構造を作って返すだけ ● 文字列結合や、配列、ハッシュが触れればOK ● Perlでデータを加工したい場合は中間表現を返したり
  25. 25. 例:ログ解析(正規表現) our $RE = qr/([^ ]+) ([^ ])+ ([^ ]+) [([^]]+)] "([^"]+)" ([^ ]+) ([^ ]+)/; our @COLS = qw(host logname user time request status bytes); sub parse { my ($class, $line) = @_; if (my @matches = $line =~ $RE) { my %data; @data{@COLS} = @matches; return %data; } return; }
  26. 26. 例:ログ解析(Ragel) word = [^ ]+; host = word >begin_host %end_directive; logname = word >begin_logname %end_directive; user = word >begin_user %end_directive; time_fmt = [^]]+ >begin_time %end_directive; time = '[' time_fmt ']'; req_fmt = [^"]+ >begin_request %end_directive; request = '"' req_fmt '"'; status = word >begin_status %end_directive; bytes = word >begin_bytes %end_directive; main := host ' ' logname ' ' user ' ' time ' ' request ' ' status ' ' bytes;
  27. 27. Benchmark Ragel Pure Perl 0 50000 100000 150000 200000 250000 300000 Process per seconds
  28. 28. 例:Whitespace ● スペースとタブと改行だけで構成される言語 ● シンプルなスタックマシン ● 実は教育用によい?
  29. 29. Whitespace(Hello world)
  30. 30. Whitespace(Hello world) [S][S][S][T][S][S][T][S][S][S][LF] [S][S][S][S][S][T][T][S][T][T][T][T][LF] [T][LF] [T][LF] [S][S][S][S][S][T][T][S][S][T][S][T][LF] [S][S][S][S][S][T][T][T][S][S][T][S][LF] [T][LF] [T][LF] [S][S][S][S][S][T][T][S][T][T][S][S][LF] [S][S][S][S][S][T][T][S][T][T][S][S][LF] [T][LF] [T][LF] [S][S][S][S][S][T][T][S][T][T][S][S][LF] [S][S][S][S][S][T][T][S][S][T][S][S][LF] [T][LF] [T][LF] [S][S][S][S][S][T][T][S][T][T][T][T][LF] [S][S][S][S][S][T][S][S][S][S][T][LF] [T][LF] [T][LF] [S][S][S][S][S][T][S][T][T][S][S][LF] [S][S][S][S][S][T][S][T][S][LF] [T][LF] [T][LF] [S][S][S][S][S][T][S][S][S][S][S][LF] [S][S][LF] [T][LF] [LF] [S][S][S][S][S][T][T][T][S][T][T][T][LF] [LF] [T][LF]
  31. 31. Whitespace(disasm) PUSH 72 PUSH 111 PUTC PUTC PUSH 101 PUSH 114 PUTC PUTC PUSH 108 PUSH 108 PUTC PUTC PUSH 108 PUSH 100 PUTC PUTC PUSH 111 PUSH 33 PUTC PUTC PUSH 44 PUSH 10 PUTC PUTC PUSH 32 EXIT PUTC PUSH 119 PUTC
  32. 32. Whitespace add = tb sp sp sp >{ op = ADD; } %end_op; sub = tb sp sp tb >{ op = SUB; } %end_op; mul = tb sp sp lf >{ op = MUL; } %end_op; div = tb sp tb sp >{ op = DIV; } %end_op; mod = tb sp tb tb >{ op = MOD; } %end_op;
  33. 33. Graphviz %%{ machine test_parser; main := 'a' ('b' | 'c') 'd'+; }%% $ ragel -Vp test.rl | dot -Tpng > test.png
  34. 34. Perl6 ● パーサのための専用構文が用意された ● grammer ● 前述のRagelの例のようなことが built-inでできる ● Perl6のパーサも Perl6 の grammer で書かれている ● http://github.com/perl6/std/blob/master/STD.pm6
  35. 35. まとめ ● よく使われるPerlのパーサライブラリを紹介した ● パーサを書く場合の手法をいくつか紹介した ● 速度が求められる場合はCのパーサジェネレータ+XSを検 討してもよい ● 保守性と速度のトレードオフ
  36. 36. 【未承諾広告】求人 ● コミュニティサイト 2001年~ ● 5億pv / month ● 100 servers ● mod_perl + Sledge + MySQL ● Subversion + Redmine + Capistrano ● TheSchwartz, Memcached, Solr ● perlbrew, cpanm ● 私 <jiro@cpan.org> まで
  37. 37. END { thank_you(); } ● References ● http://www.slideshare.net/spiritloose ● http://github.com/spiritloose/ ● http://d.hatena.ne.jp/spiritloose/
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×