ソースフィルターの用途は大きくわけて2種類
前者は、普通に文字列置換なりでやればいいのだが、後者は意外とむずかしい。
Perl のコードをパースするのは非常にむずかしいのだ。
そこで、Devel::Declare という仕組みが考案されて、キーワードなどに反応して、そこからしばらくの文字列をソースフィルターで置換する仕組みができたわけです。
しかし、Devel::Declare はちょっとゴツいし、なかなかつかいこなすのがむずかしい。
そんなわけで、Devel::Declare の機能は分割され、いくつかの B::* モジュールとして提供されています。
B::Hooks::Parser と B::OPCheck をつかうと、以下のようにして簡単に Devel::Declare 相当のことができます。
以下は、package 宣言をみつけて、package 宣言の直後に use Moo を追加するというコードです。
package Moo::Auto;
use strict;
use warnings;
use utf8;
use 5.008005;
our $VERSION = '0.01';
use B::Hooks::Parser;
sub import {
my $class = shift;
my $pkg = caller(0);
B::Hooks::Parser::setup();
my $linestr = B::Hooks::Parser::get_linestr();
my $offset = B::Hooks::Parser::get_linestr_offset();
substr($linestr, $offset, 0) = 'use B::OPCheck const => check => \&Moo::Auto::_check;';
B::Hooks::Parser::set_linestr($linestr);
}
sub _check {
my $op = shift;
return unless ref($op->gv) eq 'B::PV';
my $linestr = B::Hooks::Parser::get_linestr;
my $offset = B::Hooks::Parser::get_linestr_offset;
if (substr($linestr, $offset, 7) eq 'package') {
my $line = substr($linestr, $offset);
if ($line =~ /\A(package\s+(?:[A-Za-z0-9_:-]+)\s*[\{;])/) {
substr($linestr, $offset+length($line)-1, 0) = ';use Moo;';
B::Hooks::Parser::set_linestr($linestr);
}
}
}
1;
コードの内容自体は非常に単純でわかりやすいかとおもいます。