Here's an updated version using ideas stolen borrowed from LanX.
It sounds like the provided input might be a section of a larger file. If so, you could either
pass in the section as a string and use the reference open as shown, or pass in the file handle ($fh) and also
specify some input line that will cause a return to caller.
#!/usr/bin/perl
use strict; # https://perlmonks.org/?node_id=11136806
use warnings;
my $inplacefile = <<'END';
instreg@d1@d1
instreg@d1@d2
instreg@d2@d1
instreg@d3@d1
instreg@d4@d1
instreg@d5@d1
instreg@d6@d1
instreg@d7@d1
instreg@d8@d1
alureg@d1@d1
alureg@d2@d1
alureg@d3@d1
alureg@d4@d1
alureg@d5@d1
alureg@d6@d1
alureg@d7@d1
alureg@d8@d1
pgmctr@d1@d1
pgmctr@d2@d1
pgmctr@d3@d1
pgmctr@d4@d1
pgmctr@d5@d1
pgmctr@m1
pgmctr@m2
pgmctr@m3
pgmctr@m4
pgmctr@m5
END
my $filename = \$inplacefile; # FIXME change to filename with no refer
+ence
open my $fh, '<', $filename or die $!; # FIXME improve error message
my $hash = {};
while( <$fh> )
{
my $ref = $hash;
$ref = $ref->{$_} //= {} for split /\@|\n/;
}
#use Data::Dump 'dd'; dd $hash;
sub nest
{
my $h = shift;
join '', map {
%{$h->{$_}} ? "$_ {\n" . nest( $h->{$_} ) =~ s/^/\t/gmr . "}\n" :
+"$_\n";
} sort keys %$h;
}
print nest($hash) =~ s/\w\K\n\s*(?=\w+$)/,/gmr;
Same output as before.