-
Notifications
You must be signed in to change notification settings - Fork 46
Description
Edit: I have implemented a proof-of-concept, please review and comment: #280
This spawned from discussion in #194 and Perl-Critic/Perl-Critic#591
There are a lot of hard problems with Perl parsing that lead people to think there are simple answers, which leave out real problems. Here is a demonstration of not even how PPI parses things, but of how just by looking at code, it is impossible to predict what the Perl Parser itself will do.
strect.pm
package strect;
use strictures; use Import::Into; use Syntax::Keyword::Try (); use Try::Tiny (); 1;
sub import {
strictures->import::into(1);
( @ARGV ? "Try::Tiny" : "Syntax::Keyword::Try" )->import::into(1);
}legacy.pl
use lib '.';
use strect;
try { die "marp" } catch { print "caught $_" };
print 4;$ perl legacy.pl 1
caught marp at legacy.pl line 4.
4
$ perl legacy.pl
Use of uninitialized value $_ in concatenation (.) or string at legacy.pl line 4.
modern.pl
use lib '.';
use strect;
try { die "meep" } catch { print "caught $@" }
print 5;$ perl modern.pl 1
try() encountered an unexpected argument (1) - perhaps a missing semi-colon before or at modern.pl line 6.
5
$ perl modern.pl
caught meep at modern.pl line 5.
5
This demonstrates how in a plausible use case, the parsing of keywords can be affected even on the same interpreter, by something entirely outside the code itself, like command line arguments.
This is problematic because, depending on the imported keywords, this block:
try { die "meep" } catch { print "caught $@" }
print 5;is seen by the Perl Parser as either 1 statement (with try consuming the return value of the print), or 2 statements (try, followed by print).
NOTE ALSO: Some people might like to mentally short-circuit here with "just assume try is the modern form". That would be useless. I used try here for brevity and reproducability. Other modules might import other keywords.
And to top this off, here is an example within a single file:
stroct.pm
package stroct;
use strictures; use Import::Into; use Syntax::Keyword::Try (); use Try::Tiny (); 1;
sub import {
strictures->import::into(1);
( $_[1] ? "Try::Tiny" : "Syntax::Keyword::Try" )->import::into(1);
}mixed.pl
use lib '.';
{
use stroct "a";
try { die "marp" } catch { print "caught $_" };
print 4;
}
{
use stroct;
try { die "meep" } catch { print "caught $@" }
print 5;
}$ perl mixed.pl
caught marp at mixed.pl line 5.
4caught meep at mixed.pl line 11.
5
This demonstrates how even within the same file, based on lexical imports with no sensible hints PPI could possibly predict, try {} catch {} print 5; might be one or two statements.