File Coverage

blib/lib/Catmandu/Fix/Parser.pm
Criterion Covered Total %
statement 194 195 99.4
branch 19 22 86.3
condition 10 13 76.9
subroutine 40 40 100.0
pod 1 22 4.5
total 264 292 90.4


line stmt bran cond sub pod time code
1              
2             use Catmandu::Sane;
3 147     147   883  
  147         267  
  147         784  
4             our $VERSION = '1.2018';
5              
6             use Catmandu::Util
7             qw(check_value check_string is_array_ref is_instance is_able require_package);
8 147     147   890 use Module::Info;
  147         283  
  147         9203  
9 147     147   61743 use Moo;
  147         837008  
  147         3890  
10 147     147   911 use namespace::clean;
  147         264  
  147         936  
11 147     147   45984  
  147         432  
  147         971  
12             extends 'Parser::MGC';
13              
14             has env => (is => 'lazy');
15             has default_ns => (is => 'lazy');
16              
17             my ($class, $opts) = @_;
18             $opts->{toplevel} = 'parse_statements';
19 158     158 0 29902 %$opts;
20 158         549 }
21 158         1181  
22             my ($self) = @_;
23             $self->_build_ns('perl:catmandu.fix');
24             }
25 158     158   1230  
26 158         527 my ($self) = @_;
27             $self->init_env([]);
28             }
29              
30 158     158   1488 my ($self, $envs) = @_;
31 158         529 splice(@$envs, 0, @$envs, {ns => {'' => $self->default_ns}});
32             $envs;
33             }
34              
35 379     379 0 807 my ($self, $name) = @_;
36 379         6688 my $envs = $self->env;
37 379         32768 for my $env (@$envs) {
38             return $env->{ns}{$name}
39             if exists $env->{ns} && exists $env->{ns}{$name};
40             }
41 431     431 0 905 return;
42 431         9155 }
43 431         3481  
44             my ($self, $name, $ns) = @_;
45 463 100 100     3028 my $env = $self->env->[-1];
46             ($env->{ns} //= {})->{$name} = $ns;
47 2         14 }
48              
49             my ($self, $block) = @_;
50             my $envs = $self->env;
51 7     7 0 15 push @$envs, +{};
52 7         105 my $res = $block->();
53 7   50     64  
54             # TODO ensure env gets popped after exception
55             pop @$envs;
56             $res;
57 400     400 0 790 }
58 400         8022  
59 400         2203 my ($self, $source) = @_;
60 400         783  
61             check_value($source);
62              
63 393         23257 try {
64 393         838 $self->from_string($source);
65             }
66             catch {
67             my $err = $_;
68 221     221 1 169251 if (is_instance($err, 'Catmandu::Error')) {
69             $err->set_source($source) if is_able($err, 'set_source');
70 221         835 $err->throw;
71             }
72             Catmandu::FixParseError->throw(message => $err, source => $source,);
73 221     221   24634 }
74             finally {
75             $self->init_env;
76 21     21   3642 };
77 21 100       78 }
78 7 50       27  
79 7         22 qr/#[^\n]*/;
80             }
81 14         103  
82             my ($self) = @_;
83             my $statements
84 221     221   10175 = $self->scope(sub {$self->sequence_of('parse_statement')});
85 221         15270 [grep defined, map {is_array_ref($_) ? @$_ : $_} @$statements];
86             }
87              
88             my ($self) = @_;
89 158     158 0 5163 my $statement = $self->any_of(
90             'parse_block', 'parse_use', 'parse_filter', 'parse_if',
91             'parse_unless', 'parse_bind', 'parse_fix',
92             );
93 400     400 0 3847  
94             # support deprecated separator
95 400     400   1675 $self->maybe_expect(';');
  400         1363  
96 393 100       1248 $statement;
  418         2468  
