PNG  IHDR;IDATxܻn0K )(pA 7LeG{ §㻢|ذaÆ 6lذaÆ 6lذaÆ 6lom$^yذag5bÆ 6lذaÆ 6lذa{ 6lذaÆ `}HFkm,mӪôô! x|'ܢ˟;E:9&ᶒ}{v]n&6 h_tڠ͵-ҫZ;Z$.Pkž)!o>}leQfJTu іچ\X=8Rن4`Vwl>nG^is"ms$ui?wbs[m6K4O.4%/bC%t Mז -lG6mrz2s%9s@-k9=)kB5\+͂Zsٲ Rn~GRC wIcIn7jJhۛNCS|j08yiHKֶۛkɈ+;SzL/F*\Ԕ#"5m2[S=gnaPeғL lذaÆ 6l^ḵaÆ 6lذaÆ 6lذa; _ذaÆ 6lذaÆ 6lذaÆ RIENDB` package Pegex::Parser; use Pegex::Base; no warnings qw( recursion ); use Pegex::Input; use Pegex::Optimizer; use Scalar::Util; has grammar => (required => 1); has receiver => (); has input => (); has recursion_count => 0; has iteration_count => 0; has debug => (); has debug_indent => (); has debug_color => (); has debug_got_color => (); has debug_not_color => (); has recursion_limit => (); has recursion_warn_limit => (); has iteration_limit => (); sub BUILD { my ($self) = @_; $self->{throw_on_error} ||= 1; $self->{debug} = defined($ENV{PERL_PEGEX_DEBUG}) ? $ENV{PERL_PEGEX_DEBUG} : defined($Pegex::Parser::Debug) ? $Pegex::Parser::Debug : 0 unless defined($self->{debug}); $self->{debug_indent} = defined($ENV{PERL_PEGEX_DEBUG_INDENT}) ? $ENV{PERL_PEGEX_DEBUG_INDENT} : defined($Pegex::Parser::DebugIndent) ? $Pegex::Parser::DebugIndent : 1 unless defined($self->{debug_indent}); $self->{debug_indent} = 1 if ( not length $self->{debug_indent} or $self->{debug_indent} =~ tr/0-9//c or $self->{debug_indent} < 0 ); if ($self->{debug}) { $self->{debug_color} = defined($ENV{PERL_PEGEX_DEBUG_COLOR}) ? $ENV{PERL_PEGEX_DEBUG_COLOR} : defined($Pegex::Parser::DebugColor) ? $Pegex::Parser::DebugColor : 1 unless defined($self->{debug_color}); my ($got, $not); ($self->{debug_color}, $got, $not) = split / *, */, $self->{debug_color}; $got ||= 'bright_green'; $not ||= 'bright_red'; $_ = [split ' ', $_] for ($got, $not); $self->{debug_got_color} = $got; $self->{debug_not_color} = $not; my $c = defined($self->{debug_color}) ? $self->{debug_color} : 1; $self->{debug_color} = $c eq 'always' ? 1 : $c eq 'auto' ? (-t STDERR ? 1 : 0) : $c eq 'never' ? 0 : $c =~ /^\d+$/ ? $c : 0; if ($self->{debug_color}) { require Term::ANSIColor; if ($Term::ANSIColor::VERSION < 3.00) { s/^bright_// for @{$self->{debug_got_color}}, @{$self->{debug_not_color}}; } } } $self->{recursion_limit} = defined($ENV{PERL_PEGEX_RECURSION_LIMIT}) ? $ENV{PERL_PEGEX_RECURSION_LIMIT} : defined($Pegex::Parser::RecursionLimit) ? $Pegex::Parser::RecursionLimit : 0 unless defined($self->{recursion_limit}); $self->{recursion_warn_limit} = defined($ENV{PERL_PEGEX_RECURSION_WARN_LIMIT}) ? $ENV{PERL_PEGEX_RECURSION_WARN_LIMIT} : defined($Pegex::Parser::RecursionWarnLimit) ? $Pegex::Parser::RecursionWarnLimit : 0 unless defined($self->{recursion_warn_limit}); $self->{iteration_limit} = defined($ENV{PERL_PEGEX_ITERATION_LIMIT}) ? $ENV{PERL_PEGEX_ITERATION_LIMIT} : defined($Pegex::Parser::IterationLimit) ? $Pegex::Parser::IterationLimit : 0 unless defined($self->{iteration_limit}); } # 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; $self->{last_line_pos} = 0; $self->{last_line} = 1; $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(); } local *match_next; { no warnings 'redefine'; *match_next = ( $self->{recursion_warn_limit} or $self->{recursion_limit} or $self->{iteration_limit} ) ? \&match_next_with_limit : \&match_next_normal; } 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_normal { 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] } } 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_next_with_limit { my ($self, $next) = @_; sub limit_msg { "Deep recursion ($_[0] levels) on Pegex::Parser::match_next\n"; } $self->{iteration_count}++; $self->{recursion_count}++; if ( $self->{recursion_limit} and $self->{recursion_count} >= $self->{recursion_limit} ) { die limit_msg $self->{recursion_count} } elsif ( $self->{recursion_warn_limit} and not ($self->{recursion_count} % $self->{recursion_warn_limit}) ) { warn limit_msg $self->{recursion_count} } elsif ( $self->{iteration_limit} and $self->{iteration_count} > $self->{iteration_limit} ) { die "Pegex iteration limit of $self->{iteration_limit} reached." } my $result = $self->match_next_normal($next); $self->{recursion_count}--; return $result; } sub match_rule { my ($self, $position, $match) = (@_, []); $self->{position} = $position; $self->{farthest} = $position if $position > $self->{farthest}; $match = [ $match ] if @$match > 1; my ($ref, $parent) = @{$self}{'rule', 'parent'}; my $rule = $self->{grammar}{tree}{$ref} or die "No rule defined for '$ref'"; [ $rule->{action}->($self->{receiver}, @$match) ]; } sub match_ref { my ($self, $ref, $parent) = @_; my $rule = $self->{grammar}{tree}{$ref} or die "No rule defined for '$ref'"; my $match = $self->match_next($rule) or return; return $Pegex::Constant::Dummy unless $rule->{action}; @{$self}{'rule', 'parent'} = ($ref, $parent); # XXX Possible API mismatch. # Not sure if we should "splat" the $match. [ $rule->{action}->($self->{receiver}, @$match) ]; } sub match_rgx { my ($self, $regexp) = @_; my $buffer = $self->{buffer}; pos($$buffer) = $self->{position}; $$buffer =~ /$regexp/g or return; $self->{position} = pos($$buffer); $self->{farthest} = $self->{position} if $self->{position} > $self->{farthest}; no strict 'refs'; my $captures = [ map $$_, 1..$#+ ]; $captures = [ $captures ] if $#+ > 1; return $captures; } sub match_all { my ($self, $list) = @_; my $position = $self->{position}; my $set = []; my $len = 0; for my $elem (@$list) { if (my $match = $self->match_next($elem)) { if (not ($elem->{'+asr'} or $elem->{'-skip'})) { push @$set, @$match; $len++; } } else { $self->{farthest} = $position if ($self->{position} = $position) > $self->{farthest}; return; } } $set = [ $set ] if $len > 1; return $set; } sub match_any { my ($self, $list) = @_; for my $elem (@$list) { if (my $match = $self->match_next($elem)) { return $match; } } return; } sub match_err { my ($self, $error) = @_; $self->throw_error($error); } sub trace { my ($self, $action) = @_; my $indent = ($action =~ /^try_/) ? 1 : 0; $self->{indent} ||= 0; $self->{indent}-- unless $indent; $action = ( $action =~ m/got_/ ? Term::ANSIColor::colored($self->{debug_got_color}, $action) : $action =~ m/not_/ ? Term::ANSIColor::colored($self->{debug_not_color}, $action) : $action ) if $self->{debug_color}; print STDERR ' ' x ($self->{indent} * $self->{debug_indent}); $self->{indent}++ if $indent; my $snippet = substr(${$self->{buffer}}, $self->{position}); $snippet = substr($snippet, 0, 30) . "..." if length $snippet > 30; $snippet =~ s/\n/\\n/g; print STDERR sprintf("%-30s", $action) . ($indent ? " >$snippet<\n" : "\n"); } sub throw_error { my ($self, $msg) = @_; $@ = $self->{error} = $self->format_error($msg); return undef unless $self->{throw_on_error}; require Carp; Carp::croak($self->{error}); } sub format_error { my ($self, $msg) = @_; my $buffer = $self->{buffer}; my $position = $self->{farthest}; my $real_pos = $self->{position}; my $line = $self->line($position); my $column = $position - rindex($$buffer, "\n", $position); my $pretext = substr( $$buffer, $position < 50 ? 0 : $position - 50, $position < 50 ? $position : 50 ); my $context = substr($$buffer, $position, 50); $pretext =~ s/.*\n//gs; $context =~ s/\n/\\n/g; return <<"..."; Error parsing Pegex document: msg: $msg line: $line column: $column context: $pretext$context ${\ (' ' x (length($pretext) + 10) . '^')} position: $position ($real_pos pre-lookahead) ... } # TODO Move this to a Parser helper role/subclass sub line_column { my ($self, $position) = @_; $position ||= $self->{position}; my $buffer = $self->{buffer}; my $line = $self->line($position); my $column = $position - rindex($$buffer, "\n", $position); return [$line, $column]; } sub line { my ($self, $position) = @_; $position ||= $self->{position}; my $buffer = $self->{buffer}; my $last_line = $self->{last_line}; my $last_line_pos = $self->{last_line_pos}; my $len = $position - $last_line_pos; if ($len == 0) { return $last_line; } my $line; if ($len < 0) { $line = $last_line - scalar substr($$buffer, $position, -$len) =~ tr/\n//; } else { $line = $last_line + scalar substr($$buffer, $last_line_pos, $len) =~ tr/\n//; } $self->{last_line} = $line; $self->{last_line_pos} = $position; return $line; } # XXX Need to figure out what uses this. (sample.t) { package Pegex::Constant; our $Null = []; our $Dummy = []; } 1;