Perl如何解析未加引号的裸字(裸字,标识符)?

10
在Perl中,未引用的单词似乎有很多不同的含义。
print STDERR $msg;

$hash{key}

func( param => $arg )

my $x = str;

如何确定这些的意义?
1个回答

16
下面的图表显示了Perl按降序优先级解析标识符的顺序。
除非另有说明,否则这也适用于由“::”(或在5.42之前由“'”)链接的标识符。我将称之为“限定标识符”。
  1. 在语法上定义的含义,当在语法上期望时。

     sub foo { }          # «foo»(«sub»稍后解释)
     sub main::foo { }    # «main::foo»(«sub»稍后解释)
     method Class         # «Class»(«method»稍后解释)
     method Some::Class   # «Some::Class»(«method»稍后解释)
     $foo
     $main::foo
     //i
     =head
     <<FOO
     Class::
     Some::Class::
     LABEL:
    
  2. 字符串字面量,在后面跟着一个=>或者整个哈希索引表达式。

    这不适用于限定标识符。

     my %h = ( a => 1 );
     $h{a}
    
  3. 变量名,当整个解引用表达式。

     ${foo}
     ${main::foo}
    

    请注意,使用关键字、命名运算符或已声明的子程序的名称将导致“ambiguous use”警告。

  4. 关键字。

     while (1) { }
     sub { }
     use
     __END__
    
  5. 子程序调用,当为先前导入的子程序的名称。

     use Time::HiRes qw( time );
     time
     main::time
    
  6. 调用命名列表运算符、命名一元运算符或命名零元运算符。

     print $x, $y, $z;
     $c = chr $i;
     $t = time;
     $t = CORE::time;
    
  7. 标签,当作为nextlastredogoto的操作数。

    将限定标识符视为标签会导致编译错误,因为标签不能是限定标识符。

     next LABEL;
    
  8. 子程序调用或内联常量,当为先前声明的子程序或常量的名称。

     sub foo { }
     foo                          # 调用子程序«foo»
     main::foo                    # 调用子程序«foo»
    
     sub bar;
     bar                          # 调用子程序«bar»
    
     use constant FOO => 123;
     FOO                          # 替换为常量的值。
    
  9. 间接方法调用,当后面跟着一个可能限定的标识符、一个可能限定的标识符后缀加上::、一个标量(包括数组元素或哈希元素)或一个块。

     method Class           # 调用方法«method»(«Class»稍后解释)
     method Some::Class     # 调用方法«method»(«Some::Class»稍后解释)
     method Class::         # 调用方法«method»(«Class»稍后解释)
     method Some::Class::   # 调用方法«method»(«Some::Class»稍后解释)
     method $o              # 调用方法«method»
     method { $o }          # 调用方法«method»
    
     Base::method Class     # 调用方法«Base::method»(«Class»稍后解释)
    

    您可以使用no indirect指示来在解析代码时发出警告。

  10. 全局变量,当作为期望文件句柄的操作数。

     open(FH, '>', $qfn) or die $!;      # 等同于 open(*FH, ...) or ...;
     print FH "Hello, World!\n";         # 等同于 print *FH ...;
     print main::FH "Hello, World!\n";   # 等同于 print *main::FH ...;
    
  11. 字符串字面量,在以下情况下:

    • 作为直接方法调用的调用者。

        Class->method(@args)         # 使用字符串«Class»作为调用者。
        Some::Class->method(@args)   # 使用字符串«Some::Class»作为调用者。
      
    • 作为一元减号的操作数。

        -foo
        -foo::bar
      
    • 作为带有原型*的子程序参数的参数。

        sub myprint(*@);
        myprint(FH, "Hello, World\n");
        myprint(main::FH, "Hello, World\n");
      
  12. 字符串字面量。这在use strict qw( subs );中是不允许的。

希望我没有漏掉任何一个。
感谢 @mosvy、@Grinnz 和 @stevesliva!他们每个人都发现了我漏掉的几个案例。

目前缺失的内容:

  • SUBNAMEsort SUBNAME中。

  • BEGIN和类似的。它们有时作为关键字,有时作为声明的子程序。

  • 导入名为print的子程序不遵循上述步骤。

    $ perl -M5.010 -e'
       use subs qw( time );
       eval { time; };
       say $@ =~ /Undefined sub/ ? "ok" : "bad";
    '
    ok
    
    $ perl -M5.010 -e'
       use subs qw( system );
       eval { system; };
       say $@ =~ /Undefined sub/ ? "ok" : "bad";
    '
    ok
    
    $ perl -M5.010 -e'
       use subs qw( print );
       eval { print; };
       say $@ =~ /Undefined sub/ ? "ok" : "bad";
    '
    bad
    

    我不知道是什么使得它特殊,也不知道是否还有其他的情况。我猜想可能是因为print没有原型,但是system也没有原型。


@Grinnz 抱歉,我在措辞上把它们颠倒了,但示例还是没问题的;-) - user10678532
@mosvy,直接调用(Class->method)和间接调用(method Class)都缺失了。我会修复的。(是的,我之前发布过这个问题,但它只是一个与问题有关的脚注,我发现自己想要链接到它。) - ikegami
@stevesliva 我已根据您的评论更新了答案。结果发现标签确实很有趣。goto time; 尝试跳转到 1572794637,但 goto some_sub; 则尝试跳转到 some_sub - ikegami
这很奇怪。goto eval 'some_sub'可以工作,但是goto some_sub会导致崩溃,显示“找不到标签some_sub”。 - stevesliva
1
@stevesliva 是的,这就是我所说的。在 goto eval 'some_sub' 中,eval 被视为运算符(#6),但在 goto some_sub 中,some_sub 被视为标签(#7),而不是子程序调用(#9)。 - ikegami
显示剩余16条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接