use warnings; use strict; use Data::Dumper; sub parse { my($f) = @_; my($s, %p, @s, $b); $s = \%p; while (<$f>) { $b++ or $_ = ": " . $_; while (/\G\s*(?:([-\w.]+)|"([^"]*)"|("[^"]*$)|:([-\w.]*)\s*\(|(\)))/gc) { if (defined($1) || defined($2)) { defined($$s{""}) and die "parse error: two"; $$s{"@"} = defined($1) ? $1 : $2; } elsif (defined($3)) { $_ = $+ . <$f>; } elsif (defined($4)) { push @s, $s; $s = $$s{$+} = {}; } elsif (defined($5)) { @s or die "parse error: close"; $s = pop @s; } } /(\S.*)/g and die "parse error: junk: $1"; } $! and die "read error"; $s == \%p or die "parse error: open"; \%p; } my $p = parse(*ARGV); print Dumper($p); __END__