Perl: Basic YAML parsing in a copy/pasteable function
I need very basic YAML parsing in Perl, mostly for reading configuration files. There aren't any great options in Perl core, and I only need a subset of YAML (basic scalars, arrays, and hashes), so I worked up a simple copy/pasteable function.
my $x = yaml_parse($yaml_str);
This implementation supports: nesting, scalars, arrays, and hashes, but is missing null / ~, booleans, and complex quotes.
sub yaml_parse {
return {} unless defined $_[0] && length $_[0];
my %data; my $root = \%data; my @st = ([-1, \$root]);
for my $line (split /\n/, $_[0]) {
$line =~ s/\r$//;
next if $line =~ /^\s*(?:$|---\s*$|#)/;
if ($line =~ /^(\s*)-\s*(.*)$/) {
my ($n, $v) = (length($1), $2);
pop @st while $n < $st[-1][0];
my $cur = ${$st[-1][1]};
die "yaml_parse: array without parent: $line" if @st == 1;
$cur = ${$st[-1][1]} = [] if ref $cur eq 'HASH' && !%$cur;
die "yaml_parse: mixed array/hash" if ref $cur ne 'ARRAY';
$v =~ s/^\s+|\s+$//g; $v =~ s/^(['"])(.*)\1$/$2/s;
$v += 0 if $v =~ /^-?\d+(?:\.\d+)?$/;
push(@$cur, $v);
} elsif ($line =~ /^(\s*)([\w\-\.\/]+)\s*:\s*(.*)$/) {
my ($n, $key, $v) = (length($1), $2, $3);
pop @st while $n <= $st[-1][0];
my $cur = ${$st[-1][1]};
die "yaml_parse: '$key' under array" if ref $cur eq 'ARRAY';
$v =~ s/\s+$//; $v =~ s/^\s+//; $v =~ s/^(['"])(.*)\1$/$2/s;
if ($v =~ /^\[(.*)\]$/) {
my @a = grep { length } map { s/^\s+|\s+$//gr =~ s/^(['"])(.*)\1$/$2/sr } split /,/, $1;
for (@a) { $_ += 0 if /^-?\d+(?:\.\d+)?$/ }
$v = \@a;
} else { $v += 0 if $v =~ /^-?\d+(?:\.\d+)?$/; }
if (ref $v or length $v) { $cur->{$key} = $v }
else { $cur->{$key} = {}; push(@st, [$n, \$cur->{$key}]) }
} else { warn "yaml_parse: ignoring: $line\n" }
} return \%data;
}
YAML::XS is definitely the best Perl YAML parser, but it can be overkill if all you need is simple parsing.



