Showing posts with label perl. Show all posts
Showing posts with label perl. Show all posts

Thursday, December 9, 2010

yet another perl limitation

Another funny thing about perl.
Suppose, $i is 4.


while ($i > 0) {
    $i--;

    next if ($i == 2);

    print $i, "\n";
}


will produce
3 1 0


which is quite expected result. So far, so good.

Moving to do-while

do {
    $i--;

    next if ($i == 2);

    print $i, "\n";
} while ($i > 0);


Oops, bad luck:
3
Can't "next" outside a loop block at ./test.pl line 10.

And at the same time
do {
    $i--;

    next if ($i == 2);

    print $i, "\n";
} for (1 .. $i);


will happy to write
3 1 0

Guess why?

perldoc will shed light:
do BLOCK does not count as a loop, so the loop control statements next, last, or redo cannot be used to leave or restart the block. See perlsyn for alternative strategies.

Well... dunno... Hope, they really had reasons to do so.

Saturday, November 27, 2010

"#39861: Switch module doesn't like subroutine prototypes" part 2

Spent some time playing with perl Switch bug.

Well, let's start from the begging.
The sort of main parsing routine in Switch.pm is
sub filter_blocks, which tries to parse $source and create $text.

failter_blocks calls Text::Balanced::_match_* to distinguish code blocks
from quoted, to validate variables, etc... and that's the point where the
whole thing hits the fan. Text::Balanced::_match_variable call is too
early here, since _match_variable validates $#, $$, $^ and fails to
validate $). In other words, suppose, we are parsing

sub foo($) {
        switch($_[0]) {
                case /ACK/i {
                        return "ACK";
                }                   
                case /NACK/i {
                        return "NACK";
                }                    
        }       
}



Text::Balanced::_match_variable will return the whole block, since
it doesn't know what to do with $). Yet it works for $$, $,, etc. quite
well. My first solution was change

m{\G\$\s*(?!::)(\d+|[][&`'+*./|,";%=~:?!\@<>()-]|\^[a-z]?)}gci)

to

m{\G\$\)\?\s*(?!::)(\d+|[][&`'+*./|,";%=~:?!\@<>()-]|\^[a-z]?)}gci)


so, we now parse $) correctly. However, I didn't like it much.

What we (IMHO) really should do - is to teach filter_blocks what
subroutine is. I did very simple and general (which may fail for 
some sophisticated cases) thing:
       
diff --git a/Switch.pm b/Switch.pm
index 2189ae0..781bae8 100755
--- a/Switch.pm
+++ b/Switch.pm
@@ -111,6 +111,11 @@ sub filter_blocks
             }
             next component;
         }
+        if ($source =~ m/\G(\s*sub.+)\{/) {
+            $text .= $1;
+            pos $source += length($1);
+            next component;
+        }
         if ($source =~ m/(\G\s*$pod_or_DATA)/gc) {
             $text .= $1;
             next component;


which shifts position in currently parsed $source to avoid wrong
Text::Balanced::_match_variable call on subroutine declaration.

Of course, this is not tested at all, except for my simple script.
Just playing.

Friday, November 26, 2010

"#39861: Switch module doesn't like subroutine prototypes"

Recently I faced an unexpected perl5's behaviour.
This simple and valid code

#!/usr/bin/perl
use POSIX;
use strict;

use Switch;

sub foo($) {
    switch($_[0]) {
        case /ACK/i {
            return "ACK";
        }
        case /NACK/i {
            return "NACK";
        }
    }
}

1;

failed to execute due to
syntax error at ./test.pl line 8, near ") {"
syntax error at ./test.pl line 15, near "}"
Bareword "case" not allowed while "strict subs" in use at ./test.pl line 12.
Bareword "NACK" not allowed while "strict subs" in use at ./test.pl line 12.
Execution of ./test.pl aborted due to compilation errors.



If we change foo's prototype requirement to expect more than one scalar
parameters, or drop any requirements - everything will cure and work just
as expected. IOW, case with one scalar parameter is kind of special (read broken).

This turned out to be known problem (#39861, according to Perl5's bugzilla )...
Since around 2004. Only one thing remains to write  - WTF?