Perl has BEGIN blocks, which runs user Perl code at compile-time. This code can affect the meaning of other code to be compiled, thus making it "impossible" to parse Perl.
For example, the code:
sub foo { return "OH HAI" }
is "really":
BEGIN {
*{"${package}::foo"} = sub { return "OH HAI" };
}
That means that someone could write Perl like:
BEGIN {
print "Hi user, type the code for foo: ";
my $code = <>;
*{"${package}::foo"} = eval $code;
}
Obviously, no static analysis tool can guess what code the user is going to type in here. (And if the user says sub ($) {}
instead of sub {}
, it will even affect how calls to foo
are interpreted throughout the rest of the program, potentially throwing off the parsing.)
The good news is that the impossible cases are very corner-casey; technically possible, but almost certainly useless in real code. So if you are writing a static analysis tool, this will probably cause you no trouble.
To be fair, every language worth its salt has this problem, or something similar. As an example, throw your favorite code walker at this Lisp code:
(iter (for i from 1 to 10) (collect i))
You probably can't predict that this is a loop that produces a list, because the iter
macro is opaque and would require special knowledge to understand. The reality is that this is annoying in theory (I can't understand my code without running it, or at least running the iter
macro, which may not ever stop running with this input), but very useful in practice (iteration is easy for the programmer to write and the future programmer to read).
Finally, a lot of people think that Perl lacks static analysis and refactoring tools, like Java has, because of the relative difficulty in parsing it. I doubt this is true, I just think the need is not there and nobody has bothered to write it. (People do need a "lint", so there is Perl::Critic, for example.)
Any static analysis I have needed to do of Perl to generate code (some emacs macros for maintaining test counters and Makefile.PL) has worked fine. Could weird corner cases throw off my code? Of course, but I don't go out of my way to write code that's impossible to maintain, even though I could.