Alt-IO-All-new
view release on metacpan or search on metacpan
inc/Pegex/Parser.pm view on Meta::CPAN
package Pegex::Parser;
use Pegex::Base;
use Pegex::Input;
use Pegex::Optimizer;
use Scalar::Util;
has grammar => (required => 1);
has receiver => ();
has input => ();
has debug => (
exists($ENV{PERL_PEGEX_DEBUG}) ? $ENV{PERL_PEGEX_DEBUG} :
defined($Pegex::Parser::Debug) ? $Pegex::Parser::Debug :
0
);
sub BUILD {
my ($self) = @_;
$self->{throw_on_error} ||= 1;
# $self->{rule} = undef;
# $self->{parent} = undef;
# $self->{error} = undef;
# $self->{position} = undef;
# $self->{farthest} = undef;
}
# XXX Add an optional $position argument. Default to 0. This is the position
# to start parsing. Set position and farthest below to this value. Allows for
# sub-parsing. Need to somehow return the finishing position of a subparse.
# Maybe this all goes in a subparse() method.
sub parse {
my ($self, $input, $start) = @_;
$start =~ s/-/_/g if $start;
$self->{position} = 0;
$self->{farthest} = 0;
$self->{input} = (not ref $input)
? Pegex::Input->new(string => $input)
: $input;
$self->{input}->open
unless $self->{input}{_is_open};
$self->{buffer} = $self->{input}->read;
die "No 'grammar'. Can't parse"
unless $self->{grammar};
$self->{grammar}{tree} ||= $self->{grammar}->make_tree;
my $start_rule_ref = $start ||
$self->{grammar}{tree}{'+toprule'} ||
$self->{grammar}{tree}{'TOP'} & 'TOP' or
die "No starting rule for Pegex::Parser::parse";
die "No 'receiver'. Can't parse"
unless $self->{receiver};
my $optimizer = Pegex::Optimizer->new(
parser => $self,
grammar => $self->{grammar},
receiver => $self->{receiver},
);
$optimizer->optimize_grammar($start_rule_ref);
# Add circular ref and weaken it.
$self->{receiver}{parser} = $self;
Scalar::Util::weaken($self->{receiver}{parser});
if ($self->{receiver}->can("initial")) {
$self->{rule} = $start_rule_ref;
$self->{parent} = {};
$self->{receiver}->initial();
}
my $match = $self->debug ? do {
my $method = $optimizer->make_trace_wrapper(\&match_ref);
$self->$method($start_rule_ref, {'+asr' => 0});
} : $self->match_ref($start_rule_ref, {});
$self->{input}->close;
if (not $match or $self->{position} < length ${$self->{buffer}}) {
$self->throw_error("Parse document failed for some reason");
return; # In case $self->throw_on_error is off
}
if ($self->{receiver}->can("final")) {
$self->{rule} = $start_rule_ref;
$self->{parent} = {};
$match = [ $self->{receiver}->final(@$match) ];
}
$match->[0];
}
sub match_next {
my ($self, $next) = @_;
my ($rule, $method, $kind, $min, $max, $assertion) =
@{$next}{'rule', 'method', 'kind', '+min', '+max', '+asr'};
my ($position, $match, $count) =
($self->{position}, [], 0);
while (my $return = $method->($self, $rule, $next)) {
$position = $self->{position} unless $assertion;
$count++;
push @$match, @$return;
last if $max == 1;
}
if (not $count and $min == 0 and $kind eq 'all') {
$match = [[]];
}
if ($max != 1) {
if ($next->{-flat}) {
$match = [ map { (ref($_) eq 'ARRAY') ? (@$_) : ($_) } @$match ];
}
else {
$match = [$match]
}
$self->{farthest} = $position
if ($self->{position} = $position) > $self->{farthest};
}
my $result = ($count >= $min and (not $max or $count <= $max))
^ ($assertion == -1);
if (not($result) or $assertion) {
$self->{farthest} = $position
if ($self->{position} = $position) > $self->{farthest};
}
($result ? $next->{'-skip'} ? [] : $match : 0);
}
sub match_rule {
my ($self, $position, $match) = (@_, []);
( run in 0.945 second using v1.01-cache-2.11-cpan-39bf76dae61 )