为什么除法要被解析为正则表达式?

3

这是我的代码的一部分:

        my $suma =  U::round $item->{ suma };  # line 36 
        $ts   +=  $suma;
        $tnds +=  U::round $suma /6;
    }

    return( $ts, $tnds );
}



sub create { #line 46
    my( $c ) =  shift;

    my $info =  $c->req->json;
    my $header  =  @$info[0];
    my $details =  @$info[1];

    my $agre =  D::T Agreement =>  $header->{ agreement_id };
    my( $total_suma, $total_nds ) =  total( $details );

    my $saldo    =  0;
    my $iid      =  @$details[0]->{ period };
    my $interval =  D::T Period => $iid //7; # line 58
    # This is first Invoice if operator do not provide activation date
    my $is_first =  !$details->[0]{valid_from}  &&  $iid  &&  $interval;

当加载此模块时,我会得到一个错误:

Can't load application from file "lib/MaitreD/Controller/ManualDocument.pm line 38, near "my $interval =  D::T Period => $iid /"
Unknown regexp modifier "/6" at lib/MaitreD/Controller/ManualDocument.pm line 38, at end of line
Global symbol "$pkg" requires explicit package name (did you forget to declare "my $pkg"?) at lib/MaitreD/Controller/ManualDocument.pm line 41.
...

这个间接对象调用有问题吗?

因为当我在U::round( $suma /6 )处加上括号时,就没有错误了。


3
最小可重现示例:CORE::say $_ /6 ;-)。该代码的作用是将变量 $_ 的值除以 6 并输出结果。CORE::say 是 Perl 语言中输出函数的一种形式。 - Dada
2
虽然 sub T::t { 1 } T::t $_ /6 可能是更好的例子。因为 CORE::say $_ /6/ 是有效的 Perl($_ 被用作文件句柄),而 T::t $_ /6/ 则不是。 - Dada
还可以使用perl -e 'print $_ /6'来复现。令人惊讶的是我以前从未注意到这一点。 - DavidO
2个回答

3
这里是一些关于此事的想法和一个合理的解释。一个简单的复制。
perl -wE'sub tt { say "@_" }; $v = 7; tt $v /3'

给我

搜索模式在-e行1处没有终止。

因此,它尝试在该子例程调用中解析正则表达式,如所述,问题是:为什么?

使用参数周围的括号可以按预期工作。随着更多的参数跟随它失败了同样的方式,但与先于它的参数相反,它起作用。

perl -wE'sub tt { say "@_" }; $v = 7; tt $v /3, 3'  # fails the same way
perl -wE'sub tt { say "@_" }; $v = 7; tt 3, $v /3'  # works

装备tt子例程的原型并不会改变任何内容。
通过错误,似乎/触发寻找结束定界符,一旦未找到,整个过程就失败了。那么为什么会被解释为正则表达式而不是除法?
看起来tt $v在解析时被分组,并被解释为子例程及其参数,因为它们后面跟着一个空格;然后/3被单独取出,然后看起来像一个正则表达式。这仍然会失败作为语法错误,但也许正则表达式解析失败首先出现。
然后,在其他逗号分隔的术语之前或之后的差异就很清楚了--使用tt 3, ...,接下来的$v /3是下一个参数的术语,并解析为除法。
这仍然存在另一个问题。我尝试过的所有内置函数都没有这个问题,无论是列表还是一元运算符,具有各种原型(pushchrsplice等) - 除了print,它确实有相同的外观问题。而且即使没有括号也会失败。
perl -wE'$v=110; say for unpack "A1A1", $v /2'  #--> 5 5
perl -wE'$v=200; say chr $v /2'                 #--> d
perl -wE'$v=3; push @ary, $v /2; say "@ary"'    #--> 1.5

perl -wE'$v = 7; say $v /3'                     # fails, the same way
perl -wE'$v = 7; say( $v /3 )'                  # fails as well, same way

不同之处在于print遵循"特殊"解析规则,允许第一个参数是文件句柄。(此外,它没有原型,但似乎并不重要。)

然后表达式print $v /3...确实可以解析为print 文件句柄 EXPR,以及以/开头的EXPR被解析为正则表达式。使用括号时同样适用。

所有这些都涉及一些猜测,因为我不知道解析器是如何做到的。但显然这是关于子例程调用的细节问题,(无意中?)包括print

在我看来,使用圆括号(对于用户定义的)子例程的明显解决方法是合理的。其他修复方法是在数学运算符周围保持一致的空格,要么两侧都不使用空格,要么两侧都使用--这也是可以的,尽管有些让人感到痒痒的(空格?真的吗?)。

但我不知道say( $v /3 )存在什么问题。

对于这个问题还有几点评论。

根据问题中的错误消息文本Unknown regexp modifier "/6",看起来/被视为结束定界符,与上面的示例不同。并且该消息中还有更多不清楚的内容。最终,我们确实有一个非常相似的解析问题。
至于“这个间接对象调用有罪吗?”,我在那里没有看到间接对象调用,只有普通的子例程调用。此外,来自此答案的示例显示非常类似的行为,并排除了间接对象语法。

另一个可能性是$v /3被解析为一个术语,因为它跟随了(可识别的!)子例程名称tt。然后,正则表达式绑定运算符=~binds比除法更紧密,并且在这里默认明确尝试绑定到$_

我认为这种可能性较小,而且也无法解释内置函数的行为,特别是print


那么可以推断出,其他具有可选逗号的第一个参数(因此没有原型)的内置函数也会遵循相同的方式,但是我无法立即想到任何一个。


请注意,如果我们在 / 后面加上空格,它也可以工作:perl -wE'sub tt { say "@_" }; $v = 7; tt $v / 3' - Eugen Konkov

2
Perl认为符号“/”是正则表达式的开始而不是除法运算符。您可以查看Perl文档中与正则表达式相关的内容:https://perldoc.perl.org/perlre。您可以尝试在6之前添加空格字符,例如:$tnds += U::round $suma / 6;

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