为什么"keys ::"不是语法错误?

Zai*_*aid 4 perl

我试着下面的一行更多的是出于好奇心比任何事情和感到惊讶的是它实际工作没有%印记.

$ perl -E 'say for keys ::'
Run Code Online (Sandbox Code Playgroud)

它适用于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.
Run Code Online (Sandbox Code Playgroud)

Thi*_*Not 6

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);
}
Run Code Online (Sandbox Code Playgroud)

len 是当前令牌的长度.

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

  • 如果令牌是::,则使用提取的类型注释gv_fetchpv.

gv_fetchpv生活在gv.c中并具有处理的特殊逻辑:::

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

这将获取存储在key main::(即typeglob *main::)下的默认存储区中的 typeglob .

最后,keys期望它的参数是一个哈希值,但如果你传递一个标识符,它会将它视为哈希的名称.见Perl_ck_funop.c:

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;
Run Code Online (Sandbox Code Playgroud)

这适用于除了以外的事情:::

$ perl -e'%h = (foo => "bar"); print for keys h'
foo
Run Code Online (Sandbox Code Playgroud)

(截至5.22.0,你不再允许省略这些%印记.)

你也可以用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
Run Code Online (Sandbox Code Playgroud)