Regex/token/rule 来匹配嵌套的大括号?

Regex/token/rule to match nested curly braces?

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

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

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

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

在仔细阅读 Lenz 的“使用 Perl 6 正则表达式和语法进行解析”(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 调用)。不幸的是,在语法中对我没有直接用处。

解决方案

A way to describe nested braces (and nothing else)

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

\{ <&R>* \} 

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

'{' ~ '}' <R>*

后者避免了leaning toothpick syndrome and uses the regex ~ operator.

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

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

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

regex 对比 token

kill backtracking

my regex nested-braces {
    :ratchet 

regextoken 声明的模式之间的唯一区别是前者将棘轮 关闭 。所以使用它然后立即转动棘轮 on 是非常不合常理的。相反:

my token nested-braces {

回溯

the "regex" machinery (based on backtracking)

grammar/regex 引擎 包括 回溯作为一项 可选 功能,因为这有时正是人们想要的。

但是引擎不是“基于回溯”,许多grammars/parsers很少或根本不使用回溯。

递归

a regex can call another, and nowhere do I see a prohibition on recursive calls.

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

PCRE 自 2000 年起支持递归,自 2003 年起命名正则表达式。Perl 的默认正则表达式引擎自 2007 年起支持两者。

随着时间的推移,他们对更深层次的递归和更多命名的正则表达式的支持一直在增加。

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

能力

a lot more capable

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

但实际上,这是一项全新的交易。例如,它们以一种合理的方式完成图灵,因此能够解析任何东西。

than officially admitted

好吧,这么说很奇怪! Raku 的语法经常被吹捧为 Raku 最具创新性的功能之一。

主要注意三点:

  • 性能 当前的主要警告是,一个编写良好的 C 解析器将使一个编写良好的基于​​ Raku 语法的解析器大吃一惊。

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

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

使用现有的解析器

I know there are BibTeX parsers around, but I need to grab the complete entry for further processing, and peek at a few keys meanwhile.

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

两个可用的外语适配器已经完善得令人惊叹 -- 用于 Perl 和 C 的适配器。

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

Raku BibTeX 语法?

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

(也许你这样做的部分原因是为了让自己熟悉 Raku,或者 Raku 的 regex/grammar 方面。因此,这听起来非常理想。)

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

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

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

有关详细信息,请参阅 the official doc's Grammar tutorial 或阅读 Moritz 的书。

好的,刚刚(重新)检查过。 '{' ~ '}' 的文档留下了很多不足之处,它是用来处理 平衡的、正确嵌套的 定界符的。

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

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

谢谢大家!今天学了不少东西