【问题标题】:Why is "keys ::" not a syntax error?为什么“keys ::”不是语法错误?
【发布时间】:2016-11-02 15:03:41
【问题描述】:

出于好奇,我尝试了以下单行代码,但很惊讶它实际上在没有% sigil 的情况下工作

$ perl -E 'say for keys ::'

它适用于 5.8.8 和 5.16.3 两个版本;尽管后一个版本会发出此警告:

Hash %:: 在 -e 第 1 行缺少 keys() 参数中的 %。


这甚至是如何工作的? %:: 有什么特别之处,即使没有印记,它也可以运行和打印其密钥?

请注意,密钥不会以%main:: 打印。

$ perl -E 'say for keys main::'
Hash main:: missing the % in argument 1 of keys() at -e line 1.

【问题讨论】:

  • 我忘记了哪个版本,但您曾经能够在没有印记的情况下引用哈希或数组。现在正在寻找。
  • @HunterMcMillen:您应该扩展它并将其作为答案发布。

标签: perl


【解决方案1】:

使用:

perl -MO=Deparse -E 'say for keys ::'

说:

use feature 'current_sub', 'evalbytes', 'fc', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval';
say $_ foreach (keys %main::);

所以在这些没有严格的 perl 版本中,它将 :: 视为 %::

【讨论】:

    【解决方案2】:

    TL;DR

    :: 并不特别;在 Perl 5.22.0 之前,您可以省略 % 并将任何标识符传递给 keys

    但是:

    • keys main:: 相当于 keys %{'main'} 或只是 keys %main
    • keys :: 相当于 keys %{'::'} 或只是 keys %::
      请注意,%main::(但不是 %main)是 %:: 的别名。

    相关代码在toke.c中(以下来自5.8.8):

    /* Look for a subroutine with this name in current package,
       unless name is "Foo::", in which case Foo is a bearword
       (and a package name). */
    
    if (len > 2 &&
        PL_tokenbuf[len - 2] == ':' && PL_tokenbuf[len - 1] == ':')
    {
        if (ckWARN(WARN_BAREWORD) && ! gv_fetchpv(PL_tokenbuf, FALSE, SVt_PVHV))
            Perl_warner(aTHX_ packWARN(WARN_BAREWORD),
                "Bareword \"%s\" refers to nonexistent package",
                 PL_tokenbuf);
        len -= 2;
        PL_tokenbuf[len] = '\0';
        gv = Nullgv;
        gvp = 0;
    }
    else {
        len = 0;
        if (!gv)
            gv = gv_fetchpv(PL_tokenbuf, FALSE, SVt_PVCV);
    }
    
    /* if we saw a global override before, get the right name */
    
    if (gvp) {
        sv = newSVpvn("CORE::GLOBAL::",14);
        sv_catpv(sv,PL_tokenbuf);
    }
    else {
        /* If len is 0, newSVpv does strlen(), which is correct.
           If len is non-zero, then it will be the true length,
           and so the scalar will be created correctly.  */
        sv = newSVpv(PL_tokenbuf,len);
    }
    

    len是当前令牌的长度。

    • 如果令牌是main::,则会创建一个新标量,其 PV(字符串组件)设置为main

    • 如果令牌是::,则使用gv_fetchpv 获取一个typeglob。

    gv_fetchpv 位于 gv.c 中,具有处理 :: 的特殊逻辑:

    if (*namend == ':')
        namend++;
    namend++;
    name = namend;
    if (!*name)
        return gv ? gv : (GV*)*hv_fetch(PL_defstash, "main::", 6, TRUE);
    

    这会获取存储在 default stash 下的键 main:: 中的 typeglob(即 typeglob *main::)。

    最后,keys 期望它的参数是一个散列,但是如果你给它传递一个标识符,它就会把它当作一个散列的名称。请参阅 op.c 中的Perl_ck_fun

    case OA_HVREF:
        if (kid->op_type == OP_CONST &&
            (kid->op_private & OPpCONST_BARE))
        {
            char *name = SvPVx(((SVOP*)kid)->op_sv, n_a);
            OP * const newop = newHVREF(newGVOP(OP_GV, 0,
                gv_fetchpv(name, TRUE, SVt_PVHV) ));
            if (ckWARN2(WARN_DEPRECATED, WARN_SYNTAX))
                Perl_warner(aTHX_ packWARN2(WARN_DEPRECATED, WARN_SYNTAX),
                    "Hash %%%s missing the %% in argument %"IVdf" of %s()",
                    name, (IV)numargs, PL_op_desc[type]);
            op_free(kid);
            kid = newop;
            kid->op_sibling = sibl;
            *tokid = kid;
        }
        else if (kid->op_type != OP_RV2HV && kid->op_type != OP_PADHV)
            bad_type(numargs, "hash", PL_op_desc[type], kid);
        mod(kid, type);
        break;
    

    这也适用于 :: 以外的东西:

    $ perl -e'%h = (foo => "bar"); print for keys h'
    foo
    

    As of 5.22.0,你不能再省略% sigil。)

    您也可以通过 B::Concise 看到这一点:

    $ perl -MO=Concise -e'keys main::'
    Hash %main missing the % in argument 1 of keys() at -e line 1.
    6  <@> leave[1 ref] vKP/REFC ->(end)
    1     <0> enter ->2
    2     <;> nextstate(main 1 -e:1) v:{ ->3
    5     <1> keys[t2] vK/1 ->6
    4        <1> rv2hv[t1] lKRM/1 ->5
    3           <$> gv(*main) s ->4
    -e syntax OK
    $ perl -MO=Concise -e'keys ::'
    Hash %:: missing the % in argument 1 of keys() at -e line 1.
    6  <@> leave[1 ref] vKP/REFC ->(end)
    1     <0> enter ->2
    2     <;> nextstate(main 1 -e:1) v:{ ->3
    5     <1> keys[t2] vK/1 ->6
    4        <1> rv2hv[t1] lKRM/1 ->5
    3           <$> gv(*main::) s ->4
    -e syntax OK
    

    【讨论】:

    • 很好的答案,你已经解决了我所有的问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-01-20
    • 2015-02-11
    • 1970-01-01
    • 2013-07-22
    • 2014-04-15
    • 2014-07-22
    • 1970-01-01
    相关资源
    最近更新 更多