File Coverage

inc/YAML/Loader.pm
Criterion Covered Total %
statement 207 446 46.4
branch 69 238 28.9
condition 25 82 30.4
subroutine 20 29 68.9
pod 0 1 0.0
total 321 796 40.3


line stmt bran cond sub pod time code
1             #line 1
2 1     1   5 package YAML::Loader;
  1     1   2  
  1         32  
  1         4  
  1         2  
  1         23  
3 1     1   4 use strict; use warnings;
  1         2  
  1         70  
4 1     1   4 use YAML::Base;
  1         2  
  1         6  
5 1     1   744 use base 'YAML::Loader::Base';
  1         4  
  1         51  
6             use YAML::Types;
7              
8 1     1   6 # Context constants
  1         1  
  1         40  
9 1     1   5 use constant LEAF => 1;
  1         1  
  1         42  
10 1     1   5 use constant COLLECTION => 2;
  1         1  
  1         38  
11 1     1   5 use constant VALUE => "\x07YAML\x07VALUE\x07";
  1         1  
  1         2325  
12             use constant COMMENT => "\x07YAML\x07COMMENT\x07";
13              
14             # Common YAML character sets
15             my $ESCAPE_CHAR = '[\\x00-\\x08\\x0b-\\x0d\\x0e-\\x1f]';
16             my $FOLD_CHAR = '>';
17             my $LIT_CHAR = '|';
18             my $LIT_CHAR_RX = "\\$LIT_CHAR";
19              
20 1     1 0 2 sub load {
21 1   50     64 my $self = shift;
22 1         4 $self->stream($_[0] || '');
23             return $self->_parse();
24             }
25              
26             # Top level function for parsing. Parse each document in order and
27             # handle processing for YAML headers.
28 1     1   1 sub _parse {
29 1         2 my $self = shift;
30 1         4 my (%directives, $preface);
31 1         3 $self->{stream} =~ s|\015\012|\012|g;
32 1         32 $self->{stream} =~ s|\015|\012|g;
33 1 50       23 $self->line(0);
34             $self->die('YAML_PARSE_ERR_BAD_CHARS')
35 1 50 33     21 if $self->stream =~ /$ESCAPE_CHAR/;
36             $self->die('YAML_PARSE_ERR_NO_FINAL_NEWLINE')
37             if length($self->stream) and
38 1         21 $self->{stream} !~ s/(.)\n\Z/$1/s;
39 1         30 $self->lines([split /\x0a/, $self->stream, -1]);
40             $self->line(1);
41             # Throw away any comments or blanks before the header (or start of
42 1         5 # content for headerless streams)
43 1         24 $self->_parse_throwaway_comments();
44 1         24 $self->document(0);
45             $self->documents([]);
46             # Add an "assumed" header if there is no header and the stream is
47 1 50       21 # not empty (after initial throwaways).
48 1 50       20 if (not $self->eos) {
49 0         0 if ($self->lines->[0] !~ /^---(\s|$)/) {
  0         0  
50 0         0 unshift @{$self->lines}, '---';
51             $self->{line}--;
52             }
53             }
54              
55 1         20 # Main Loop. Parse out all the top level nodes and return them.
56 1         25 while (not $self->eos) {
57 1         1 $self->anchor2node({});
58 1         24 $self->{document}++;
59 1         34 $self->done(0);
60 1         29 $self->level(0);
61             $self->offset->[0] = -1;
62 1 50       28  
63 1         4 if ($self->lines->[0] =~ /^---\s*(.*)$/) {
64 1         2 my @words = split /\s+/, $1;
65 1   33     6 %directives = ();
66 0         0 while (@words && $words[0] =~ /^#(\w+):(\S.*)$/) {
67 0         0 my ($key, $value) = ($1, $2);
68 0 0       0 shift(@words);
69 0         0 if (defined $directives{$key}) {
70             $self->warn('YAML_PARSE_WARN_MULTIPLE_DIRECTIVES',
71 0         0 $key, $self->document);
72             next;
73 0         0 }
74             $directives{$key} = $value;
75 1         34 }
76             $self->preface(join ' ', @words);
77             }
78 0         0 else {
79             $self->die('YAML_PARSE_ERR_NO_SEPARATOR');
80             }
81 1 50       31  
82 1         3 if (not $self->done) {
83             $self->_parse_next_line(COLLECTION);
84 1 50       28 }
85 0         0 if ($self->done) {
86 0         0 $self->{indent} = -1;
87             $self->content('');
88             }
89 1   50     23  
90 1   50     14 $directives{YAML} ||= '1.0';
91 1         7 $directives{TAB} ||= 'NONE';
92             ($self->{major_version}, $self->{minor_version}) =
93 1 50       33 split /\./, $directives{YAML}, 2;
94             $self->die('YAML_PARSE_ERR_BAD_MAJOR_VERSION', $directives{YAML})
95 1 50       40 if $self->major_version ne '1';
96             $self->warn('YAML_PARSE_WARN_BAD_MINOR_VERSION', $directives{YAML})
97 1 50       9 if $self->minor_version ne '0';
98             $self->die('Unrecognized TAB policy')
99             unless $directives{TAB} =~ /^(NONE|\d+)(:HARD)?$/;
100 1         2  
  1         28  
101             push @{$self->documents}, $self->_parse_node();
102 1 50       4 }
  1         22  
103             return wantarray ? @{$self->documents} : $self->documents->[-1];
104             }
105              
106             # This function is the dispatcher for parsing each node. Every node
107             # recurses back through here. (Inlines are an exception as they have
108             # their own sub-parser.)
109 4     4   7 sub _parse_node {
110 4         100 my $self = shift;
111 4         98 my $preface = $self->preface;
112 4         11 $self->preface('');
113 4         9 my ($node, $type, $indicator, $escape, $chomp) = ('') x 5;
114 4         11 my ($anchor, $alias, $explicit, $implicit, $class) = ('') x 5;
115             ($anchor, $alias, $explicit, $implicit, $preface) =
116 4 50       13 $self->_parse_qualifiers($preface);
117 0         0 if ($anchor) {
118             $self->anchor2node->{$anchor} = CORE::bless [], 'YAML-anchor2node';
119 4         93 }
120 4         13 $self->inline('');
121 3         80 while (length $preface) {
122 3 100       47 my $line = $self->line - 1;
123 1         3 if ($preface =~ s/^($FOLD_CHAR|$LIT_CHAR_RX)(-|\+)?\d*\s*//) {
124 1 50       7 $indicator = $1;
125             $chomp = $2 if defined($2);
126             }
127 2 50       9 else {
128 2         47 $self->die('YAML_PARSE_ERR_TEXT_AFTER_INDICATOR') if $indicator;
129 2         7 $self->inline($preface);
130             $preface = '';
131             }
132 4 50       98 }
    100          
    100          
    50          
133 0 0       0 if ($alias) {
134             $self->die('YAML_PARSE_ERR_NO_ANCHOR', $alias)
135 0 0       0 unless defined $self->anchor2node->{$alias};
136 0         0 if (ref($self->anchor2node->{$alias}) ne 'YAML-anchor2node') {
137             $node = $self->anchor2node->{$alias};
138             }
139 0         0 else {
  0         0  
140 0         0 $node = do {my $sv = "*$alias"};
  0         0  
141             push @{$self->anchor2node->{$alias}}, [\$node, $self->line];
142             }
143             }
144 2         8 elsif (length $self->inline) {
145 2 50       44 $node = $self->_parse_inline(1, $implicit, $explicit);
146 0         0 if (length $self->inline) {
147             $self->die('YAML_PARSE_ERR_SINGLE_LINE');
148             }
149             }
150 1         3 elsif ($indicator eq $LIT_CHAR) {
151 1         4 $self->{level}++;
152 1 50       3 $node = $self->_parse_block($chomp);
153 1         4 $node = $self->_parse_implicit($node) if $implicit;
154             $self->{level}--;
155             }
156 0         0 elsif ($indicator eq $FOLD_CHAR) {
157 0         0 $self->{level}++;
158 0 0       0 $node = $self->_parse_unfold($chomp);
159 0         0 $node = $self->_parse_implicit($node) if $implicit;
160             $self->{level}--;
161             }
162 1         3 else {
163 1   50     21 $self->{level}++;
164 1 50       20 $self->offset->[$self->level] ||= 0;
165 1 50       19 if ($self->indent == $self->offset->[$self->level]) {
    0          
    0          
166 1         3 if ($self->content =~ /^-( |$)/) {
167             $node = $self->_parse_seq($anchor);
168             }
169 0         0 elsif ($self->content =~ /(^\?|\:( |$))/) {
170             $node = $self->_parse_mapping($anchor);
171             }
172 0         0 elsif ($preface =~ /^\s*$/) {
173             $node = $self->_parse_implicit('');
174             }
175 0         0 else {
176             $self->die('YAML_PARSE_ERR_BAD_NODE');
177             }
178             }
179 0         0 else {
180             $node = undef;
181 1         3 }
182             $self->{level}--;
183 4         85 }
  4         83  
184             $#{$self->offset} = $self->level;
185 4 50       12  
186 0 0       0 if ($explicit) {
187 0 0       0 if ($class) {
188 0         0 if (not ref $node) {
189 0         0 my $copy = $node;
190 0         0 undef $node;
191             $node = \$copy;
192 0         0 }
193             CORE::bless $node, $class;
194             }
195 0         0 else {
196             $node = $self->_parse_explicit($node, $explicit);
197             }
198 4 50       8 }
199 0 0       0 if ($anchor) {
200             if (ref($self->anchor2node->{$anchor}) eq 'YAML-anchor2node') {
201 0         0 # XXX Can't remember what this code actually does
  0         0  
202 0         0 for my $ref (@{$self->anchor2node->{$anchor}}) {
  0         0  
203 0         0 ${$ref->[0]} = $node;
204             $self->warn('YAML_LOAD_WARN_UNRESOLVED_ALIAS',
205             $anchor, $ref->[1]);
206             }
207 0         0 }
208             $self->anchor2node->{$anchor} = $node;
209 4         97 }
210             return $node;
211             }
212              
213             # Preprocess the qualifiers that may be attached to any node.
214 6     6   8 sub _parse_qualifiers {
215 6         9 my $self = shift;
216 6         10 my ($preface) = @_;
217 6         138 my ($anchor, $alias, $explicit, $implicit, $token) = ('') x 5;
218 6         33 $self->inline('');
219 0         0 while ($preface =~ /^[&*!]/) {
220 0 0       0 my $line = $self->line - 1;
    0          
    0          
    0          
221 0 0       0 if ($preface =~ s/^\!(\S+)\s*//) {
222 0         0 $self->die('YAML_PARSE_ERR_MANY_EXPLICIT') if $explicit;
223             $explicit = $1;
224             }
225 0 0       0 elsif ($preface =~ s/^\!\s*//) {
226 0         0 $self->die('YAML_PARSE_ERR_MANY_IMPLICIT') if $implicit;
227             $implicit = 1;
228             }
229 0         0 elsif ($preface =~ s/^\&([^ ,:]+)\s*//) {
230 0 0       0 $token = $1;
231             $self->die('YAML_PARSE_ERR_BAD_ANCHOR')
232 0 0       0 unless $token =~ /^[a-zA-Z0-9]+$/;
233 0 0       0 $self->die('YAML_PARSE_ERR_MANY_ANCHOR') if $anchor;
234 0         0 $self->die('YAML_PARSE_ERR_ANCHOR_ALIAS') if $alias;
235             $anchor = $token;
236             }
237 0         0 elsif ($preface =~ s/^\*([^ ,:]+)\s*//) {
238 0 0       0 $token = $1;
239             $self->die('YAML_PARSE_ERR_BAD_ALIAS')
240 0 0       0 unless $token =~ /^[a-zA-Z0-9]+$/;
241 0 0       0 $self->die('YAML_PARSE_ERR_MANY_ALIAS') if $alias;
242 0         0 $self->die('YAML_PARSE_ERR_ANCHOR_ALIAS') if $anchor;
243             $alias = $token;
244             }
245 6         42 }
246             return ($anchor, $alias, $explicit, $implicit, $preface);
247             }
248              
249             # Morph a node to it's explicit type
250 0     0   0 sub _parse_explicit {
251 0         0 my $self = shift;
252 0         0 my ($node, $explicit) = @_;
253 0 0       0 my ($type, $class);
254 0   0     0 if ($explicit =~ /^\!?perl\/(hash|array|ref|scalar)(?:\:(\w(\w|\:\:)*)?)?$/) {
      0        
255             ($type, $class) = (($1 || ''), ($2 || ''));
256              
257             # FIXME # die unless uc($type) eq ref($node) ?
258 0 0       0  
259 0 0 0     0 if ( $type eq "ref" ) {
260             $self->die('YAML_LOAD_ERR_NO_DEFAULT_VALUE', 'XXX', $explicit)
261             unless exists $node->{VALUE()} and scalar(keys %$node) == 1;
262 0         0  
263 0         0 my $value = $node->{VALUE()};
264             $node = \$value;
265             }
266 0 0 0     0
      0        
267 0         0 if ( $type eq "scalar" and length($class) and !ref($node) ) {
268 0         0 my $value = $node;
269             $node = \$value;
270             }
271 0 0       0  
272 0         0 if ( length($class) ) {
273             CORE::bless($node, $class);
274             }
275 0         0  
276             return $node;
277 0 0 0     0 }
    0          
    0          
278 0   0     0 if ($explicit =~ m{^!?perl/(glob|regexp|code)(?:\:(\w(\w|\:\:)*)?)?$}) {
      0        
279 0         0 ($type, $class) = (($1 || ''), ($2 || ''));
280 1     1   7 my $type_class = "YAML::Type::$type";
  1         2  
  1         3653  
281 0 0       0 no strict 'refs';
282 0         0 if ($type_class->can('yaml_load')) {
283             return $type_class->yaml_load($node, $class, $self);
284             }
285 0         0 else {
286             $self->die('YAML_LOAD_ERR_NO_CONVERT', 'XXX', $explicit);
287             }
288             }
289             # This !perl/@Foo and !perl/$Foo are deprecated but still parsed
290             elsif ($YAML::TagClass->{$explicit} ||
291             $explicit =~ m{^perl/(\@|\$)?([a-zA-Z](\w|::)+)$}
292 0   0     0 ) {
293 0 0       0 $class = $YAML::TagClass->{$explicit} || $2;
294 0         0 if ($class->can('yaml_load')) {
295 0         0 require YAML::Node;
296             return $class->yaml_load(YAML::Node->new($node, $explicit));
297             }
298 0 0       0 else {
299 0         0 if (ref $node) {
300             return CORE::bless $node, $class;
301             }
302 0         0 else {
303             return CORE::bless \$node, $class;
304             }
305             }
306             }
307 0         0 elsif (ref $node) {
308 0         0 require YAML::Node;
309             return YAML::Node->new($node, $explicit);
310             }
311             else {
312             # XXX This is likely wrong. Failing test:
313 0         0 # --- !unknown 'scalar value'
314             return $node;
315             }
316             }
317              
318             # Parse a YAML mapping into a Perl hash
319 0     0   0 sub _parse_mapping {
320 0         0 my $self = shift;
321 0         0 my ($anchor) = @_;
322 0         0 my $mapping = {};
323 0         0 $self->anchor2node->{$anchor} = $mapping;
324 0   0     0 my $key;
325             while (not $self->done and $self->indent == $self->offset->[$self->level]) {
326 0 0       0 # If structured key:
    0          
    0          
327 0         0 if ($self->{content} =~ s/^\?\s*//) {
328 0         0 $self->preface($self->content);
329 0         0 $self->_parse_next_line(COLLECTION);
330 0         0 $key = $self->_parse_node();
331             $key = "$key";
332             }
333             # If "default" key (equals sign)
334 0         0 elsif ($self->{content} =~ s/^\=\s*//) {
335             $key = VALUE;
336             }
337             # If "comment" key (slash slash)
338 0         0 elsif ($self->{content} =~ s/^\=\s*//) {
339             $key = COMMENT;
340             }
341             # Regular scalar key:
342 0         0 else {
343 0         0 $self->inline($self->content);
344 0         0 $key = $self->_parse_inline();
345 0         0 $key = "$key";
346 0         0 $self->content($self->inline);
347             $self->inline('');
348             }
349 0 0       0
350 0         0 unless ($self->{content} =~ s/^:\s*//) {
351             $self->die('YAML_LOAD_ERR_BAD_MAP_ELEMENT');
352 0         0 }
353 0         0 $self->preface($self->content);
354 0         0 my $line = $self->line;
355 0         0 $self->_parse_next_line(COLLECTION);
356 0 0       0 my $value = $self->_parse_node();
357 0         0 if (exists $mapping->{$key}) {
358             $self->warn('YAML_LOAD_WARN_DUPLICATE_KEY');
359             }
360 0         0 else {
361             $mapping->{$key} = $value;
362             }
363 0         0 }
364             return $mapping;
365             }
366              
367             # Parse a YAML sequence into a Perl array
368 1     1   2 sub _parse_seq {
369 1         6 my $self = shift;
370 1         3 my ($anchor) = @_;
371 1         26 my $seq = [];
372 1   66     21 $self->anchor2node->{$anchor} = $seq;
373 3 50       74 while (not $self->done and $self->indent == $self->offset->[$self->level]) {
374 3 50       75 if ($self->content =~ /^-(?: (.*))?$/) {
375             $self->preface(defined($1) ? $1 : '');
376             }
377 0         0 else {
378             $self->die('YAML_LOAD_ERR_BAD_SEQ_ELEMENT');
379 3 50       70 }
380 0         0 if ($self->preface =~ /^(\s*)(\w.*\:(?: |$).*)$/) {
381 0         0 $self->indent($self->offset->[$self->level] + 2 + length($1));
382 0         0 $self->content($2);
383 0         0 $self->level($self->level + 1);
384 0         0 $self->offset->[$self->level] = $self->indent;
385 0         0 $self->preface('');
386 0         0 push @$seq, $self->_parse_mapping('');
387 0         0 $self->{level}--;
  0         0  
388             $#{$self->offset} = $self->level;
389             }
390 3         7 else {
391 3         37 $self->_parse_next_line(COLLECTION);
392             push @$seq, $self->_parse_node();
393             }
394 1         4 }
395             return $seq;
396             }
397              
398             # Parse an inline value. Since YAML supports inline collections, this is
399             # the top level of a sub parsing.
400 2     2   4 sub _parse_inline {
401 2         5 my $self = shift;
402 2         10 my ($top, $top_implicit, $top_explicit) = (@_, '', '', '');
403 2         5 $self->{inline} =~ s/^\s*(.*)\s*$/$1/; # OUCH - mugwump
404 2         46 my ($node, $anchor, $alias, $explicit, $implicit) = ('') x 5;
405             ($anchor, $alias, $explicit, $implicit, $self->{inline}) =
406 2 50       8 $self->_parse_qualifiers($self->inline);
407 0         0 if ($anchor) {
408             $self->anchor2node->{$anchor} = CORE::bless [], 'YAML-anchor2node';
409 2   33     11 }
410 2   33     17 $implicit ||= $top_implicit;
411 2         3 $explicit ||= $top_explicit;
412 2 50       47 ($top_implicit, $top_explicit) = ('', '');
    50          
    50          
    50          
    50          
413 0 0       0 if ($alias) {
414             $self->die('YAML_PARSE_ERR_NO_ANCHOR', $alias)
415 0 0       0 unless defined $self->anchor2node->{$alias};
416 0         0 if (ref($self->anchor2node->{$alias}) ne 'YAML-anchor2node') {
417             $node = $self->anchor2node->{$alias};
418             }
419 0         0 else {
  0         0  
420 0         0 $node = do {my $sv = "*$alias"};
  0         0  
421             push @{$self->anchor2node->{$alias}}, [\$node, $self->line];
422             }
423             }
424 0         0 elsif ($self->inline =~ /^\{/) {
425             $node = $self->_parse_inline_mapping($anchor);
426             }
427 0         0 elsif ($self->inline =~ /^\[/) {
428             $node = $self->_parse_inline_seq($anchor);
429             }
430 0         0 elsif ($self->inline =~ /^"/) {
431 0         0 $node = $self->_parse_inline_double_quoted();
432 0 0       0 $node = $self->_unescape($node);
433             $node = $self->_parse_implicit($node) if $implicit;
434             }
435 0         0 elsif ($self->inline =~ /^'/) {
436 0 0       0 $node = $self->_parse_inline_single_quoted();
437             $node = $self->_parse_implicit($node) if $implicit;
438             }
439 2 50       4 else {
440 2         47 if ($top) {
441 2         42 $node = $self->inline;
442             $self->inline('');
443             }
444 0         0 else {
445             $node = $self->_parse_inline_simple();
446 2 50       17 }
447             $node = $self->_parse_implicit($node) unless $explicit;
448 2 50       6 }
449 0         0 if ($explicit) {
450             $node = $self->_parse_explicit($node, $explicit);
451 2 50       6 }
452 0 0       0 if ($anchor) {
453 0         0 if (ref($self->anchor2node->{$anchor}) eq 'YAML-anchor2node') {
  0         0  
454 0         0 for my $ref (@{$self->anchor2node->{$anchor}}) {
  0         0  
455 0         0 ${$ref->[0]} = $node;
456             $self->warn('YAML_LOAD_WARN_UNRESOLVED_ALIAS',
457             $anchor, $ref->[1]);
458             }
459 0         0 }
460             $self->anchor2node->{$anchor} = $node;
461 2         6 }
462             return $node;
463             }
464              
465             # Parse the inline YAML mapping into a Perl hash
466 0     0   0 sub _parse_inline_mapping {
467 0         0 my $self = shift;
468 0         0 my ($anchor) = @_;
469 0         0 my $node = {};
470             $self->anchor2node->{$anchor} = $node;
471 0 0       0  
472             $self->die('YAML_PARSE_ERR_INLINE_MAP')
473 0         0 unless $self->{inline} =~ s/^\{\s*//;
474 0         0 while (not $self->{inline} =~ s/^\s*\}//) {
475 0 0       0 my $key = $self->_parse_inline();
476             $self->die('YAML_PARSE_ERR_INLINE_MAP')
477 0         0 unless $self->{inline} =~ s/^\: \s*//;
478 0 0       0 my $value = $self->_parse_inline();
479 0         0 if (exists $node->{$key}) {
480             $self->warn('YAML_LOAD_WARN_DUPLICATE_KEY');
481             }
482 0         0 else {
483             $node->{$key} = $value;
484 0 0       0 }
485 0 0       0 next if $self->inline =~ /^\s*\}/;
486             $self->die('YAML_PARSE_ERR_INLINE_MAP')
487             unless $self->{inline} =~ s/^\,\s*//;
488 0         0 }
489             return $node;
490             }
491              
492             # Parse the inline YAML sequence into a Perl array
493 0     0   0 sub _parse_inline_seq {
494 0         0 my $self = shift;
495 0         0 my ($anchor) = @_;
496 0         0 my $node = [];
497             $self->anchor2node->{$anchor} = $node;
498 0 0       0  
499             $self->die('YAML_PARSE_ERR_INLINE_SEQUENCE')
500 0         0 unless $self->{inline} =~ s/^\[\s*//;
501 0         0 while (not $self->{inline} =~ s/^\s*\]//) {
502 0         0 my $value = $self->_parse_inline();
503 0 0       0 push @$node, $value;
504 0 0       0 next if $self->inline =~ /^\s*\]/;
505             $self->die('YAML_PARSE_ERR_INLINE_SEQUENCE')
506             unless $self->{inline} =~ s/^\,\s*//;
507 0         0 }
508             return $node;
509             }
510              
511             # Parse the inline double quoted string.
512 0     0   0 sub _parse_inline_double_quoted {
513 0         0 my $self = shift;
514 0 0       0 my $node;
515 0         0 if ($self->inline =~ /^"((?:\\"|[^"])*)"\s*(.*)$/) {
516 0         0 $node = $1;
517 0         0 $self->inline($2);
518             $node =~ s/\\"/"/g;
519             }
520 0         0 else {
521             $self->die('YAML_PARSE_ERR_BAD_DOUBLE');
522 0         0 }
523             return $node;
524             }
525              
526              
527             # Parse the inline single quoted string.
528 0     0   0 sub _parse_inline_single_quoted {
529 0         0 my $self = shift;
530 0 0       0 my $node;
531 0         0 if ($self->inline =~ /^'((?:''|[^'])*)'\s*(.*)$/) {
532 0         0 $node = $1;
533 0         0 $self->inline($2);
534             $node =~ s/''/'/g;
535             }
536 0         0 else {
537             $self->die('YAML_PARSE_ERR_BAD_SINGLE');
538 0         0 }
539             return $node;
540             }
541              
542             # Parse the inline unquoted string and do implicit typing.
543 0     0   0 sub _parse_inline_simple {
544 0         0 my $self = shift;
545 0 0       0 my $value;
546 0         0 if ($self->inline =~ /^(|[^!@#%^&*].*?)(?=[\[\]\{\},]|, |: |- |:\s*$|$)/) {
547 0         0 $value = $1;
548             substr($self->{inline}, 0, length($1)) = '';
549             }
550 0         0 else {
551             $self->die('YAML_PARSE_ERR_BAD_INLINE_IMPLICIT', $value);
552 0         0 }
553             return $value;
554             }
555              
556 2     2   3 sub _parse_implicit {
557 2         4 my $self = shift;
558 2         10 my ($value) = @_;
559 2 50       7 $value =~ s/\s*$//;
560 2 50       21 return $value if $value eq '';
561 2 50 33     15 return undef if $value =~ /^~$/;
562             return $value
563             unless $value =~ /^[\@\`\^]/ or
564 0         0 $value =~ /^[\-\?]\s/;
565             $self->die('YAML_PARSE_ERR_BAD_IMPLICIT', $value);
566             }
567              
568             # Unfold a YAML multiline scalar into a single string.
569 0     0   0 sub _parse_unfold {
570 0         0 my $self = shift;
571 0         0 my ($chomp) = @_;
572 0         0 my $node = '';
573 0   0     0 my $space = 0;
574 0         0 while (not $self->done and $self->indent == $self->offset->[$self->level]) {
575 0         0 $node .= $self->content. "\n";
576             $self->_parse_next_line(LEAF);
577 0         0 }
578 0         0 $node =~ s/^(\S.*)\n(?=\S)/$1 /gm;
579 0 0       0 $node =~ s/^(\S.*)\n(\n+\S)/$1$2/gm;
580 0 0       0 $node =~ s/\n*\Z// unless $chomp eq '+';
581 0         0 $node .= "\n" unless $chomp;
582             return $node;
583             }
584              
585             # Parse a YAML block style scalar. This is like a Perl here-document.
586 1     1   2 sub _parse_block {
587 1         1 my $self = shift;
588 1         3 my ($chomp) = @_;
589 1   66     25 my $node = '';
590 11         250 while (not $self->done and $self->indent == $self->offset->[$self->level]) {
591 11         28 $node .= $self->content . "\n";
592             $self->_parse_next_line(LEAF);
593 1 50       5 }
594 1         20 return $node if '+' eq $chomp;
595 1 50       4 $node =~ s/\n*\Z/\n/;
596 1         4 $node =~ s/\n\Z// if $chomp eq '-';
597             return $node;
598             }
599              
600             # Handle Perl style '#' comments. Comments must be at the same indentation
601             # level as the collection line following them.
602 4     4   6 sub _parse_throwaway_comments {
603 4   33     5 my $self = shift;
  4         85  
604             while (@{$self->lines} and
605             $self->lines->[0] =~ m{^\s*(\#|$)}
606 0         0 ) {
  0         0  
607 0         0 shift @{$self->lines};
608             $self->{line}++;
609 4         7 }
  4         80  
610             $self->eos($self->{done} = not @{$self->lines});
611             }
612              
613             # This is the routine that controls what line is being parsed. It gets called
614             # once for each line in the YAML stream.
615             #
616             # This routine must:
617             # 1) Skip past the current line
618             # 2) Determine the indentation offset for a new level
619             # 3) Find the next _content_ line
620             # A) Skip over any throwaways (Comments/blanks)
621             # B) Set $self->indent, $self->content, $self->line
622             # 4) Expand tabs appropriately
623 15     15   23 sub _parse_next_line {
624 15         19 my $self = shift;
625 15         319 my ($type) = @_;
626 15         311 my $level = $self->level;
627 15 50       36 my $offset = $self->offset->[$level];
628 15         16 $self->die('YAML_EMIT_ERR_BAD_LEVEL') unless defined $offset;
  15         306  
629 15         22 shift @{$self->lines};
  15         300  
630 15 100       314 $self->eos($self->{done} = not @{$self->lines});
631 14         19 return if $self->eos;
632             $self->{line}++;
633              
634 14 100 100     281 # Determine the offset for a new leaf node
    100          
635             if ($self->preface =~
636             qr/(?:^|\s)(?:$FOLD_CHAR|$LIT_CHAR_RX)(?:-|\+)?(\d*)\s*$/
637 1 50 33     5 ) {
638             $self->die('YAML_PARSE_ERR_ZERO_INDENT')
639 1         2 if length($1) and $1 == 0;
640 1 50       2 $type = LEAF;
641 0         0 if (length($1)) {
642             $self->offset->[$level + 1] = $offset + $1;
643             }
644             else {
645 1   33     2 # First get rid of any comments.
  1         20  
646 0 0       0 while (@{$self->lines} && ($self->lines->[0] =~ /^\s*#/)) {
647 0 0       0 $self->lines->[0] =~ /^( *)/ or die;
648 0         0 last unless length($1) <= $offset;
  0         0  
649 0         0 shift @{$self->lines};
650             $self->{line}++;
651 1         2 }
  1         35  
652 1 50       25 $self->eos($self->{done} = not @{$self->lines});
653 1 50 33     21 return if $self->eos;
654 1         21 if ($self->lines->[0] =~ /^( *)\S/ and length($1) > $offset) {
655             $self->offset->[$level+1] = length($1);
656             }
657 0         0 else {
658             $self->offset->[$level+1] = $offset + 1;
659             }
660 1         22 }
661             $offset = $self->offset->[++$level];
662             }
663             # Determine the offset for a new collection level
664             elsif ($type == COLLECTION and
665 1         3 $self->preface =~ /^(\s*(\!\S*|\&\S+))*\s*$/) {
666 1 50       20 $self->_parse_throwaway_comments();
667 0         0 if ($self->eos) {
668 0         0 $self->offset->[$level+1] = $offset + 1;
669             return;
670             }
671 1 50       21 else {
672 1 50       4 $self->lines->[0] =~ /^( *)\S/ or die;
673 1         21 if (length($1) > $offset) {
674             $self->offset->[$level+1] = length($1);
675             }
676 0         0 else {
677             $self->offset->[$level+1] = $offset + 1;
678             }
679 1         20 }
680             $offset = $self->offset->[++$level];
681             }
682 14 100       41
683 12   33     15 if ($type == LEAF) {
  12   33     245  
684             while (@{$self->lines} and
685             $self->lines->[0] =~ m{^( *)(\#)} and
686             length($1) < $offset
687 0         0 ) {
  0         0  
688 0         0 shift @{$self->lines};
689             $self->{line}++;
690 12         17 }
  12         240  
691             $self->eos($self->{done} = not @{$self->lines});
692             }
693 2         5 else {
694             $self->_parse_throwaway_comments();
695 14 50       286 }
696             return if $self->eos;
697 14 50       280
698 0         0 if ($self->lines->[0] =~ /^---(\s|$)/) {
699 0         0 $self->done(1);
700             return;
701 14 100 100     305 }
    50          
702             if ($type == LEAF and
703             $self->lines->[0] =~ /^ {$offset}(.*)$/
704 11         225 ) {
705 11         221 $self->indent($offset);
706             $self->content($1);
707             }
708 0         0 elsif ($self->lines->[0] =~ /^\s*$/) {
709 0         0 $self->indent($offset);
710             $self->content('');
711             }
712 3         91 else {
713 3         73 $self->lines->[0] =~ /^( *)(\S.*)$/;
714 1         27 while ($self->offset->[$level] > length($1)) {
715             $level--;
716 3 50       80 }
717             $self->die('YAML_PARSE_ERR_INCONSISTENT_INDENTATION')
718 3         92 if $self->offset->[$level] != length($1);
719 3         76 $self->indent(length($1));
720             $self->content($2);
721 14 50       293 }
722             $self->die('YAML_PARSE_ERR_INDENTATION')
723             if $self->indent - $offset > 1;
724             }
725              
726             #==============================================================================
727             # Utility subroutines.
728             #==============================================================================
729              
730             # Printable characters for escapes
731             my %unescapes =
732             (
733             0 => "\x00", a => "\x07", t => "\x09",
734             n => "\x0a", v => "\x0b", f => "\x0c",
735             r => "\x0d", e => "\x1b", '\\' => '\\',
736             );
737            
738             # Transform all the backslash style escape characters to their literal meaning
739 0     0     sub _unescape {
740 0           my $self = shift;
741 0           my ($node) = @_;
742 0 0         $node =~ s/\\([never\\fart0]|x([0-9a-fA-F]{2}))/
743 0           (length($1)>1)?pack("H2",$2):$unescapes{$1}/gex;
744             return $node;
745             }
746              
747             1;
748              
749             __END__