File Coverage

blib/lib/Language/P/Parser/Regex.pm
Criterion Covered Total %
statement 45 79 56.9
branch 19 46 41.3
condition 2 6 33.3
subroutine 7 9 77.7
pod 0 2 0.0
total 73 142 51.4


line stmt bran cond sub pod time code
1             package Language::P::Parser::Regex;
2              
3 90     90   4122 use strict;
  90         191  
  90         4174  
4 90     90   533 use warnings;
  90         195  
  90         3640  
5              
6 90     90   544 use base qw(Class::Accessor::Fast);
  90         192  
  90         10271  
7              
8 90     90   7516 use Language::P::Lexer qw(:all);
  90         216  
  90         58873  
9 90     90   623 use Language::P::ParseTree qw(:all);
  90         221  
  90         272166  
10              
11             __PACKAGE__->mk_ro_accessors( qw(lexer generator runtime
12             interpolate) );
13              
14             # will be used to parse embedded code blocks
15 0     0 0 0 sub parser { die; }
16              
17             sub parse_string {
18 16     16 0 45724 my( $self, $string ) = @_;
19              
20 16         178 $self->{lexer} = Language::P::Lexer->new( { string => $string } );
21 16         275 $self->_parse;
22             }
23              
24             sub _parse {
25 16     16   33 my( $self ) = @_;
26 16         30 my( @values );
27              
28 16         78 $self->lexer->quote( { interpolate => $self->interpolate,
29             pattern => 1,
30             interpolated_pattern => 0,
31             } );
32              
33 16         415 my( $in_group, $st ) = ( 0, \@values );
34 16         29 for(;;) {
35 80         339 my $value = $self->lexer->lex_quote;
36              
37 80 100 0     368 if( $value->[O_TYPE] == T_STRING ) {
    100          
    50          
    0          
38 28         259 push @$st, Language::P::ParseTree::Constant->new
39             ( { flags => CONST_STRING,
40             value => $value->[O_VALUE],
41             } );
42             } elsif( $value->[O_TYPE] == T_PATTERN ) {
43 36 100       359 if( $value->[O_VALUE] eq ')' ) {
    50          
    100          
    100          
    100          
    50          
    0          
    0          
44 7 50       20 die 'Unmatched ) in regex' unless $in_group;
45              
46 7         10 --$in_group;
47 7         19 $st = pop @values;
48             } elsif( $value->[O_VALUE] eq '(?' ) {
49 0         0 ++$in_group;
50 0         0 my $type = $self->lexer->lex_pattern_group;
51              
52 0 0       0 if( $type->[O_VALUE] eq ':' ) {
53 0         0 push @$st, Language::P::ParseTree::RXGroup->new
54             ( { components => [],
55             capture => 0,
56             } );
57             } else {
58             # remaining (?...) constructs
59 0         0 die "Unhandled (?" . $type->[O_VALUE] . ") in regex";
60             }
61              
62 0         0 my $nst = $st->[-1]->components;
63 0         0 push @values, $st;
64 0         0 $st = $nst;
65             } elsif( $value->[O_VALUE] eq '(' ) {
66 7         10 ++$in_group;
67 7         60 push @$st, Language::P::ParseTree::RXGroup->new
68             ( { components => [],
69             capture => 1,
70             } );
71 7         35 my $nst = $st->[-1]->components;
72 7         34 push @values, $st;
73 7         18 $st = $nst;
74             } elsif( $value->[O_VALUE] eq '|' ) {
75 4         37 my $alt = Language::P::ParseTree::RXAlternation->new
76             ( { left => [ @$st ],
77             right => [],
78             } );
79 4         15 @$st = $alt;
80 4         17 $st = $alt->right;
81             } elsif( $value->[O_RX_REST]->[0] == T_QUANTIFIER ) {
82 11 50       25 die 'Nothing to quantify in regex' unless @$st;
83              
84 11 50 66     55 if( $st->[-1]->is_constant
85             && length( $st->[-1]->value ) > 1 ) {
86 0         0 my $last = chop $st->[-1]->{value}; # XXX
87              
88 0         0 push @$st, Language::P::ParseTree::Constant->new
89             ( { flags => CONST_STRING,
90             value => $last,
91             } );
92             }
93              
94 11         165 $st->[-1] = Language::P::ParseTree::RXQuantifier->new
95             ( { node => $st->[-1],
96             min => $value->[O_RX_REST]->[1],
97             max => $value->[O_RX_REST]->[2],
98             greedy => $value->[O_RX_REST]->[3],
99             } );
100             } elsif( $value->[O_RX_REST]->[0] == T_ASSERTION ) {
101 7         80 push @$st, Language::P::ParseTree::RXAssertion->new
102             ( { type => $value->[O_RX_REST]->[1],
103             } );
104             } elsif( $value->[O_RX_REST]->[0] == T_CLASS ) {
105 0         0 push @$st, Language::P::ParseTree::RXSpecialClass->new
106             ( { type => $value->[O_RX_REST]->[1],
107             } );
108             } elsif( $value->[O_RX_REST]->[0] == T_CLASS_START ) {
109 0         0 push @$st, Language::P::ParseTree::RXClass->new
110             ( { elements => [],
111             } );
112              
113 0         0 _parse_charclass( $self, $st->[-1] );
114             } else {
115 0         0 Carp::confess $value->[O_TYPE], ' ', $value->[O_VALUE], ' ',
116             $value->[O_RX_REST]->[0];
117             }
118             } elsif( $value->[O_TYPE] == T_EOF ) {
119 16         39 last;
120             } elsif( $value->[O_TYPE] == T_DOLLAR || $value->[O_TYPE] == T_AT ) {
121 0         0 Carp::confess $value->[O_TYPE], ' ', $value->[O_VALUE];
122             }
123             }
124              
125 16 50       40 die 'Unmatched ( in regex' if $in_group;
126              
127 16         60 return \@values;
128             }
129              
130             sub _parse_charclass {
131 0     0     my( $self, $class ) = @_;
132 0           my $st = $class->elements;
133 0           my @la;
134              
135 0           for(;;) {
136 0 0         my $value = @la ? pop @la : $self->lexer->lex_charclass;
137 0 0         last if $value->[O_TYPE] == T_CLASS_END;
138 0 0         if( $value->[O_TYPE] == T_STRING ) {
    0          
139 0           my $next = $self->lexer->lex_charclass;
140              
141 0 0         if( $next->[O_TYPE] == T_MINUS ) {
142 0           my $next_next = $self->lexer->lex_charclass;
143 0 0         if( $next_next->[O_TYPE] == T_STRING ) {
144 0           push @$st, Language::P::ParseTree::RXRange->new
145             ( { start => $value->[O_VALUE],
146             end => $next_next->[O_VALUE],
147             } );
148 0           next;
149             } else {
150 0           push @la, $next_next, $next;
151             }
152             } else {
153 0           push @la, $next;
154             }
155             } elsif( $value->[O_TYPE] == T_CLASS ) {
156 0           push @$st, Language::P::ParseTree::RXSpecialClass->new
157             ( { type => $value->[O_VALUE],
158             } );
159 0           next;
160             }
161              
162 0           push @$st, $value->[O_VALUE];
163             }
164             }
165              
166             1;