如何在 Perl 中进行条件贪婪匹配?

How to do conditional greedy match in Perl?

我希望 Perl 解析代码文本并识别某些内容,示例代码:

use strict;
use warnings;

$/ = undef;

while (<DATA>) {
  s/(\w+)(\s*<=.*?;)/_yes/gs;
  print;
}

__DATA__
always @(posedge clk or negedge rst_n)
if(!rst_n)begin
        d1 <= 0; //perl_comment_4
        //perl_comment_5
        d2 <= 1  //perl_comment_6
                 + 2;
        end
else if( d3 <= d4 && ( d5 <= 3 ) ) begin
        d6 <= d7 +
                 (d8 <= d9 ? 1 : 0);
        //perl_comment_7
        d10 <= d11 <=
                      d12
                        + d13
                            <= d14 ? 1 : 0;
        end

匹配目标是满足以下所有条件的东西:

(1) 以组合word\s*<=开头。这里 \s* 可能有 0 个或多个空格、换行符、制表符。

(2) 前面提到的 "combination" 应该是 () 中的任何一对。

(3) 如果连续出现多个"combinations",则以第一个为开始。 (类似 "greedy" 在左边界匹配)

(4) 它以(1)中提到的"combination"之后的第一个;结束。

代码注释中可能有word\s*<=;(注释中可能有任何内容);这使事情变得更加复杂。为了让生活更轻松,我已经对文本进行了预处理,扫描评论并将其替换为 //perl_comment_6 之类的内容。 (这个方案看起来比较笨拙,有没有更聪明更优雅的方案?)

我想做什么:

对于所有匹配的 word\s*<=,将 word 替换为 word_yes。对于示例代码,d1d2d6d10 应替换为 d1_yesd2_yesd6_yesd10_yes,文本的所有其他部分应保持不变。

在我当前的代码中,我使用 s/(\w+)(\s*<=.*?;)/_yes/gs;,它可以正确识别 d1d2d10 ,但无法识别d6,误识别d3.

有什么建议吗?提前致谢~

这比您想象的要复杂得多,如果不为您尝试处理的语言编写解析器,就不可能正确完成。但是,如果您的样本是该语言的一贯有限子集,那么您可能很幸运

我能看到的最好的方法是使用 split 将括号中的字符串的所有子部分与要进行替换的 "top level" 部分分开.然后可以对相关部分进行更改,并将拆分的部分重新组合在一起

即使这依赖于具有适当平衡括号的代码,并且出现在字符串或注释中的奇怪的左括号或右括号也会使过程失败。 split 中使用的正则表达式必须是递归的,以便可以匹配嵌套的括号,并使其成为 capturing 正则表达式使得 split returns all字符串的部分而不仅仅是匹配项之间的部分

此代码将按照您的要求执行,但请注意,正如我所描述的,它非常脆弱

use strict;
use warnings;

my $data = do {
    local $/;
    <DATA>;
};

my @split = split / ( \( (?> [^()] | (?1) )* \) ) /x, $data;

for ( @split ) {
    next if /[()]/;
    s/ ^ \s* \w+ \K (?= \s* <= ) /_yes/xgm;
}

print join '', @split;


__DATA__
always @(posedge clk or negedge rst_n)
if(!rst_n)begin
        d1 <= 0; //perl_comment_4
        //perl_comment_5
        d2 <= 1  //perl_comment_6
                 + 2;
        end
else if( d3 <= d4 && ( d5 <= 3 ) ) begin
        d6 <= d7 +
                 (d8 <= d9 ? 1 : 0);
        //perl_comment_7
        d10 <= d11 <=
                      d12
                        + d13
                            <= d14 ? 1 : 0;
        end

输出

always @(posedge clk or negedge rst_n)
if(!rst_n)begin
        d1_yes <= 0; //perl_comment_4
        //perl_comment_5
        d2_yes <= 1  //perl_comment_6
                 + 2;
        end
else if( d3 <= d4 && ( d5 <= 3 ) ) begin
        d6_yes <= d7 +
                 (d8 <= d9 ? 1 : 0);
        //perl_comment_7
        d10_yes <= d11 <=
                      d12
                        + d13
                            <= d14 ? 1 : 0;
        end