97             }
98              
99             my ($self) = @_;
100 616     616 0 26833 $self->token_kw('block');
101 616         1637 my $statements = $self->parse_statements;
102             $self->expect('end');
103             $statements;
104             }
105              
106             my ($self) = @_;
107 420         1277 $self->token_kw('use');
108 420         7570 my $args = $self->parse_arguments;
109             my $name = check_string(shift(@$args));
110             my $ns = $self->_build_ns($name);
111             my %opts = @$args;
112 616     616 0 7986 $self->add_ns($opts{as} // $name, $ns);
113 616         1820 return;
114 6         550 }
115 6         16  
116 5         214 my ($self) = @_;
117             my $type = $self->token_kw('select', 'reject');
118             my $name = $self->parse_name;
119             my $args = $self->parse_arguments;
120 611     611 0 91535  
121 611         1647 # support deprecated separator
122 7         435 $self->maybe_expect(';');
123 7         23 $self->_build_condition(
124 7         481 $name, $args,
125 7         136 $type eq 'reject',
126 7   66     107 require_package('Catmandu::Fix::reject')->new
127 7         37 );
128             }
129              
130             my ($self) = @_;
131 604     604 0 59951 $self->token_kw('if');
132 604         1448 my $name = $self->parse_name;
133 25         1615 my $args = $self->parse_arguments;
134 20         1391  
135             # support deprecated separator
136             $self->maybe_expect(';');
137 20         79 my $cond
138 20         528 = $self->_build_condition($name, $args, 1, $self->parse_statements);
139             my $elsif_conditions = $self->sequence_of(
140             sub {
141             $self->token_kw('elsif');
142             my $name = $self->parse_name;
143             my $args = $self->parse_arguments;
144              
145             # support deprecated separator
146 584     584 0 55811 $self->maybe_expect(';');
147 584         1551 $self->_build_condition($name, $args, 1, $self->parse_statements);
148 37         2251 }
149 37         2555 );
150             my $else_fixes = $self->maybe(
151             sub {
152 36         124 $self->expect('else');
153 36         891 $self->parse_statements;
154             }
155             );
156             $self->expect('end');
157 38     38   1737  
158 4         322 # support deprecated separator
159 4         240 $self->maybe_expect(';');
160              
161             my $last_cond = $cond;
162 4         10  
163 4         92 if ($elsif_conditions) {
164             for my $c (@$elsif_conditions) {
165 36         254 $last_cond->fail_fixes([$c]);
166             $last_cond = $c;
167             }
168 36     36   494 }
169 5         238  
170             if ($else_fixes) {
171 36         4922 $last_cond->fail_fixes($else_fixes);
172 36         2225 }
173              
174             $cond;
175 34         1446 }
176              
177 34         785 my ($self) = @_;
178             $self->token_kw('unless');
179 34 50       106 my $name = $self->parse_name;
180 34         92 my $args = $self->parse_arguments;
181 4         13  
182 4         9 # support deprecated separator
183             $self->maybe_expect(';');
184             my $cond
185             = $self->_build_condition($name, $args, 0, $self->parse_statements);
186 34 100       82 $self->expect('end');
187 5         21  
188             # support deprecated separator
189             $self->maybe_expect(';');
190 34         199 $cond;
191             }
192              
193             my ($self) = @_;
194 550     550 0 51282 my $type = $self->token_kw('bind', 'do', 'doset');
195 550         1432 my $name = $self->parse_name;
196 13         842 my $args = $self->parse_arguments;
197 13         925  
198             # support deprecated separator
199             $self->maybe_expect(';');
200 12         54 my $bind = $self->_build_bind($name, $args, $type eq 'doset',
201 12         319 $self->parse_statements);
202             $self->expect('end');
203 12         74  
204             # support deprecated separator
205             $self->maybe_expect(';');
206 11         757 $bind;
207 11         361 }
208              
209             my ($self) = @_;
210             my $lft_name = $self->parse_name;
211 539     539 0 49884 my $lft_args = $self->parse_arguments;
212 539         1250 my $bool = $self->maybe(
213 116         7537 sub {
214 116         8366 $self->any_of(
215             sub {$self->expect(qr/and|&&/); 1},
216             sub {$self->expect(qr/or|\|\|/); 0},
217 116         374 );
218 116         2988 }
219             );
220 116         423  
221             my $fix;
222              
223 116         5605 if (defined $bool) {
224 116         3361 $self->commit;
225             my $rgt_name = $self->parse_name;
226             my $rgt_args = $self->parse_arguments;
227             $fix = $self->_build_condition($lft_name, $lft_args, $bool,
228 423     423 0 40628 $self->_build_fix($rgt_name, $rgt_args));
229 423         1034 }
230 421         27371 else {
231             $fix = $self->_build_fix($lft_name, $lft_args);
232             }
233              
234 235         3083 # support deprecated separator
  7         333  
235 228         17962 $self->maybe_expect(';');
  3         140  
