diff --git a/t/comp/parser_run.t b/t/comp/parser_run.t index 6f2b41eff237..5e14ae29493b 100644 --- a/t/comp/parser_run.t +++ b/t/comp/parser_run.t @@ -10,7 +10,7 @@ BEGIN { set_up_inc( qw(. ../lib ) ); } -plan(70); +plan(82); # [perl #130814] can reallocate lineptr while looking ahead for # "Missing $ on loop variable" diagnostic. @@ -38,7 +38,10 @@ for my $var ('$00','${00}','$001','${001}','$01','${01}','$09324', '${09324}') { } } -for my $var ('$0', '${0}', '$1', '${1}', '$10', '${10}', '$9324', '${9324}') { +for my $var ( + '$0', '${0}', '$1', '${1}', '$10', '${10}', '$9324', '${9324}', + '${0x10}', '${0b10000}', '${0xA}', +) { for my $utf8 ("","use utf8;") { for my $strict ("","use strict;") { fresh_perl_is( diff --git a/t/re/pat.t b/t/re/pat.t index 2ce4ca8764a6..4af2da417497 100644 --- a/t/re/pat.t +++ b/t/re/pat.t @@ -51,7 +51,6 @@ sub run_tests { $ok= eval sprintf 'is ${0%d}, "%s", q(${0%d} = %s); 1', ($n, substr($string,$n-1,1))x2; ok(!$ok, "eval failed as expected for \${0$n} test"); - no strict 'refs'; $ok= eval sprintf 'is ${0b%b}, "%s", q(${0b%b} = %s); 1', ($n, substr($string,$n-1,1))x2; ok($ok, sprintf "eval for \${0b%b} test", $n); $ok= eval sprintf 'is ${0x%x}, "%s", q(${0x%x} = %s); 1', ($n, substr($string,$n-1,1))x2; diff --git a/toke.c b/toke.c index d7f27a48de28..0044a89db444 100644 --- a/toke.c +++ b/toke.c @@ -10195,18 +10195,52 @@ S_scan_ident(pTHX_ char *s, char *dest, STRLEN destlen, I32 ck_uni) } } - /* special case to handle ${10}, ${11} the same way we handle ${1} etc */ - if (isDIGIT(*d)) { - bool is_zero= *d == '0' ? TRUE : FALSE; + /* special case to handle ${10}, ${11} the same way we handle $1 and ${1} etc, + * also special case ${0x10} and ${0b10000} to do the right thing and refer to + * $16 at compile time without triggering use strict violations */ + if (isDIGIT(*d) && s < PL_bufend) { char *digit_start= d; - while (s < PL_bufend && isDIGIT(*s)) { - d++; - if (d >= e) - Perl_croak(aTHX_ "%s", ident_too_long); - *d= *s++; + bool has_leading_zero= *d == '0' ? TRUE : FALSE; + if (isDIGIT(*s)) { + do { + d++; + if (d >= e) + Perl_croak(aTHX_ "%s", ident_too_long); + *d = *s++; + } while (s < PL_bufend && isDIGIT(*s)); + if (has_leading_zero && d - digit_start >= 1) /* d points at the last digit */ + Perl_croak(aTHX_ ident_var_zero_multi_digit); } - if (is_zero && d - digit_start >= 1) /* d points at the last digit */ - Perl_croak(aTHX_ ident_var_zero_multi_digit); + else + if ( has_leading_zero ) { + int shift = 0; + U8 class_bit = 0; + if (*s == 'x') { + shift = 4; + class_bit = CC_XDIGIT_; + } else if (*s == 'b') { + shift = 1; + class_bit = CC_BINDIGIT_; + } else if (*s == 'o') { + shift = 3; + class_bit = CC_OCTDIGIT_; + } + if (shift) { + STRLEN len = PL_bufend - s; + I32 flags = PERL_SCAN_SILENT_ILLDIGIT | PERL_SCAN_DISALLOW_PREFIX; + UV uv = Perl_grok_bin_oct_hex(aTHX_ s+1, &len, &flags, NULL, shift, class_bit, *s); + if (len) { + s += len + 1; /* move past the prefix digit and tail */ + if (flags & PERL_SCAN_GREATER_THAN_UV_MAX || d+len >= e) + Perl_croak(aTHX_ "%s", ident_too_long); + len = snprintf(dest,destlen, "%" UVuf, uv); + d = dest + len - 1; + /* note we dont need to worry about trailing garbage, that will be + * handled later. */ + } + } + } + d[1] = '\0'; }