使用BNF语法提取信息

7
我希望从一段文本中提取信息并进行查询。
这段文本的结构由BNF语法(或其变体)指定,提取的信息在运行时指定(查询语法目前不重要)。
所以,要求非常简单:
- 接收一些结构化的文本 - 使用语法分析器将其加载为可利用的形式 - 运行查询以选择其中的某些部分
举个例子,假设我们有这样的语法(使用自定义的BNF格式):
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<id> ::= 15 * digit

<hex> ::= 10 * (<digit> | a | b | c | d | e | f)

<anything> ::= <digit> | .... (all characters)

<match> ::= <id> (" " <hex>)*

<nomatch> ::= "." <anything>*

<line> ::= (<match> | <nomatch> | "") [<CR>] <LF>

<text> ::= <line>+

适用于这样的文本:

012345678901234
012345678901234 abcdef0123

Nor the previous line nor this one would match

然后我想列出在规则中出现的所有标签,例如使用类似于XPath的语法:

match//id

这个问题听起来相对简单,但是我有两个很大的限制:

  • BNF语法应该在运行时从字符串/向量等结构中读取
  • 查询也将在运行时读取

一些细节:

  • 不希望经常更改语法,因此可以接受“编译”步骤以生成内存中的结构(为了实现良好的速度可能是必要的)
  • 速度至关重要,可以实时收集所需部分得到额外加分
  • 提供回调函数以消除歧义会得到额外加分(有时必要的消除歧义信息可能需要访问数据库)
  • 支持多部分语法会得到额外加分(偏爱模块化和语法元素的重用)

我知道lex/yacc和flex/bison等工具,但它们似乎只能创建C/C++代码进行编译,这不是我想要的。

您是否知道一个强大的库(最好是免费开源的),可以将BNF语法转换为解析器并“即时”产生结构化的内存输出,使用此解析器从文本体中获取内容?

编辑:我对其他替代方案也很开放。目前的想法是,也许正则表达式可以允许此提取,但是由于语法的复杂性,这可能会很快变得混乱,并且因此维护正则表达式将是一项相当可怕的任务。此外,通过分离语法和提取,我希望能够重用同一语法以满足不同的提取需求,而不是每次都有略微不同的正则表达式。


我认为这是部分重复:https://dev59.com/QGs05IYBdhLWcg3wLfAj。它只涵盖了解析器生成,而没有涉及到此问题的其他具体细节。并且它没有任何形式为“是的,并在此处提供链接”的答案。因此,我不认为此问题是多余的。 - Steve Jessop
看起来你正在尝试构建一个正则表达式引擎(如果我错了请纠正我)。如果是的话,建议使用状态机。 - dirkgently
1
@dirkgently 不是正则语法(可以用正则表达式描述),而是上下文无关文法。虽然,他可以使用带有堆栈的状态机。 - Eric Finn
注意,我使用了“regex”这个术语。 我的重点是状态机; 我个人对状态机与正则表达式引擎的经验已经严重偏向状态机,因为它性能更卓越,并且您可以定义运行时转换并仍然能够解析几乎任何格式良好的输入。 请注意,尽管如此,这种情况非常普遍:在几乎每个项目中,都会有一个时刻,您陷入了发明DSL(lo' and behold here I go off again :P)的困境。 - dirkgently
你认为嵌入Lua/JavaScript解释器并让用户在一个通用的语言中进行操作会更好吗?这取决于你的用户类别和他们的技能水平。(很可能这会是一种过度设计!) - dirkgently
显示剩余8条评论
2个回答

2
我有一种专有解决方案,可以将语法源代码转换为内存表示。结果是一个纯数据结构。任何代码都可以使用它。我还有一个实际实现解析器的C++类。规则处理程序被实现为虚拟方法。
我们的解决方案与YACC/Bison的主要区别在于不生成C/C++代码。这意味着可以重新加载语法而无需重新编译应用程序。语法可以用应用程序ID进行注释,这些ID在规则处理程序的代码中使用。

谢谢你的回答,我需要一些时间来仔细检查它,同时因为已经提供了一个有希望的答案,所以点赞。 - Matthieu M.
我查看了该网站,但未找到相应的代码。是否可以查看一些解析器的文档/API?就目前而言,这对我来说很难发表意见... - Matthieu M.
我没有理解这个最后的评论,抱歉。你在谈论哪些冲突? - Matthieu M.
在这里解释语法冲突的概念很难。您可以在http://en.wikipedia.org/wiki/LL_parser中找到简要信息。像解析表达式“(5+3)*7”所需的语法这样的简单语法没有冲突。更复杂的语法,如C++语法,会有冲突。Bison仅适用于没有冲突的语法。我的语法定义解析器在这个领域更加先进。 - Kirill Kobelev
啊,谢谢提醒。我还不确定我将要处理的语法是否会有冲突(我看到的前几个没有)。 - Matthieu M.
显示剩余3条评论

1

GOLD解析器系统生成一个LALR解析表,据我所知在运行时加载。我相信它有一个C++“解析”引擎,因此应该很容易集成。

您可以阅读语法,派生一个子进程来获取GOLD解析器生成器以生成表,然后调用您的内置GOLD解析器进行加载和解析。

我不知道如何将操作附加到规约中,这可能是您想要做的事情。我没有使用GOLD的具体经验。祝您好运。


如果可能的话,我想能够将“验证”操作附加到某些项目上,但如果不行的话,这可以在之后处理。只要我能有效地提取信息,我就有外部代码来处理信息。 - Matthieu M.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接