File Coverage

blib/lib/Locale/Babelfish/Phrase/Parser.pm
Criterion Covered Total %
statement 117 124 94.3
branch 45 52 86.5
condition 15 18 83.3
subroutine 13 13 100.0
pod 4 4 100.0
total 194 211 91.9


line stmt bran cond sub pod time code
1             package Locale::Babelfish::Phrase::Parser;
2              
3             # ABSTRACT: Babelfish syntax parser.
4              
5 7     7   470327 use utf8;
  7         420  
  7         82  
6 7     7   318 use strict;
  7         14  
  7         197  
7 7     7   36 use warnings;
  7         13  
  7         473  
8              
9 7     7   3735 use Locale::Babelfish::Phrase::Literal ();
  7         24  
  7         225  
10 7     7   3999 use Locale::Babelfish::Phrase::Variable ();
  7         22  
  7         235  
11 7     7   3894 use Locale::Babelfish::Phrase::PluralForms ();
  7         26  
  7         256  
12 7     7   3142 use Locale::Babelfish::Phrase::PluralFormsParser ();
  7         23  
  7         250  
13              
14 7     7   45 use parent qw( Locale::Babelfish::Phrase::ParserBase );
  7         13  
  7         64  
15              
16             our $VERSION = '2.13'; # VERSION
17              
18             __PACKAGE__->mk_accessors( qw( locale mode pieces escape pf0 ) );
19              
20             use constant {
21 7         9127 LITERAL_MODE => 'Locale::Babelfish::Phrase::Literal',
22             VARIABLE_MODE => 'Locale::Babelfish::Phrase::Variable',
23             PLURALS_MODE => 'Locale::Babelfish::Phrase::PluralForms',
24             VARIABLE_RE => qr/^[a-zA-Z0-9_\.]+$/,
25 7     7   1079 };
  7         15  
