Syntax error when map() returns LIST

后端 未结 2 854
执笔经年
执笔经年 2021-01-17 17:32

This works,

print map { $_.\" x\" => $_ } 1..5;
print map { (\"$_ x\" => $_) } 1..5;
print map { (\"$_ x\") => $_ } 1..5;

but this

相关标签:
2条回答
  • 2021-01-17 18:20

    From perlref

    Because curly brackets (braces) are used for several other things including BLOCKs, you may occasionally have to disambiguate braces at the beginning of a statement by putting a + or a return in front so that Perl realizes the opening brace isn't starting a BLOCK. The economy and mnemonic value of using curlies is deemed worth this occasional extra hassle.

    To make your intentions clearer and to help the parser,

    • Say +{...} to unambiguously specify a hash reference

      @list_of_hashrefs = map +{ "$_ x" => $_ }, 1..5;
      
    • Say {; ...} to unambiguously specify a code block

      %mappings = map {; "$_ x" => $_ } 1..5;
      
    0 讨论(0)
  • 2021-01-17 18:21

    Why perl thinks this should be map EXPR, LIST instead of map BLOCK LIST?

    The relevant section of code is in toke.c, Perl's lexer (the below is from 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;
    }
    

    Explanation

    If the first term after the opening curly is a string (delimited by ', ", or `) or a bareword beginning with a capital letter, and the following term is , or =>, the curly is treated as the beginning of an anonymous hash (that's what OPERATOR(HASHBRACK); means).

    The other cases are a little harder for me to understand. I ran the following program through gdb:

    { (x => 1) }
    

    and ended up in the final else block:

    else {
        PL_lex_brackstack[PL_lex_brackets-1] = XSTATE;
        PL_expect = XSTATE;
    }
    

    Suffice it to say, the execution path is clearly different; it ends up being parsed as a block.

    0 讨论(0)
提交回复
热议问题