File Coverage

inc/YAML/Loader.pm
Criterion Covered Total %
statement 213 440 48.4
branch 70 238 29.4
condition 20 82 24.3
subroutine 20 27 74.0
pod 0 1 0.0
total 323 788 40.9


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