使用单个语法声明的c ++解析器和格式化程序

时间:2015-09-18 10:05:40

标签: c++ parsing boost

我有这个想法能够宣布'语法并使用相同的声明来生成格式函数。

解析器生成器(例如antlr)能够从bnf语法生成解析器。 但有没有办法使用相同的语法来生成格式代码?

我只是想避免手动将解析代码(生成)与手动编写的格式代码同步,因为语法是相同的。

我可以使用抽象语法树吗? boost::spirit?元编程? 有人试过吗?

感谢

2 个答案:

答案 0 :(得分:2)

我不清楚这个问题是在寻找现有的产品或库(在这种情况下,问题是超出Stack Overflow的范围),还是在自动生成漂亮的打印机的算法中来自(一些形式主义)的语法。在这里,我试图为第二种可能性提供一些指示。

对语法导向的漂亮打印有很长的研究历史,谷歌或Citeseer对该短语的搜索可能会给你很多阅读材料。我建议尝试查找Derek Oppen 1979年论文 Prettyprinting 的副本,该论文描述了一种基于将一些漂亮的打印操作符插入到标记化源代码中的线性时间算法。

Oppen的基本运算符非常简单:它们包含有关如何(递归地)分组代码段的指示,关于换行必须和可能插入的位置的指示,以及组中增加缩进深度的位置。使用所提出的运算符集,可以创建一个在线算法,该算法更喜欢在解析树中打破更高的行,避免过度缩进深度嵌套的代码,这是天真缩进算法的经典失败

本质上,该算法使用双指解决方案,其中前导手指消耗新的令牌并在必须包裹线时发出通知,此时它向尾随手指发出信号。然后,尾随手指找到可以插入换行符的最早点以及必须插入以符合操作符的所有其他换行符和缩进,前进直到手指之间没有换行符。

在线算法可能无法产生最佳压痕/回流(并且“最佳”的定义可能并不是很明显);对于漂亮打印的某些方面,考虑Donald Knuth's optimal line-wrapping algorithm中的想法可能是有用的,如他1999年的文本数字排版中所述。 (Wikipedia article在线包装中的更多参考。)

Oppen的算法并不完美(如文中所示),但对于许多实际目的而言,它可能“足够好”。 (我注意到下面的一些限制。)跟踪本文的引文历史将为您提供许多实现,改进和替代算法。

很明显,可以轻松修改解析器生成器,只需将精美打印注释插入令牌流,我相信已经有各种尝试来创建类似yacc的漂亮打印机生成器。 (也可能是ANTLR衍生物。)基本思想是在语法描述中嵌入漂亮的打印注释,允许自动生成减少动作,输出带注释的令牌流。

使用类似的注释系统将{Syntic-directed pretty printing添加到ASF+SDF Meta-Environment; M.G.J.描述了基本算法和形式。范德品牌在Pretty Printing in the ASF+SDF Meta-environment Past, Present and Future (1995),这也是有趣的阅读。 (ASF + SDF已被Rascal Metaprogramming Language取代,其中包括可视化工具。)

语法导向的漂亮打印算法的一个重要问题是它们基于标记化流的解析,这意味着已经删除了注释。显然,希望将注释保留在程序的漂亮打印版本中,但是正确地将注释附加到相关代码并不是微不足道的,特别是当注释与某些代码在同一行时。例如,考虑嵌入代码中的注释掉操作的情况:

// This is the simplified form of actual code
int needed_ = (current_ /* + adjustment_ */ ) * 2;

或者用于记录变量的尾随注释的常见情况:

   /* Tracking the current allocation */
   int   needed_;      // Bytes required.
   int   current_;     // Bytes currently allocated.
// int adjustment_;    // (TODO) Why is this needed?
   /* Either points to the current allocation, or is 0 */
   char* buffer_;

在上面的例子中,请注意空白的重要性:注释可能适用于前一个声明(即使它们出现在分号后面的终止它)或下面的声明,主要取决于它们是否是后缀评论或全行注释,但注释掉的代码是一个例外。此外,程序员已尝试排列成员变量的名称。

自动语法导向的漂亮打印的另一个问题是处理不正确(或不完整)的程序,如果漂亮打印是开发环境的一部分则需要完成。错误处理(和错误恢复)是自动生成的解析器中最困难的部分;在这种情况下保持有用的漂亮印刷更加复杂。正是由于这个原因,大多数IDE使用一种窥视孔漂亮打印(另一种可能的搜索短语),甚至是自适应漂亮打印,其中用户缩进被用作指向尚未写入的代码的位置。

答案 1 :(得分:-2)

OP问道,有人试过这个吗?

是。我们的DMS Software Reengineering Toolkit可以做到这一点:你给它一个语法,你得到一个构建AST的解析器,你得到一个漂亮的打印机。我们用过这个 在过去的20年里,许多语言都有很多解析/更改/删除任务,完全保留了源程序的含义。

该过程是根据语法进行解析,构建AST,然后遍历AST以执行漂亮的打印操作。

但是,你没有得到好的 prettyprinter。重新格式化的源代码的良好布局要求块嵌套的语言提示(例如,匹配'{'...'}','BEGIN'...'END'对,特殊关键字'if','for'等。)用于驱动格式和缩进。虽然可以猜出这些元素是什么(正如我刚才所做的那样),但这只是一种猜测,在实践中,人类需要检查语法以确定哪些事物是提示以及如何格式化每个构造。 (从语法派生的默认的prettyprinter进行了这样的猜测。)

DMS以编程语法编写的prettyprinter声明的形式提供对该问题的支持,以便为格式化工程师提供对布局的大量控制。 (请参阅此SO答案进行详细讨论:https://stackoverflow.com/a/5834775/120163)这会产生 (我们的意见)相当不错的漂亮的打印机。 DMS确实具有完整C ++ 14的显式语法/格式化程序。 [编辑2018年8月:MS和GCC方言中的完整C ++ 17]]

编辑:rici的回答表明评论很难处理。他是对的,你必须处理它们,是的,如果在解析时将它们作为空格删除,则很难处理它们。这个问题的本质是“作为空白被删除”;如果你不这样做就离开DMS提供捕获注释的方法(而不是将它们忽略为空格)并将它们(自动地)附加到AST节点。关于哪个AST节点捕获注释的决定在词法分析器中通过将注释声明为“pre”(在令牌之前发生)或“post”来处理;这个决定对于语法/词法工程师来说是启发式的,但实际上效果很好。带注释的标记将传递给解析器,解析器从中构建AST节点。通过附加到AST节点的注释,prettyprinter也可以重新生成它们。