对于你解决的特定挑战,使用语法就像用大锤砸蚂蚁。
正如@Scimon所说,一个正则表达式就足够了。你可以通过适当地排版使其易读,并将捕获命名并保持在顶层:
/ ^
'#' $<id>=(\d+) ' '
'@ ' $<x>=(\d+) ',' $<y>=(\d+)
': ' $<w>=(\d+) x $<d>=(\d+)
$
/;
say ~$<id x y w d>; # 1 1 3 4 4
(前缀
~
在其右侧的值上调用
.Str
。当在
Match
对象上调用时,它将字符串化为匹配的字符串。)
有了这个前提,你的问题仍然非常重要,因为了解P6如何从简单的正则表达式(如上面的示例)扩展到最大和最复杂的解析任务很重要。因此,本答案的其余部分将涵盖这一方面,并以您的示例作为起点。
更少混乱地挖掘
say $match<id>.hash<digits>.<digit>; # [「1」]
这看起来有点混乱,有更好的方法吗?
你的say
包含了不必要的代码和输出嵌套。你可以简化成类似这样的东西:
say ~$match<id> # 1
更深入地挖掘,更少地弄脏
我对提取实际匹配的标记很感兴趣,即从坐标中提取id、x+y,从维度中提取height+width。
对于匹配多个标记的情况,您不能再依赖Perl 6猜测您想要哪个标记了。(当只有一个时,猜测一下它会猜到你想要哪个。:))
获取y
坐标的一种方法是编写您的say
:
say ~$match<coordinates><digits>[1]
如果您想删除 <digits>
,您可以标记模式的哪些部分应该存储在编号捕获列表中。一种方法是将括号放在这些部分周围:
token coordinates { (<digits>) ',' (<digits>) }
现在,您不再需要提及
<digits>
:
say ~$match<coordinates>[1]
您可以为新的括号捕获命名:
token coordinates { $<x>=(<digits>) ',' $<y>=(<digits>) }
say ~$match<coordinates><y> # 3
预先挖掘
我必须逐个语法产生式地挖掘,才能得到我需要的值。
以上技术仍然都是在深入自动生成的解析树中进行挖掘,而这棵树默认情况下恰好对应于语法层次结构中的规则调用。以上技术只是让你挖掘它的方式看起来更浅。
另一步是将挖掘工作作为解析过程的一部分来完成,以使say
变得简单。
你可以将一些代码嵌入到TOP
标记中,以存储你所需的有趣数据。只需在适当的位置插入一个{...}
块(对于这种情况,这意味着在标记的末尾,因为你需要标记模式已经完成匹配工作):
my $made;
grammar Claim {
token TOP {
'#' <id> \s* '@' \s* <coordinates> ':' \s* <dimensions>
{ $made = ~($<id>, $<coordinatess><x y>, $<dimensions><digits>[0,1]) }
}
...
现在你只需要写成这样:
say $made # 1 1 3 4 4
这说明您可以在任何规则的任何位置编写任意代码——这是大多数解析形式和相关工具所不可能的——并且该代码可以访问在那一点上的解析状态。
更简洁地预处理
内联代码是快速而不太规范的方法。使用变量也是如此。
存储数据的常见方法是使用
make
函数。这将数据挂在正在构建的匹配对象上,对应于给定的规则。然后可以使用
.made
方法检索它。因此,不再需要
$make =
,而是:
{ make ~($<id>, $<coordinatess><x y>, $<dimensions><digits>[0,1]) }
现在,您可以编写:
say $match.made # 1 1 3 4 4
这样更加简洁。但还有更多内容。
解析树的稀疏子树
.oO ( 在 2019 年想象中的 Perl 6 圣诞节日历 的第一天,一个 StackOverflow 的标题对我说... )
在上面的例子中,我只为 TOP
节点构造了一个 .made
荷载。对于较大的语法,通常会形成一个稀疏子树(我为此创造了这个术语,因为找不到标准的现有术语)。
这个稀疏子树包括对 TOP
的 .made
荷载,它是一个数据结构,引用低级规则的 .made
荷载,这些荷载又引用低级规则,依此类推,跳过无趣的中间规则。
在解析一些编程代码后,这个稀疏子树的典型用途是形成一个抽象语法树。
实际上,.made
还有一个别名,即 .ast
:
say $match.ast # 1 1 3 4 4
虽然这很容易使用,但也是完全通用的。P6使用P6语法解析P6代码,然后使用此机制构建AST。
使所有内容优雅化,为了可维护性和可重用性,您可以并且通常不应在规则末尾内联插入代码,而应该使用Action对象。
总之,有一系列从简单到复杂的通用机制,可以根据任何给定的用例组合最佳。
如上所述,添加括号,并命名括号零点的捕获,如果这是深入解析树的良好简化,则可以执行此操作。
内联任何您希望在规则解析过程中执行的操作。此时,您可以完全访问解析状态。这非常适合从解析中轻松提取所需数据,因为您可以使用make便捷函数。您还可以将所有要在成功匹配规则后执行的操作抽象出来,使语法保持干净,并确保单个语法可重复使用于多个操作。
最后,您可能希望修剪解析树以省略不必要的叶子详细信息(以减少内存消耗和/或简化解析树显示)。为此,请编写<.foo>,其中点表示规则名称,并关闭该规则的默认自动捕获。