package Pegex::Optimizer; use Pegex::Base; has parser => (required => 1); has grammar => (required => 1); has receiver => (required => 1); sub optimize_grammar { my ($self, $start) = @_; my $tree = $self->grammar->{tree}; return if $tree->{'+optimized'}; $self->set_max_parse if $self->parser->{maxparse}; $self->{extra} = {}; while (my ($name, $node) = each %$tree) { next unless ref($node); $self->optimize_node($node); } $self->optimize_node({'.ref' => $start}); my $extra = delete $self->{extra}; for my $key (%$extra) { $tree->{$key} = $extra->{$key}; } $tree->{'+optimized'} = 1; } sub optimize_node { my ($self, $node) = @_; my ($min, $max) = @{$node}{'+min', '+max'}; $node->{'+min'} = defined($max) ? 0 : 1 unless defined $node->{'+min'}; $node->{'+max'} = defined($min) ? 0 : 1 unless defined $node->{'+max'}; $node->{'+asr'} = 0 unless defined $node->{'+asr'}; for my $kind (qw(ref rgx all any err code xxx)) { return if $kind eq 'xxx'; if ($node->{rule} = $node->{".$kind"}) { delete $node->{".$kind"}; $node->{kind} = $kind; if ($kind eq 'ref') { my $rule = $node->{rule} or die; if (my $method = $self->grammar->can("rule_$rule")) { $node->{method} = $self->make_method_wrapper($method); } elsif (not $self->grammar->{tree}{$rule}) { if (my $method = $self->grammar->can("$rule")) { warn <<"..."; Warning: You have a method called '$rule' in your grammar. It should probably be called 'rule_$rule'. ... } die "No rule '$rule' defined in grammar"; } } $node->{method} ||= $self->parser->can("match_$kind") or die; last; } } if ($node->{kind} =~ /^(?:all|any)$/) { $self->optimize_node($_) for @{$node->{rule}}; } elsif ($node->{kind} eq 'ref') { my $ref = $node->{rule}; my $rule = $self->grammar->{tree}{$ref}; $rule ||= $self->{extra}{$ref} = {}; if (my $action = $self->receiver->can("got_$ref")) { $rule->{action} = $action; } elsif (my $gotrule = $self->receiver->can("gotrule")) { $rule->{action} = $gotrule; } if ($self->parser->{debug}) { $node->{method} = $self->make_trace_wrapper($node->{method}); } } elsif ($node->{kind} eq 'rgx') { # XXX $node; } } sub make_method_wrapper { my ($self, $method) = @_; return sub { my ($parser, $ref, $parent) = @_; @{$parser}{'rule', 'parent'} = ($ref, $parent); $method->( $parser->{grammar}, $parser, $parser->{buffer}, $parser->{position}, ); } } sub make_trace_wrapper { my ($self, $method) = @_; return sub { my ($self, $ref, $parent) = @_; my $asr = $parent->{'+asr'}; my $note = $asr == -1 ? '(!)' : $asr == 1 ? '(=)' : ''; $self->trace("try_$ref$note"); my $result; if ($result = $self->$method($ref, $parent)) { $self->trace("got_$ref$note"); } else { $self->trace("not_$ref$note"); } return $result; } } sub set_max_parse { require Pegex::Parser; my ($self) = @_; my $maxparse = $self->parser->{maxparse}; no warnings 'redefine'; my $method = \&Pegex::Parser::match_ref; my $counter = 0; *Pegex::Parser::match_ref = sub { die "Maximum parsing rules reached ($maxparse)\n" if $counter++ >= $maxparse; my $self = shift; $self->$method(@_); }; } 1;