File Coverage

blib/lib/Catmandu/Fix/Parser.pm
Criterion Covered Total %
statement 194 195 99.4
branch 19 22 86.3
condition 12 16 75.0
subroutine 40 40 100.0
pod 1 22 4.5
total 266 295 90.1


line stmt bran cond sub pod time code
1              
2             use Catmandu::Sane;
3 147     147   895  
  147         254  
  147         796  
4             our $VERSION = '1.2019';
5              
6             use Catmandu::Util
7             qw(check_value check_string is_array_ref is_instance is_able require_package);
8 147     147   968 use Module::Info;
  147         301  
  147         9282  
9 147     147   62311 use Moo;
  147         846698  
  147         4028  
10 147     147   894 use namespace::clean;
  147         277  
  147         894  
11 147     147   47174  
  147         412  
  147         980  
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 29271 %$opts;
20 158         454 }
21 158         1121  
22             my ($self) = @_;
23             $self->_build_ns('perl:catmandu.fix');
24             }
25 158     158   1188  
26 158         425 my ($self) = @_;
27             $self->init_env([]);
28             }
29              
30 158     158   1392 my ($self, $envs) = @_;
31 158         424 splice(@$envs, 0, @$envs, {ns => {'' => $self->default_ns}});
32             $envs;
33             }
34              
35 379     379 0 737 my ($self, $name) = @_;
36 379         6749 my $envs = $self->env;
37 379         32217 for my $env (@$envs) {
38             return $env->{ns}{$name}
39             if exists $env->{ns} && exists $env->{ns}{$name};
40             }
41 431     431 0 1146 return;
42 431         8742 }
43 431         3254  
44             my ($self, $name, $ns) = @_;
45 463 100 100     2796 my $env = $self->env->[-1];
46             ($env->{ns} //= {})->{$name} = $ns;
47 2         15 }
48              
49             my ($self, $block) = @_;
50             my $envs = $self->env;
51 7     7 0 13 push @$envs, +{};
52 7         197 my $res = $block->();
53 7   50     75  
54             # TODO ensure env gets popped after exception
55             pop @$envs;
56             $res;
57 400     400 0 735 }
58 400         7609  
59 400         2111 my ($self, $source) = @_;
60 400         770  
61             check_value($source);
62              
63 393         22739 try {
64 393         825 $self->from_string($source);
65             }
66             catch {
67             my $err = $_;
68 221     221 1 166334 if (ref($err) && ref($err) =~ /^Catmandu/) {
69             $err->set_source($source) if is_able($err, 'set_source');
70 221         749 $err->throw;
71             }
72             Catmandu::FixParseError->throw(message => $err, source => $source,);
73 221     221   23916 }
74             finally {
75             $self->init_env;
76 21     21   2706 };
77 21 100 66     126 }
78 7 50       28  
79 7         23 qr/#[^\n]*/;
80             }
81 14         102  
82             my ($self) = @_;
83             my $statements
84 221     221   9562 = $self->scope(sub {$self->sequence_of('parse_statement')});
85 221         15084 [grep defined, map {is_array_ref($_) ? @$_ : $_} @$statements];
86             }
87              
88             my ($self) = @_;
89 158     158 0 4665 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 3666  
94             # support deprecated separator
95 400     400   1575 $self->maybe_expect(';');
  400         1254  
96 393 100       1112 $statement;
  418         2248  
97             }
98              
99             my ($self) = @_;
100 616     616 0 25578 $self->token_kw('block');
101 616         1528 my $statements = $self->parse_statements;
102             $self->expect('end');
103             $statements;
104             }
105              
106             my ($self) = @_;
107 420         1214 $self->token_kw('use');
108 420         7531 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 7764 $self->add_ns($opts{as} // $name, $ns);
113 616         1582 return;
114 6         611 }
115 6         17  
116 5         220 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 88358  
121 611         1569 # support deprecated separator
122 7         441 $self->maybe_expect(';');
123 7         26 $self->_build_condition(
124 7         487 $name, $args,
125 7         149 $type eq 'reject',
126 7   66     51 require_package('Catmandu::Fix::reject')->new
127 7         34 );
128             }
129              
130             my ($self) = @_;
131 604     604 0 58821 $self->token_kw('if');
132 604         1344 my $name = $self->parse_name;
133 25         1618 my $args = $self->parse_arguments;
134 20         1343  
135             # support deprecated separator
136             $self->maybe_expect(';');
137 20         76 my $cond
138 20         513 = $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 55517 $self->maybe_expect(';');
147 584         1429 $self->_build_condition($name, $args, 1, $self->parse_statements);
148 37         2745 }
149 37         2557 );
150             my $else_fixes = $self->maybe(
151             sub {
152 36         126 $self->expect('else');
153 36         895 $self->parse_statements;
154             }
155             );
156             $self->expect('end');
157 38     38   1712  
158 4         323 # support deprecated separator
159 4         243 $self->maybe_expect(';');
160              
161             my $last_cond = $cond;
162 4         26  
163 4         94 if ($elsif_conditions) {
164             for my $c (@$elsif_conditions) {
165 36         241 $last_cond->fail_fixes([$c]);
166             $last_cond = $c;
167             }
168 36     36   479 }
169 5         229  
170             if ($else_fixes) {
171 36         4850 $last_cond->fail_fixes($else_fixes);
172 36         2217 }
173              
174             $cond;
175 34         1418 }
176              
177 34         886 my ($self) = @_;
178             $self->token_kw('unless');
179 34 50       141 my $name = $self->parse_name;
180 34         94 my $args = $self->parse_arguments;
181 4         14  
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       81 $self->expect('end');
187 5         18  
188             # support deprecated separator
189             $self->maybe_expect(';');
190 34         197 $cond;
191             }
192              
193             my ($self) = @_;
194 550     550 0 50879 my $type = $self->token_kw('bind', 'do', 'doset');
195 550         1336 my $name = $self->parse_name;
196 13         829 my $args = $self->parse_arguments;
197 13         873  
198             # support deprecated separator
199             $self->maybe_expect(';');
200 12         50 my $bind = $self->_build_bind($name, $args, $type eq 'doset',
201 12         315 $self->parse_statements);
202             $self->expect('end');
203 12         60  
204             # support deprecated separator
205             $self->maybe_expect(';');
206 11         653 $bind;
207 11         350 }
208              
209             my ($self) = @_;
210             my $lft_name = $self->parse_name;
211 539     539 0 49787 my $lft_args = $self->parse_arguments;
212 539         1167 my $bool = $self->maybe(
213 116         7403 sub {
214 116         7797 $self->any_of(
215             sub {$self->expect(qr/and|&&/); 1},
216             sub {$self->expect(qr/or|\|\|/); 0},
217 116         363 );
218 116         2875 }
219             );
220 116         390  
221             my $fix;
222              
223 116         5432 if (defined $bool) {
224 116         3246 $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 40058 $self->_build_fix($rgt_name, $rgt_args));
229 423         972 }
230 421         27012 else {
231             $fix = $self->_build_fix($lft_name, $lft_args);
232             }
233              
234 235         3443 # support deprecated separator
  7         335  