26              
27              
28             sub new {
29 7     7 1 284277 my ( $class, $phrase, $locale ) = @_;
30 7         56 my $self = $class->SUPER::new( $phrase );
31 7 50       26 $self->locale( $locale ) if $locale;
32 7         19 return $self;
33             }
34              
35              
36             sub init {
37 291     291 1 655 my ( $self, $phrase ) = @_;
38 291         1060 $self->SUPER::init( $phrase );
39 291         9361 $self->mode( LITERAL_MODE );
40 291         7793 $self->pieces( [] );
41 291         7941 $self->pf0( undef ); # plural forms without name yet
42 291         2080 return $self;
43             }
44              
45              
46             sub finalize_mode {
47 289     289 1 747 my ( $self ) = @_;
48 289 100       6360 if ( $self->mode eq LITERAL_MODE ) {
    50          
    50          
49 237         6039 push @{ $self->pieces }, LITERAL_MODE->new( text => $self->piece )
50 282 100 100     7011 if length($self->piece) || scalar(@{ $self->pieces }) == 0;
  46         1206  
51             }
52             elsif ( $self->mode eq VARIABLE_MODE ) {
53 0         0 $self->throw( "Variable definition not ended with \"}\": ". $self->piece );
54             }
55             elsif ( $self->mode eq PLURALS_MODE ) {
56 7 50       495 $self->throw( "Plural forms definition not ended with \"))\": ". $self->piece )
57             unless defined $self->pf0;
58 7         40 push @{ $self->pieces }, PLURALS_MODE->new( forms => $self->pf0, name => $self->piece, locale => $self->locale, );
  7         143  
59             }
60             else {
61 0         0 $self->throw( "Logic broken, unknown parser mode: ". $self->mode );
62             }
63             }
64              
65              
66             sub parse {
67 292     292 1 77771 my ( $self, $phrase, $locale ) = @_;
68              
69 292         1271 $self->SUPER::parse( $phrase );
70 291 100       5802 $self->locale( $locale ) if $locale;
71              
72 291         2254 my $plurals_parser = Locale::Babelfish::Phrase::PluralFormsParser->new();
73              
74 291         570 while ( 1 ) {
75 3570         39191 my $char = $self->to_next_char;
76              
77 3570 100       110004 unless ( length $char ) {
78 289         1016 $self->finalize_mode;
79 289         7043 return $self->pieces;
80             }
81              
82 3281 100       76378 if ( $self->mode eq LITERAL_MODE ) {
83 2014 100       52248 if ( $self->escape ) {
84 10         68 $self->add_to_piece( $char );
85 10         276 $self->escape(0);
86 10         87 next;
87             }
88              
89 2004 100       11953 if ( $char eq "\\" ) {
90 10         202 $self->escape( 1 );
91 10         66 next;
92             }
93              
94 1994 100 66     5591 if ( $char eq '#' && $self->next_char eq '{' ) {
95 70 100       3857 if ( length $self->piece ) {
96 34         285 push @{ $self->pieces }, LITERAL_MODE->new( text => $self->piece );
  34         716  
97 34         755 $self->piece('');
98             }
99 70         714 $self->to_next_char; # skip "{"
100 70         3558 $self->mode( VARIABLE_MODE );
101 70         573 next;
102             }
103              
104 1924 100 100     5253 if ( $char eq '(' && $self->next_char eq '(' ) {
105 29 50       1471 if ( length $self->piece ) {
106 29         185 push @{ $self->pieces }, LITERAL_MODE->new( text => $self->piece );
  29         650  
107 29         651 $self->piece('');
108             }
109 29         233 $self->to_next_char; # skip second "("
110 29         3853 $self->mode( PLURALS_MODE );
111 29         214 next;
112             }
113             }
114              
115 3162 100       73442 if ( $self->mode eq VARIABLE_MODE ) {
116 569 50       18023 if ( $self->escape ) {
117 0         0 $self->add_to_piece( $char );
118 0         0 $self->escape(0);
119 0         0 next;
120             }
121              
122 569 50       3559 if ( $char eq "\\" ) {
123 0         0 $self->escape( 1 );
124 0         0 next;
125             }
126              
127 569 100       1455 if ( $char eq '}' ) {
128 70         1595 my $name = $self->trim( $self->piece );
129 70 100       292 unless ( length $name ) {
130 1         7 $self->throw( "No variable name given." );
131             }
132 69 100       389 if ( $name !~ VARIABLE_RE ) {
133 1         8 $self->throw( "Variable name doesn't meet conditions: $name." );
134             }
135 68         127 push @{ $self->pieces }, VARIABLE_MODE->new( name => $name );
  68         1709  
136 68         1708 $self->piece('');
137 68         2042 $self->mode( LITERAL_MODE );
138 68         527 next;
139             }
140             }
141              
142 3092 100       73369 if ( $self->mode eq PLURALS_MODE ) {
143 698 100       17580 if ( defined $self->pf0 ) {
144 120 100 66     1374 if ( $char =~ VARIABLE_RE && ($char ne '.' || $self->next_char =~ VARIABLE_RE) ) {
      100        
145 102         377 $self->add_to_piece( $char );
146 102         1135 next;
147             }
148             else {
149 18         76 push @{ $self->pieces }, PLURALS_MODE->new( forms => $self->pf0, name => $self->piece, locale => $self->locale, );
  18         379  
150 18         441 $self->pf0( undef );
151 18         462 $self->mode( LITERAL_MODE );
152 18         452 $self->piece('');
153 18         176 $self->backward;
154 18         979 next;
155             }
156             }
157 578 100 66     3617 if ( $char eq ')' && $self->next_char eq ')' ) {
158 29         1454 $self->pf0( $plurals_parser->parse( $self->piece ) );
159 29         2275 $self->piece('');
160 29         225 $self->to_next_char; # skip second ")"
161 29 100       846 if ( $self->next_char eq ':' ) {
162 25         913 $self->to_next_char; # skip ":"
163 25         728 next;
164             }
165 4         158 push @{ $self->pieces }, PLURALS_MODE->new( forms => $self->pf0, name => 'count', locale => $self->locale, );
  4         89  
166 4         97 $self->pf0( undef );
167 4         105 $self->mode( LITERAL_MODE );
168 4         29 next;
169             }
170             }
171 2943         18011 $self->add_to_piece( $char );
172             } # while ( 1 )
173             }
174              
175             1;
176              
177             __END__
178              
179             =pod
180              
181             =encoding UTF-8
182              
183             =head1 NAME
184              
185             Locale::Babelfish::Phrase::Parser - Babelfish syntax parser.
186              
187             =head1 VERSION
188              
189             version 2.13
190              
191             =head1 METHODS
192              
193             =head2 new
194              
195             $class->new()
196             $class->new( $phrase )
197              
198             Instantiates parser.
199              
200             =head2 init
201              
202             Initializes parser. Should not be called directly.
203              
204             =head2 finalize_mode
205              
206             Finalizes all operations after phrase end.
207              
208             =head2 parse
209              
210             $parser->parse()
211             $parser->parse( $phrase )
212              
213             Parses specified phrase.
214              
215             =head1 AUTHORS
216              
217             =over 4
218              
219             =item *
220              
221             Akzhan Abdulin <akzhan@cpan.org>
222              
223             =item *
224              
225             Igor Mironov <grif@cpan.org>
226              
227             =item *
228              
229             Victor Efimov <efimov@reg.ru>
230              
231             =item *
232              
233             REG.RU LLC
234              
235             =item *
236              
237             Kirill Sysoev <k.sysoev@me.com>
238              
239             =item *
240              
241             Alexandr Tkach <tkach@reg.ru>
242              
243             =back
244              
245             =head1 COPYRIGHT AND LICENSE
246              
247             This software is Copyright (c) 2014 by REG.RU LLC.
248              
249             This is free software, licensed under:
250              
251             The MIT (X11) License
252              
253             =cut