Skip to content

The Keyword Question #273

@wchristian

Description

@wchristian

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions