代码之家  ›  专栏  ›  技术社区  ›  lisprogtor

Perl6语法,不确定示例中的某些语法

  •  16
  • lisprogtor  · 技术社区  · 7 年前

    我还在学习Perl6,我正在阅读本页的语法示例: http://examples.perl6.org/categories/parsers/SimpleStrings.html 我已经读过多次关于regex的文档,但是仍然有一些语法我不理解;有人能启发我吗?非常感谢!!!!

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

    问题1:代币中的“”是做什么的?捕获标记是<()>,嵌套结构是tilda'('~')';但什么是?

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

    问题2a:在<>中转义的($quote)将是一个regex函数,对吗?它以$Quote作为参数并返回另一个regex?

    问题2b:如果我想指出“不在引号之前的字符”,我应该使用它。&!在$Quote>“之前而不是”<!在$Quote>之前。“??

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

    非常感谢!!!!

    Lisprog公司

    2 回复  |  直到 6 年前
        1
  •  6
  •   raiph    7 年前

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

    问题1:这是什么? {} 做什么?

    这是一个代码块 1、2、3、4 .

    它是一个空的,只是为了强制 $<quote> 在里面 quotebody($<quote>) 以评估 <quote> 在正则表达式的开头。

    为什么 $lt;报价 包含正确的值而不插入代码块是Rakudo Perl 6编译器的限制或与“发布匹配变量”相关的bug。

    Rakudo对匹配变量的“发布”

    莫里茨·伦茨在 a Rakudo bug report 那个 “除非认为有必要,否则regex引擎不会发布匹配变量。” .

    “regex引擎”是指nqp中的regex/grammar引擎,它是rakudo perl 6编译器的一部分。

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

    • 这个 当前匹配变量 $/ ;

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

    • 命名 窗体的子匹配变量 $<foo> .

    “publish”的意思是regex/grammar引擎做它需要做的事情,以便在regex(令牌也是regex)中提到任何变量时,对它们应该拥有的值进行评估。在给定的regex中,匹配变量是 本应如此 包含 Match object 对应于在处理该regex的任何给定阶段为它们捕获的内容,或 Nil 如果什么都没有被抓获。

    他所说的“被认为是必要的”,意思是regex/grammar引擎对匹配过程中的每个步骤之后是否值得进行发布工作进行保守的评价。我所说的“保守”是指引擎经常避免发布,因为它会减慢速度,而且通常是不必要的。不幸的是,它有时对何时出版过于乐观 实际上是必要的。因此,程序员有时需要通过显式插入代码块来强制发布匹配变量(以及其他变量的技术)来进行干预。 )在这方面,regex/grammar引擎可能会随着时间的推移而改进,从而减少需要手动干预的情况。如果您希望帮助实现这一点,请为现有的相关bug创建对您很重要的测试用例。

    的“发布” $lt;报价 的值

    命名的捕获 $lt;报价 这里的情况是这样的吗?

    据我所知,所有子匹配变量在直接写入regex而不使用周围的构造时都正确地引用了它们捕获的值。这项工作:

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

    我想 $lt;报价 获取正确的值,因为它被分析为 瑞格斯俚语 构造。

    相反,如果 {} 已从中删除

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

    然后 $lt;报价 在里面 报价正文($<Quote>) 包含由开口捕获的值 <报价> .

    我认为这是因为 $lt;报价 在这种情况下,被解析为 主要的 俚语结构。

    问题2a: escaped($quote) 里面 <> 会是一个regex函数,对吗?这需要 $quote 作为一个论点

    这是一个很好的一阶近似。

    更具体地说,形式的regex原子 <foo(...)> 是的呼叫 方法 foo .

    所有正则表达式--是否用声明 token , regex , rule , /.../ 或者任何其他形式——都是方法。但是声明的方法 method 正则表达式:

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

    <escaped($quote)> 遇到regex atom,regex/grammar引擎不知道或不关心 escaped 是不是一个正则表达式,也不是关于 the details of method dispatch inside a regex or grammar . 它只调用方法分派,invocant设置为 比赛 正在由封闭regex构造的对象。

    调用将控制最终运行该方法的对象。通常情况下,regex/语法引擎只是递归地回调自身,因为通常情况下,这是一个regex调用另一个regex的问题。但不一定如此。

    并返回另一个regex

    不,形式的正则表达式原子 <转义($quote)> 不返回另一个regex。

    相反,它调用一个将/应该返回 比赛 对象。

    如果调用的方法是regex,p6将确保regex生成并填充 比赛 对象自动。

    如果调用的方法不是regex,而是普通方法,那么该方法的代码应该是手动创建并返回 比赛 对象。莫里茨在回答这个问题时举了一个例子。 Can I change the Perl 6 slang inside a method? .

    这个 比赛 对象返回到驱动regex匹配/语法分析的“regex/语法引擎”。

    然后,发动机根据结果决定接下来要做什么:

    • 如果匹配成功,引擎将更新与调用regex相对应的总体匹配对象。更新可能包括保存返回的 比赛 对象作为调用regex的子匹配捕获。这就是匹配/分析的方式 开始建造。

    • 如果比赛是 不成功的 ,引擎可能会回溯,撤消以前的更新;因此,解析树可能会随着匹配过程动态增长和收缩。

    问题2b:如果我想指出“不在引号之前的字符”,我应该使用 . <!before $quote> 而不是 <!before $quote> . ??

    对。

    但这不是 quotebody 雷格克斯,如果你是这么说的话。

    关于后一个话题,在@briandfoy的回答中,他建议使用“匹配…任何不是“引言”结构而不是负面展望的东西( <!before $quote> )他的观点是,匹配“非报价”比“我们不是在报价之前”更容易理解?然后匹配任何字符”。

    但是,当引号是一个变量,其值设置为开始引号的捕获时,决不能直接执行此操作。这种复杂性是由于rakudo中的错误造成的。我已经想出了我认为最简单的方法,但我认为最好还是坚持使用 <!在$Quote>之前。 除非/直到这些长期存在的rakudo错误被修复。

    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
    

    体内的代码 { ... } 位)的一个regex(在本例中,代码是唯一的 . 在里面 token { . } 它是一个与单个字符匹配的regex原子)写在p6 regex的“slag”中,而代码在 方法 程序是写在主p6“俚语”。

    使用 ~

    这个 regex tilde ( ~ ) operator 是专门为这个问题所涉及的示例中的解析而设计的。它读起来更好,因为它可以立即识别,并保持开头和结尾的引号在一起。更重要的是,它可以在发生故障时提供一个人类可理解的错误消息,因为它可以说出它要查找的结束分隔符。

    但是,如果在regex中插入一个代码块(包含或不包含代码),则必须考虑在regex旁边插入一个关键的褶皱。 ~ 操作员(在其两侧)。您需要将代码块分组,除非您特别希望tilde将代码块视为自己的原子。例如:

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

    将匹配一对 <报价> S 他们之间什么都没有 . (然后尝试匹配 <quotebody...> )

    相反,这里有一种方法可以复制 string 中的令牌 String::Simple::Grammar 语法:

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

    脚注

    2002年,拉里·沃尔写道 "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得出了合乎逻辑的结论——一个简单的 {...} 只需在regex中间插入任意过程代码。语言设计和regex/语法引擎实现 确保在regex中识别传统风格的纯声明性区域,以便将形式正则表达式理论和优化应用于它们,但是也可以插入任意的规则过程代码。简单用法包括 matching logic debugging . 但天空是极限。

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

    不严格地说,regex/grammar引擎 NQP 对P6来说是什么 PCRE 到P5。

    一个关键的区别是,regex语言及其相关的regex/语法引擎和它所合作的主要语言(在rakudo的情况下是perl 6)在控制方面是相同的。这是 Larry Wall's original 2002 vision for integration between regexes and "rich languages" . 每种语言/运行时都可以相互调用,并通过高级别的FFI进行通信。因此,它们可以看起来是,可以表现为,实际上是,一个协作语言和协作运行时间的单一系统。

    (P6的设计是这样的 全部的 语言可以通过两个互补的p6以“丰富”的方式显式地设计或重新装配以进行合作。 FFIs :元模型ffi 6model 和/或C呼叫公约FFI NativeCall )

    p6语言实际上是一起使用的子语言(又称俚语)的集合。当您阅读或编写p6代码时,您正在阅读或编写源代码,源代码以一个俚语开头,但有一些部分是用其他语言编写的。文件中的第一行使用主俚语。我们假设这类似于英语。正则表达式是用另一种俚语写的,比如说西班牙语。所以在这种情况下 the grammar String::Simple::Grammar ,代码以英文开头 use v6; 语句),然后递归为西班牙语(在 { 属于 rule TOP { )即 ^ <string> $ 位,然后返回到英文(注释从 # Note ... )然后它又回到西班牙语中 <quote> {} <quotebody($<quote>)> $<quote> 在西班牙中部, {} 代码块,它递归到 另一个层次 又是英语了。这就是西班牙语中的英语。当然,代码块是空的,所以它就像用英语写/读什么都不写,然后立即返回西班牙语,但重要的是要理解,这种语言/运行时间的递归叠加是p6的工作方式,无论是作为一种整体语言/运行时,还是在合作时。使用其他非p6语言/运行时间。

    在应用两个潜在改进的过程中,我遇到了几个错误,列在脚注的末尾。(这两个都在布里安福的回答和这一个中提到。)这两个“改进”是使用 ~ 构造,而不是使用“非引号”构造 <!before foo> . . 最后的结果,加上相关的错误:

    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 bugs数据库中搜索所有regex bug。我知道Bug数据库不是这样的,但我认为我应该注意以下几点。前两个直接与匹配变量的发布问题交互。

    这个问题和我的答案已经把我推向了我对p6一个雄心勃勃和复杂的方面的理解的外部极限。我计划很快深入了解nqp和完整p6之间的精确交互,以及它们的regex俚语和主要俚语之间的切换,如上文脚注所述。(我现在的希望主要在于刚刚买了 commaide 如果/当我有结果时,我会更新这个答案。

        2
  •  8
  •   brian d foy    6 年前

    这个 {} 是空代码块。它是语法的过程性(而不是声明性)元素。您可以在其中放入常规的Perl6代码,让它做一些事情。

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

    这个 $lt;报价 实际上是对匹配对象的单个元素访问 $/ . 就像杂凑一样,这真的是 $/<quote> 角括号之间的东西就是“钥匙”。Perl6喜欢有点聪明,所以它可以让你远离 / 得到 $lt;报价 . 其他匹配变量,如 $1 也是类似的快捷方式。

    对于最后一个问题,查看一些您试图匹配的示例数据将有所帮助。Perl6语法有许多特性来匹配平衡文本,这可能使任务变得简单。例如,请参见, Tilde for nesting structures Regexp documentation :

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

    下面是repl中的一个简短示例。有一个字符串中有一些带引号的文本:

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

    这个 ~ 在regex中是在分隔符之间。在结束分隔符之后出现的内容是您期望的 ~ 是:

    > $s ~~ m/ '"' ~ '"' .+ /
    ï½¢"Hello"ï½£
    

    你可以匹配开场白,然后拍下来 $0 )因此,可以使用与结束分隔符完全相同的内容:

    > $s ~~ m/ (<["']>) ~ $0 .+ /
    ï½¢"Hello"ï½£
     0 => ï½¢"ï½£
    

    为了 that particular example 我认为有一个更简单的方法。匹配转义引号或任何不是引号的内容,而不是环顾四周和任何字符。这可不像是在折腾。