This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Fix listop-hash-infix parsing
authorFather Chrysostomos <sprout@cpan.org>
Wed, 12 Sep 2012 21:03:08 +0000 (14:03 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Wed, 12 Sep 2012 23:23:50 +0000 (16:23 -0700)
commit819b004ed974e0fe5ac23e956c3830dcc56b7c3e
tree56bfd4d8fc4d85ce9ea06857f20e1f6566380ef1
parent74736ae6be18c8156adf865e9641f1f6d230ede1
Fix listop-hash-infix parsing

With some list operators, this happens:

$ ./miniperl -e 'warn({$_ => 1} + 1) if 0'
syntax error at -e line 1, near "} +"
Execution of -e aborted due to compilation errors.

Putting + before the { or changing warn to print makes the prob-
lem go away.

The lexer is losing track of what token it expects next, so it ends
up interpreting the + as a unary plus, instead of an infix plus.  The
parser doesn’t like that.

It happens because of this logic under case '{' (aka leftbracket:) in
toke.c:yylex:

switch (PL_expect) {
case XTERM:
    if (PL_oldoldbufptr == PL_last_lop)
PL_lex_brackstack[PL_lex_brackets++] = XTERM;
    else
PL_lex_brackstack[PL_lex_brackets++] = XOPERATOR;
    PL_lex_allbrackets++;
    OPERATOR(HASHBRACK);

The value we put on the brackstack is what we expect to find after the
closing brace (case '}' pops it off).

This particular if/else goes all the back to ef6361f9c226 (perl
5.000), or at least that was when it moved inside the XTERM case.
Before that, we had this:

if (oldoldbufptr == last_lop)
    lex_brackstack[lex_brackets++] = XTERM;
else
    lex_brackstack[lex_brackets++] = XOPERATOR;
if (expect == XTERM)
    OPERATOR(HASHBRACK);

So it appears that the XTERM/XOPERATOR distinction, based on last_lop
was the ‘old’ (and wrong) way of doing it, but it had to be changed in
perl 5.000 for cases other than XTERM.  That it remained for XTERM was
probably an oversight, which is easy to understand, since I seem to be
the first one to stumble across this after 18 years (what’s the rele-
vant Klortho number?).

Removing this last_lop check causes no tests to fail.  And it makes
sense, since anything coming right after an anonymous hash that could
be either an infix or prefix operator must be infix.
t/base/lex.t
toke.c