语法中的部分匹配

sug*_*rfi 7 raku

我有一个简单的语法,我用它来解析一些文本。文本是用户输入的,但我的程序保证它与语法匹配。(即,如果我的语法只匹配a,则文本可能是abcaa_。)但是,当我.parse在语法上使用该方法时,它在任何非完全匹配时都会失败。如何执行部分匹配?

Tyi*_*yil 6

在 Raku 中,Grammar.parse必须匹配整个字符串。如果您的语法仅a在 string 中匹配,这就是导致它失败的原因abc。要允许仅匹配输入字符串的一部分,您可以Grammar.subparse改用。

grammar Foo {
    token TOP { 'a' }
}

my $string = 'abc';

say Foo.parse($string);    # Nil
say Foo.subparse($string); # ?a?
Run Code Online (Sandbox Code Playgroud)

输入字符串需要以电位开头Match。否则,您将获得失败的匹配。

say Foo.subparse('cbacb'); # #<failed match>
Run Code Online (Sandbox Code Playgroud)

您可以使用Capture 标记解决此问题。

grammar Bar {
    token TOP {
        <-[a]>*   # Match 0 or more characters that are *not* a
        <( 'a'    # Start the match, and match a single 'a'
    }
}

say Bar.parse('a');        # ?a?
say Bar.subparse('a');     # ?a?
say Bar.parse('abc');      # Nil
say Bar.subparse('abc');   # ?a?
say Bar.parse('cbabc');    # Nil
say Bar.subparse('cbabc'); # ?a?
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为<-[a]>*包含字母之外的任何字符的字符类a将消耗潜在之前的所有字符a。但是,捕获标记会导致它们从最终Match对象中删除,只留下a您想要匹配的对象。


rai*_*iph 5

TL; 博士

grammar foo { token TOP { a* } } 

# Partial match anchored at start of string:
say .subparse: 'abcaa' given foo; # ?a? 

# Partial match anchored to end of string:
say 'abcaa' ~~ / <.foo::TOP> $ /; #  ?aa?

# Longest partial match, no anchoring:
say ('abcaaabcaabc' ~~ m:g/ <.foo::TOP> /).max(*.chars); #  ?aaa?
Run Code Online (Sandbox Code Playgroud)

词汇

传统上对文本“匹配”的一般概念有两种看法:

  • “解析”

  • “正则表达式”

乐:

  • 提供统一的文本模式语言和引擎,可以完成这两项工作。

  • 可以很容易地坚持一个或其他观点,或者混合它们,或者在它们之间重构,以适合个人开发和/或个人用例。

  • 采取“解析”来表示或多或少从输入字符串的开头开始的单个匹配,而“正则表达式”则更加灵活。

您在问题中所写的内容以及您对 Tyil 回答的第一条评论反映了该主题固有的歧义。我将提供两个而不是一个答案来尝试帮助您和/或其他读者更清楚 Raku 对词汇的使用以及您的选项功能明智。

通过.parse等人的有限“部分匹配”

你开始于:

语法中的部分匹配......我有一个简单的语法......我的程序保证它以语法的匹配开始

考虑到这一点,这是您的问题:

如何执行部分匹配

短语“保证它开始”和“部分匹配”是模棱两可的。

一种情况是,您需要我称之为“前缀”匹配的内容,匹配从字符串开头锚定的一个或多个字符,而不仅仅是在输入字符串中的任何位置开始和结束的任何子字符串。

这非常适合“解析”,或者至少 Raku 在其语法方法中使用这个词。

所有以它们的名字命名的内置Grammar方法都会parse在它们用来启动解析过程的任何语法规则中在字符串的开头插入一个锚点。你不能移除那个锚点。这反映了词汇的选择;无论发生什么,“解析”都意味着从一开始就匹配。

这个“前缀”场景的解析方法是.subparse

grammar foo { token TOP { a* } } 

# Partial match anchored at start of string:
say .subparse: 'abcaa' given foo; # ?a? 
Run Code Online (Sandbox Code Playgroud)

也可以看看:


但也许“保证它开始”和“部分匹配”并不意味着你想要在开始时锚定。您对 Tyil 回答的评论突出了这种含糊之处:

.subparse只匹配在一开始,或者匹配字符串中的任何地方?

Tyil 提供了一种解决方法。您可以执行 Tyil 显示的操作,但只有a在输入字符串中遇到的第一个字符串是您希望“解析”匹配的子字符串开头的字符串时,它才会匹配。

如果第一个a是误报,并且有第二个或后续a您希望“解析”匹配开始,那么,至少在 Raku 世界中,将其称为“正则表达式”而不是“解析”会很有帮助并通过使用“正则表达式”匹配~~smartmatch操作

限制“部分匹配”经由~~

如果您将其构造与正则表达式一起使用,Raku 可以让您进行无限制的部分匹配~~

例如,你可以写:

# End of match at end of string:
                          ?
say 'abcaa' ~~ token { a* $ } #  ?aa?
Run Code Online (Sandbox Code Playgroud)

~~ 使用正则表达式告诉 Raku:

  • 从 LHS 上字符串的第一个字符位置开始尝试匹配;

  • 如果失败,则前进一个字符,然后再试一次,将输入字符串中的新位置视为新的起点;

  • 重复该操作,直到匹配一次,或者在整个字符串中找不到任何匹配项。

在这里,我没有指定匹配的开始位置(这~~意味着它可以在字符串中的任何位置)并将模式的末尾锚定到输入字符串的末尾。所以它成功匹配aa了字符串末尾的 。

这种锚定自由仅说明了~~智能匹配提供比使用这些parse方法更大的匹配灵活性的众多方式中的一种。


如果您有现有的语法,您仍然可以使用它:

grammar foo { token TOP { a* } } 

# Anchor matching to end of string:
                             ?
say 'abcaa' ~~ / <.foo::TOP> $ /; #  ?aa?
Run Code Online (Sandbox Code Playgroud)

您必须命名要调用的语法和其中的规则,并将它们放入<...>. 并且您需要插入 a.以避免相应命名的子捕获,假设您不想要那样。


这是另一个例子:

# Longest partial match, no anchoring:
say ('abcaaabcaabc' ~~ m:g/ <.foo::TOP> /).max(*.chars); #  ?aaa?
Run Code Online (Sandbox Code Playgroud)

“分析”在乐总是开始于输入字符串的开头和结果要么不匹配或一个匹配

相比之下,“regex”可以匹配任意片段,并且可以匹配任意数量的片段。(您甚至可以匹配重叠片段。)

在我的最后一个示例中,我使用了:g,它是 的缩写:global,这是传统正则表达式引擎中众所周知的特性。:g匹配次数与在输入字符串中找到匹配项的次数相同(但不重叠)。

然后匹配操作返回Nil(根本没有匹配项)或匹配对象列表(一个或多个)。我已经应用 a.max(*.chars)来产生最长的匹配(如果有多个最长的子字符串,则是第一个)。