236 235     235   3523  
237             $fix;
238 235         1431 }
239              
240 235         20207 my ($self) = @_;
241             $self->generic_token(
242 235 100       623 name => qr/(?:[a-z][_0-9a-zA-Z]*\.)*[a-z][_0-9a-zA-Z]*/);
243 10         31 }
244 10         68  
245 10         649 my ($self) = @_;
246 9         20 $self->expect('(');
247             my $args = $self->list_of(qr/[,:]|=>/, 'parse_value');
248             $self->expect(')');
249             $args;
250 225         647 }
251              
252             my ($self) = @_;
253             $self->any_of('parse_double_quoted_string', 'parse_single_quoted_string',
254 227         10538 'parse_bare_string',);
255             }
256 227         8507  
257             my ($self) = @_;
258             $self->generic_token(bare_string => qr/[^\s\\,;:=>()"']+/);
259             }
260 628     628 0 1048  
261 628         2368 my ($self) = @_;
262              
263             my $str = $self->generic_token(string => qr/'(?:\\?+.)*?'/);
264             $str = substr($str, 1, length($str) - 2);
265              
266 628     628 0 1187 $str =~ s{\\'}{'}gxms;
267 628         1812  
268 443         19950 $str;
269 443         37764 }
270 439         17582  
271             my ($self) = @_;
272              
273             my $str = $self->generic_token(string => qr/"(?:\\?+.)*?"/);
274 743     743 0 49419 $str = substr($str, 1, length($str) - 2);
275 743         1746  
276             if (index($str, '\\') != -1) {
277             $str =~ s/\\u([0-9A-Fa-f]{4})/chr(hex($1))/egxms;
278             $str =~ s/\\n/\n/gxms;
279             $str =~ s/\\r/\r/gxms;
280 685     685 0 51255 $str =~ s/\\b/\b/gxms;
281 685         2140 $str =~ s/\\f/\f/gxms;
282             $str =~ s/\\t/\t/gxms;
283             $str =~ s/\\\\/\\/gxms;
284             $str =~ s{\\/}{/}gxms;
285 738     738 0 57940 $str =~ s{\\"}{"}gxms;
286             }
287 738         2419  
288 53         2804 $str;
289             }
290 53         140  
291             my ($self, $name, $args, $pass, $fixes) = @_;
292 53         302 $fixes = [$fixes] unless is_array_ref($fixes);
293             my $cond = $self->_build_fix($name, $args, 'Condition');
294             if ($pass) {
295             $cond->pass_fixes($fixes);
296 743     743 0 9128 }
297             else {
298 743         2408 $cond->fail_fixes($fixes);
299 5         239 }
300             $cond;
301 5 100       21 }
302 3         6  
  0         0  
303 3         7 my ($self, $name, $args, $return, $fixes) = @_;
304 3         4 $fixes = [$fixes] unless is_array_ref($fixes);
305 3         5 my $bind = $self->_build_fix($name, $args, 'Bind');
306 3         4 $bind->__return__($return);
307 3         6 $bind->__fixes__($fixes);
308 3         5 $bind;
309 3         4 }
310 3         5  
311             my ($self, $name, $args, $type) = @_;
312             my @name_parts = split(/\./, $name);
313 5         28 my $fix_name = pop @name_parts;
314             my $ns_name = join('.', @name_parts);
315             my $ns = $self->get_ns($ns_name)
316             // Catmandu::FixParseError->throw("Unknown namespace: $ns_name");
317 81     81   9810 $ns->load($fix_name, $args, $type);
318 81 100       362 }
319 81         227  
320 81 100       1702 my ($self, $name) = @_;
321 56         208 my @name_parts = split(/:/, $name);
322             $name = pop @name_parts;
323             my $pkg_name = $name_parts[0] // 'perl';
324 25         104 my $pkg = require_package($pkg_name, 'Catmandu::Fix::Namespace');
325             $pkg->new(name => $name);
326 81         301 }
327              
328             1;
329              
330 116     116   340  
331 116 50       409 =pod
332 116         324  
333 116         2384 =head1 NAME
334 116         315  
335 116         241 Catmandu::Fix::Parser - the parser of the Catmandu::Fix language
336              
337             =head1 SYNOPSIS
338              
339 431     431   994 use Catmandu::Sane;
340 431         1291 use Catmandu::Fix::Parser;
341 431         782 use Catmandu::Fix;
342 431         889  
343 431   66     1048 use Data::Dumper;
344              
345 429         1619 my $parser = Catmandu::Fix::Parser->new;
346              
347             my $fixes;
348              
349 165     165   388 try {
350 165         602 $fixes = $parser->parse(<<EOF);
351 165         358 add_field(test,123)
352 165   100     517 EOF
353 165         604 }
354 165         2618 catch {
355             printf "[%s]\nscript:\n%s\nerror: %s\n"
356             , ref($_)
357             , $_->source
358             , $_->message;
359             };
360              
361             my $fixer = Catmandu::Fix->new(fixes => $fixes);
362              
363             print Dumper($fixer->fix({}));
364              
365             =head1 DESCRIPTION
366              
367             Programmers are discouraged to use the Catmandu::Parser directly in code but
368             use the Catmandu package that provides the same functionality:
369              
370             use Catmandu;
371              
372             my $fixer = Catmandu->fixer(<<EOF);
373             add_field(test,123)
374             EOF
375              
376             print Dumper($fixer->fix({}));
377              
378             =head1 METHODS
379              
380             =head2 new()
381              
382             Create a new Catmandu::Fix parser
383              
384             =head2 parse($string)
385              
386             Reads a string and returns a blessed object with parsed
387             Catmandu::Fixes. Throws an Catmandu::ParseError on failure.
388              
389             =head1 SEE ALSO
390              
391             L<Catmandu::Fix>
392              
393             Or consult the webpages below for more information on the Catmandu::Fix language
394              
395             http://librecat.org/Catmandu/#fixes
396             http://librecat.org/Catmandu/#fix-language
397              
398             =cut