【问题标题】:What's are reasonable upper bounds for the number of states, symbols, and rules of LR(1) grammars?LR(1) 文法的状态、符号和规则数量的合理上限是多少?
【发布时间】:2013-01-04 04:50:19
【问题描述】:

我正在制作一个 LR(1) 解析器,但我在各个地方都遇到了性能瓶颈。

我想尝试优化解析器的数据结构,但为了做到这一点,我需要大致了解有多少状态、规则和终端符号对于(可能很复杂的)计算机语言是合理的,像 C++。

我的猜测是复杂语言的典型语法会:

  • ≤100 个终端符号
  • 每个产品≤ 50 个符号
  • ≤ 2,000 条规则
  • ≤10,000 个州

但我真的不知道它们有多正确。

请注意,我假设每个规则的形式为 nonterminalsymbol symbol symbol...,因此,看起来像 foo: (bar | baz)+ 的单个复合“规则”实际上可能包含 5 条规则,而不仅仅是 1 条规则。

它们合理吗?如果没有,我在哪里可以找到这些数字?

【问题讨论】:

  • 我建议在您的问题中讨论其他语言,因为我认为 LR(1) 根本无法解析 C++。
  • @BenVoigt:我计划让它成为一个广义的 LR 解析器 (GLR),我认为它理论上可以处理 C++。如果我理解正确,C++ 的问题在于 LR(1) 语法是模棱两可的,而不是它不存在,对吧?所以应该没问题。
  • 瓶颈在哪里?查找表/集的下一个状态应该只花费O(1)
  • @leppie:不幸的是(或幸运的是?)瓶颈在解析器的 generation 中(找出所有状态),而不是在实际的解析器中。
  • @Mehrdad:啊,这更有意义。但是生成解析器应该只是一个“一次性”的工作。就个人而言,我不会担心。 ;p

标签: c++ parsing lr


【解决方案1】:

我每天开发的 DMS 系统在一台简陋的笔记本电脑上(刚刚在该笔记本电脑上测量)在大约 7 秒内处理生产 IBM Enterprise COBOL 前端语法。

语法有大约 500 个终端和 2500 个产生式,平均大约 2.5 个标记 每个生产。我们的作品与您描述的完全一样(没有 EBNF,只是买的东西不够重要,是的,我们是 DSL 的忠实粉丝。有时,人们放入 DSL 的 geegaws 不值得)。解析器生成器产生 3800 个状态。 (这些值也是刚刚测量的)。

DMS 具有完整的 C++11 语法,其中包含许多额外的东西来处理 GCC 和 MS 方言,以及 OpenMP。该语法有 457 个终端,大约 3000 个产生式,每个产生式平均有 2.3 个标记。解析器生成器产生 5800 个状态。需要更长的时间来生成:11 秒,在 i7 上。您可能会感到惊讶的是,它需要 几十秒来生成词法分析器(实际上是多个词法分析器); C++11 中的词法怪异比你想象的要多得多。

生成器是我们自己实现的 GLR 生成器。

我们没有做很多优化生成时间的工作。它可能会加速 10 倍或更多;我们没有像大多数关于 LR 解析器生成的论文中建议的那样进行复杂的循环检测优化。结果是生成表需要更长的时间,但功能上没有任何损失。我们从来没有足够的动力进行这种优化,因为除了担心解析器表的生成时间之外,语言前端还有很多其他事情要做。

如果设计合理,我怀疑数据结构是否重要。我们不太担心规则、项目集或状态的大小;我们只使用动态数组,它们会照顾自己。我们确实将前瞻打包到密集的位向量中。

作为额外的背景数据,您可能会发现这篇论文很有用:Tiago Alves and Joost Visser, Metrication of SDF Grammars. Technical Report, DI-Research.PURe-05.05.01, Departamento de Informática, Universidade do Minho, May 2005.

解析器生成器不是您在语法方面遇到困难的地方。它正在获取特定实现的语法规则正确

【讨论】:

  • 哇,感谢您克服测量的麻烦!以及建议。这是一个很棒的答案,非常感谢! :)
  • 知道我的 ~4s 在正确的球场上也令人放心!
  • 您说这是针对精简的 Python 语法,而不是完整的生产语法。我怀疑它很高,因为您正在生成 LR(1) 表。生成所有这些额外状态需要相应的时间成本。
  • 啊,是的,它是为了简化 Python 语法,所以它仍然很慢 - 我说它在正确的范围内的意思是它似乎在正确的轨道上,并不是说它已经达到了它的目标。 :)
  • 所以我设法将其缩短到约 2 秒,方法是将所有产品放入单个连续数组中,并将每个项目核心表示为该数组的迭代器,然后使用位向量表示项目集在一个地方。瓶颈仍然在于内存分配(以及数据中的一些非局部性)。使用哈希表(unordered_map 而不是 map)将其缩短到约 1.4 秒……现在我似乎遇到了困难,除非我尝试使用更好的分配器或其他东西……事实上,我'm 将项目集表示为 set<set<Item> > 似乎是它减慢速度的原因。 :\
猜你喜欢
  • 1970-01-01
  • 2016-02-01
  • 2012-02-28
  • 1970-01-01
  • 2011-09-23
  • 1970-01-01
  • 2020-06-18
  • 2015-01-21
  • 2019-04-02
相关资源
最近更新 更多