日向夏特殊応援部隊

俺様向けメモ

Parse::Yapp ヨチヨチ歩き

Perlyacc風パーサーである Parse::Yapp でちょっと遊んでみてます。

とにかく書き方に関する解説とか全然オンラインでヒットしないので、少し分かってきたので、個人的にまとめてみます。門外漢なので突っ込み歓迎。

お題

IPv4なアドレスをパースします。
ってもこれなら正規表現だけで瞬殺なんだけど、ヨチヨチ歩き版なので許して下さい。

ypファイル

%{
use strict;
use warnings;

use Data::Dump qw(dump);
%}
%%
ipv4
    : dec_octet '.' dec_octet '.' dec_octet '.' dec_octet
    {
        my ($self, @patterns) = @_;
        return [
            grep { $_ ne '.' } @patterns
        ];
    }
    ;
dec_octet
    : DIGIT
    | DIGIT DIGIT { return $_[1] . $_[2]; }
    | '1' DIGIT DIGIT { return $_[1] . $_[2] . $_[3]; }
    | '20' DIGIT { return $_[1] . $_[2]; }
    | '21' DIGIT { return $_[1] . $_[2]; }
    | '22' DIGIT { return $_[1] . $_[2]; }
    | '23' DIGIT { return $_[1] . $_[2]; }
    | '24' DIGIT { return $_[1] . $_[2]; }
    | '250'
    | '251'
    | '252'
    | '253'
    | '254'
    | '255'
    ;
%%

sub yyerror {}
sub yylex {
    my ($self) = @_;

    $self->YYData->{INPUT} || return ('', undef);

    for ($self->YYData->{INPUT}) {
        printf("[debug] %s\n", $_);
        s/^(25[0-5]|2[0-4]\d)// and return ($1, $1);
        s/^(1)(\d{2})/$2/ and return ($1, $1);
        s|^(\d)|| and return ('DIGIT', $1); # DIGIT
        s|^(\.)|| and return ($1, $1);
    }
}

sub run {
    my $self = shift;
    return $self->YYParse( yylex => \&yylex, yyerror => \&yyerror );
}

紆余曲折経てこんな風になりました。

$ yapp -m IPv4Parser IPv4Parser.yp

とかやるとIPv4Parser.pmが生成されます。

使い方

どうもお作法なのか、YYData->{INPUT}ってのに元の文字列を入れるみたいです。
そんな訳でこういう感じで使う。

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dump qw(dump);
use IPv4Parser;

my $parser = IPv4Parser->new;
$parser->YYData->{INPUT} = "192.168.0.1";
my $result = $parser->run;

print dump($result); # [qw/192 168 0 1/]

run()とかは好みで別の名前付けても良いみたい。

レクサの話

トークンとして切り出して次のトークンを取り出す際に、優先すべきはルールがバッチリ満たされるような物なのかなーと。

つまり、dec_octedだと250-255を直で指定してるんで、DIGITより優先的にトークン化しないと、全部DIGIT扱いになっちゃう〜みたいに考えれば良さそう。

まとめ

もっと勉強する。あと正規表現ももっと勉強する(ぉぃ
またこのネタならid:dankogaiに添削して貰えるかもしれないと思った。*1

*1:正規表現または、Yapp使わないパーサーで添削される気がする