【问题标题】:How does Perl parse unquoted bare words?Perl 如何解析未引用的裸词?
【发布时间】:2020-02-04 08:59:18
【问题描述】:

不带引号的词在 Perl 中似乎有很多含义。

print STDERR $msg;

$hash{key}

func( param => $arg )

my $x = str;

如何确定这些的含义?

【问题讨论】:

    标签: perl parsing


    【解决方案1】:

    下图显示 Perl 如何按优先级降序解析标识符。

    除非另有说明,否则它也适用于由:: 链接的标识符(我将其称为“合格标识符”)。

    1. 语法定义的含义,当语法预期时。

       sub foo { }          # «foo» («sub» is covered later)
       sub main::foo { }    # «main::foo» («sub» is covered later)
       method Class         # «Class» («method» is covered later)
       method Some::Class   # «Some::Class» («method» is covered later)
       $foo
       $main::foo
       //i
       =head
       <<FOO
       Class::
       Some::Class::
       LABEL:
      
    2. 字符串字面量,后跟=&gt; 或整个哈希索引表达式时。

      这不适用于合格的标识符。

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

       ${foo}
       ${main::foo}
      

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

    4. 关键字。

       while (1) { }
       sub { }
       use
       __END__
      
    5. Sub 调用,当以前导入的 sub 的名称时。

       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                          # Calls sub «foo»
       main::foo                    # Calls sub «foo»
      
       sub bar;
       bar                          # Calls sub «bar»
      
       use constant FOO => 123;
       FOO                          # Replaced with the value of the constant.
      
    9. 间接方法调用,后跟可能限定标识符、后缀为::的可能限定标识符、标量(包括数组元素或散列元素)或块。

       method Class           # Calls method «method» («Class» is covered earlier)
       method Some::Class     # Calls method «method» («Some::Class» is covered earlier)
       method Class::         # Calls method «method» («Class» is covered earlier)
       method Some::Class::   # Calls method «method» («Some::Class» is covered earlier)
       method $o              # Calls method «method»
       method { $o }          # Calls method «method»
      
       Base::method Class     # Calls method «Base::method» («Class» is covered earlier)
      

      您可以使用no indirect pragma 在以这种方式解析代码时发出警告。

    10. Glob,当用作期望文件句柄的运算符的操作数时。

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

      • 当用作直接方法调用的调用者时。

          Class->method(@args)         # Uses the string «Class» as the invocant.
          Some::Class->method(@args)   # Uses the string «Some::Class» as the invocant.
        
      • 用作一元减号的操作数时。

          -foo
          -foo::bar
        
      • 当用作原型为* 的子参数时。

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

    希望我没有错过任何一个。

    感谢@mosvy、@Grinnz 和@stevesliva!每个人都发现了一些我错过的案例。


    目前失踪

    • funcnamesort funcname

    • 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 也没有原型。

    【讨论】:

    • 我有一种强烈的似曾相识的感觉,但你没有忘记package method args 语法吗? use strict; sub bar::foo {} foo bar foo bar。不,它与print STDOUT 不一样,因为print(STDOUT "foo\n") 有效,但通常不是method(PKG "foo\n")
    • @mosvy 那是indirect object syntax,实际上是method package args。作为推论,一些像 print/say 和 system/exec 这样的内置函数接受第一个不带逗号的参数,假装使用间接对象语法,但实际上是解析器的特殊情况。
    • 我还要补充一些其他特殊情况,例如 sort 的第一个参数,它采用裸词作为子名称,而不管严格的“子”。
    • @Grinnz 我的错,我在措辞中颠倒了它们,但示例还可以;-)
    • @stevesliva 是的,我就是这么说的。 goto eval 'some_sub' 中的eval 被视为运算符(#6),但goto some_sub 中的some_sub 被视为标签(#7),而不是子调用(#9)。
    猜你喜欢
    • 1970-01-01
    • 2012-12-12
    • 2017-05-03
    • 1970-01-01
    • 2021-08-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多