为啥除法被解析为正则表达式?

Posted

技术标签:

【中文标题】为啥除法被解析为正则表达式?【英文标题】:Why is division parsed as regular expression?为什么除法被解析为正则表达式? 【发布时间】:2021-10-22 02:46:43 【问题描述】:

这是我的代码的一部分:

        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 ) 加上括号时没有错误

【问题讨论】:

最小可重现示例:CORE::say $_ /6 ;-) 虽然sub T::t 1 T::t $_ /6 可能是更好的例子。因为CORE::say $_ /6/ 是有效的 Perl($_ 用于文件句柄),而 T::t $_ /6/ 不是。 也可以用perl -e 'print $_ /6' 重现。令人惊讶的是我以前从未注意到它。 【参考方案1】:

以下是对此的一些想法,以及一个合理的解释。一个简单的复制

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 sub 配备原型并不会改变这一切。

通过错误看来,/ 触发了search for 结束分隔符,一旦it's not found 整个事情都失败了。那么为什么这被解释为正则表达式而不是除法呢?

似乎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 filehandle 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'【参考方案2】:

Perl 认为符号/ 是正则表达式的开始而不是除法运算符。 https://perldoc.perl.org/perlre - 您可以检查 perldoc 中的正则表达式。 您可以尝试在6 之前添加一个空格字符,如下所示:$tnds += U::round $suma / 6;

【讨论】:

以上是关于为啥除法被解析为正则表达式?的主要内容,如果未能解决你的问题,请参考以下文章

您能否提供一些示例说明为啥使用正则表达式难以解析 XML 和 HTML? [关闭]

为啥用正则表达式解析 XML 是个坏主意? [关闭]

为啥在这个正则表达式中左括号被转义?

为啥所有浏览器都可以用正则表达式

为啥正则表达式构造函数需要双重转义?

为啥这个正则表达式调用 substcont 次数过多?