On Thu, 24 Jun 2004, Greg Bacon wrote:
> I was writing code to scan an assembly-language definition of
> operational data and produce a report and ended up writing code
> that gave me the "there has to be a better way" feeling.
>
> Single parameters are easy to spot, e.g.,
>
> label1 .word 1234ABCDh
> label2 .float 3.14159
>
> Most arrays are trivial too:
>
> label3 .word 1, 2, 3
>
> Array specifications can span multiple lines, however. For example:
>
> label4 .float 0.0, 0.5, 1.0
> .float 1.5, 2.0, 2.5
>
(snipped)
> Here's a sketch of the code:
>
> while (<$fh>) {
> next unless /^(\w+)\s+\.(word|float)\s+(.+?),?\s*$/;
> my($label,$type,$data) = ($1,$2,$3);
>
>
> # look for continued spec
> my $needredo = 0;
> while (<$fh>) {
> if (/^\s*\.(word|float)\s+(.+?),?\s*$/) {
> $data .= ", $2";
> }
> else {
> $needredo = 1;
> }
> }
>
> # now $label, $type, and $data comprise an
> # entire parameter
> ...;
>
> redo if $needredo;
> }
>
> That's already kind of klunky, but I also saw that the inner while loop
> will exhaust the input, which the outer loop's implicitly tests too. I
> tested for C<eof $fh> at each iteration of the inner loop and reset
> $needredo if I needed to C<last> out of the inner loop.
>
> The code now feels very klunky. Is there a more elegant way to code
> this scan?
>
I don't think nested loops are needed. How about:
#!/usr/local/bin/perl
use strict;
use warnings;
my ($label, $type, $data);
while (<DATA>) {
if (/^\s*\.(word|float)\s+(.+?),?\s*$/) {
$data .= ", $2";
} elsif (/^(\w+)\s+\.(word|float)\s+(.+?),?\s*$/) {
do_stuff($label, $type, $data) if ($label); # previously found label
($label, $type, $data) = ($1, $2, $3);
}
}
do_stuff($label, $type, $data) if ($label);
sub do_stuff {
print "$label, $type, $data\n";
}
__DATA__
label1 .word 1234ABCDh
label2 .float 3.14159
label3 .word 1, 2, 3
label4 .float 0.0, 0.5, 1.0
.float 1.5, 2.0, 2.5
--
Hope this helps,
Steven