【问题标题】:Regex/token/rule to match nested curly braces?正则表达式/令牌/规则匹配嵌套的花括号?
【发布时间】:2020-07-17 03:09:32
【问题描述】:

我需要匹配 BibTeX 文件中key = value 对的值,其中可以包含任意嵌套的大括号,由大括号分隔。我最多可以匹配两个深嵌套的花括号,例如 {some {stuff} like {this}} 与 kludgey:

    token brace-value {
    '{' <-[{}]>* ['{' <-[}]>* '}' <-[{}]>* ]* '}'
    }

我对再往下一层的想法感到不寒而栗......但是正确解析我的 BibTeX 内容至少需要三层深度。

是的,我知道周围有 BibTeX 解析器,但我需要获取完整的条目以进行进一步处理,同时查看几个键。我的*.bib 文件相当温顺(而且我不介意手动处理一些杂散条目),问题是我有很多它们,有很多重叠。但是一些“相同”的条目有不同的键或额外的数据。我想将它们合并成几个主文件(BibTeX 背后的整个想法,对吧?)。如果bibtool 提供的文件没有重复(哈哈!)大约 20000 行,手动操作并不有趣...

【问题讨论】:

标签: grammar raku


【解决方案1】:

在阅读了 Lenz 的“Parsing with Perl 6 Regexes and Grammars”(Apress,2017 年)之后,我意识到“正则表达式”机制(基于回溯)实际上可能比官方承认的要强大得多,因为正则表达式可以调用另一个,而且我在任何地方都没有看到对递归调用的禁令。

在深入研究之前,先了解一些上下文无关语法:描述嵌套大括号(仅此而已)的一种方法是使用语法:

S -> { S } S |

即,嵌套大括号是左大括号、嵌套大括号、右大括号、更多嵌套大括号;或者什么都没有。这或多或少直接转换为 Raku(没有空的正则表达式,通过使构造可选来伪造它):

my regex nb {
   [ '{' <nb> '}' <nb> ]?
}

瞧,这行得通。需要修复以避免捕获,终止回溯(如果第一次尝试不匹配,则永远不会匹配),并用“任何其他”填充物装饰。

my regex nested-braces {
    :ratchet 
     <-[{}]>*
     [ '{' <.nested-braces> '}' <.nested-braces> ]?
     <-[{}]>*
};

这与我的测试用例有关。

对于不太喜欢冒险的人,有用于 Perl 的 Text::Balanced 模块(以前的 Perl 5,可使用 Inline::Perl5 从 Raku 调用)。不幸的是,在语法中对我没有直接用处。

【讨论】:

  • 为了避免回溯,您通常只需将其称为token 而不是regex(或在正则表达式中的某处使用修饰符:r [for :ratcheting])
  • @user0721090601,是的,这就是我的想法。只是在进行概念验证。最终版本现已发布。
  • "上下文无关语法" my answer to SO What's the real difference between a token and a rule?什么 Raku 规则/语法? 部分可能很有趣。
【解决方案2】:

解决方案

一种描述嵌套大括号的方法(仅此而已)

假设一个名为 &amp;R 的规则,如果我正在编写一个快速的小型一次性脚本,我可能会编写以下模式:

\{ <&R>* \} 

如果我正在编写一个应该可维护的大型程序,我可能会编写一个语法,并且使用名为 R 的规则,模式将是:

'{' ~ '}' <R>*

后者避免使用leaning toothpick syndrome 并使用the regex ~ operator

这些都将解析任意深度嵌套的成对大括号,例如:

say '{{{{}}}}' ~~ token { \{ <&?ROUTINE>* \} } # 「{{{{}}}}」

&amp;?ROUTINE 指的是它出现的例程。正则表达式是例程。(虽然你不能在用/ ... / 语法声明的正则表达式中使用&lt;&amp;?ROUTINE&gt;。)

regextoken

杀死回溯

