gpt4 book ai didi

grammar - 如何在Raku中使用匹配的定界符

转载 作者:行者123 更新时间:2023-12-03 22:15:01 33 4
gpt4 key购买 nike

我正在尝试编写一个 token ,该 token 允许嵌套的内容具有匹配的定界符。如果(AB)不等于“(AB)”,则至少应匹配“AB”。并且(A(c)B)将返回两个匹配项“(A(c)B)”,依此类推。

代码从源头精炼而成:

#!/home/hsmyers/rakudo741/bin/perl6
use v6d;

my @tie;

class add-in {
method tie($/) { @tie.push: $/; }
}

grammar tied {
rule TOP { <line>* }
token line {
<.ws>?
[
| <tie>
| <simpleNotes>
]+
<.ws>?
}
token tie {
[
|| <.ws>? <simpleNotes>+ <tie>* <simpleNotes>* <.ws>?
|| <openParen> ~ <closeParen> <tie>
]+
}
token openParen { '(' }
token closeParen { ')' }
token simpleNotes {
[
| <[A..Ga..g,'>0..9]>
| <[|\]]>
| <blank>
]
}
}

my $text = "(c2D) | (aA) (A2 | B)>G A>F G>E (A,2 |\nD)>F A>c d>f |]";

tied.parse($text, actions => add-in.new).say;
$text.say;
for (@tie) {
s:g/\v/\\n/;
say "«$_»";
}

这给出了部分正确的结果:
«c2D»
«aA»
«(aA)»
«A2 | B»
«\nD»
«A,2 |\nD»
«(A,2 |\nD)>F A>c d>f |]»
«(c2D) | (aA) (A2 | B)>G A>F G>E (A,2 |\nD)>F A>c d>f |]»

顺便说一句,我不关心换行符,它只是在检查该方法是否可以将文本跨两行。因此,我看到带有和不带有括号的捕获的 Ember ,还有一个或两个非常贪婪的捕获。

显然,我的代码存在问题。我对perl6的了解最好被形容为“初学者”,所以我寻求您的帮助。我正在寻找一个通用的解决方案或至少一个可以推广的示例,一如既往地欢迎提出建议和更正。

最佳答案

您还需要增加一些复杂性。例如,您将tie定义为(...)或只是...。但是内部内容与该行相同。

这是一个重写的语法,可以极大地简化您想要的内容。在编写语法时,从小处着手并逐步发展对您很有帮助。

grammar Tied {
rule TOP { <notes>+ %% \v+ }
token notes {
[
| <tie>
| <simple-note>
] +
%%
<.ws>?
}
token open-tie { '(' }
token close-tie { ')' }
token tie { <.open-tie> ~ <.close-tie> <notes> }
token simple-note { <[A..Ga..g,'>0..9|\]]> }
}

这里有一些文体上的注释。语法是类,习惯将它们大写。 token 是方法,使用kebap大小写时通常是小写字母(当然,您可以使用所需的任何类型)。在 tie token 中,您会注意到我使用了 <.open-tie>.意味着我们不需要捕获它(也就是说,我们只是将其用于匹配,而没有其他用途)。在 notes token 中,我可以通过使用 %%并将 TOP设置为自动添加一些空白的规则来简化很多事情。

现在,我创建 token 的顺序是这样的:
  • <simple-note>,因为它是最基础的项目。其中一组将是
  • <notes>,所以我接下来要讲。这样做的时候,我意识到一些笔记也可以包含一个…
  • <tie>,这是下一个。在领带内部,尽管我将要进行其他笔记,所以我可以在其中使用<notes>
  • 最后是
  • <TOP>,因为如果一行仅包含一系列注释,我们可以省略行并使用%% \v+

  • Action (通常与语法同名,加上 -Actions,所以在这里我使用 class Tied-Actions { … })通常用于创建抽象语法树。但是,实际上,思考此问题的最佳方法是询问语法的每个级别,我们希望从语法中获得什么。我发现尽管编写语法最容易从最小的元素开始进行构建,但对于 Action 而言,最简单的是从TOP向下进行构建。这也将帮助您制定更复杂的操作:
  • 我们想从TOP中得到什么?
    在我们的例子中,我们只想要在每个<note> token 中找到的所有联系。这可以通过一个简单的循环来完成(因为我们在<notes>上做了一个量词,所以它就是Positional:method TOP ($/) { 
    my @ties;
    @ties.append: .made for $<notes>;
    make @ties;
    }
    上面的代码创建了我们的temp变量,遍历每个<note>并追加到<note>为我们创建的所有内容上—目前尚无,但这没关系。然后,由于我们要使用TOP中的联系,因此我们对它们进行make编码,这使我们可以在解析后对其进行访问。
  • 您想从<notes>中得到什么?
    同样,我们只需要联系(但也许在其他时间,您需要联系和眼神,或其他一些信息)。因此,我们基本上可以做完全相同的事情来捕获纽带:method notes ($/) { 
    my @ties;
    @ties.append: .made for $<tie>.grep(*.defined);
    make @ties;
    }
    唯一的区别是不只是做for $<tie>,我们只需要抓取已定义的-这是做[<foo>|<bar>]+的结果:$<foo>将为每个量化的匹配都有一个插槽,无论是否注意到<foo>进行了匹配(这是您经常会想将事情弹出到带有领带和简单便笺变体的proto token note中,但是对此有点建议)。再次,我们获取为我们制作的任何$<tie>-我们稍后将对其进行定义,然后我们对其进行``制作''。无论我们make是什么,其他操作也会通过made找到<notes>(例如TOP)。
  • 您想从<tie>中得到什么?
    在这里,我将只介绍领带的内容-如果您愿意的话,也很容易捕获括号。您可能会以为我们只使用make ~$<notes>,但这遗漏了一些重要的东西:$<notes>也有一些联系。但是这些很容易捕获:method tie ($/) {
    my @ties = ~$<notes>;
    @ties.append: $<notes>.made;
    make @ties;
    }
    这样可以确保我们不仅传递当前的外部领带,而且传递每个单独的内部领带(进而可以传递另一个内部领带,依此类推)。

  • 解析时,您需要做的就是获取 .madeMatch:
    say Tied.parse("a(b(c))d");
    # 「a(b(c))d」
    # notes => 「a(b(c))d」
    # simple-note => 「a」
    # tie => 「(b(c))」 <-- there's a tie!
    # notes => 「b(c)」
    # simple-note => 「b」
    # tie => 「(c)」 <-- there's another!
    # notes => 「c」
    # simple-note => 「c」
    # simple-note => 「d」
    say Tied.parse("a(b(c))d", actions => TiedActions).made;
    # [b(c) c]

    现在,如果您真的只需要联系-而没有其他需要-(我认为情况并非如此),那么您可以做得简单得多。使用相同的语法,而是使用以下操作:
    class Tied-Actions {
    has @!ties;
    method TOP ($/) { make @!ties }
    method tie ($/) { @!ties.push: ~$<notes> }
    }

    与以前相比,它有几个缺点:尽管可以工作,但伸缩性不是很好。虽然会获得每条领带,但您对其上下文一无所知。另外,您必须实例化Tied-Actions(即 actions => TiedActions.new),而如果可以避免使用任何属性,则可以传递type对象。

    关于grammar - 如何在Raku中使用匹配的定界符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59779527/

    33 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com