Closed p5pRT closed 14 years ago
I should have fixed this a decade ago\, but I fixed it tonight.
Perl's commitment to allowing white space most anywhere has been broken in the case of here-document string terminators without quotes\, such as
print \<\< blah; test blah
which is a syntax error\, while
print \<\< "blah"; test blah
prints "test\n" as desired.
I think I reported this a long time ago and someone agreed that it was a bug\, but I couldn't find my report in rt so here's a new ticket.
And a patch to duplicate the existing logic for skipping spaces and tabs before the quoted terminators for bare terminators and backslash-prefaced terminators\, consisting of two new lines in toke.c (not dependent on my other work there\, but the comment in the earlier patch does show up in this one) and two new tests\, expected to go in heredoc.t which doesn't exist in origin yet.
Please note the use of test.pl's C\
On Sun\, Jun 21\, 2009 at 10:25:43PM -0700\, David Nicol wrote:
I should have fixed this a decade ago\, but I fixed it tonight.
Perl's commitment to allowing white space most anywhere has been broken in the case of here-document string terminators without quotes\, such as
print \<\< blah; test blah
which is a syntax error\, while
print \<\< "blah"; test blah
prints "test\n" as desired.
I think I reported this a long time ago and someone agreed that it was a bug\, but I couldn't find my report in rt so here's a new ticket.
That is actually the documented behavior.
perldoc perlop:
\<\<EOF
... There must be no space between the "\<\<" and the identifier\, unless the identifier is quoted. (If you put a space it will be treated as a null identifier\, which is valid\, and matches the first empty line.) ...
Ronald
The RT System itself - Status changed from 'new' to 'open'
On Thu\, Jul 2\, 2009 at 8:13 PM\, Ronald J Kimball\rjk\-perl\-p5p@​tamias\.net wrote:
That is actually the documented behavior.
perldoc perlop:
\<\<EOF
... There must be no space between the "\<\<" and the identifier\, unless the identifier is quoted. (If you put a space it will be treated as a null identifier\, which is valid\, and matches the first empty line.) ...
Ronald
Hmm. No hint of deprecation of the unquoted null identifier in that paragraph.
Before I go and include a touch to perlop.pod in an addendum to my Big Heredoc Omnibus Patch\, is there any situation where HEREDOC WHITESPACE BAREWORD makes sense? I have not been able to contrive one\, the "juxtaposition operator" having been rejected in http://dev.perl.org/perl6/doc/design/apo/A03.html
until now:
push @pieces\, split \<\< while (@pieces \< 20) padding padding
is that worth saving?
also there might be variables interpolated in the doc with Tie::Function type side effects.
@rgs - Status changed from 'open' to 'rejected'
Are there any situations where HEREDOC WHITESPACE BAREWORD makes sense and BAREWORD is not in the following short list?
and eq if for foreach ge gt le lt ne or unless until while x
Everything else always binds rightward\, and there is still no way to define infix subroutines.
What I'd like to do is check the possible terminator against that list and treat the situation like the implied null terminator (issue an optional syntax warning that does not mention deprecation) when there is a match\, otherwise treat the unlisted bareword as the terminator.
On Mon\, Jul 6\, 2009 at 5:35 PM\, David Nicol\davidnico@​cpan\.org wrote:
and eq if for foreach ge gt le lt ne or unless until while x
also cmp. Here's a revised patch.
http://tipjar.com/perlhacking/heredocpatch_revised.txt
-- Gaming Edison's ratio by shunning air conditioning doesn't actually work.
, C, and the like are not supported in place of C<''> and C<"">, and the only interpolation is for diff --git a/t/lib/warnings/toke b/t/lib/warnings/toke index e5ca400..caddeb5 100644 --- a/t/lib/warnings/toke +++ b/t/lib/warnings/toke @@ -141,14 +141,14 @@ Use of comma-less variable list is deprecated at - line 5. Use of comma-less variable list is deprecated at - line 5. ######## # toke.c -use warnings 'deprecated' ; +use warnings 'syntax' ; $a = <<; -no warnings 'deprecated' ; +no warnings 'syntax' ; $a = <<; EXPECT -Use of bare << to mean <<"" is deprecated at - line 3. + << interpreted as heredoc with null terminator at - line 3. ######## # toke.c use warnings 'syntax' ; diff --git a/t/test.pl b/t/test.pl index 32c4a37..e96d2c7 100644 --- a/t/test.pl +++ b/t/test.pl @@ -660,8 +660,8 @@ sub _fresh_perl { my($prog, $resolve, $runperl_args, $name) = @_; $runperl_args ||= {}; - $runperl_args->{progfile} = $tmpfile; - $runperl_args->{stderr} = 1; + $runperl_args->{progfile} ||= $tmpfile; + $runperl_args->{stderr} = 1 unless exists $runperl_args->{stderr}; open TEST, ">$tmpfile" or die "Cannot open $tmpfile: $!"; @@ -698,12 +698,6 @@ sub _fresh_perl { } my $pass = $resolve->($results); - unless ($pass) { - _diag "# PROG: \n$prog\n"; - _diag "# EXPECTED:\n", $resolve->(), "\n"; - _diag "# GOT:\n$results\n"; - _diag "# STATUS: $status\n"; - } # Use the first line of the program as a name if none was given unless( $name ) { @@ -712,6 +706,15 @@ sub _fresh_perl { } _ok($pass, _where(), "fresh_perl - $name"); + + unless ($pass) { + _diag "# PROG: \n$prog\n"; + _diag "# EXPECTED:\n", $resolve->(), "\n"; + _diag "# GOT:\n$results\n"; + _diag "# STATUS: $status\n"; + } + + return $pass; } # diff --git a/toke.c b/toke.c index a15dca6..0342ced 100644 --- a/toke.c +++ b/toke.c @@ -11240,6 +11240,29 @@ S_scan_trans(pTHX_ char *start) return s; } +/* and cmp eq if for foreach ge gt le lt ne or unless until while x */ +#define LeftBindingBareword(p) (\ +(*p == 'x' && !isALNUM_lazy_if(&p[1],UTF)) ||\ +(*p == 'e' && p[1] == 'q' && !isALNUM_lazy_if(&p[2],UTF)) ||\ +(*p == 'i' && p[1] == 'f' && !isALNUM_lazy_if(&p[2],UTF)) ||\ +(*p == 'g' && p[1] == 'e' && !isALNUM_lazy_if(&p[2],UTF)) ||\ +(*p == 'g' && p[1] == 't' && !isALNUM_lazy_if(&p[2],UTF)) ||\ +(*p == 'l' && p[1] == 'e' && !isALNUM_lazy_if(&p[2],UTF)) ||\ +(*p == 'l' && p[1] == 't' && !isALNUM_lazy_if(&p[2],UTF)) ||\ +(*p == 'n' && p[1] == 'e' && !isALNUM_lazy_if(&p[2],UTF)) ||\ +(*p == 'o' && p[1] == 'r' && !isALNUM_lazy_if(&p[2],UTF)) ||\ +(*p == 'a' && p[1] == 'n' && p[2] == 'd' && !isALNUM_lazy_if(&p[3],UTF)) ||\ +(*p == 'c' && p[1] == 'm' && p[2] == 'p' && !isALNUM_lazy_if(&p[3],UTF)) ||\ +(*p == 'f' && p[1] == 'o' && p[2] == 'r' && (!isALNUM_lazy_if(&p[3],UTF) ||\ + (p[3] == 'e' && p[4] == 'a' && p[5] == 'c' && p[6] == 'h' && \ + !isALNUM_lazy_if(&p[3],UTF)))) ||\ +(*p == 'u' && p[1] == 'n' && ( \ + (p[2] == 't' && p[3]=='i' && p[4]=='l' && !isALNUM_lazy_if(&p[3],UTF)) || \ + (p[2] == 'l' && p[3]=='e' && p[4]=='s' && p[5] == 's' && \ + !isALNUM_lazy_if(&p[6],UTF)))) ||\ +(*p == 'w' && p[1] == 'h' && p[2] == 'i' && p[3]== 'l' && p[4] == 'e' && \ + !isALNUM_lazy_if(&p[5],UTF)) \ +) STATIC char * S_scan_heredoc(pTHX_ register char *s) @@ -11268,25 +11291,29 @@ S_scan_heredoc(pTHX_ register char *s) d = PL_tokenbuf; e = PL_tokenbuf + sizeof PL_tokenbuf - 1; if (!outer) - *d++ = '\n'; + *d++ = '\n'; peek = s; while (SPACE_OR_TAB(*peek)) - peek++; + peek++; if (*peek == '`' || *peek == '\'' || *peek =='"') { - s = peek; - term = *s++; - s = delimcpy(d, e, s, PL_bufend, term, &len); - d += len; - if (s < PL_bufend) - s++; + s = peek; + term = *s++; + s = delimcpy(d, e, s, PL_bufend, term, &len); + d += len; + if (s < PL_bufend) + s++; } else { + if (*peek == '\\' || ( isALNUM_lazy_if(peek,UTF) && !LeftBindingBareword(peek))) + s = peek; if (*s == '\\') + /* <<\FOO is equivalent to <<'FOO' */ s++, term = '\''; else term = '"'; - if (!isALNUM_lazy_if(s,UTF)) - deprecate_old("bare << to mean <<\"\""); + if (!isALNUM_lazy_if(s,UTF) && ckWARN(WARN_SYNTAX)) + Perl_warner(aTHX_ packWARN(WARN_SYNTAX), + " << interpreted as heredoc with null terminator"); for (; isALNUM_lazy_if(s,UTF); s++) { if (d < e) *d++ = *s; @@ -11448,8 +11475,30 @@ S_scan_heredoc(pTHX_ register char *s) #endif if (!outer || !(PL_oldoldbufptr = PL_oldbufptr = s = PL_linestart = filter_gets(PL_linestr, PL_rsfp, 0))) { - CopLINE_set(PL_curcop, (line_t)PL_multi_start); - missingterm(PL_tokenbuf); + I32 len_without; + /* in other words, we are out of input */ + /* maybe the string terminator didn't have a newline? */ + sv_catpvn(tmpstr,"\n",1); /* add a newline */ + len_without = SvCUR(tmpstr) - len; + s = SvPVX(tmpstr) + len_without; + if ( len_without > -1 /* there has been enough data */ + && *(s-1) == '\n' /* the terminator starts at line-begin */ + && *s == term /* the terminator starts correctly */ + && memEQ(s,PL_tokenbuf,len) /* and checks out okay */ + ) + { + /* chop the quoted heredoc at the right place */ + SvCUR_set(tmpstr,len_without); + /* pretend we got the string terminator from filter_gets */ + sv_setpvn(PL_linestr,s,len); + PL_oldoldbufptr = PL_oldbufptr = s = PL_linestart = SvPVX(PL_linestr);; + } + else + { + /* No, we have reached EOF without finding the terminator. */ + CopLINE_set(PL_curcop, (line_t)PL_multi_start); + missingterm(PL_tokenbuf); + } } #ifdef PERL_MAD stuffstart = s - SvPVX(PL_linestr); ```
Migrated from rt.perl.org#66834 (status was 'rejected')
Searchable as RT66834$