my regex nested-braces {
    :ratchet 

regextoken 声明的模式之间的唯一区别是前者关闭了棘轮。因此,使用它然后立即打开棘轮 on 是非常不习惯的。而是:

my token nested-braces {

回溯

“正则表达式”机制(基于回溯)

语法/正则表达式引擎将回溯作为一项可选功能包含,因为这有时正是人们想要的。

但是引擎不是“基于回溯”,许多语法/解析器很少或根本不使用回溯。

递归

一个正则表达式可以调用另一个,我在任何地方都没有看到禁止递归调用。

仅此一点对于当代正则表达式引擎来说并没有什么特别之处。

PCRE 从 2000 年开始支持递归,从 2003 年开始支持正则表达式。Perl 的默认正则表达式引擎从 2007 年开始支持这两者。

他们对更深层次的递归和同时存储更多命名正则表达式的支持随着时间的推移而增加。

Damian Conway 的 PPR 使用正则表达式的这些特性来构建非平凡(但仍然很小)的解析树。

能力

能力更强

Raku“正则表达式”可以被视为对正在展开的正则表达式演变的一种清理。在某种程度上,这有助于人们理解它们,太好了。

但实际上,这是一个全新的交易。例如,它们以合理的方式图灵完备,因此能够解析任何内容。

比官方承认

嗯,说起来很奇怪! Raku 的语法经常被吹捧为 Raku 最具创新性的功能之一。

有三个主要的警告:

  • 性能当前的主要警告是,编写良好的 C 解析器将击败编写良好的基于​​ Raku 语法的解析器。

  • 回报如果有一个现有的解析器,为非平凡的格式编写一个完全正确的解析器通常是不值得的。

  • 左递归 Raku 不会自动重写left recursion(无限循环)。

使用现有的解析器

我知道周围有 BibTeX 解析器,但我需要获取完整的条目以进行进一步处理,同时查看几个键。

在 Raku 中使用外来模块可能会带来一些启示。它不一定像你以前经历过的任何事情。 Raku 的外语适配器可以为您提供智能 marshaling,因此您可以像使用本地 Raku 功能一样。

两个可用的外语适配器已经足够完善,令人惊叹——Perl 和 C 的。

我很确定有一个用于 Perl 的 BibTeX 包,它包装了一个 C BibTeX 解析器。如果您使用它,您希望将所有解析结果都很好地封装到 Raku 对象中,就好像它最初都是 Raku 一样,但保留了 C 代码的大部分高性能。

Raku BibTeX 语法?

也许您确实需要创建和使用小型 Raku 语法。

(也许您这样做部分是为了让自己熟悉 Raku 或 Raku 的正则表达式/语法方面。因为这听起来很理想。)

一旦你开始同时使用多个正则表达式——即使只有两个——你就接近了grammar 领域。毕竟,它们只是一个易于使用的结构,可以同时使用多个正则表达式。

因此,如果您决定坚持在 Raku 中编写解析代码,请编写如下内容:

grammar BiBTeX {
  token TOP { ... }
  token ...
  token ...
}
BiBTeX.parse: my-bib-file

更多详情,请参阅the official doc's Grammar tutorial 或阅读 Moritz 的书。

【讨论】:

  • "虽然你不能在 /…/ 中使用 " ... 你不能使用 但有 语法,但我如果我不小心记起来,那该死的。有人在这里发布了它。我认为是
  • 是的。只是仔细检查了一下。 /'{' ~ '}' &lt;~~&gt;*/ 将匹配任意随机序列的平衡{{{{{ }}}}}
  • 嘿@user0721090601 - 太酷了 - 捕捉内容和动作看起来如何?*
  • @p6steve 仅&lt;...&gt; 以 alpha 开头的断言,捕获。所以&lt;~~&gt; 不会单独捕获递归调用。 (我刚刚测试确认。)因此,为了独立捕获递归调用,您需要在方法中使用规则并在递归调用中明确命名规则。我还测试了操作,每次特定调用成功完成匹配时都会调用一次,正如预期的那样。
  • @raiph 没错,但是简单地添加 $&lt;inner&gt;=&lt;~~&gt; 相当容易,它会变成一个易于访问的命名捕获。
【解决方案3】:

好的,刚刚(重新)检查。 '{' ~ '}' 的文档有很多不足之处,它是否意味着处理 平衡、正确嵌套 分隔符并不清楚。

所以我的最终解决方案实际上就是这样:

my regex nested-braces {
   :ratchet
   '{' ~ '}' .*
}

谢谢大家!今天学了不少。

【讨论】:

  • my regex nested-braces { :ratchet '{' ~ '}' .* } 将匹配一个 single 对,而不考虑嵌套/平衡。例如,与'{}}}}}nnn}' 匹配,它返回'「{}}}}}nnn}」'
  • “完全不清楚 ['{' ~ '}'] 是用来处理平衡的、正确嵌套的分隔符”。这不是故意的。它用于匹配 single 对。文档部分标题(“嵌套结构的波浪号”)是不幸的。文档部分的正文在现场——但它是verbiage Larry wrote for compiler implementors,而不是用户。 “嵌套子规则”中的“嵌套”是关于嵌套子规则匹配单次出现的一对分隔符之间的内容。
  • ~ 存在于正则表达式语法中的原因是将分隔符在语法上彼此靠近,而不是在中间的相对两侧。这是它唯一的作用。 '{' ~ '}' .* 的工作方式与 '{' .* '}' 完全相同。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多