File Coverage

blib/lib/Tiny/YAML.pm
Criterion Covered Total %
statement 78 172 45.3
branch 25 86 29.0
condition 4 12 33.3
subroutine 17 23 73.9
pod 4 8 50.0
total 128 301 42.5


line stmt bran cond sub pod time code
1 2     2   25296 use strict; use warnings;
  2     2   4  
  2         85  
  2         12  
  2         5  
  2         134  
2             package Tiny::YAML;
3             our $VERSION = '0.0.14';
4              
5             #####################################################################
6             # The Tiny::YAML API.
7             #
8             # These are the currently documented API functions/methods and
9             # exports:
10              
11 2     2   30 use base 'Exporter';
  2         4  
  2         1654  
12             our @EXPORT = qw{ Load Dump };
13             our @EXPORT_OK = qw{ LoadFile DumpFile };
14              
15             ###
16             # Functional/Export API:
17              
18             sub Load {
19 6     6 1 2486 my @data = Tiny::YAML->New->load(@_);
20 6 50       41139 wantarray ? @data : $data[0];
21             }
22              
23             sub LoadFile {
24 0     0 1 0 my $file = shift;
25 0         0 my @data = Tiny::YAML->New->load_file($file);
26 0 0       0 wantarray ? @data : $data[0];
27             }
28              
29             sub Dump {
30 6     6 1 22 return Tiny::YAML->new(@_)->_dump_string;
31             }
32              
33             sub DumpFile {
34 0     0 1 0 my $file = shift;
35 0         0 return Tiny::YAML->new(@_)->_dump_file($file);
36             }
37              
38              
39             ###
40             # Object Oriented API:
41              
42             # Create an empty Tiny::YAML object
43             # XXX-INGY Why do we use ARRAY object?
44             # NOTE: I get it now, but I think it's confusing and not needed.
45             # Will change it on a branch later, for review.
46             #
47             # XXX-XDG I don't support changing it yet. It's a very well-documented
48             # "API" of Tiny::YAML. I'd support deprecating it, but Adam suggested
49             # we not change it until YAML.pm's own OO API is established so that
50             # users only have one API change to digest, not two
51             sub new {
52 6     6 0 12 my $class = shift;
53 6         34 bless [ @_ ], $class;
54             }
55              
56             # XXX/YTTY - Normal style `new()` for migration.
57             sub New {
58 6     6 0 26 bless {}, shift;
59             }
60              
61              
62             #####################################################################
63             # Constants
64              
65             # Printed form of the unprintable characters in the lowest range
66             # of ASCII characters, listed by ASCII ordinal position.
67             my @UNPRINTABLE = qw(
68             0 x01 x02 x03 x04 x05 x06 a
69             b t n v f r x0E x0F
70             x10 x11 x12 x13 x14 x15 x16 x17
71             x18 x19 x1A e x1C x1D x1E x1F
72             );
73              
74             # Printable characters for escapes
75             my %UNESCAPES = (
76             0 => "\x00", z => "\x00", N => "\x85",
77             a => "\x07", b => "\x08", t => "\x09",
78             n => "\x0a", v => "\x0b", f => "\x0c",
79             r => "\x0d", e => "\x1b", '\\' => '\\',
80             );
81              
82             # These 3 values have special meaning when unquoted and using the
83             # default YAML schema. They need quotes if they are strings.
84             my %QUOTE = map { $_ => 1 } qw{
85             null true false
86             };
87              
88             #####################################################################
89             # Tiny::YAML Implementation.
90             #
91             # These are the private methods that do all the work. They may change
92             # at any time.
93              
94              
95             ###
96             # Loader functions:
97              
98             # Create an object from a file
99             sub load_file {
100 0     0 0 0 my $self = shift;
101              
102             # Check the file
103 0 0       0 my $file = shift or $self->_error( 'You did not specify a file name' );
104 0 0       0 $self->_error( "File '$file' does not exist" )
105             unless -e $file;
106 0 0       0 $self->_error( "'$file' is a directory, not a file" )
107             unless -f _;
108 0 0       0 $self->_error( "Insufficient permissions to read '$file'" )
109             unless -r _;
110              
111             # Open unbuffered with strict UTF-8 decoding and no translation layers
112 0         0 open( my $fh, "<:unix:encoding(UTF-8)", $file );
113 0 0       0 unless ( $fh ) {
114 0         0 $self->_error("Failed to open file '$file': $!");
115             }
116              
117             # slurp the contents
118 0         0 my $contents = eval {
119 2     2   12 use warnings FATAL => 'utf8';
  2         4  
  2         5368  
120 0         0 local $/;
121             <$fh>
122 0         0 };
123 0 0       0 if ( my $err = $@ ) {
124 0         0 $self->_error("Error reading from file '$file': $err");
125             }
126              
127             # close the file (release the lock)
128 0 0       0 unless ( close $fh ) {
129 0         0 $self->_error("Failed to close file '$file': $!");
130             }
131              
132 0         0 $self->_load_string( $contents );
133             }
134              
135             # Create an object from a string
136             sub load {
137 6     6 0 11 my $self = shift;
138 6         9 my $string = $_[0];
139 6 50       17 unless ( defined $string ) {
140 0         0 die \"Did not provide a string to load";
141             }
142              
143             # Check if Perl has it marked as characters, but it's internally
144             # inconsistent. E.g. maybe latin1 got read on a :utf8 layer
145 6 50 33     33 if ( utf8::is_utf8($string) && ! utf8::valid($string) ) {
146 0         0 die \<<'...';
147             Read an invalid UTF-8 string (maybe mixed UTF-8 and 8-bit character set).
148             Did you decode with lax ":utf8" instead of strict ":encoding(UTF-8)"?
149             ...
150             }
151              
152             # Ensure Unicode character semantics, even for 0x80-0xff
153 6         28 utf8::upgrade($string);
154              
155             # Check for and strip any leading UTF-8 BOM
156 6         11 $string =~ s/^\x{FEFF}//;
157              
158 6         36 return + Pegex::Parser->new(
159             grammar => 'YAML::Pegex::Grammar'->new,
160             receiver => 'Tiny::YAML::Constructor'->new,
161             # debug => 1,
162             )->parse($string);
163              
164 0 0       0 if ( ref $@ eq 'SCALAR' ) {
    0          
165 0         0 $self->_error(${$@});
  0         0  
166             } elsif ( $@ ) {
167 0         0 $self->_error($@);
168             }
169             }
170              
171             # sub _unquote_single {
172             # my ($self, $string) = @_;
173             # return '' unless length $string;
174             # $string =~ s/\'\'/\'/g;
175             # return $string;
176             # }
177             #
178             # sub _unquote_double {
179             # my ($self, $string) = @_;
180             # return '' unless length $string;
181             # $string =~ s/\\"/"/g;
182             # $string =~
183             # s{\\([Nnever\\fartz0b]|x([0-9a-fA-F]{2}))}
184             # {(length($1)>1)?pack("H2",$2):$UNESCAPES{$1}}gex;
185             # return $string;
186             # }
187              
188             ###
189             # Dumper functions:
190              
191             # Save an object to a file
192             sub _dump_file {
193 0     0   0 my $self = shift;
194              
195 0         0 require Fcntl;
196              
197             # Check the file
198 0 0       0 my $file = shift or $self->_error( 'You did not specify a file name' );
199              
200 0         0 my $fh;
201 0         0 open $fh, ">:unix:encoding(UTF-8)", $file;
202              
203             # serialize and spew to the handle
204 0         0 print {$fh} $self->_dump_string;
  0         0  
205              
206             # close the file (release the lock)
207 0 0       0 unless ( close $fh ) {
208 0         0 $self->_error("Failed to close file '$file': $!");
209             }
210              
211 0         0 return 1;
212             }
213              
214             # Save an object to a string
215             sub _dump_string {
216 6     6   10 my $self = shift;
217 6 50 33     51 return '' unless ref $self && @$self;
218              
219             # Iterate over the documents
220 6         7 my $indent = 0;
221 6         10 my @lines = ();
222              
223 6         9 eval {
224 6         12 foreach my $cursor ( @$self ) {
225 6         10 push @lines, '---';
226              
227             # An empty document
228 6 50       43 if ( ! defined $cursor ) {
    50          
    50          
    50          
229             # Do nothing
230              
231             # A scalar document
232             } elsif ( ! ref $cursor ) {
233 0         0 $lines[-1] .= ' ' . $self->_dump_scalar( $cursor );
234              
235             # A list at the root
236             } elsif ( ref $cursor eq 'ARRAY' ) {
237 0 0       0 unless ( @$cursor ) {
238 0         0 $lines[-1] .= ' []';
239 0         0 next;
240             }
241 0         0 push @lines, $self->_dump_array( $cursor, $indent, {} );
242              
243             # A hash at the root
244             } elsif ( ref $cursor eq 'HASH' ) {
245 6 50       34 unless ( %$cursor ) {
246 0         0 $lines[-1] .= ' {}';
247 0         0 next;
248             }
249 6         29 push @lines, $self->_dump_hash( $cursor, $indent, {} );
250              
251             } else {
252 0         0 die \("Cannot serialize " . ref($cursor));
253             }
254             }
255             };
256 6 50       21 if ( ref $@ eq 'SCALAR' ) {
    50          
257 0         0 $self->_error(${$@});
  0         0  
258             } elsif ( $@ ) {
259 0         0 $self->_error($@);
260             }
261              
262 6         7 join '', map { "$_\n" } @lines;
  15         65  
263             }
264              
265             sub _has_internal_string_value {
266 16     16   18 my $value = shift;
267 16         74 my $b_obj = B::svref_2object(\$value); # for round trip problem
268 16         66 return $b_obj->FLAGS & B::SVf_POK();
269             }
270              
271             sub _dump_scalar {
272 16     16   23 my $string = $_[1];
273 16         18 my $is_key = $_[2];
274             # Check this before checking length or it winds up looking like a string!
275 16         27 my $has_string_flag = _has_internal_string_value($string);
276 16 50       36 return '~' unless defined $string;
277 16 50       40 return "''" unless length $string;
278 16 100       48 if (Scalar::Util::looks_like_number($string)) {
279             # keys and values that have been used as strings get quoted
280 2 50 33     11 if ( $is_key || $has_string_flag ) {
281 2         8 return qq['$string'];
282             }
283             else {
284 0         0 return $string;
285             }
286             }
287 14 50       41 if ( $string =~ /[\x00-\x09\x0b-\x0d\x0e-\x1f\x7f-\x9f\'\n]/ ) {
288 0         0 $string =~ s/\\/\\\\/g;
289 0         0 $string =~ s/"/\\"/g;
290 0         0 $string =~ s/\n/\\n/g;
291 0         0 $string =~ s/[\x85]/\\N/g;
292 0         0 $string =~ s/([\x00-\x1f])/\\$UNPRINTABLE[ord($1)]/g;
293 0         0 $string =~ s/([\x7f-\x9f])/'\x' . sprintf("%X",ord($1))/ge;
  0         0  
294 0         0 return qq|"$string"|;
295             }
296 14 50 33     101 if ( $string =~ /(?:^[~!@#%&*|>?:,'"`{}\[\]]|^-+$|\s|:\z)/ or
297             $QUOTE{$string}
298             ) {
299 0         0 return "'$string'";
300             }
301 14         45 return $string;
302             }
303              
304             sub _dump_array {
305 0     0   0 my ($self, $array, $indent, $seen) = @_;
306 0 0       0 if ( $seen->{refaddr($array)}++ ) {
307 0         0 die \"Tiny::YAML does not support circular references";
308             }
309 0         0 my @lines = ();
310 0         0 foreach my $el ( @$array ) {
311 0         0 my $line = (' ' x $indent) . '-';
312 0         0 my $type = ref $el;
313 0 0       0 if ( ! $type ) {
    0          
    0          
314 0         0 $line .= ' ' . $self->_dump_scalar( $el );
315 0         0 push @lines, $line;
316              
317             } elsif ( $type eq 'ARRAY' ) {
318 0 0       0 if ( @$el ) {
319 0         0 push @lines, $line;
320 0         0 push @lines, $self->_dump_array( $el, $indent + 1, $seen );
321             } else {
322 0         0 $line .= ' []';
323 0         0 push @lines, $line;
324             }
325              
326             } elsif ( $type eq 'HASH' ) {
327 0 0       0 if ( keys %$el ) {
328 0         0 push @lines, $line;
329 0         0 push @lines, $self->_dump_hash( $el, $indent + 1, $seen );
330             } else {
331 0         0 $line .= ' {}';
332 0         0 push @lines, $line;
333             }
334              
335             } else {
336 0         0 die \"Tiny::YAML does not support $type references";
337             }
338             }
339              
340 0         0 @lines;
341             }
342              
343             sub _dump_hash {
344 8     8   16 my ($self, $hash, $indent, $seen) = @_;
345 8 50       47 if ( $seen->{refaddr($hash)}++ ) {
346 0         0 die \"Tiny::YAML does not support circular references";
347             }
348 8         12 my @lines = ();
349 8         33 foreach my $name ( sort keys %$hash ) {
350 9         51 my $el = $hash->{$name};
351 9         35 my $line = (' ' x $indent) . $self->_dump_scalar($name, 1) . ":";
352 9         13 my $type = ref $el;
353 9 100       25 if ( ! $type ) {
    50          
    50          
354 7         17 $line .= ' ' . $self->_dump_scalar( $el );
355 7         20 push @lines, $line;
356              
357             } elsif ( $type eq 'ARRAY' ) {
358 0 0       0 if ( @$el ) {
359 0         0 push @lines, $line;
360 0         0 push @lines, $self->_dump_array( $el, $indent + 1, $seen );
361             } else {
362 0         0 $line .= ' []';
363 0         0 push @lines, $line;
364             }
365              
366             } elsif ( $type eq 'HASH' ) {
367 2 50       7 if ( keys %$el ) {
368 2         5 push @lines, $line;
369 2         12 push @lines, $self->_dump_hash( $el, $indent + 1, $seen );
370             } else {
371 0         0 $line .= ' {}';
372 0         0 push @lines, $line;
373             }
374              
375             } else {
376 0         0 die \"Tiny::YAML does not support $type references";
377             }
378             }
379              
380 8         35 @lines;
381             }
382              
383             # Set error
384             sub _error {
385 0     0     require Carp;
386 0           my $errstr = $_[1];
387 0           $errstr =~ s/ at \S+ line \d+.*//;
388 0           Carp::croak( $errstr );
389             }
390              
391             #####################################################################
392             # Helper functions. Possibly not needed.
393              
394             # Use to detect nv or iv
395 2     2   16 use B;
  2         4  
  2         367  
396              
397             # Use Scalar::Util if possible, otherwise emulate it
398             BEGIN {
399 2     2   5 local $@;
400 2 50       3 if ( eval { require Scalar::Util; Scalar::Util->VERSION(1.18); } ) {
  2         12  
  2         84  
401 2         59 *refaddr = *Scalar::Util::refaddr;
402             }
403             else {
404 0         0 eval <<'END_PERL';
405             # Scalar::Util failed to load or too old
406             sub refaddr {
407             my $pkg = ref($_[0]) or return undef;
408             if ( !! UNIVERSAL::can($_[0], 'can') ) {
409             bless $_[0], 'Scalar::Util::Fake';
410             } else {
411             $pkg = undef;
412             }
413             "$_[0]" =~ /0x(\w+)/;
414             my $i = do { no warnings 'portable'; hex $1 };
415             bless $_[0], $pkg if defined $pkg;
416             $i;
417             }
418             END_PERL
419             }
420             }
421              
422             # For Tiny::YAML we want one simple file. These `INLINE`s get inlined before
423             # going to CPAN. We want to optimize this section over time. It gives us
424             # something *very* specific to optimize.
425              
426 2     2   11 no strict; # Needed for Pegex::Base to compile.
  2         3  
  2         1350  
427             #use Pegex::Base(); #INLINE
428 2     2   480 BEGIN { $INC{'Pegex/Base.pm'} = 'INLINE/Pegex/Base.pm' }
429             BEGIN {
430             #line 1 "Pegex::Base"
431             package
432             Pegex::Base;
433             # use Mo qw'build default builder xxx import nonlazy required';
434             # The following line of code was produced from the previous line by
435             # Mo::Inline version 0.38
436             no warnings;my$M=__PACKAGE__.'::';*{$M.Object::new}=sub{my$c=shift;my$s=bless{@_},$c;my%n=%{$c.'::'.':E'};map{$s->{$_}=$n{$_}->()if!exists$s->{$_}}keys%n;$s};*{$M.import}=sub{import warnings;$^H|=1538;my($P,%e,%o)=caller.'::';shift;eval"no Mo::$_",&{$M.$_.::e}($P,\%e,\%o,\@_)for@_;return if$e{M};%e=(extends,sub{eval"no $_[0]()";@{$P.ISA}=$_[0]},has,sub{my$n=shift;my$m=sub{$#_?$_[0]{$n}=$_[1]:$_[0]{$n}};@_=(default,@_)if!($#_%2);$m=$o{$_}->($m,$n,@_)for sort keys%o;*{$P.$n}=$m},%e,);*{$P.$_}=$e{$_}for keys%e;@{$P.ISA}=$M.Object};*{$M.'build::e'}=sub{my($P,$e)=@_;$e->{new}=sub{$c=shift;my$s=&{$M.Object::new}($c,@_);my@B;do{@B=($c.::BUILD,@B)}while($c)=@{$c.::ISA};exists&$_&&&$_($s)for@B;$s}};*{$M.'default::e'}=sub{my($P,$e,$o)=@_;$o->{default}=sub{my($m,$n,%a)=@_;exists$a{default}or return$m;my($d,$r)=$a{default};my$g='HASH'eq($r=ref$d)?sub{+{%$d}}:'ARRAY'eq$r?sub{[@$d]}:'CODE'eq$r?$d:sub{$d};my$i=exists$a{lazy}?$a{lazy}:!${$P.':N'};$i or ${$P.':E'}{$n}=$g and return$m;sub{$#_?$m->(@_):!exists$_[0]{$n}?$_[0]{$n}=$g->(@_):$m->(@_)}}};*{$M.'builder::e'}=sub{my($P,$e,$o)=@_;$o->{builder}=sub{my($m,$n,%a)=@_;my$b=$a{builder}or return$m;my$i=exists$a{lazy}?$a{lazy}:!${$P.':N'};$i or ${$P.':E'}{$n}=\&{$P.$b}and return$m;sub{$#_?$m->(@_):!exists$_[0]{$n}?$_[0]{$n}=$_[0]->$b:$m->(@_)}}};use constant XXX_skip=>1;my$dm='YAML::XS';*{$M.'xxx::e'}=sub{my($P,$e)=@_;$e->{WWW}=sub{require XXX;local$XXX::DumpModule=$dm;XXX::WWW(@_)};$e->{XXX}=sub{require XXX;local$XXX::DumpModule=$dm;XXX::XXX(@_)};$e->{YYY}=sub{require XXX;local$XXX::DumpModule=$dm;XXX::YYY(@_)};$e->{ZZZ}=sub{require XXX;local$XXX::DumpModule=$dm}};my$i=\&import;*{$M.import}=sub{(@_==2 and not$_[1])?pop@_:@_==1?push@_,grep!/import/,@f:();goto&$i};*{$M.'nonlazy::e'}=sub{${shift.':N'}=1};*{$M.'required::e'}=sub{my($P,$e,$o)=@_;$o->{required}=sub{my($m,$n,%a)=@_;if($a{required}){my$C=*{$P."new"}{CODE}||*{$M.Object::new}{CODE};no warnings 'redefine';*{$P."new"}=sub{my$s=$C->(@_);my%a=@_[1..$#_];die$n." required"if!exists$a{$n};$s}}$m}};@f=qw[build default builder xxx import nonlazy required];use strict;use warnings;
437              
438             our $DumpModule = 'YAML';
439             }
440             use strict;
441             #use Pegex::Optimizer; #INLINE
442             BEGIN { $INC{'Pegex/Optimizer.pm'} = 'INLINE/Pegex/Optimizer.pm' }
443             BEGIN {
444             #line 1 "Pegex::Optimizer"
445             package
446             Pegex::Optimizer;
447             use Pegex::Base;
448              
449             has parser => (required => 1);
450             has grammar => (required => 1);
451             has receiver => (required => 1);
452              
453             sub optimize_grammar {
454             my ($self, $start) = @_;
455             my $tree = $self->grammar->{tree};
456             return if $tree->{'+optimized'};
457             $self->set_max_parse if $self->parser->{maxparse};
458             $self->{extra} = {};
459             while (my ($name, $node) = each %$tree) {
460             next unless ref($node);
461             $self->optimize_node($node);
462             }
463             $self->optimize_node({'.ref' => $start});
464             my $extra = delete $self->{extra};
465             for my $key (%$extra) {
466             $tree->{$key} = $extra->{$key};
467             }
468             $tree->{'+optimized'} = 1;
469             }
470              
471             sub optimize_node {
472             my ($self, $node) = @_;
473              
474             my ($min, $max) = @{$node}{'+min', '+max'};
475             $node->{'+min'} = defined($max) ? 0 : 1
476             unless defined $node->{'+min'};
477             $node->{'+max'} = defined($min) ? 0 : 1
478             unless defined $node->{'+max'};
479             $node->{'+asr'} = 0
480             unless defined $node->{'+asr'};
481              
482             for my $kind (qw(ref rgx all any err code xxx)) {
483             return if $kind eq 'xxx';
484             if ($node->{rule} = $node->{".$kind"}) {
485             delete $node->{".$kind"};
486             $node->{kind} = $kind;
487             if ($kind eq 'ref') {
488             my $rule = $node->{rule} or die;
489             if (my $method = $self->grammar->can("rule_$rule")) {
490             $node->{method} = $self->make_method_wrapper($method);
491             }
492             elsif (not $self->grammar->{tree}{$rule}) {
493             if (my $method = $self->grammar->can("$rule")) {
494             warn <<"...";
495             Warning:
496              
497             You have a method called '$rule' in your grammar.
498             It should probably be called 'rule_$rule'.
499              
500             ...
501             }
502             die "No rule '$rule' defined in grammar";
503             }
504             }
505             $node->{method} ||= $self->parser->can("match_$kind") or die;
506             last;
507             }
508             }
509              
510             if ($node->{kind} =~ /^(?:all|any)$/) {
511             $self->optimize_node($_) for @{$node->{rule}};
512             }
513             elsif ($node->{kind} eq 'ref') {
514             my $ref = $node->{rule};
515             my $rule = $self->grammar->{tree}{$ref};
516             $rule ||= $self->{extra}{$ref} = {};
517             if (my $action = $self->receiver->can("got_$ref")) {
518             $rule->{action} = $action;
519             }
520             elsif (my $gotrule = $self->receiver->can("gotrule")) {
521             $rule->{action} = $gotrule;
522             }
523             if ($self->parser->{debug}) {
524             $node->{method} = $self->make_trace_wrapper($node->{method});
525             }
526             }
527             elsif ($node->{kind} eq 'rgx') {
528             # XXX $node;
529             }
530             }
531              
532             sub make_method_wrapper {
533             my ($self, $method) = @_;
534             return sub {
535             my ($parser, $ref, $parent) = @_;
536             @{$parser}{'rule', 'parent'} = ($ref, $parent);
537             $method->(
538             $parser->{grammar},
539             $parser,
540             $parser->{buffer},
541             $parser->{position},
542             );
543             }
544             }
545              
546             sub make_trace_wrapper {
547             my ($self, $method) = @_;
548             return sub {
549             my ($self, $ref, $parent) = @_;
550             my $asr = $parent->{'+asr'};
551             my $note =
552             $asr == -1 ? '(!)' :
553             $asr == 1 ? '(=)' :
554             '';
555             $self->trace("try_$ref$note");
556             my $result;
557             if ($result = $self->$method($ref, $parent)) {
558             $self->trace("got_$ref$note");
559             }
560             else {
561             $self->trace("not_$ref$note");
562             }
563             return $result;
564             }
565             }
566              
567             sub set_max_parse {
568             require Pegex::Parser;
569             my ($self) = @_;
570             my $maxparse = $self->parser->{maxparse};
571             no warnings 'redefine';
572             my $method = \&Pegex::Parser::match_ref;
573             my $counter = 0;
574             *Pegex::Parser::match_ref = sub {
575             die "Maximum parsing rules reached ($maxparse)\n"
576             if $counter++ >= $maxparse;
577             my $self = shift;
578             $self->$method(@_);
579             };
580             }
581             }
582             #use Pegex::Grammar; #INLINE
583             BEGIN { $INC{'Pegex/Grammar.pm'} = 'INLINE/Pegex/Grammar.pm' }
584             BEGIN {
585             #line 1 "Pegex::Grammar"
586             package
587             Pegex::Grammar;
588             use Pegex::Base;
589              
590             # Grammar can be in text or tree form. Tree will be compiled from text.
591             # Grammar can also be stored in a file.
592             has file => ();
593             has text => (
594             builder => 'make_text',
595             lazy => 1,
596             );
597             has tree => (
598             builder => 'make_tree',
599             lazy => 1,
600             );
601             has start_rules => [];
602              
603             sub make_text {
604             my ($self) = @_;
605             my $filename = $self->file
606             or return '';
607             open TEXT, $filename
608             or die "Can't open '$filename' for input\n:$!";
609             return do {local $/; }
610             }
611              
612             sub make_tree {
613             my ($self) = @_;
614             my $text = $self->text
615             or die "Can't create a '" . ref($self) .
616             "' grammar. No tree or text or file.";
617             require Pegex::Compiler;
618             return Pegex::Compiler->new->compile(
619             $text,
620             @{$self->start_rules || []}
621             )->tree;
622             }
623              
624             # This import is to support: perl -MPegex::Grammar::Module=compile
625             sub import {
626             my ($package) = @_;
627             if (((caller))[1] =~ /^-e?$/ and @_ == 2 and $_[1] eq 'compile') {
628             $package->compile_into_module();
629             exit;
630             }
631             if (my $env = $ENV{PERL_PEGEX_AUTO_COMPILE}) {
632             my %modules = map {($_, 1)} split ',', $env;
633             if ($modules{$package}) {
634             if (my $grammar_file = $package->file) {
635             if (-f $grammar_file) {
636             my $module = $package;
637             $module =~ s!::!/!g;
638             $module .= '.pm';
639             my $module_file = $INC{$module};
640             if (-M $grammar_file < -M $module_file) {
641             $package->compile_into_module();
642             local $SIG{__WARN__};
643             delete $INC{$module};
644             require $module;
645             }
646             }
647             }
648             }
649             }
650             }
651              
652             sub compile_into_module {
653             my ($package) = @_;
654             my $grammar_file = $package->file;
655             open GRAMMAR, $grammar_file
656             or die "Can't open $grammar_file for input";
657             my $grammar_text = do {local $/; };
658             close GRAMMAR;
659             my $module = $package;
660             $module =~ s!::!/!g;
661             $module = "$module.pm";
662             my $file = $INC{$module} or return;
663             my $perl;
664             my @rules;
665             if ($package->can('start_rules')) {
666             @rules = @{$package->start_rules || []};
667             }
668             if ($module eq 'Pegex/Pegex/Grammar.pm') {
669             require Pegex::Bootstrap;
670             $perl = Pegex::Bootstrap->new->compile($grammar_text, @rules)->to_perl;
671             }
672             else {
673             require Pegex::Compiler;
674             $perl = Pegex::Compiler->new->compile($grammar_text, @rules)->to_perl;
675             }
676             open IN, $file or die $!;
677             my $module_text = do {local $/; };
678             require Pegex;
679             my $msg = " # Generated/Inlined by Pegex::Grammar ($Pegex::VERSION)";
680             close IN;
681             $perl =~ s/^/ /gm;
682             $module_text =~ s/^(sub\s+make_tree\s*\{).*?(^\})/$1$msg\n$perl$2/ms;
683             $module_text =~ s/^(sub\s+tree\s*\{).*?(^\})/$1$msg\n$perl$2/ms;
684             chomp $grammar_text;
685             $grammar_text = "<<'...';\n$grammar_text\n...\n";
686             $module_text =~ s/^(sub\s+text\s*\{).*?(^\})/$1$msg\n$grammar_text$2/ms;
687             $grammar_text =~ s/^/# /gm;
688             $module_text =~ s/^(# sub\s+text\s*\{).*?(^# \})/$1$msg\n$grammar_text$2/ms;
689             open OUT, '>', $file or die $!;
690             print OUT $module_text;
691             close OUT;
692             print "Compiled '$grammar_file' into '$file'.\n";
693             }
694             }
695             #use Pegex::Tree; #INLINE
696             BEGIN { $INC{'Pegex/Tree.pm'} = 'INLINE/Pegex/Tree.pm' }
697             BEGIN {
698             #line 1 "Pegex::Tree"
699             package
700             Pegex::Tree;
701             use Pegex::Base;
702             extends 'Pegex::Receiver';
703              
704             sub gotrule {
705             my $self = shift;
706             @_ || return ();
707             return {$self->{parser}{rule} => $_[0]}
708             if $self->{parser}{parent}{-wrap};
709             return $_[0];
710             }
711              
712             sub final {
713             my $self = shift;
714             return(shift) if @_;
715             return [];
716             }
717             }
718             #use Pegex::Input; #INLINE
719             BEGIN { $INC{'Pegex/Input.pm'} = 'INLINE/Pegex/Input.pm' }
720             BEGIN {
721             #line 1 "Pegex::Input"
722             package
723             Pegex::Input;
724              
725             use Pegex::Base;
726              
727             has string => ();
728             has stringref => ();
729             has file => ();
730             has handle => ();
731             has _buffer => ();
732             has _is_eof => 0;
733             has _is_open => 0;
734             has _is_close => 0;
735              
736             # NOTE: Current implementation reads entire input into _buffer on open().
737             sub read {
738             my ($self) = @_;
739             die "Attempted Pegex::Input::read before open" if not $self->{_is_open};
740             die "Attempted Pegex::Input::read after EOF" if $self->{_is_eof};
741              
742             my $buffer = $self->{_buffer};
743             $self->{_buffer} = undef;
744             $self->{_is_eof} = 1;
745              
746             return $buffer;
747             }
748              
749             sub open {
750             my ($self) = @_;
751             die "Attempted to reopen Pegex::Input object"
752             if $self->{_is_open} or $self->{_is_close};
753              
754             if (my $ref = $self->{stringref}) {
755             $self->{_buffer} = $ref;
756             }
757             elsif (my $handle = $self->{handle}) {
758             $self->{_buffer} = \ do { local $/; <$handle> };
759             }
760             elsif (my $path = $self->{file}) {
761             open my $handle, $path
762             or die "Pegex::Input can't open $path for input:\n$!";
763             $self->{_buffer} = \ do { local $/; <$handle> };
764             }
765             elsif (exists $self->{string}) {
766             $self->{_buffer} = \$self->{string};
767             }
768             else {
769             die "Pegex::Input::open failed. No source to open";
770             }
771             $self->{_is_open} = 1;
772             return $self;
773             }
774              
775             sub close {
776             my ($self) = @_;
777             die "Attempted to close an unopen Pegex::Input object"
778             if $self->{_is_close};
779             close $self->{handle} if $self->{handle};
780             $self->{_is_open} = 0;
781             $self->{_is_close} = 1;
782             $self->{_buffer} = undef;
783             return $self;
784             }
785             }
786             #use Pegex::Parser; #INLINE
787             BEGIN { $INC{'Pegex/Parser.pm'} = 'INLINE/Pegex/Parser.pm' }
788             BEGIN {
789             #line 1 "Pegex::Parser"
790             package
791             Pegex::Parser;
792             use Pegex::Base;
793              
794             use Pegex::Input;
795             use Pegex::Optimizer;
796             use Scalar::Util;
797              
798             has grammar => (required => 1);
799             has receiver => ();
800             has input => ();
801             has debug => (
802             exists($ENV{PERL_PEGEX_DEBUG}) ? $ENV{PERL_PEGEX_DEBUG} :
803             defined($Pegex::Parser::Debug) ? $Pegex::Parser::Debug :
804             0
805             );
806             sub BUILD {
807             my ($self) = @_;
808             $self->{throw_on_error} ||= 1;
809             # $self->{rule} = undef;
810             # $self->{parent} = undef;
811             # $self->{error} = undef;
812             # $self->{position} = undef;
813             # $self->{farthest} = undef;
814             }
815              
816             # XXX Add an optional $position argument. Default to 0. This is the position
817             # to start parsing. Set position and farthest below to this value. Allows for
818             # sub-parsing. Need to somehow return the finishing position of a subparse.
819             # Maybe this all goes in a subparse() method.
820             sub parse {
821             my ($self, $input, $start) = @_;
822              
823             $start =~ s/-/_/g if $start;
824              
825             $self->{position} = 0;
826             $self->{farthest} = 0;
827              
828             $self->{input} = (not ref $input)
829             ? Pegex::Input->new(string => $input)
830             : $input;
831              
832             $self->{input}->open
833             unless $self->{input}{_is_open};
834             $self->{buffer} = $self->{input}->read;
835              
836             die "No 'grammar'. Can't parse"
837             unless $self->{grammar};
838              
839             $self->{grammar}{tree} ||= $self->{grammar}->make_tree;
840              
841             my $start_rule_ref = $start ||
842             $self->{grammar}{tree}{'+toprule'} ||
843             $self->{grammar}{tree}{'TOP'} & 'TOP' or
844             die "No starting rule for Pegex::Parser::parse";
845              
846             die "No 'receiver'. Can't parse"
847             unless $self->{receiver};
848              
849             my $optimizer = Pegex::Optimizer->new(
850             parser => $self,
851             grammar => $self->{grammar},
852             receiver => $self->{receiver},
853             );
854              
855             $optimizer->optimize_grammar($start_rule_ref);
856              
857             # Add circular ref and weaken it.
858             $self->{receiver}{parser} = $self;
859             Scalar::Util::weaken($self->{receiver}{parser});
860              
861             if ($self->{receiver}->can("initial")) {
862             $self->{rule} = $start_rule_ref;
863             $self->{parent} = {};
864             $self->{receiver}->initial();
865             }
866              
867             my $match = $self->debug ? do {
868             my $method = $optimizer->make_trace_wrapper(\&match_ref);
869             $self->$method($start_rule_ref, {'+asr' => 0});
870             } : $self->match_ref($start_rule_ref, {});
871              
872             $self->{input}->close;
873              
874             if (not $match or $self->{position} < length ${$self->{buffer}}) {
875             $self->throw_error("Parse document failed for some reason");
876             return; # In case $self->throw_on_error is off
877             }
878              
879             if ($self->{receiver}->can("final")) {
880             $self->{rule} = $start_rule_ref;
881             $self->{parent} = {};
882             $match = [ $self->{receiver}->final(@$match) ];
883             }
884              
885             $match->[0];
886             }
887              
888             sub match_next {
889             my ($self, $next) = @_;
890              
891             my ($rule, $method, $kind, $min, $max, $assertion) =
892             @{$next}{'rule', 'method', 'kind', '+min', '+max', '+asr'};
893              
894             my ($position, $match, $count) =
895             ($self->{position}, [], 0);
896              
897             while (my $return = $method->($self, $rule, $next)) {
898             $position = $self->{position} unless $assertion;
899             $count++;
900             push @$match, @$return;
901             last if $max == 1;
902             }
903             if (not $count and $min == 0 and $kind eq 'all') {
904             $match = [[]];
905             }
906             if ($max != 1) {
907             if ($next->{-flat}) {
908             $match = [ map { (ref($_) eq 'ARRAY') ? (@$_) : ($_) } @$match ];
909             }
910             else {
911             $match = [$match]
912             }
913             $self->{farthest} = $position
914             if ($self->{position} = $position) > $self->{farthest};
915             }
916             my $result = ($count >= $min and (not $max or $count <= $max))
917             ^ ($assertion == -1);
918             if (not($result) or $assertion) {
919             $self->{farthest} = $position
920             if ($self->{position} = $position) > $self->{farthest};
921             }
922              
923             ($result ? $next->{'-skip'} ? [] : $match : 0);
924             }
925              
926             sub match_rule {
927             my ($self, $position, $match) = (@_, []);
928             $self->{position} = $position;
929             $self->{farthest} = $position
930             if $position > $self->{farthest};
931             $match = [ $match ] if @$match > 1;
932             my ($ref, $parent) = @{$self}{'rule', 'parent'};
933             my $rule = $self->{grammar}{tree}{$ref}
934             or die "No rule defined for '$ref'";
935              
936             [ $rule->{action}->($self->{receiver}, @$match) ];
937             }
938              
939             sub match_ref {
940             my ($self, $ref, $parent) = @_;
941             my $rule = $self->{grammar}{tree}{$ref}
942             or die "No rule defined for '$ref'";
943             my $match = $self->match_next($rule) or return;
944             return $Pegex::Constant::Dummy unless $rule->{action};
945             @{$self}{'rule', 'parent'} = ($ref, $parent);
946              
947             # XXX Possible API mismatch.
948             # Not sure if we should "splat" the $match.
949             [ $rule->{action}->($self->{receiver}, @$match) ];
950             }
951              
952             sub match_rgx {
953             my ($self, $regexp) = @_;
954             my $buffer = $self->{buffer};
955              
956             pos($$buffer) = $self->{position};
957             $$buffer =~ /$regexp/g or return;
958              
959             $self->{position} = pos($$buffer);
960              
961             $self->{farthest} = $self->{position}
962             if $self->{position} > $self->{farthest};
963              
964             no strict 'refs';
965             my $captures = [ map $$_, 1..$#+ ];
966             $captures = [ $captures ] if $#+ > 1;
967              
968             return $captures;
969             }
970              
971             sub match_all {
972             my ($self, $list) = @_;
973             my $position = $self->{position};
974             my $set = [];
975             my $len = 0;
976             for my $elem (@$list) {
977             if (my $match = $self->match_next($elem)) {
978             if (not ($elem->{'+asr'} or $elem->{'-skip'})) {
979             push @$set, @$match;
980             $len++;
981             }
982             }
983             else {
984             $self->{farthest} = $position
985             if ($self->{position} = $position) > $self->{farthest};
986             return;
987             }
988             }
989             $set = [ $set ] if $len > 1;
990             return $set;
991             }
992              
993             sub match_any {
994             my ($self, $list) = @_;
995             for my $elem (@$list) {
996             if (my $match = $self->match_next($elem)) {
997             return $match;
998             }
999             }
1000             return;
1001             }
1002              
1003             sub match_err {
1004             my ($self, $error) = @_;
1005             $self->throw_error($error);
1006             }
1007              
1008             sub trace {
1009             my ($self, $action) = @_;
1010             my $indent = ($action =~ /^try_/) ? 1 : 0;
1011             $self->{indent} ||= 0;
1012             $self->{indent}-- unless $indent;
1013             print STDERR ' ' x $self->{indent};
1014             $self->{indent}++ if $indent;
1015             my $snippet = substr(${$self->{buffer}}, $self->{position});
1016             $snippet = substr($snippet, 0, 30) . "..."
1017             if length $snippet > 30;
1018             $snippet =~ s/\n/\\n/g;
1019             print STDERR sprintf("%-30s", $action) .
1020             ($indent ? " >$snippet<\n" : "\n");
1021             }
1022              
1023             sub throw_error {
1024             my ($self, $msg) = @_;
1025             $@ = $self->{error} = $self->format_error($msg);
1026             return undef unless $self->{throw_on_error};
1027             require Carp;
1028             Carp::croak($self->{error});
1029             }
1030              
1031             sub format_error {
1032             my ($self, $msg) = @_;
1033             my $buffer = $self->{buffer};
1034             my $position = $self->{farthest};
1035             my $real_pos = $self->{position};
1036              
1037             my $line = @{[substr($$buffer, 0, $position) =~ /(\n)/g]} + 1;
1038             my $column = $position - rindex($$buffer, "\n", $position);
1039              
1040             my $pretext = substr(
1041             $$buffer,
1042             $position < 50 ? 0 : $position - 50,
1043             $position < 50 ? $position : 50
1044             );
1045             my $context = substr($$buffer, $position, 50);
1046             $pretext =~ s/.*\n//gs;
1047             $context =~ s/\n/\\n/g;
1048              
1049             return <<"...";
1050             Error parsing Pegex document:
1051             msg: $msg
1052             line: $line
1053             column: $column
1054             context: $pretext$context
1055             ${\ (' ' x (length($pretext) + 10) . '^')}
1056             position: $position ($real_pos pre-lookahead)
1057             ...
1058             }
1059              
1060             # TODO Move this to a Parser helper role/subclass
1061             sub line_column {
1062             my ($self, $position) = @_;
1063             $position ||= $self->{position};
1064             my $buffer = $self->{buffer};
1065             my $line = @{[substr($$buffer, 0, $position) =~ /(\n)/g]} + 1;
1066             my $column = $position - rindex($$buffer, "\n", $position);
1067             return [$line, $position];
1068             }
1069              
1070             # XXX Need to figure out what uses this. (sample.t)
1071             {
1072             package
1073             Pegex::Constant;
1074             our $Null = [];
1075             our $Dummy = [];
1076             }
1077             }
1078             #use YAML::Pegex::Grammar 0.0.8; #INLINE
1079             BEGIN { $INC{'YAML/Pegex/Grammar.pm'} = 'INLINE/YAML/Pegex/Grammar.pm' }
1080             BEGIN {
1081             #line 1 "YAML::Pegex::Grammar"
1082             use strict; use warnings;
1083             package
1084             YAML::Pegex::Grammar;
1085             our $VERSION = '0.0.8';
1086              
1087             use Pegex::Base;
1088             extends 'Pegex::Grammar';
1089              
1090             use constant file => '../yaml-pgx/yaml.pgx';
1091              
1092             has indent => [];
1093              
1094             sub rule_block_indent {
1095             my ($self, $parser, $buffer, $pos) = @_;
1096             my $indents = $self->{indent};
1097             pos($$buffer) = $pos;
1098             return if $pos >= length($$buffer);
1099             if ($pos == 0) {
1100             $$buffer =~ /\G( *)(?=[^\s\#])/g or die;
1101             push @$indents, length($1);
1102             return $parser->match_rule($pos);
1103             }
1104             my $len = @$indents ? $indents->[-1] + 1 : 0;
1105             $$buffer =~ /\G\r?\n( {$len,})(?=[^\s\#])/g or return;
1106             push @$indents, length($1);
1107             return $parser->match_rule($pos);
1108             }
1109              
1110             sub rule_block_ondent {
1111             my ($self, $parser, $buffer, $pos) = @_;
1112             my $indents = $self->{indent};
1113             my $len = $indents->[-1];
1114             my $re = $pos > 0 ? '\r?\n' : '';
1115             pos($$buffer) = $pos;
1116             $$buffer =~ /\G$re( {$len})(?=[^\s\#])/g or return;
1117             return $parser->match_rule(pos($$buffer));
1118             }
1119              
1120             sub rule_block_undent {
1121             my ($self, $parser, $buffer, $pos) = @_;
1122             my $indents = $self->{indent};
1123             return unless @$indents;
1124             my $len = $indents->[-1];
1125             pos($$buffer) = $pos;
1126             if ($$buffer =~ /\G((?:\r?\n)?)(?=\z|\.\.\.\r?\n|\-\-\-\r?\n)/ or
1127             $$buffer !~ /\G\r?\n( {$len})/g
1128             ) {
1129             pop @$indents;
1130             return $parser->match_rule($pos);
1131             }
1132             return;
1133             }
1134              
1135             # sub make_tree {
1136             # use Pegex::Bootstrap;
1137             # use IO::All;
1138             # my $grammar = io->file(file)->all;
1139             # Pegex::Bootstrap->new->compile($grammar)->tree;
1140             # }
1141             # sub make_treeXXX {
1142             sub make_tree {
1143             {
1144             '+grammar' => 'yaml',
1145             '+toprule' => 'yaml_stream',
1146             '+version' => '0.0.1',
1147             'EOL' => {
1148             '.rgx' => qr/\G\r?\n/
1149             },
1150             'SPACE' => {
1151             '.rgx' => qr/\G\ /
1152             },
1153             'block_key' => {
1154             '.rgx' => qr/\G(\|\r?\nXXX|\>\r?\nXXX|"[^"]*"|'[^']*'|(?![&\*\#\{\}\[\]%`\@]).+?(?=:\s|\r?\n|\z)):(?:\ +|\ *(?=\r?\n))/
1155             },
1156             'block_mapping' => {
1157             '.all' => [
1158             {
1159             '.ref' => 'block_indent'
1160             },
1161             {
1162             '+min' => 1,
1163             '.ref' => 'block_mapping_pair'
1164             },
1165             {
1166             '.ref' => 'block_undent'
1167             }
1168             ]
1169             },
1170             'block_mapping_pair' => {
1171             '.all' => [
1172             {
1173             '.ref' => 'block_ondent'
1174             },
1175             {
1176             '.ref' => 'block_key'
1177             },
1178             {
1179             '.ref' => 'block_value'
1180             }
1181             ]
1182             },
1183             'block_node' => {
1184             '.any' => [
1185             {
1186             '.ref' => 'block_sequence'
1187             },
1188             {
1189             '.ref' => 'block_mapping'
1190             },
1191             {
1192             '.ref' => 'block_scalar'
1193             }
1194             ]
1195             },
1196             'block_scalar' => {
1197             '.rgx' => qr/\G(\|\r?\nXXX|\>\r?\nXXX|"[^"]*"|'[^']*'|(?![&\*\#\{\}\[\]%`\@]).+?(?=:\s|\r?\n|\z))/
1198             },
1199             'block_sequence' => {
1200             '+min' => 1,
1201             '.ref' => 'block_sequence_entry'
1202             },
1203             'block_sequence_entry' => {
1204             '.rgx' => qr/\G\-\ +(\|\r?\nXXX|\>\r?\nXXX|"[^"]*"|'[^']*'|(?![&\*\#\{\}\[\]%`\@]).+?(?=:\s|\r?\n|\z))\r?\n/
1205             },
1206             'block_value' => {
1207             '.any' => [
1208             {
1209             '.ref' => 'flow_mapping'
1210             },
1211             {
1212             '.ref' => 'flow_sequence'
1213             },
1214             {
1215             '.ref' => 'block_node'
1216             }
1217             ]
1218             },
1219             'document_foot' => {
1220             '.rgx' => qr/\G\.\.\.\r?\n/
1221             },
1222             'document_head' => {
1223             '.rgx' => qr/\G\-\-\-(?:\ +|(?=\r?\n))/
1224             },
1225             'flow_mapping' => {
1226             '.all' => [
1227             {
1228             '.ref' => 'flow_mapping_start'
1229             },
1230             {
1231             '+max' => 1,
1232             '.all' => [
1233             {
1234             '.ref' => 'flow_mapping_pair'
1235             },
1236             {
1237             '+min' => 0,
1238             '-flat' => 1,
1239             '.all' => [
1240             {
1241             '.ref' => 'list_separator'
1242             },
1243             {
1244             '.ref' => 'flow_mapping_pair'
1245             }
1246             ]
1247             },
1248             {
1249             '+max' => 1,
1250             '.ref' => 'list_separator'
1251             }
1252             ]
1253             },
1254             {
1255             '.ref' => 'flow_mapping_end'
1256             }
1257             ]
1258             },
1259             'flow_mapping_end' => {
1260             '.rgx' => qr/\G\s*\}\s*/
1261             },
1262             'flow_mapping_pair' => {
1263             '.all' => [
1264             {
1265             '.ref' => 'flow_node'
1266             },
1267             {
1268             '.ref' => 'flow_mapping_separator'
1269             },
1270             {
1271             '.ref' => 'flow_node'
1272             }
1273             ]
1274             },
1275             'flow_mapping_separator' => {
1276             '.rgx' => qr/\G:(?:\ +|\ *(?=\r?\n))/
1277             },
1278             'flow_mapping_start' => {
1279             '.rgx' => qr/\G\s*\{\s*/
1280             },
1281             'flow_node' => {
1282             '.any' => [
1283             {
1284             '.ref' => 'flow_sequence'
1285             },
1286             {
1287             '.ref' => 'flow_mapping'
1288             },
1289             {
1290             '.ref' => 'flow_scalar'
1291             }
1292             ]
1293             },
1294             'flow_scalar' => {
1295             '.rgx' => qr/\G("[^"]*"|'[^']*'|(?![&\*\#\{\}\[\]%`\@]).+?(?=[&\*\#\{\}\[\]%,]|:\ |,\ |\r?\n|\z))/
1296             },
1297             'flow_sequence' => {
1298             '.all' => [
1299             {
1300             '.ref' => 'flow_sequence_start'
1301             },
1302             {
1303             '+max' => 1,
1304             '.all' => [
1305             {
1306             '.ref' => 'flow_sequence_entry'
1307             },
1308             {
1309             '+min' => 0,
1310             '-flat' => 1,
1311             '.all' => [
1312             {
1313             '.ref' => 'list_separator'
1314             },
1315             {
1316             '.ref' => 'flow_sequence_entry'
1317             }
1318             ]
1319             },
1320             {
1321             '+max' => 1,
1322             '.ref' => 'list_separator'
1323             }
1324             ]
1325             },
1326             {
1327             '.ref' => 'flow_sequence_end'
1328             }
1329             ]
1330             },
1331             'flow_sequence_end' => {
1332             '.rgx' => qr/\G\s*\]\s*/
1333             },
1334             'flow_sequence_entry' => {
1335             '.ref' => 'flow_scalar'
1336             },
1337             'flow_sequence_start' => {
1338             '.rgx' => qr/\G\s*\[\s*/
1339             },
1340             'ignore_line' => {
1341             '.rgx' => qr/\G(?:\#.*|[\ \t]*)(?=\r?\n)/
1342             },
1343             'list_separator' => {
1344             '.rgx' => qr/\G,\ +/
1345             },
1346             'node_alias' => {
1347             '.rgx' => qr/\G\*(\w+)/
1348             },
1349             'node_anchor' => {
1350             '.rgx' => qr/\G\&(\w+)/
1351             },
1352             'node_prefix' => {
1353             '.any' => [
1354             {
1355             '.all' => [
1356             {
1357             '.ref' => 'node_anchor'
1358             },
1359             {
1360             '+max' => 1,
1361             '.all' => [
1362             {
1363             '+min' => 1,
1364             '.ref' => 'SPACE'
1365             },
1366             {
1367             '.ref' => 'node_tag'
1368             }
1369             ]
1370             }
1371             ]
1372             },
1373             {
1374             '.all' => [
1375             {
1376             '.ref' => 'node_tag'
1377             },
1378             {
1379             '+max' => 1,
1380             '.all' => [
1381             {
1382             '+min' => 1,
1383             '.ref' => 'SPACE'
1384             },
1385             {
1386             '.ref' => 'node_anchor'
1387             }
1388             ]
1389             }
1390             ]
1391             }
1392             ]
1393             },
1394             'node_tag' => {
1395             '.rgx' => qr/\G!!?(\w+)/
1396             },
1397             'top_node' => {
1398             '.all' => [
1399             {
1400             '+max' => 1,
1401             '.ref' => 'node_prefix'
1402             },
1403             {
1404             '.any' => [
1405             {
1406             '.ref' => 'node_alias'
1407             },
1408             {
1409             '.ref' => 'flow_mapping'
1410             },
1411             {
1412             '.ref' => 'flow_sequence'
1413             },
1414             {
1415             '.ref' => 'block_sequence'
1416             },
1417             {
1418             '.ref' => 'block_mapping'
1419             },
1420             {
1421             '.ref' => 'block_scalar'
1422             }
1423             ]
1424             },
1425             {
1426             '+max' => 1,
1427             '.ref' => 'EOL'
1428             }
1429             ]
1430             },
1431             'yaml_document' => {
1432             '.all' => [
1433             {
1434             '+max' => 1,
1435             '.ref' => 'document_head'
1436             },
1437             {
1438             '.ref' => 'top_node'
1439             },
1440             {
1441             '+max' => 1,
1442             '.ref' => 'ignore_line'
1443             },
1444             {
1445             '+max' => 1,
1446             '.ref' => 'document_foot'
1447             }
1448             ]
1449             },
1450             'yaml_stream' => {
1451             '.all' => [
1452             {
1453             '+min' => 0,
1454             '.ref' => 'ignore_line'
1455             },
1456             {
1457             '+min' => 0,
1458             '.all' => [
1459             {
1460             '.ref' => 'yaml_document'
1461             },
1462             {
1463             '+min' => 0,
1464             '.ref' => 'ignore_line'
1465             }
1466             ]
1467             }
1468             ]
1469             }
1470             }
1471             }
1472             }
1473             #use Tiny::YAML::Constructor; #INLINE
1474             BEGIN { $INC{'Tiny/YAML/Constructor.pm'} = 'INLINE/Tiny/YAML/Constructor.pm' }
1475             BEGIN {
1476             #line 1 "Tiny::YAML::Constructor"
1477             use strict; use warnings;
1478             package
1479             Tiny::YAML::Constructor;
1480             use Pegex::Base;
1481             extends 'Pegex::Tree';
1482              
1483             sub init {
1484             my ($self) = @_;
1485             $self->{data} = [];
1486             return;
1487             }
1488              
1489             sub final {
1490             my ($self) = @_;
1491             return @{$self->{data}};
1492             }
1493              
1494             sub got_block_mapping {
1495             my ($self, $got) = @_;
1496             return +{
1497             map {
1498             @$_
1499             } @{$got->[0]}
1500             };
1501             }
1502              
1503             sub got_yaml_document {
1504             my ($self, $got) = @_;
1505             push @{$self->{data}}, $got->[0][0];
1506             return;
1507             }
1508             }
1509              
1510             1;