这是一个相当酷的想法,我喜欢它;尤其有用的是真正学习如何使用C++ 模板。
但他们的文档建议在小到中等规模的解析器中使用spirit。一个完整语言的解析器需要花费很长时间来编译。 我将列出三个原因。
无扫描仪解析。虽然它很简单,但在需要回溯时可能会使解析器变慢。不过这是可选的 - 词法分析器可以被集成,在使用Spirit构建的C预处理器中可以看到。一个包括.h和.cpp文件的约300行语法(包括两者)编译(未优化)成一个6M的文件,使用GCC进行内联和最大优化可将其降至约1.7M。
解析速度慢 - 语法没有静态检查,既不能提示所需的超额前瞻,也不能验证基本错误,例如使用左递归(这导致递归下降解析器LL语法中的无限递归)。虽然左递归不是真正难以跟踪的错误,但过多的前瞻可能会导致指数级的解析时间。
大量使用模板 - 虽然这具有某些优点,但这会影响编译时间和代码大小。此外,语法定义通常必须可见于所有其他用户,更影响编译时间。 我已经能够通过添加正确参数的显式模板实例来将语法移动到.cpp文件中,但这并不容易。
更新:我的回复仅限于我对Spirit classic的经验,而不是Spirit V2。我仍然希望Spirit基于模板,但现在只是猜测。
在Boost 1.41中,将发布Spirit的新版本,而且它击败了spirit::classic:
经过长时间的测试(使用Spirit 2.0已超过2年),Spirit 2.1终于将随即将发布的Boost 1.41一同发布。现在代码非常稳定,完全可以用于生产环境。我们正在努力完成文档以便于在Boost 1.41发布之前发布。您可以在此处查看当前文档状态。目前,您可以在Boost SVN主线中找到代码和文档。如果您有一个涉及Spirit的新项目,我们强烈建议立即开始使用Spirit 2.1。请允许我引用来自Spirit邮件列表的OvermindDL的帖子:
我可能听起来像机器人一样,因为我总是这么说,但是Spirit.Classic太古老了,您应该转向Spirit2.1,在Spirit2.1中可以更轻松地完成您上面所做的所有事情,需要更少的代码,并且执行速度更快。例如,Spirit2.1可以内联构建整个AST,无需奇怪的重写,无需事后建立,等等...,全部作为一个不错且快速的步骤。您确实需要更新。 参见过去一天的其他帖子,获取Spirit2.1的文档等相关链接。Spirit2.1目前在Boost Trunk中,但是它已经完成并将随Boost 1.41一同正式发布。
对我来说,最大的问题是Spirit中表达式过长(我在下面复制了Spirit Classic的一部分表达式)。这些表达式让我感到害怕。当我在使用Spirit的程序时,我不敢使用valgrind或者在gdb中打印回溯信息。
boost::spirit::classic::parser_result< boost::spirit::classic::action< boost::spirit::classic::sequence< boost::spirit::classic::action< boost::spirit::classic::action< optional_suffix_parser, boost::spirit::classic::ref_actor >, boost::spirit::classic::clear_action> >, boost::spirit::classic::ref_actor >, boost::spirit::classic::clear_action> >, boost::spirit::classic::sequence< boost::spirit::classic::alternative< boost::spirit::classic::alternative< boost::spirit::classic::action< boost::spirit::classic::contiguous< boost::spirit::classic::sequence< boost::spirit::classic::alternative< boost::spirit::classic::chlit, boost::spirit::classic::chlit >, boost::spirit::classic::positive< boost::spirit::classic::alternative< boost::spirit::classic::alternative< boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit >, boost::spirit::classic::chlit > > > >, boost::spirit::classic::ref_value_actor >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action< boost::spirit::classic::rule< boost::spirit::classic::scanner, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> >, boost::spirit::classic::nil_t, boost::spirit::classic::nil_t>, boost::spirit::classic::ref_const_ref_actor >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous< boost::spirit::classic::sequence< boost::spirit::classic::chlit, boost::spirit::classic::action< boost::spirit::classic::uint_parser, boost::spirit::classic::ref_value_actor >, boost::spirit::classic::push_back_action> > > > >, boost::spirit::classic::kleene_star< boost::spirit::classic::sequence< boost::spirit::classic::chlit, boost::spirit::classic::alternative< boost::spirit::classic::alternative< boost::spirit::classic::action< boost::spirit::classic::contiguous< boost::spirit::classic::sequence< boost::spirit::classic::alternative< boost::spirit::classic::chlit, boost::spirit::classic::chlit >, boost::spirit::classic::positive< boost::spirit::classic::alternative< boost::spirit::classic::alternative< boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit >, boost::spirit::classic::chlit > > > >, boost::spirit::classic::ref_value_actor >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action< boost::spirit::classic::rule< boost::spirit::classic::scanner, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> >, boost::spirit::classic::nil_t, boost::spirit::classic::nil_t>, boost::spirit::classic::ref_const_ref_actor >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous< boost::spirit::classic::sequence< boost::spirit::classic::chlit, boost::spirit::classic::action< boost::spirit::classic::uint_parser, boost::spirit::classic::ref_value_actor >, boost::spirit::classic::push_back_action> > > > > > > > >, void (*)(char const, char const*)>, boost::spirit::classic::scanner, boost::spirit::以下是我不喜欢它的原因:
文档有限。虽然有一个大网页解释了“一切”,但目前的解释缺乏细节。
AST生成质量较差。AST的解释很差,即使你努力理解AST修饰符的工作方式,也难以获得易于操作的AST(即映射到问题域的AST)。
它会极大地增加编译时间,即使对于“中等”规模的语法
语法过于笨重。在C/C++中,你必须复制代码之间的部分(即声明和定义)。然而,在boost::spirit中,当你声明grammar <>时,你必须重复某些内容3次:D(当你想要ASTs,这正是我想要的:D)
除此之外,我认为他们在解析器方面做得相当不错,考虑到C++的限制。但我认为他们应该进一步改进。历史页面描述了当前“静态”spirit之前存在一个“动态”的spirit;我在想它有多快,有多好的语法。
我认为最大的问题是缺乏对语法问题的任何诊断或其他帮助。如果你的语法含糊不清,解析器可能无法解析你期望的内容,而且没有好的方法来注意到这一点。