This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
toke.c: Fix confused interp-in-format parsing
authorFather Chrysostomos <sprout@cpan.org>
Mon, 6 Aug 2012 21:34:25 +0000 (14:34 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Wed, 8 Aug 2012 19:24:49 +0000 (12:24 -0700)
The lexer keeps track of how many levels of brackets it is currently
in (including the = and . delimiting formats, which count as brackets)
in PL_lex_brackets.

When parsing a format, the lexer sets lex_formbrack to the form’s
bracket number.  Whitespace parsing takes note of this, and avoids
reading beyond the end of the current line inside a format, unless it
is in an inner bracketed construct.  So this:

format =
@
$a
 + $b
.

treats ‘ + $b’ as a literal string, which becomes the second line
of output.

But this:

format =
@
{ $a
   + $b }
.

treats the whole {...} block as the argument for the first format pic-
ture line.

For interpolating (i.e., quoted) constructs, the lexer temporarily
resets the bracket count.  This is so that when it reaches the final
brace in "${...}foo()" it can parse the rest (foo()) as a constant,
rather than a sub call.

When the bracket count was reset, lex_formbrack was left as it was,
resulting in confusion:

# ok
format =
@
${; use
    strict}
.

# also ok
format =
@
"${; {use
      strict} }"
.

# syntax error (or crash)
format =
@
"${; use
     strict }"
.

# crash
format =
@
"${ use
    strict }"
.

When the bracket count is localised and reset at the start of a quoted
construct, we now do the same with lex_formbrack.

t/op/write.t
toke.c

index 2d5b0ac..60ae9b3 100644 (file)
@@ -58,7 +58,7 @@ for my $tref ( @NumTests ){
 #---------------------------------------------------------
 
 # number of tests in section 1
-my $bas_tests = 20;
+my $bas_tests = 21;
 
 # number of tests in section 3
 my $bug_tests = 8 + 3 * 3 * 5 * 2 * 3 + 2 + 66 + 4 + 2 + 3 + 96 + 5;
@@ -278,6 +278,18 @@ write (OUT4);
 close  OUT4 or die "Could not close: $!";
 is cat('Op_write.tmp'), "1\n" and unlink_all "Op_write.tmp";
 
+# More LEX_INTERPNORMAL
+format OUT4a=
+@<<<<<<<<<<<<<<<
+"${; use
+     strict; \'Nasdaq dropping like flies'}"
+.
+open   OUT4a, ">Op_write.tmp" or die "Can't create Op_write.tmp";
+write (OUT4a);
+close  OUT4a or die "Could not close: $!";
+is cat('Op_write.tmp'), "Nasdaq dropping\n", 'skipspace inside "${...}"'
+    and unlink_all "Op_write.tmp";
+
 eval <<'EOFORMAT';
 format OUT10 =
 @####.## @0###.##
diff --git a/toke.c b/toke.c
index f04dfd1..596f931 100644 (file)
--- a/toke.c
+++ b/toke.c
@@ -2443,6 +2443,7 @@ S_sublex_push(pTHX)
     SAVEBOOL(PL_lex_dojoin);
     SAVEI32(PL_lex_brackets);
     SAVEI32(PL_lex_allbrackets);
+    SAVEI32(PL_lex_formbrack);
     SAVEI8(PL_lex_fakeeof);
     SAVEI32(PL_lex_casemods);
     SAVEI32(PL_lex_starts);
@@ -2473,7 +2474,7 @@ S_sublex_push(pTHX)
     SAVEFREESV(PL_linestr);
 
     PL_lex_dojoin = FALSE;
-    PL_lex_brackets = 0;
+    PL_lex_brackets = PL_lex_formbrack = 0;
     PL_lex_allbrackets = 0;
     PL_lex_fakeeof = LEX_FAKEEOF_NEVER;
     Newx(PL_lex_brackstack, 120, char);