用于捕获和修改 LFM 代码块的 Raku Regex

Raku Regex to capture and modify the LFM code blocks

更新:下面添加了更正的代码

我有一个 Leanpub 风格的 markdown* 文件,名为 sample.md 我想将其代码块转换为 Github 风格markdown 样式使用 Raku Regex

Here's a sample **ruby** code, which
prints the elements of an array:

{:lang="ruby"}
    ['Ian','Rich','Jon'].each {|x| puts x}

Here's a sample **shell** code, which
removes the ending commas and
finds all folders in the current path:

{:lang="shell"}
    sed s/,$//g
    find . -type d

为了获取 lang 值,例如ruby{:lang="ruby"} 转换成

```ruby

我用这个代码

my @in="sample.md".IO.lines;
my @out;
for @in.kv -> $key,$val {
    if $val.starts-with("\{:lang") {
       if $val ~~ /^{:lang="([a-z]+)"}$/ { # capture lang
           @out[$key]="```[=12=]"; # convert it into ```ruby
           $key++;
           while @in[$key].starts-with("    ") {
                 @out[$key]=@in[$key].trim-leading;
                 $key++;
           }
           @out[$key]="```";
       }
    }
    @out[$key]=$val;
}

包含正则表达式的行给出 无法修改不可变对 (lang => True) 错误。

我刚开始使用正则表达式。我试过 (\w) 而不是 ([a-z]+),它给出了 Unrecognized backslash sequence: '\w' 错误,等等。

如何使用 Regex 正确捕获和修改 lang 值?

更正后的代码:

my @in="sample.md".IO.lines;
my \len=@in.elems;
my @out;
my $k = 0;

while ($k < len) {
    if @in[$k] ~~ / ^ '{:lang="' (\w+) '"}' $ / { 
    push @out, "```[=13=]";
    $k++;
    while @in[$k].starts-with("    ") {
        push @out, @in[$k].trim-leading;
        $k++;   }
    push @out, "```";
    }
    push @out, @in[$k];
    $k++;
}

for @out {print "$_\n"}

TL;DR

  • TL? 然后阅读@jjemerelo 的优秀答案,它不仅提供了 one-line 解决方案,而且提供了更多以紧凑的形式 ;

  • DR? 哦,在我看来,你在这个答案中遗漏了一些被 JJ(合理地!)忽略的好东西。不过,JJ's 又一次成为了炸弹。先去读吧。 :)

使用 Perl 正则表达式

正则表达式有很多方言。您使用的正则表达式模式是 Perl 正则表达式,但您没有告诉 Raku。所以它将您的正则表达式解释为 Raku 正则表达式,而不是 Perl 正则表达式。这就像将 Python 代码喂给 perl。所以报错信息没有用。


一个选项是切换到 Perl 正则表达式处理。为此,此代码:

      /^{:lang="([a-z]+)"}$/

开始时需要 m :P5

m :P5 /^{:lang="([a-z]+)"}$/

当您在假定您打算立即匹配的上下文中使用 /.../ 时,m 是隐式的,但是因为添加了 :P5“副词”来修饰Raku 如何解释正则表达式中的模式,还必须添加 m.

:P5 仅支持一组有限的 Perl 正则表达式模式。也就是说,对于您在问题中编写的正则表达式来说应该足够了。

使用 Raku 正则表达式

如果您想使用 Raku 正则表达式,您必须学习 Raku 正则表达式语言。

Raku 正则表达式语言的“精神”与 Perl 的相同,一些绝对的基本语法与 Perl 的相同,但它的不同之处足以让您将它视为正则表达式的另一种方言,只是相对于 Perl 的正则表达式,通常是“增强”的。

要以 Raku 格式重写正则表达式,我认为应该是:

/ ^ '{:lang="' (<[a..z]>+) '"}' $ /

(利用 Raku 正则表达式中的空格被忽略的事实。)

您的代码中的其他问题

修复正则表达式后,您的代码中会遇到其他问题。

我遇到的第一个问题是$key是只读的,所以$key++失败了。一种选择是通过写入 -> $key is copy ... 使其可写,这使得 $key 成为 .kv.

传递的索引的读写副本

但解决这个问题会导致另一个问题。而且代码是如此复杂,我得出结论,我最好不要再追究了。我已经解决了您眼前的障碍,希望对您有所帮助。

这一行似乎解决了问题:

say S:g /\{\: "lang" \= \" (\w+) \" \} /```[=10=]/ given "text.md".IO.slurp;

不过,让我们尝试解释一下发生了什么。该错误是一个正则表达式语法错误,是由于 : 后跟一个名称,并且所有内容都在卷曲内引起的。 {} 在正则表达式中运行代码。 Raiph 的答案(显然)是正确的,将其更改为 Perl 正则表达式。但我在这里所做的是将其更改为带有 :g 全局标志的 Raku 的 non-destructive substitution,以使其作用于整个文件(在行尾被吸;我已经将其保存到名为 text.md 的文件中)。所以这样做是为了吞噬你的目标文件,given 它被保存在 $_ 主题变量中,并在替换完成后打印出来。好处是如果你想做更多的替换,你可以将另一个这样的表达式推到前面,它会作用于输出。 使用这种表达式在概念上总是比逐行处理文本更简单,而且可能更快。