ApacheLog-Parser
view release on metacpan or search on metacpan
lib/ApacheLog/Parser/Report.pm view on Meta::CPAN
my $self = shift;
my @conf = @{$self->{conf}};
my $s = $self->{store} = {};
my @preface;
my @codes;
foreach my $item (@conf) {
my $name = $item->{name};
$s->{$name} = {};
unless($item->{where}) {
#warn "$name is a stub\n";
next;
}
#warn "gen code for $name ($item->{title})\n";
my ($code, $pre) = $self->_code_for($item);
push(@codes, $code);
push(@preface, $pre) if($pre);
if(0) {
warn "#"x72, "\n";
warn "for $name\n$code", ($pre ? "\n\n$pre\n" : '');
}
}
$ENV{DBG} and warn join("\n", @preface), join("\n", @codes);
$self->_compile(join("\n", @preface), join("\n", @codes));
} # end subroutine get_func definition
########################################################################
sub _compile {
my $s = $_[0]->{store};
my $func = eval("$_[1];
use ApacheLog::Parser qw(:fields);
sub {
my \$v = shift;
my \$p;
my \@ans;
$_[2]
no ApacheLog::Parser;
}
");
$@ and croak("cannot compile $_[1]/\n$_[2]\n -- $@");
return($func);
}
sub _code_for {
my $self = shift;
my ($item) = @_;
my $name = $item->{name};
$ENV{DBG} and warn "building rules for $name\n";
# need to work-out the pre-reqs
my $preface;
my $callcode;
if(my $code = $item->{code}) {
$callcode = '$_' . $name . '_code';
$preface = join("\n",
'my ' . $callcode . ' = sub {',
$code,
'};'
);
}
# then the total number of captures?
# bind everything to ^$ ?
# switch some to eq?
my $has_matches = sub {
my ($string) = @_;
defined($string) or die "no string";
return($string =~ m/(?<!\\)\((?!\?)/ ? 1 : 0);
};
my $before;
my @code;
my @conds;
my $some_matches = 0;
foreach my $cond (@{$item->{where}}) {
my @subs;
foreach my $thing (sort(keys(%$cond))) {
my $re = $cond->{$thing};
if($thing eq 'params') {
$before =
'$p ||= {map({my @g = split(/=/, $_, 2); ($#g?@g:())}' .
' split(/&/, $v->[params]))};';
foreach my $p (split(/ & /, $re)) {
my ($name, $want) = split(/=/, $p, 2);
push(@subs, ["(\$p->{$name}||'')", $want]);
}
}
else {
# the \$v->[$thing] =~ m#$re# bit
push(@subs, ["\$v->[$thing]", $re]);
}
}
# and-together all of the subconditions
my $had_match = 0;
my @pref = ('(@ans = ', 'push(@ans, ');
my @built;
foreach my $subc (@subs) {
my $start;
if($has_matches->($subc->[1])) {
$start = $pref[$had_match];
$some_matches = 1;
$had_match = 1;
}
else {
$start = '(';
}
push(@built, $start . $subc->[0] . ' =~ m#^' . $subc->[1] . '$#)');
}
# single subcondition
push(@conds, $#built ? join(' and ',map({"($_)"} @built)) : @built);
}
#warn "$name ", $some_matches ? 'yes' : 'no', "\n\n";
# or-together all of the where's
my $code = ($before ? "$before\n" : '') .
'if(' . (
$#conds ?
"\n " . join(" or\n", map({" ($_)"} @conds)) . "\n" :
$conds[0]
) .
") {\n " .
# must clear-out the answer slot if there were never any match vars
($callcode ?
($some_matches ? '' : '@ans = ();') . $callcode . '->(@ans)' :
"(\$s->{$name}{" . ($some_matches ? '$ans[0]' : q('') ) .
( run in 3.397 seconds using v1.01-cache-2.11-cpan-2398b32b56e )