Ответ 1
Почему perl считает, что это должно быть
map EXPR, LIST
вместоmap BLOCK LIST
?
Соответствующий раздел кода находится в toke.c
, Perl lexer (ниже из Perl 5.22.0):
/* This hack serves to disambiguate a pair of curlies
* as being a block or an anon hash. Normally, expectation
* determines that, but in cases where we're not in a
* position to expect anything in particular (like inside
* eval"") we have to resolve the ambiguity. This code
* covers the case where the first term in the curlies is a
* quoted string. Most other cases need to be explicitly
* disambiguated by prepending a "+" before the opening
* curly in order to force resolution as an anon hash.
*
* XXX should probably propagate the outer expectation
* into eval"" to rely less on this hack, but that could
* potentially break current behavior of eval"".
* GSAR 97-07-21
*/
t = s;
if (*s == '\'' || *s == '"' || *s == '`') {
/* common case: get past first string, handling escapes */
for (t++; t < PL_bufend && *t != *s;)
if (*t++ == '\\')
t++;
t++;
}
else if (*s == 'q') {
if (++t < PL_bufend
&& (!isWORDCHAR(*t)
|| ((*t == 'q' || *t == 'x') && ++t < PL_bufend
&& !isWORDCHAR(*t))))
{
/* skip q//-like construct */
const char *tmps;
char open, close, term;
I32 brackets = 1;
while (t < PL_bufend && isSPACE(*t))
t++;
/* check for q => */
if (t+1 < PL_bufend && t[0] == '=' && t[1] == '>') {
OPERATOR(HASHBRACK);
}
term = *t;
open = term;
if (term && (tmps = strchr("([{< )]}> )]}>",term)))
term = tmps[5];
close = term;
if (open == close)
for (t++; t < PL_bufend; t++) {
if (*t == '\\' && t+1 < PL_bufend && open != '\\')
t++;
else if (*t == open)
break;
}
else {
for (t++; t < PL_bufend; t++) {
if (*t == '\\' && t+1 < PL_bufend)
t++;
else if (*t == close && --brackets <= 0)
break;
else if (*t == open)
brackets++;
}
}
t++;
}
else
/* skip plain q word */
while (t < PL_bufend && isWORDCHAR_lazy_if(t,UTF))
t += UTF8SKIP(t);
}
else if (isWORDCHAR_lazy_if(t,UTF)) {
t += UTF8SKIP(t);
while (t < PL_bufend && isWORDCHAR_lazy_if(t,UTF))
t += UTF8SKIP(t);
}
while (t < PL_bufend && isSPACE(*t))
t++;
/* if comma follows first term, call it an anon hash */
/* XXX it could be a comma expression with loop modifiers */
if (t < PL_bufend && ((*t == ',' && (*s == 'q' || !isLOWER(*s)))
|| (*t == '=' && t[1] == '>')))
OPERATOR(HASHBRACK);
if (PL_expect == XREF)
{
block_expectation:
/* If there is an opening brace or 'sub:', treat it
as a term to make ${{...}}{k} and &{sub:attr...}
dwim. Otherwise, treat it as a statement, so
map {no strict; ...} works.
*/
s = skipspace(s);
if (*s == '{') {
PL_expect = XTERM;
break;
}
if (strnEQ(s, "sub", 3)) {
d = s + 3;
d = skipspace(d);
if (*d == ':') {
PL_expect = XTERM;
break;
}
}
PL_expect = XSTATE;
}
else {
PL_lex_brackstack[PL_lex_brackets-1] = XSTATE;
PL_expect = XSTATE;
}
Объяснение
Если первое слагаемое после открытия курсивом является строкой (с разделителем '
, "
или `
)), либо с начальным словом, начинающимся с заглавной буквы, а следующий термин ,
или =>
, курчавый рассматривается как начало анонимного хэша (что означает OPERATOR(HASHBRACK);
).
Другие случаи для меня немного сложнее понять. Я выполнил следующую программу через gdb:
{ (x => 1) }
и закончил в последнем блоке else
:
else {
PL_lex_brackstack[PL_lex_brackets-1] = XSTATE;
PL_expect = XSTATE;
}
Достаточно сказать, что путь выполнения явно отличается; он заканчивается анализом в виде блока.