Bio-Regexp
view release on metacpan or search on metacpan
lib/Bio/Regexp.pm view on Meta::CPAN
sub linear { _arg($_[0], 'circular', 0) }
sub single_stranded { _arg($_[0], 'strands', 1) }
sub double_stranded { _arg($_[0], 'strands', 2) }
sub strict_thymine_uracil { _arg($_[0], 'strict_thymine_uracil', 1) }
sub strict_case { _arg($_[0], 'strict_case', 1) }
sub no_substr { _arg($_[0], 'no_substr', 1) }
sub _arg_defaults {
my ($self) = @_;
$self->{type} //= 'dna';
if ($self->{type} eq 'dna') {
$self->{arg}->{strands} //= 2;
} elsif ($self->{type} eq 'rna') {
$self->{arg}->{strands} //= 1;
} elsif ($self->{type} eq 'protein') {
die "protein search not implemented";
}
}
sub new {
my ($class, @args) = @_;
my $self = {};
bless $self, $class;
return $self;
}
sub add {
my ($self, $regexp) = @_;
die "Can't add new regexp because regexp has already been compiled" if $self->{compiled_regexp};
push @{ $self->{regexps} }, $regexp;
return $self;
}
sub compile {
my ($self) = @_;
return if $self->{compiled_regexp};
$self->_arg_defaults;
my $regexp_index = 0;
my @regexp_fragments;
foreach my $regexp (@{ $self->{regexps} }) {
## Parse
my $ast = Bio::Regexp::AST->new($regexp, $self->{type}, $self->{arg});
## Compute meta data
my ($min, $max) = $ast->compute_min_max;
$self->{min} = $min if !defined $self->{min} || $min < $self->{min};
$self->{max} = $max if !defined $self->{max} || $max > $self->{max};
## Main "sense" strand
my $rendered = $ast->render;
push @regexp_fragments, "$rendered(?{ $regexp_index })";
$regexp_index++;
my $component = { regexp => $regexp, };
$component->{strand} = 1 if $self->{arg}->{strands} == 2;
push @{ $self->{components} }, $component;
## Reverse complement strand
if ($self->{arg}->{strands} == 2) {
$ast->reverse_complement;
$rendered = $ast->render;
push @regexp_fragments, "$rendered(?{ $regexp_index })";
$regexp_index++;
my $component = { regexp => $regexp, strand => 2, };
push @{ $self->{components} }, $component;
}
}
my $compiled_regexp = ($self->{arg}->{strict_case} ? '' : '(?i)') .
'(' .
($self->{arg}->{no_substr} ? '?:' : '') .
join('|', @regexp_fragments) .
')';
{
use re 'eval';
$self->{compiled_regexp} = qr{$compiled_regexp};
}
return $self;
}
sub match {
alias my ($self, $input, $callback) = @_;
$self->compile;
my @output;
my @matches = Regexp::Exhaustive::exhaustive($input => $self->{compiled_regexp},
qw[ $1 @- @+ $^R ]);
foreach my $match (@matches) {
my $element = {
match => $match->[0],
start => $match->[1]->[0],
end => $match->[2]->[0],
%{ $self->{components}->[$match->[3]] },
};
push @output, $element;
}
## Check circular overlap
if ($self->{arg}->{circular}) {
my $start = length($input) - $self->{max} + 1;
$start = 1 if $start < 1;
my $end = $self->{max} - 1;
$end = 0 if $end < 0;
my $input_overlap = substr($input, $start) . substr($input, 0, $end);
my @matches_overlap = Regexp::Exhaustive::exhaustive($input_overlap => $self->{compiled_regexp},
qw[ $1 @- @+ $^R ]);
foreach my $match (@matches_overlap) {
my $element = {
match => $match->[0],
start => $match->[1]->[0],
end => $match->[2]->[0],
%{ $self->{components}->[$match->[3]] },
};
$element->{start} += $start;
$element->{end} += $start;
push @output, $element;
( run in 1.892 second using v1.01-cache-2.11-cpan-b16cb0d3907 )