【问题标题】:perl6 grammar , not sure about some syntax in an exampleperl6 语法,不确定示例中的某些语法
【发布时间】:2018-12-26 17:38:47
【问题描述】:

我还在学习 perl6,我正在从这个页面阅读语法示例:http://examples.perl6.org/categories/parsers/SimpleStrings.html;我已经多次阅读有关正则表达式的文档,但仍有一些我不明白的语法。

token string { <quote> {} <quotebody($<quote>)> $<quote> }

问题1:令牌中的这个“{}”是做什么的?捕获标记为 ,嵌套结构为 tilda '(' ~ ')';但什么是 {}?

token quotebody($quote) { ( <escaped($quote)> | <!before $quote> . )* }

问题 2a: 中的 escaped($quote) 将是一个正则表达式函数,对吗?它接受 $quote 作为参数并返回另一个正则表达式?

问题 2b:如果我想指出“char that is not before quote”,我应该使用“. ”而不是“”。 ??

token escaped($quote) { '\\' ( $quote | '\\' ) } # I think this is a function;

【问题讨论】:

  • 嗨,raiph,非常感谢!我太忙了,直到现在才能够回到 stackoverflow。我读了你的解释,他们真的帮助我理解了。我将测试我们的更多代码。谢谢!!!

标签: regex grammar raku


【解决方案1】:

{} 是一个空代码块。它是语法的程序性(而不是声明性)元素。你可以把常规的 Perl 6 代码放在那里让它做一些事情。

在这种模式下,它正在做另一项工作。它提供了一个序列点,语法引擎知道它需要做各种事情才能继续。这包括填写捕获变量的值(例如$&lt;quote&gt;)。模式的下一部分需要确保$&lt;quote&gt; 具有它的值,因此它需要一些东西来确保该值可用。

$&lt;quote&gt; 实际上是对 Match 对象 $/ 的单个元素访问。作为类似哈希的东西,这真的是$/&lt;quote&gt;,其中尖括号之间的东西是“关键”。 Perl 6 喜欢有点聪明,所以它让你离开/ 得到$&lt;quote&gt;$1 等其他匹配变量也是类似的快捷方式。

对于您的最后一个问题,查看您尝试匹配的一些示例数据会有所帮助。 Perl 6 Grammars 有许多匹配平衡文本的特性,这可能使任务变得微不足道。例如,参见Regexp documentation 中的Tilde for nesting structures

 / '(' ~ ')' <expression> /

这是 REPL 中的一个简短示例。有一个字符串,其中包含一些带引号的文本:

$ perl6
To exit type 'exit' or '^D'
> my $s = Q/abcdf "Hello" xyz/
abcdf "Hello" xyz

正则表达式中的~ 位于分隔符之间。结束分隔符之后的内容是您希望在 ~ 所在位置的内容:

> $s ~~ m/ '"' ~ '"' .+ /
「"Hello"」

您可以匹配开头的内容并捕获它(现在它位于 $0 中),因此您可以使用与结束分隔符完全相同的内容:

