Perl 6 Grammar与我认为不应该匹配

msc*_*cha 6 regex grammar perl6

我正在做第9天的代码:

你坐了一会儿,记录了一部分流(你的拼图输入).字符代表组 - 以开头{和结尾的序列}.在一个组中,有零个或多个其他东西,用逗号分隔:另一个组或垃圾.由于组可以包含其他组,因此}只关闭最近打开的未关闭组 - 也就是说,它们是可嵌套的.您的拼图输入表示一个单独的大组,其本身包含许多较小的组.

有时候,你会发现垃圾,而不是一个团体.垃圾开始于<和结束>.在这些尖括号之间,几乎可以出现任何字符,包括{}.在垃圾中,<没有特别的意义.

在徒劳地尝试清理垃圾时,一些程序已经取消了其中的一些字符!:内部垃圾,后面的任何字符都!应该被忽略,包括<,>甚至是另一个!.

当然,这对于Perl 6 Grammar来说是尖叫......

grammar Stream
{
    rule TOP { ^ <group> $ }

    rule group { '{' [ <group> || <garbage> ]* % ',' '}' }
    rule garbage { '<' [ <garbchar> | <garbignore> ]* '>' }

    token garbignore { '!' . }
    token garbchar { <-[ !> ]> }
}
Run Code Online (Sandbox Code Playgroud)

这似乎在简单的例子上工作正常,但它garbchar连续两个错误:

say Stream.parse('{<aa>}');
Run Code Online (Sandbox Code Playgroud)

Nil.

Grammar::Tracer 没有帮助:

TOP
|  group
|  |  group
|  |  * FAIL
|  |  garbage
|  |  |  garbchar
|  |  |  * MATCH "a"
|  |  * FAIL
|  * FAIL
* FAIL
Nil
Run Code Online (Sandbox Code Playgroud)

多个garbignores没问题:

say Stream.parse('{<!!a!a>}');
Run Code Online (Sandbox Code Playgroud)

得到:

?{<!!a!a>}?
 group => ?{<!!a!a>}?
  garbage => ?<!!a!a>?
   garbignore => ?!!?
   garbchar => ?a?
   garbignore => ?!a?
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?

rai*_*iph 6

UPD鉴于代码问题的出现没有提到空格,你根本不应该使用该rule构造.只需将所有rules 切换为s即可token设置.一般来说,遵循布拉德的建议 - 使用token除非你知道你需要rule(如下所述)或regex(如果你需要回溯).


我在下面的原始答案探讨了为什么rules不起作用.我现在就把它留下来.


TL; DR <garbchar> |包含空格.直接如下任何空白原子在一个rule指示标记化断裂.你可以简单地删除这个不合适的空间,即改写<garbchar>|(或者更好的是,<.garbchar>|如果你不需要捕获垃圾)来获得你想要的结果.


正如您的原始问题所允许的那样,这不是一个错误,只是您的心理模型已关闭.

您的答案正确识别问题:标记化.

所以我们留下的是你的后续问题,这是关于你的标记化的心理模型,或者至少是默认情况下Perl 6如何标记:

为什么...我的第二个例子......连续两个garbchars出错了:

'{<aa>}'
Run Code Online (Sandbox Code Playgroud)

简化,问题是如何标记这个:

aa
Run Code Online (Sandbox Code Playgroud)

简单的高级答案是,在解析白话时,aa通常将被视为一个标记,而不是两个标记,并且默认情况下,Perl 6假定这个普通的定义.这是你遇到的问题.

您可以否决这个普通的定义,以获得您想要达到的任何标记化结果.但是很少有必要这样做,当然不是像这样的简单情况.

我将提供两条冗余路径,我希望这些路径能够引导民间人士找到正确的心理模型:

摘自维基百科页面上关于标记化的"障碍"部分,并将摘录与P6特定讨论交错:

通常,标记化发生在单词级别.但是,有时很难定义"单词"的含义.通常,标记生成器依赖于简单的启发式方法,例如:

  • 标点符号和空格可能包含也可能不包含在结果的标记列表中.

在Perl 6中,您可以使用与标记化正交的捕获功能来控制在解析树中包含或不包含的内容.

  • 所有连续的字母字符串都是一个标记的一部分; 同样有数字.

  • 标记由空格字符分隔,例如空格或换行符,或者用标点字符分隔.

默认情况下,Perl 6设计体现了这两种启发式的等价物.

要获得的关键是它rule是处理一串令牌,复数的构造.该token构造用于为每个调用定义单个标记.

我想我会在这里结束我的答案,因为它已经很长了.请使用评论来帮助我们改进这个答案.我希望到目前为止我所写的内容有所帮助.