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

时间:2016-02-22 11:11:33

标签: regex perl conditional match greedy

我希望Perl解析代码文本并识别某些东西,例如代码:

use strict;
use warnings;

$/ = undef;

while (<DATA>) {
  s/(\w+)(\s*<=.*?;)/$1_yes$2/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)上述&#34;组合&#34;应该是()之间的任何一对。

(3)如果多个&#34;组合&#34;连续出现,然后以第一个为开头。 (类似于&#34;贪婪&#34;在左边界处匹配)

(4)它以&#34;组合后的第一个;结束&#34;在(1)中提到。

代码注释中可能有word\s*<=;(评论中可能有任何内容);这使事情变得更加复杂。为了让生活更轻松,我已经预处理了文本,扫描了评论并用//perl_comment_6之类的内容替换了它们。 (这个解决方案看起来相当麻烦和愚蠢。任何更智能,更优雅的解决方案?)

我想做什么:

对于所有匹配的word\s*<=,请将word替换为word_yes。对于示例代码, d1 d6 d10 应替换为 d1_yes d2_yes d6_yes d10_yes ,文本的所有其他部分应保持不变。

在我目前的代码中,我使用s/(\w+)(\s*<=.*?;)/$1_yes$2/gs;,正确识别 d1 d2 d10 ,但无法识别< strong> d6 并错误地识别 d3

有什么建议吗?提前谢谢〜

1 个答案:

答案 0 :(得分:7)

这可能是您想象的更复杂,如果没有为您尝试处理的语言编写解析器,则无法正常执行。但是,如果您的样本始终是语言的有限子集,那么您可能会很幸运

我可以看到这样做的最好方法是使用split从要完成替换的“顶级”部分中分离括号中所有字符串的子部分。然后可以对相关部分进行更改,并将拆分部分连接在一起

即使这依赖于具有正确平衡括号的代码,并且出现在字符串或注释中的奇数打开或关闭括号将抛出该过程。 split中使用的正则表达式必须是递归的,以便可以匹配嵌套括号,并使其成为捕获正则表达式使split返回字符串的所有部分而不是只是比赛之间的部分

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

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