File Coverage

blib/lib/Babble/Grammar.pm
Criterion Covered Total %
statement 66 66 100.0
branch 11 14 78.5
condition 2 3 66.6
subroutine 17 17 100.0
pod 0 6 0.0
total 96 106 90.5


line stmt bran cond sub pod time code
1             package Babble::Grammar;
2              
3 12     12   12262 use PPR::X;
  12         550816  
  12         465  
4 12     12   6060 use Mu;
  12         70290  
  12         85  
5 12     12   13230 use strictures 2;
  12         60  
  12         430  
6              
7 70     70   912 lazy base_grammar_regexp => sub { $PPR::X::GRAMMAR };
8              
9             lazy base_rule_names => sub {
10 70     70   1700 my $g = $_[0]->base_grammar_regexp;
11 70         30802 +{ map +($_ => 1), $g =~ /\(\?<PerlStd(\w+)>/g };
12             };
13              
14             lazy rules => sub {
15 70     70   779 +{ map +($_ => [ undef ]), keys %{ $_[0]->base_rule_names } }
  70         1236  
16             };
17              
18             # global cache of compiled grammar regexps
19             my %COMPILE_CACHE;
20             lazy grammar_regexp => sub {
21 373     373   10121 my ($self) = @_;
22 373         713 my @parts;
23 373         764 foreach my $name (sort keys %{$self->rules}) {
  373         5918  
24 35449         69590 my @layers = @{$self->rules->{$name}};
  35449         519481  
25 35449         268574 foreach my $idx (0..$#layers) {
26 36083 100       89441 next unless defined(my $rule = $layers[$idx]);
27 1031         2588 my $layer_name = $self->_rule_name($name, $idx);
28 1031         3174 my $define = '(?<'.$layer_name.'>'.$rule.')';
29 1031 100       3273 $define = '(?<Perl'.$name.'>'.$define.')' if $idx == $#layers;
30 1031         3302 unshift @parts, $define;
31             }
32             }
33 373         9562 my $base_re = $self->base_grammar_regexp;
34 373 100       337758 return $base_re unless @parts;
35 364         2004 my $define_block = join "\n", '(?(DEFINE)', '', @parts, '', ')';
36             # This stringify is required for Perl v5.18 - v5.28
37             # (RT #126285, RT #144248).
38 364         16794 my $final_re = "${define_block} ${base_re}";
39 364   66     95394 return $COMPILE_CACHE{$final_re} ||= do {
40 12     12   6240 use re 'eval';
  12         29  
  12         1044  
41 49         1965366 my $re = qr{$final_re}x;
42 12     12   80 no re 'eval';
  12         35  
  12         7286  
43 49         7630 $re;
44             }
45             };
46              
47             sub _rule_name {
48 1557     1557   3675 my ($self, $name, $index) = @_;
49 1557 100       4468 return 'PerlStd'.$name unless $index;
50 638         3879 return 'PerlWrapper'.$name.'_'.sprintf("%03i", $index);
51             }
52              
53             sub add_rule {
54 258     258 0 2766 my ($self, $name, $rule) = @_;
55 258 50       4747 die "Rule ${name} already exists" if exists $self->rules->{$name};
56 258         7194 $self->rules->{$name} = [ $rule ];
57 258         2961 return $self;
58             }
59              
60             sub replace_rule {
61 10     10 0 41 my ($self, $name, $rule) = @_;
62 10 50       156 die "Rule ${name} does not exist" unless exists $self->rules->{$name};
63 10         256 $self->rules->{$name} = [ $rule ];
64 10         105 return $self;
65             }
66              
67             sub extend_rule {
68 526     526 0 31449 my ($self, $name, $cb) = @_;
69 526 50       8937 die "Rule ${name} does not exist" unless my $r = $self->rules->{$name};
70 526         5028 my $inner_name = $self->_rule_name($name, $#{$r});
  526         1908  
71 526         2245 $self->rules->{$name} = [ @$r, $cb->('(?&'.$inner_name.')') ];
72 526         8671 return $self;
73             }
74              
75             sub augment_rule {
76 245     245 0 698 my ($self, $name, $extra) = @_;
77 245     245   1736 $self->extend_rule($name, sub { join '|', $extra, $_[0] });
  245         4793  
78 245         1020 return $self;
79             }
80              
81             sub clone {
82 534     534 0 18178 my ($self) = @_;
83             return ref($self)->new(
84             base_grammar_regexp => $self->base_grammar_regexp,
85 534         9835 rules => { %{$self->rules} },
  534         11976  
86             );
87             }
88              
89             sub match {
90 7     7 0 6351 my ($self, $as, $text) = @_;
91 7         57 require Babble::Match;
92 7         154 Babble::Match->new(
93             top_rule => $as,
94             text => $text,
95             grammar => $self
96             );
97             }
98              
99             1;