Parse::RecDescentを一回ぐらい使っておこうかなということで使ってみた。
書いてみたのは .iniファイルのパーサです。たくさんモジュールがあるし、
実際に使う気があるわけでもなく、ただ練習ということで。
iniの仕様
書くにあたってわかったのですが、.iniファイルって厳格な仕様があると
いうわけではないんですね。なんで、Config::INIの PODに書いてある
BNFを参考に、若干簡略化したものになってます。文字列の正規表現はいい加減です。
コード
#!/usr/bin/env perl use strict; use warnings; use Config::INI; use Parse::RecDescent; ## for debug #$::RD_HINT = 1; #$::RD_TRACE = 1; my $grammer =<<'__GRAMMER__'; INIFILE : block(s) { my $obj; for my $block (@{$item[1]}) { if(ref $block && ref $block eq 'ARRAY') { my ($key, $value) = @{$block}[0,1]; $obj->{$key} = $value; } } $return = $obj; } block : empty_line | section section : section_header value(s?) { my $section = {}; for my $assignment (@{$item[2]}) { my ($key, $value) = @{$assignment}[0,1]; $section->{$key} = $value; } $return = [$item[1], $section]; } section_header : "[" section_name "]" comment(?) { $return = $item[2] } section_name : string value : value_assignment | empty_line value_assignment : property_name "=" value comment(?) { $return = [ $item[1], $item[3] ]; } property_name : string value : string_with_space comment : /;[^\n]+/ { $return = 0; } string : /[A-Za-z0-9-!@.'"]+/ string_with_space : /[A-Za-z0-9-!@.'" ]+/ { (my $str = $item[1]) =~ s{\s+$}{}; $return = $str; } empty_line : /\s+/ | comment __GRAMMER__ my $ini =<<'__INI__'; ; last modified 1 April 2001 by John Doe [owner] name=John Doe organization=Acme Widgets Inc. [database] server=192.0.2.62 ; use IP address in case network name resolution is not working port=143 file = "payroll.dat" __INI__ my $parser = Parse::RecDescent->new($grammer); my $result = $parser->INIFILE($ini); use YAML; die YAML::Dump($result);
結果
上記を実行した結果以下のようになります。期待したとおりです。
--- database: file: '"payroll.dat"' port: 143 server: 192.0.2.62 owner: name: John Doe organization: Acme Widgets Inc.
評価
Config::Tinyと比較してみました。
use Benchmark qw(cmpthese); use Config::Tiny; my $parser = Parse::RecDescent->new($grammer); cmpthese(-10, { parser => sub { my $result = $parser->INIFILE($ini); }, tiny => sub { my $result = Config::Tiny->read_string($ini);}, });
結果は以下の通りです。
Rate parser tiny parser 338/s -- -97% tiny 13246/s 3814% --
Config::Tinyに比べずいぶん遅いことがわかりました。
Devel::NYTProfで全体を測ってみると, string evalしまくってることが
遅いみたいです。中を見ていないので詳しいことはわかりません。