【问题标题】:Build parser from grammar at runtime在运行时从语法构建解析器
【发布时间】:2011-09-12 18:36:08
【问题描述】:

许多(大多数)C++ 正则表达式库允许在运行时从字符串创建表达式。是否有人知道任何允许在运行时将表示为字符串的语法(最好是 BNF)提供给生成器的 C++ 解析器生成器?我发现的所有实现要么需要运行显式代码生成器,要么需要通过巧妙的模板元编程来表达语法。

【问题讨论】:

  • 在运行时为整个解析器生成代码很强大,但可能不需要。除非您真的必须支持 abritary BNF 语法(顺便说一句,这是为了什么?),否则您可能可以编写一个足够灵活的解析器,以相对较少的麻烦适应语法中的某些变化。
  • @delnan,在运行时生成解析器在 Lisp 世界中是很常见的事情,尤其是现在,在 Packrat 时代。但是,为了在 C++ 中做同样的事情,必须遵守 Greenspun 的第十条规则。
  • 元编程有什么问题?有什么比使用 Spirit 或 AXE 创建解析器更容易的呢?坦率地说,我不知道有任何类似 BNF 的解析器生成器会在易用性、性能或……任何方面击败这两个。只需为您的输入定义语法和语义即可。
  • @delnan 我曾经写过类似的东西——它是一个完整的 Earley 解析器,它是从一个语法文件动态构建的。我的代码正在替换现有代码(仅通过蛮力将 CFG 扩展为常规语法来处理 CFG),我们不得不使用现有的语法文件。但它本可以在另一种情况下以不同的方式(并且更有效地)完成。它是用 C 语言编写的,当时我正在寻找类似的东西。
  • @Gene 问题是语法是用户在运行时指定的。元编程根本不可能。

标签: c++ parsing runtime grammar parser-generator


【解决方案1】:

构建一个接受语法作为输入的递归下降、回溯解析器应该很容易。您可以将所有规则简化为以下形式(或假装拥有):

 A = B C D ;

通过递归下降来解析这样的规则很容易:调用一个对应于找到 B 的例程,然后是一个找到 C 的例程,然后是一个找到 D 的例程。假设你正在做一个通用解析器,你总是可以调用一个"parse_next_sentential_form(x)" 函数,并将所需表单的名称(终端或非终端标记)作为 x 传递(例如,“B”、“C”、“D”)。

在处理这样一个规则时,解析器想要生成一个 A,通过找到一个 B,然后是 C,然后是 D。要找到 B(或 C 或 D),您希望在其中所有的左边都是一样的,所以可以很容易地枚举出B的产生规则,并递归处理它们的内容。如果您的解析器失败,它只会回溯。

这不会是一个闪电般快速的解析器,但如果实施得当,应该不会很糟糕。

也可以使用 Earley 解析器,它通过创建部分处理的规则状态来进行解析。

如果你想让它快一点,我想你可以简单地把 Bison 的胆量做成一个图书馆。然后,如果您有语法文本或语法规则(Bison 的不同入口点),您可以启动它并让它在内存中生成它的表(它必须以某种形式执行)。不要把它们吐出来;只需构建一个使用它们的 LR 解析引擎。瞧,即时高效的解析器生成。 如果你这样做,你必须担心语法的歧义和 LALR(1)ness;前两个解决方案适用于任何上下文无关语法。

【讨论】:

  • 这个故事的寓意是每个 LL(*) 语法都可以转换为任何其他 LL(*) 语法,因此实际上在运行时指定语法与在运行时指定另一个语法没有什么不同编译时间。
【解决方案2】:

我不知道有这方面的现有库。但是,如果性能和健壮性不重要,那么您可以分拆 bison 或任何其他生成 C 代码的工具(通过 popen(3) 或类似工具),在生成的代码上分拆 gcc,将其链接到共享库并加载库通过 dlopen(3)/dlsym(3)。在 Windows 上 -- DLL 和 LoadLibrary() 代替。

【讨论】:

    【解决方案3】:

    最简单的选择是嵌入一些脚本语言甚至是成熟的虚拟机(例如 Mono),然后在其上运行生成的解析器。 Lua 有相当强大的 JIT 编译器、不错的元编程能力和几个可供使用的 Packrat 实现,所以这可能是最省力的方式。

    【讨论】:

    • 我正在考虑这条路线。我知道 Lua 有一些不错的 C++ 绑定,看起来很有希望。不过,我希望大家知道更好的解决方案。
    【解决方案4】:

    我刚刚遇到了这个http://cocom.sourceforge.net/ammunition++-13.html
    最后一个是 Earley Parser,它似乎将语法视为字符串。
    其中一个功能是:

    公共函数`parse_grammar'

             `int parse_grammar (int strict_p, const char *description)'
    

    是另一个将解析器调整为给定语法的函数。 语法由字符串“描述”给出。 描述类似YACC one。

    实际代码在http://sourceforge.net/projects/cocom/

    编辑

    更新版本位于https://github.com/vnmakarov/yaep

    【讨论】:

      【解决方案5】:

      boost::spirit是一个C++解析框架,可用于在运行时动态构造解析器。

      【讨论】:

      • "表示为字符串"?!?没办法。
      猜你喜欢
      • 2022-06-28
      • 2020-07-22
      • 1970-01-01
      • 2011-01-26
      • 2015-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多