Perl6 中的语法有点过于贪婪
Grammar a bit too greedy in Perl6
我遇到这个 mini-grammar 的问题,它试图匹配 markdown-like header 结构。
role Like-a-word {
regex like-a-word { \S+ }
}
role Span does Like-a-word {
regex span { <like-a-word>[\s+ <like-a-word>]* }
}
grammar Grammar::Headers does Span {
token TOP {^ <header> \v+ $}
token hashes { '#'**1..6 }
regex header {^^ <hashes> \h+ <span> [\h* [=10=]]? $$}
}
我希望它匹配 ## Easier ##
作为 header,但它需要 ##
作为 span
的一部分:
TOP
| header
| | hashes
| | * MATCH "##"
| | span
| | | like-a-word
| | | * MATCH "Easier"
| | | like-a-word
| | | * MATCH "##"
| | | like-a-word
| | | * FAIL
| | * MATCH "Easier ##"
| * MATCH "## Easier ##"
* MATCH "## Easier ##\n"
「## Easier ##
」
header => 「## Easier ##」
hashes => 「##」
span => 「Easier ##」
like-a-word => 「Easier」
like-a-word => 「##」
问题是 [\h* [=15=]]?
似乎根本不起作用,span
吞噬了所有可用的单词。有什么想法吗?
只是改变
regex header {^^ <hashes> \h+ <span> [\h* [=10=]]? $$}
至
regex header {^^ (<hashes>) \h+ <span> [\h* [=11=]]? $$}
以便捕获有效。感谢 Eugene Barsky 的调用。
首先,正如其他人指出的那样,<hashes>
不会捕获到 [=14=]
,而是捕获到 $<hashes>
,所以你必须写:
regex header {^^ <hashes> \h+ <span> [\h* $<hashes>]? $$}
但这仍然不符合您想要的方式,因为 [\h* $<hashes>]?
部分很高兴地匹配了零次出现。
正确的解决方法是不要让 span
匹配 ##
作为一个词:
role Like-a-word {
regex like-a-word { <!before '#'> \S+ }
}
role Span does Like-a-word {
regex span { <like-a-word>[\s+ <like-a-word>]* }
}
grammar Grammar::Headers does Span {
token TOP {^ <header> \v+ $}
token hashes { '#'**1..6 }
regex header {^^ <hashes> \h+ <span> [\h* $<hashes>]? $$}
}
say Grammar::Headers.subparse("## Easier ##\n", :rule<header>);
如果您不愿意修改 like-a-word
,您也可以像这样强制排除最后的 #
:
role Like-a-word {
regex like-a-word { \S+ }
}
role Span does Like-a-word {
regex span { <like-a-word>[\s+ <like-a-word>]* }
}
grammar Grammar::Headers does Span {
token TOP {^ <header> \v+ $}
token hashes { '#'**1..6 }
regex header {^^ <hashes> \h+ <span> <!after '#'> [\h* $<hashes>]? $$}
}
say Grammar::Headers.subparse("## Easier ##\n", :rule<header>);
我试了一下这个,因为我认为您可能会做两件有趣的事情。
首先,您可以让 hashes
就匹配的数量进行争论。这样你就可以根据你的水平做一些特殊的事情。您可以在语法的不同部分重复使用 hashes
,在这些地方您需要不同但确切数量的散列标记。
接下来,~
拼接器允许您指定某些内容将出现在两个内容的中间,这样您就可以将这些包装内容并排放置。例如,要匹配 (Foo)
,您可以编写 '(' ~ ')' Foo
。这样看来我想出了 :
use Grammar::Tracer;
role Like-a-word {
regex like-a-word { \S+ }
}
role Span does Like-a-word {
regex span { <like-a-word>[\s+ <like-a-word>]* }
}
grammar Grammar::Headers does Span {
token TOP {^ <header> \v+ $}
token hashes ( $n = 1 ) { '#' ** {$n} }
regex header { [(<hashes(2)>) \h*] ~ [\h* [=10=]] <span> }
}
my $result = Grammar::Headers.parse( "## Easier ##\n" );
say $result;
我遇到这个 mini-grammar 的问题,它试图匹配 markdown-like header 结构。
role Like-a-word {
regex like-a-word { \S+ }
}
role Span does Like-a-word {
regex span { <like-a-word>[\s+ <like-a-word>]* }
}
grammar Grammar::Headers does Span {
token TOP {^ <header> \v+ $}
token hashes { '#'**1..6 }
regex header {^^ <hashes> \h+ <span> [\h* [=10=]]? $$}
}
我希望它匹配 ## Easier ##
作为 header,但它需要 ##
作为 span
的一部分:
TOP
| header
| | hashes
| | * MATCH "##"
| | span
| | | like-a-word
| | | * MATCH "Easier"
| | | like-a-word
| | | * MATCH "##"
| | | like-a-word
| | | * FAIL
| | * MATCH "Easier ##"
| * MATCH "## Easier ##"
* MATCH "## Easier ##\n"
「## Easier ##
」
header => 「## Easier ##」
hashes => 「##」
span => 「Easier ##」
like-a-word => 「Easier」
like-a-word => 「##」
问题是 [\h* [=15=]]?
似乎根本不起作用,span
吞噬了所有可用的单词。有什么想法吗?
只是改变
regex header {^^ <hashes> \h+ <span> [\h* [=10=]]? $$}
至
regex header {^^ (<hashes>) \h+ <span> [\h* [=11=]]? $$}
以便捕获有效。感谢 Eugene Barsky 的调用。
首先,正如其他人指出的那样,<hashes>
不会捕获到 [=14=]
,而是捕获到 $<hashes>
,所以你必须写:
regex header {^^ <hashes> \h+ <span> [\h* $<hashes>]? $$}
但这仍然不符合您想要的方式,因为 [\h* $<hashes>]?
部分很高兴地匹配了零次出现。
正确的解决方法是不要让 span
匹配 ##
作为一个词:
role Like-a-word {
regex like-a-word { <!before '#'> \S+ }
}
role Span does Like-a-word {
regex span { <like-a-word>[\s+ <like-a-word>]* }
}
grammar Grammar::Headers does Span {
token TOP {^ <header> \v+ $}
token hashes { '#'**1..6 }
regex header {^^ <hashes> \h+ <span> [\h* $<hashes>]? $$}
}
say Grammar::Headers.subparse("## Easier ##\n", :rule<header>);
如果您不愿意修改 like-a-word
,您也可以像这样强制排除最后的 #
:
role Like-a-word {
regex like-a-word { \S+ }
}
role Span does Like-a-word {
regex span { <like-a-word>[\s+ <like-a-word>]* }
}
grammar Grammar::Headers does Span {
token TOP {^ <header> \v+ $}
token hashes { '#'**1..6 }
regex header {^^ <hashes> \h+ <span> <!after '#'> [\h* $<hashes>]? $$}
}
say Grammar::Headers.subparse("## Easier ##\n", :rule<header>);
我试了一下这个,因为我认为您可能会做两件有趣的事情。
首先,您可以让 hashes
就匹配的数量进行争论。这样你就可以根据你的水平做一些特殊的事情。您可以在语法的不同部分重复使用 hashes
,在这些地方您需要不同但确切数量的散列标记。
接下来,~
拼接器允许您指定某些内容将出现在两个内容的中间,这样您就可以将这些包装内容并排放置。例如,要匹配 (Foo)
,您可以编写 '(' ~ ')' Foo
。这样看来我想出了
use Grammar::Tracer;
role Like-a-word {
regex like-a-word { \S+ }
}
role Span does Like-a-word {
regex span { <like-a-word>[\s+ <like-a-word>]* }
}
grammar Grammar::Headers does Span {
token TOP {^ <header> \v+ $}
token hashes ( $n = 1 ) { '#' ** {$n} }
regex header { [(<hashes(2)>) \h*] ~ [\h* [=10=]] <span> }
}
my $result = Grammar::Headers.parse( "## Easier ##\n" );
say $result;