> $s ~~ m/ (<["']>) ~ $0 .+ /
「"Hello"」
 0 => 「"」

对于that particular example,我认为有一个更简单的方法。匹配转义的引号或任何不是引号的内容,而不是环顾四周和任何字符。这可不是脑筋急转弯。

【讨论】:

  • 我测试了~(牛仔套索):my $a="123 \"abc\" 456";说 $a ~~ m/ () ~ {$/[0]} .+ /; 答案1:「"abc" 456」 0 => 「"」 ;并且说 $a ~~ m/ () ~ $0 .+ / ; 答案2: 「"abc"」 0 => 「"」 ;并且说 $a ~~ m/ () ~ "{$/[0]}" .+ / ; ANSWER3: 「"abc"」 0 => 「"」 ;不知何故 $/[0] 和 $0 不是 100% 相同的。 ANSWER1 != (ANSWER2 == ANSWER3);
  • @lisprogtor 我已经删除了我对您上面评论中的代码/结果的最后评论。那是假的。 ANSWER1 错误的原因是正则表达式中的{...} 只是在大括号内执行您告诉它的任何操作,但对正则表达式没有影响。而$0 插入$0 内容的字符串化作为正则表达式模式的一部分,"{$/[0]}" 也插入一个字符串作为模式的一部分。如果你写&lt;{$/[0]}&gt; 而不仅仅是{$/[0]},它会将{$/[0]} 的结果插入到正则表达式中。另外,我用更多的更正/清晰/细节再次完全重写了我的答案。
  • 顺便说一句,raiph,我认为 Perl6 官方文档应该包括你的解释 :-)
【解决方案2】:

TL;DR @briandfoy 提供了 an easy to digest answer。但这里有他没有提到的龙。还有漂亮的蝴蝶。这个答案很深入。

问题1:令牌中的{}在做什么?

这是一个代码块1,2,3,4

它是一个空的,被插入纯粹是为了强制quotebody($&lt;quote&gt;) 中的$&lt;quote&gt; 评估为正则表达式开头的&lt;quote&gt; 捕获的值。

$&lt;quote&gt; 在不插入代码块的情况下包含正确值的原因是 Rakudo Perl 6 编译器限制或与“匹配变量的发布”相关的错误。

Rakudo 对匹配变量的“发布”

Moritz Lenz 在a Rakudo bug report 中指出“除非认为有必要,否则正则表达式引擎不会发布匹配变量”

“正则表达式引擎”是指 NQP 中的正则表达式/语法引擎,它是 Rakudo Perl 6 编译器的一部分。3

“匹配变量”是指存储匹配结果捕获的变量:

  • 当前匹配变量$/;

  • 编号子匹配变量$0$1等;

  • 命名子匹配变量$&lt;foo&gt;

“发布”是指正则表达式/语法引擎执行所需的操作,以便在正则表达式(标记也是正则表达式)中对任何变量的任何提及都评估为它们应该具有的值重新应该拥有它们。在给定的正则表达式中,匹配变量应该包含一个Match object,对应于在处理该正则表达式的任何给定阶段为它们捕获的内容,如果没有捕获任何内容,则为Nil

“认为必要”是指正则表达式/语法引擎在匹配过程的每个步骤之后对是否值得进行发布工作做出保守判断。我所说的“保守”是指引擎经常避免发布,因为它会减慢速度并且通常是不必要的。不幸的是,对于何时真正需要发布 有时过于乐观。因此,程序员有时需要通过显式插入代码块来强制发布匹配变量(以及其他变量的其他技术5)进行干预。随着时间的推移,正则表达式/语法引擎可能会在这方面有所改进,从而减少需要手动干预的情况。如果您希望帮助完成此任务,请针对现有的相关错误创建对您很重要的测试用例。5

$&lt;quote&gt; 值的“发布”

命名的捕获$&lt;quote&gt; 就是这里的例子。

据我所知,当直接写入正则表达式时,所有子匹配变量都正确地引用了它们捕获的值,而没有周围的构造。这有效:

my regex quote { <['"]> }
say so '"aa"' ~~ / <quote> aa $<quote> /; # True

我认为6$&lt;quote&gt; 得到了正确的值,因为它被解析为 regex 俚语 构造。4

相比之下,如果 {} 被删除

token string { <quote> {} <quotebody($<quote>)> $<quote> }

那么quotebody($&lt;quote&gt;) 中的$&lt;quote&gt;包含由开头&lt;quote&gt; 捕获的值。

我认为这是因为 $&lt;quote&gt; 在这种情况下被解析为 main 俚语结构。

问题 2a:escaped($quote)&lt;&gt; 将是一个正则表达式函数,对吗?它以$quote 作为参数

这是一个很好的初步近似。

更具体地说,&lt;foo(...)&gt; 形式的正则表达式原子是对 方法 foo 的调用。

所有正则表达式——无论是用tokenregexrule/.../ 还是任何其他形式声明——都是方法。但是用method 声明的方法是不是正则表达式:

say Method ~~ Regex; # False
say WHAT token { . } # (Regex)
say Regex ~~ Method; # True
say / . / ~~ Method; # True

当遇到&lt;escaped($quote)&gt; 正则表达式原子时,正则表达式/语法引擎不知道或关心escaped 是否是正则表达式,也不关心the details of method dispatch inside a regex or grammar。它只是调用方法分派,调用者设置为 Match 由封闭的正则表达式构造的对象。

调用将控制权交给最终运行该方法的任何对象。事实证明,正则表达式/语法引擎只是递归地回调自身,因为通常这是一个正则表达式调用另一个正则表达式的问题。但不一定如此。

并返回另一个正则表达式

不,&lt;escaped($quote)&gt; 形式的正则表达式原子不会返回另一个正则表达式。

相反,它调用一个将/应该返回Match 对象的方法。

如果调用的方法是正则表达式,P6 将确保正则表达式自动生成并填充 Match 对象。

如果调用的方法不是正则表达式,而只是普通方法,则该方法的代码应该手动创建并返回一个Match 对象。 Moritz 在他对 SO 问题 Can I change the Perl 6 slang inside a method? 的回答中展示了一个示例。

Match 对象返回到驱动正则表达式匹配/语法解析的“正则表达式/语法引擎”。3

然后引擎根据结果决定下一步做什么:

  • 如果匹配成功,引擎会更新与调用正则表达式对应的整体匹配对象。更新可以包括将返回的Match 对象保存为调用正则表达式的子匹配捕获。这就是构建匹配/解析的方式。

  • 如果匹配不成功,引擎可能会回溯,撤消之前的更新;因此解析树可能会随着匹配的进行而动态增长和缩小。

问题2b:如果我想指出“char that is not before quote”,我应该使用. &lt;!before $quote&gt;而不是&lt;!before $quote&gt; .吗??

是的。

但这不是 quotebody 正则表达式所需要的,如果这就是您所说的。

关于后一个主题,在@briandfoy 的回答中,他建议使用“匹配...任何不是引用的内容”结构,而不是消极地向前看 (&lt;!before $quote&gt;)。他的观点是匹配“not a quote”比“are we not before a quote? then match any character”更容易理解。

但是,当引用是一个变量,其值被设置为开始引用的捕获时,这样做绝不是直截了当的。这种复杂性是由于 Rakudo 中的错误造成的。我已经找到了我认为最简单的解决方法,但我认为最好还是坚持使用&lt;!before $quote&gt; .,除非/直到这些长期存在的 Rakudo 错误得到修复。5

token escaped($quote) { '\\' ( $quote | '\\' ) } # I think this is a function;

这是一个令牌,这是一个Regex,这是一个Method,这是一个Routine

say token { . } ~~ Regex;   # True
say Regex       ~~ Method;  # True
say Method      ~~ Routine; # True

正则表达式主体内的代码({ ... } 位)(在这种情况下,代码是 token { . } 中唯一的 .,它是匹配单个字符的正则表达式原子)写在P6 正则表达式“俚语”,而在 method 例程主体内使用的代码是用 P6 主“俚语”编写的。4

使用~

regex tilde (~) operator 是专门为这个问题所涉及的示例中的解析而设计的。它读起来更好,因为它可以立即识别并将开始和结束引号放在一起。更重要的是,它可以在失败时提供人类可理解的错误消息,因为它可以说明它正在寻找什么结束分隔符。

但是,如果您在正则表达式 ~ 运算符(在其任一侧)旁边的正则表达式(其中包含或不包含代码)中插入代码块,则必须考虑一个关键问题。除非您特别希望波浪号将代码块视为自己的原子,否则您将需要对代码块进行分组。例如:

token foo { <quote> ~ $<quote> {} <quotebody($<quote>) }

将匹配一对&lt;quote&gt;s 它们之间没有任何内容。 (然后尝试匹配&lt;quotebody...&gt;。)

相比之下,这是一种在String::Simple::Grammar 语法中复制string 标记的匹配行为的方法:

token string { <quote> ~ $<quote> [ {} <quotebody($<quote>) ] }

脚注

1 2002 年,Larry Wall 写了"It needs to be just as easy for a regex to call Perl code as it is for Perl code to call a regex."。计算机科学家注意到你不能在a traditional regular expression 中间有程序代码。但是 Perls 很久以前就导致了向non-traditional regexes 的转变,P6 得出了合乎逻辑的结论——只需一个简单的{...} 就可以在正则表达式中间插入任意过程代码。语言设计和正则表达式/语法引擎实现3 确保识别正则表达式中的传统样式纯声明性区域,以便可以将正式的正则表达式理论和优化应用于它们,但仍然是任意的正则过程代码也可以插入。简单的用途包括matching logicdebugging。但天空是极限。

2 正则表达式的第一个过程元素(如果有)终止正则表达式的所谓“声明性前缀”。插入空代码块 ({}) 的一个常见原因是,当它为正则表达式中的给定 longest alternation 提供所需的匹配语义时,故意终止正则表达式的声明性前缀。 (但这不是将其包含在您试图理解的令牌中的原因。)

3 粗略地说,NQP 中的正则表达式/语法引擎对于 P6 就像 PCRE 对于 P5。

一个关键的区别在于,正则表达式语言,连同其相关的正则表达式/语法引擎,以及与之合作的主要语言,在 Rakudo 的情况下是 Perl 6,在控制方面是同等重要的。这是Larry Wall's original 2002 vision for integration between regexes and "rich languages" 的实现。每种语言/运行时都可以调用另一种语言并通过高级 FFI 进行通信。因此,它们可以看起来,可以表现为,并且确实是,协作语言和协作运行时的单一系统。

(P6 设计使得所有语言都可以通过两个互补的 P6 FFIs 以“丰富”的方式进行显式设计或改造,以“丰富”的方式进行合作:元模型 FFI @ 987654338@ 和/或 C 调用约定 FFI NativeCall。)

4 P6 语言实际上是一起使用的子语言(也称为俚语)的集合。当您阅读或编写 P6 代码时,您正在阅读或编写以一种俚语开始但有其他部分编写的源代码。文件的第一行使用主要俚语。假设这类似于英语。正则表达式是用另一种俚语写的;假设这就像西班牙语。所以在the grammar String::Simple::Grammar的情况下,代码以英文开头(use v6;语句),然后递归成西班牙文(在rule TOP {{之后),即^ &lt;string&gt; $位,然后返回成英文(评论以# Note ...开头)。然后它为&lt;quote&gt; {} &lt;quotebody($&lt;quote&gt;)&gt; $&lt;quote&gt; 递归回西班牙语,在该西班牙语中间,在{} 代码块,它再次递归到另一个级别的英语。这就是英语中的西班牙语中的英语。当然,代码块是空的,所以就像什么都不用英语写/读,然后立即回到西班牙语,但重要的是要理解这种语言/运行时的递归堆叠是 P6 的工作方式,两者都是单一的整体语言/运行时以及与其他非 P6 语言/运行时合作时。

5 在应用两个潜在改进的过程中,我遇到了几个错误,列在本脚注的末尾。 (在 briandfoy 的回答和这个回答中都提到了。)这两个“改进”是使用 ~ 构造,以及“不是引用”构造而不是使用 &lt;!before foo&gt; .。最终结果,以及相关错误的提及:

grammar String::Simple::Grammar {
  rule TOP {^ <string> $}
  token string {
    :my $*not-quote;
    <quote> ~ $<quote>
    [
      { $*not-quote = "<-[$<quote>]>" }
      <quotebody($<quote>)>
    ]
  }
  token quote { '"' | "'" }
  token quotebody($quote) { ( <escaped($quote)> | <$*not-quote> )* }
  token escaped($quote) { '\\' ( $quote | '\\' ) }
}

如果有人知道更简单的方法,我很乐意在下面的评论中听到它。

我最终在 RT 错误数据库中搜索所有正则表达式错误。我知道 SO 不是错误数据库,但我认为注意以下几点对我来说是合理的。 Aiui 前两个直接与发布匹配变量的问题交互。

6 这个问题和我的回答把我推到了我对 P6 雄心勃勃和复杂方面的理解的外部极限。我计划很快深入了解 nqp 和完整 P6 之间的精确交互,以及它们的正则表达式俚语和主要俚语之间的切换,如上文脚注中所述。 (我目前的希望主要在于刚刚购买了commaide。)如果/当我有一些结果时,我会更新这个答案。

【讨论】:

  • 嗨,raiph,非常感谢您的详细解释。我知道您花了很多时间回答我的问题并修改了您的答案以使其易于理解。你真是太好了,我和所有认真学习perl6的人一样,衷心感谢你。您的答案扩展了许多通过阅读文档网页无法获得的概念、怪癖和细微差别。 (继续)
  • 不幸的是,对于我们许多大学毕业而不从事软件业务的人来说,perl6 站点是可以求助的来源。幸运的是,这个世界有幸拥有像你这样愿意教书的人 :-) !!!如果我们在任何 perl6 会议上见面,我一定会请你喝一杯 :-)
  • @lisprogtor 感谢您的 cmets。 :) 我从来没有参加过 perl 会议,但是 LMK 如果你发现自己要去英国...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-16
相关资源
最近更新 更多