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 3     3   153291 use utf8;
  3         19  
  3         20  
6 3     3   96 use strict;
  3         12  
  3         58  
7 3     3   20 use warnings;
  3         6  
  3         70  
8              
9 3     3   1307 use Locale::Babelfish::Phrase::Literal ();
  3         9  
  3         69  
10 3     3   1297 use Locale::Babelfish::Phrase::Variable ();
  3         10  
  3         70  
11 3     3   1325 use Locale::Babelfish::Phrase::PluralForms ();
  3         8  
  3         72  
12 3     3   930 use Locale::Babelfish::Phrase::PluralFormsParser ();
  3         9  
  3         134  
13              
14 3     3   41 use parent qw( Locale::Babelfish::Phrase::ParserBase );
  3         6  
  3         16  
15              
16             our $VERSION = '2.10'; # VERSION
17              
18             __PACKAGE__->mk_accessors( qw( locale mode pieces escape pf0 ) );
19              
20             use constant {
21 3         2702 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 3     3   339 };
  3         6  
26              
27              
28             sub new {
29 5     5 1 2623 my ( $class, $phrase, $locale ) = @_;
30 5         36 my $self = $class->SUPER::new( $phrase );
31 5 50       15 $self->locale( $locale ) if $locale;
32 5         13 return $self;
33             }
34              
35              
36             sub init {
37 95     95 1 174 my ( $self, $phrase ) = @_;
38 95         271 $self->SUPER::init( $phrase );
39 95         1688 $self->mode( LITERAL_MODE );
40 95         2123 $self->pieces( [] );
41 95         2134 $self->pf0( undef ); # plural forms without name yet
42 95         593 return $self;
43             }
44              
45              
46             sub finalize_mode {
47 93     93 1 184 my ( $self ) = @_;
48 93 100       1538 if ( $self->mode eq LITERAL_MODE ) {
    50          
    50          
49 77         1636 push @{ $self->pieces }, LITERAL_MODE->new( text => $self->piece )
50 88 100 100     1839 if length($self->piece) || scalar(@{ $self->pieces }) == 0;
  12         303  
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 5 50       301 $self->throw( "Plural forms definition not ended with \"))\": ". $self->piece )
57             unless defined $self->pf0;
58 5         28 push @{ $self->pieces }, PLURALS_MODE->new( forms => $self->pf0, name => $self->piece, locale => $self->locale, );
  5         86  
59             }
60             else {
61 0         0 $self->throw( "Logic broken, unknown parser mode: ". $self->mode );
62             }
63             }
64              
65              
66             sub parse {
67 96     96 1 57017 my ( $self, $phrase, $locale ) = @_;
68              
69 96         322 $self->SUPER::parse( $phrase );
70 95 100       943 $self->locale( $locale ) if $locale;
71              
72 95         436 my $plurals_parser = Locale::Babelfish::Phrase::PluralFormsParser->new();
73              
74 95         169 while ( 1 ) {
75 1500         12995 my $char = $self->to_next_char;
76              
77 1500 100       36775 unless ( length $char ) {
78 93         257 $self->finalize_mode;
79 93         1698 return $self->pieces;
80             }
81              
82 1407 100       23491 if ( $self->mode eq LITERAL_MODE ) {
83 820 100       16355 if ( $self->escape ) {
84 8         55 $self->add_to_piece( $char );
85 8         218 $self->escape(0);
86 8         47 next;
87             }
88              
89 812 100       4344 if ( $char eq "\\" ) {
90 8         143 $self->escape( 1 );
91 8         49 next;
92             }
93              
94 804 100 66     1728 if ( $char eq '#' && $self->next_char eq '{' ) {
95 28 100       1174 if ( length $self->piece ) {
96 24         141 push @{ $self->pieces }, LITERAL_MODE->new( text => $self->piece );
  24         422  
97 24         515 $self->piece('');
98             }
99 28         279 $self->to_next_char; # skip "{"
100 28         1094 $self->mode( VARIABLE_MODE );
101 28         181 next;
102             }
103              
104 776 100 100     1647 if ( $char eq '(' && $self->next_char eq '(' ) {
105 17 50       699 if ( length $self->piece ) {
106 17         106 push @{ $self->pieces }, LITERAL_MODE->new( text => $self->piece );
  17         292  
107 17         306 $self->piece('');
108             }
109 17         129 $self->to_next_char; # skip second "("
110 17         1008 $self->mode( PLURALS_MODE );
111 17         109 next;
112             }
113             }
114              
115 1346 100       24023 if ( $self->mode eq VARIABLE_MODE ) {
116 171 50       3333 if ( $self->escape ) {
117 0         0 $self->add_to_piece( $char );
118 0         0 $self->escape(0);
119 0         0 next;
120             }
121              
122 171 50       916 if ( $char eq "\\" ) {
123 0         0 $self->escape( 1 );
124 0         0 next;
125             }
126              
127 171 100       351 if ( $char eq '}' ) {
128 28         463 my $name = $self->trim( $self->piece );
129 28 100       100 unless ( length $name ) {
130 1         5 $self->throw( "No variable name given." );
131             }
132 27 100       112 if ( $name !~ VARIABLE_RE ) {
133 1         7 $self->throw( "Variable name doesn't meet conditions: $name." );
134             }
135 26         50 push @{ $self->pieces }, VARIABLE_MODE->new( name => $name );
  26         463  
136 26         479 $self->piece('');
137 26         553 $self->mode( LITERAL_MODE );
138 26         165 next;
139             }
140             }
141              
142 1318 100       25201 if ( $self->mode eq PLURALS_MODE ) {
143 416 100       8073 if ( defined $self->pf0 ) {
144 62 100 66     557 if ( $char =~ VARIABLE_RE && ($char ne '.' || $self->next_char =~ VARIABLE_RE) ) {
      100        
145 54         172 $self->add_to_piece( $char );
146 54         534 next;
147             }
148             else {
149 8         42 push @{ $self->pieces }, PLURALS_MODE->new( forms => $self->pf0, name => $self->piece, locale => $self->locale, );
  8         135  
150 8         154 $self->pf0( undef );
151 8         199 $self->mode( LITERAL_MODE );
152 8         179 $self->piece('');
153 8         80 $self->backward;
154 8         366 next;
155             }
156             }
157 354 100 66     1953 if ( $char eq ')' && $self->next_char eq ')' ) {
158 17         715 $self->pf0( $plurals_parser->parse( $self->piece ) );
159 17         1031 $self->piece('');
160 17         141 $self->to_next_char; # skip second ")"
161 17 100       465 if ( $self->next_char eq ':' ) {
162 13         344 $self->to_next_char; # skip ":"
163 13         327 next;
164             }
165 4         117 push @{ $self->pieces }, PLURALS_MODE->new( forms => $self->pf0, name => 'count', locale => $self->locale, );
  4         73  
166 4         85 $self->pf0( undef );
167 4         89 $self->mode( LITERAL_MODE );
168 4         30 next;
169             }
170             }
171 1239         6195 $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.10
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