1 package TAP::Parser::YAMLish::Reader;
10 # Handle blessed object syntax
12 # Printable characters for escapes
14 z => "\x00", a => "\x07", t => "\x09",
15 n => "\x0a", v => "\x0b", f => "\x0c",
16 r => "\x0d", e => "\x1b", '\\' => '\\',
19 my $QQ_STRING = qr{ " (?:\\. | [^"])* " }x;
20 my $HASH_LINE = qr{ ^ ($QQ_STRING|\S+) \s* : (?: \s+ (.+?) \s* )? $ }x;
21 my $IS_HASH_KEY = qr{ ^ [\w\'\"] }x;
22 my $IS_END_YAML = qr{ ^ \.\.\. \s* $ }x;
23 my $IS_QQ_STRING = qr{ ^ $QQ_STRING $ }x;
25 # Create an empty TAP::Parser::YAMLish::Reader object
35 die "Must have a code reference to read input from"
36 unless ref $obj eq 'CODE';
38 $self->{reader} = $obj;
39 $self->{capture} = [];
44 my $doc = $self->_read;
46 # The terminator is mandatory otherwise we'd consume a line from the
47 # iterator that doesn't belong to us. If we want to remove this
48 # restriction we'll have to implement look-ahead in the iterators.
49 # Which might not be a bad idea.
50 my $dots = $self->_peek;
51 die "Missing '...' at end of YAMLish"
53 and $dots =~ $IS_END_YAML;
55 delete $self->{reader};
64 if ( defined( my $capture = $self->{capture} ) ) {
65 return join( "\n", @$capture ) . "\n";
73 return $self->{next} unless wantarray;
74 my $line = $self->{next};
75 $line =~ /^ (\s*) (.*) $ /x;
76 return ( $2, length $1 );
81 die "_next called with no reader"
82 unless $self->{reader};
83 my $line = $self->{reader}->();
84 $self->{next} = $line;
85 push @{ $self->{capture} }, $line;
91 my $line = $self->_peek;
93 # Do we have a document header?
94 if ( $line =~ /^ --- (?: \s* (.+?) \s* )? $/x ) {
97 return $self->_read_scalar($1) if defined $1; # Inline?
99 my ( $next, $indent ) = $self->_peek;
101 if ( $next =~ /^ - /x ) {
102 return $self->_read_array($indent);
104 elsif ( $next =~ $IS_HASH_KEY ) {
105 return $self->_read_hash( $next, $indent );
107 elsif ( $next =~ $IS_END_YAML ) {
108 die "Premature end of YAMLish";
111 die "Unsupported YAMLish syntax: '$next'";
115 die "YAMLish document header not found";
119 # Parse a double quoted string
124 unless ( $str =~ s/^ " (.*?) " $/$1/x ) {
125 die "Internal: not a quoted string";
129 $str =~ s/ \\ ( [tartan\\favez] | x([0-9a-fA-F]{2}) )
130 / (length($1) > 1) ? pack("H2", $2) : $UNESCAPES{$1} /gex;
134 # Parse a scalar string to the actual scalar
139 return undef if $string eq '~';
140 return {} if $string eq '{}';
141 return [] if $string eq '[]';
143 if ( $string eq '>' || $string eq '|' ) {
145 my ( $line, $indent ) = $self->_peek;
146 die "Multi-line scalar content missing" unless defined $line;
148 my @multiline = ($line);
152 my ( $next, $ind ) = $self->_peek;
153 last if $ind < $indent;
154 push @multiline, $next;
157 return join( ( $string eq '>' ? ' ' : "\n" ), @multiline ) . "\n";
160 if ( $string =~ /^ ' (.*) ' $/x ) {
161 ( my $rv = $1 ) =~ s/''/'/g;
165 if ( $string =~ $IS_QQ_STRING ) {
166 return $self->_read_qq($string);
169 if ( $string =~ /^['"]/ ) {
171 # A quote with folding... we don't support that
172 die __PACKAGE__ . " does not support multi-line quoted scalars";
175 # Regular unquoted string
182 my ( $line, $indent ) = $self->_peek;
184 if ( $line =~ /^ -/x ) {
185 return $self->_read_array($indent);
187 elsif ( $line =~ $IS_HASH_KEY ) {
188 return $self->_read_hash( $line, $indent );
191 die "Unsupported YAMLish syntax: '$line'";
197 my ( $self, $limit ) = @_;
202 my ( $line, $indent ) = $self->_peek;
206 || $line =~ $IS_END_YAML;
208 if ( $indent > $limit ) {
209 die "Array line over-indented";
212 if ( $line =~ /^ (- \s+) \S+ \s* : (?: \s+ | $ ) /x ) {
213 $indent += length $1;
215 push @$ar, $self->_read_hash( $line, $indent );
217 elsif ( $line =~ /^ - \s* (.+?) \s* $/x ) {
218 die "Unexpected start of YAMLish" if $line =~ /^---/;
220 push @$ar, $self->_read_scalar($1);
222 elsif ( $line =~ /^ - \s* $/x ) {
224 push @$ar, $self->_read_nested;
226 elsif ( $line =~ $IS_HASH_KEY ) {
228 push @$ar, $self->_read_hash( $line, $indent, );
231 die "Unsupported YAMLish syntax: '$line'";
239 my ( $self, $line, $limit ) = @_;
245 die "Badly formed hash line: '$line'"
246 unless $line =~ $HASH_LINE;
248 my ( $key, $value ) = ( $self->_read_scalar($1), $2 );
251 if ( defined $value ) {
252 $hash->{$key} = $self->_read_scalar($value);
255 $hash->{$key} = $self->_read_nested;
258 ( $line, $indent ) = $self->_peek;
262 || $line =~ $IS_END_YAML;
276 TAP::Parser::YAMLish::Reader - Read YAMLish data from iterator
286 Note that parts of this code were derived from L<YAML::Tiny> with the
287 permission of Adam Kennedy.
295 The constructor C<new> creates and returns an empty
296 C<TAP::Parser::YAMLish::Reader> object.
298 my $reader = TAP::Parser::YAMLish::Reader->new;
300 =head2 Instance Methods
304 my $got = $reader->read($stream);
306 Read YAMLish from a L<TAP::Parser::Iterator> and return the data structure it
311 my $source = $reader->get_source;
313 Return the raw YAMLish source from the most recent C<read>.
317 Andy Armstrong, <andy@hexten.net>
319 Adam Kennedy wrote L<YAML::Tiny> which provided the template and many of
320 the YAML matching regular expressions for this module.
324 L<YAML::Tiny>, L<YAML>, L<YAML::Syck>, L<Config::Tiny>, L<CSS::Tiny>,
325 L<http://use.perl.org/~Alias/journal/29427>
329 Copyright 2007 Andy Armstrong.
331 Portions copyright 2006-2007 Adam Kennedy.
333 This program is free software; you can redistribute
334 it and/or modify it under the same terms as Perl itself.
336 The full text of the license can be found in the
337 LICENSE file included with this module.