235 228         17910 $self->maybe_expect(';');
  3         139  
236 235     235   3438  
237             $fix;
238 235         1387 }
239              
240 235         20140 my ($self) = @_;
241             $self->generic_token(
242 235 100       603 name => qr/(?:[a-z][_0-9a-zA-Z]*\.)*[a-z][_0-9a-zA-Z]*/);
243 10         42 }
244 10         71  
245 10         655 my ($self) = @_;
246 9         21 $self->expect('(');
247             my $args = $self->list_of(qr/[,:]|=>/, 'parse_value');
248             $self->expect(')');
249             $args;
250 225         564 }
251              
252             my ($self) = @_;
253             $self->any_of('parse_double_quoted_string', 'parse_single_quoted_string',
254 227         10781 'parse_bare_string',);
255             }
256 227         8205  
257             my ($self) = @_;
258             $self->generic_token(bare_string => qr/[^\s\\,;:=>()"']+/);
259             }
260 628     628 0 991  
261 628         2170 my ($self) = @_;
262              
263             my $str = $self->generic_token(string => qr/'(?:\\?+.)*?'/);
264             $str = substr($str, 1, length($str) - 2);
265              
266 628     628 0 1157 $str =~ s{\\'}{'}gxms;
267 628         1676  
268 443         19686 $str;
269 443         37253 }
270 439         17571  
271             my ($self) = @_;
272              
273             my $str = $self->generic_token(string => qr/"(?:\\?+.)*?"/);
274 743     743 0 47889 $str = substr($str, 1, length($str) - 2);
275 743         1723  
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 51323 $str =~ s/\\b/\b/gxms;
281 685         2162 $str =~ s/\\f/\f/gxms;
282             $str =~ s/\\t/\t/gxms;
283             $str =~ s/\\\\/\\/gxms;
284             $str =~ s{\\/}{/}gxms;
285 738     738 0 56983 $str =~ s{\\"}{"}gxms;
286             }
287 738         2324  
288 53         3038 $str;
289             }
290 53         145  
291             my ($self, $name, $args, $pass, $fixes) = @_;
292 53         324 $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 8716 }
297             else {
298 743         2292 $cond->fail_fixes($fixes);
299 5         282 }
300             $cond;
301 5 100       21 }
302 3         6  
  0         0  
303 3         9 my ($self, $name, $args, $return, $fixes) = @_;
304 3         16 $fixes = [$fixes] unless is_array_ref($fixes);
305 3         5 my $bind = $self->_build_fix($name, $args, 'Bind');
306 3         6 $bind->__return__($return);
307 3         4 $bind->__fixes__($fixes);
308 3         7 $bind;
309 3         4 }
310 3         7  
311             my ($self, $name, $args, $type) = @_;
312             my @name_parts = split(/\./, $name);
313 5         29 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   8853 $ns->load($fix_name, $args, $type);
318 81 100       320 }
319 81         213  
320 81 100       1614 my ($self, $name) = @_;
321 56         199 my @name_parts = split(/:/, $name);
322             $name = pop @name_parts;
323             my $pkg_name = $name_parts[0] // 'perl';
324 25         94 my $pkg = require_package($pkg_name, 'Catmandu::Fix::Namespace');
325             $pkg->new(name => $name);
326 81         259 }
327              
328             1;
329              
330 116     116   309  
331 116 50       361 =pod
332 116         301  
333 116         2294 =head1 NAME
334 116         255  
335 116         207 Catmandu::Fix::Parser - the parser of the Catmandu::Fix language
336              
337             =head1 SYNOPSIS
338              
339 431     431   908 use Catmandu::Sane;
340 431         1141 use Catmandu::Fix::Parser;
341 431         783 use Catmandu::Fix;
342 431         854  
343 431   66     940 use Data::Dumper;
344              
345 429         1436 my $parser = Catmandu::Fix::Parser->new;
346              
347             my $fixes;
348              
349 165     165   334 try {
350 165         542 $fixes = $parser->parse(<<EOF);
351 165         321 add_field(test,123)
352 165   100     476 EOF
353 165         545 }
354 165         2411 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