boost.org的Spirit parser-generator框架的缺点是什么?

在几个问题中,我看到了来自boost.org的Spiritparsing器 – 生成器框架的build议 ,但是在评论中,那些使用Spirit的人感到抱怨并不开心。 请这些人站出来向我们解释使用圣灵有什么缺点或缺点?

这是一个很酷的想法,我喜欢它。 真正学习如何使用C ++模板是特别有用的。

但是他们的文档build议对中小型parsing器使用spirit。 一个完整的语言parsing器将花费时间来编译。 我将列出三个原因。

  • 无扫描程序parsing。 虽然它很简单,但是当需要回溯时,可能会减慢parsing器的速度。 这是可选的 – 可以集成一个词法分析器,参见用Spirit构build的C预处理器。 大约300行的语法(包括.h和.cpp文件)使用GCC编译(未优化)为6M的文件。 内联和最大优化可以降低到~17M。

  • 缓慢的parsing – 没有语法的静态检查,也没有提示需要过量的lookahead,也没有validation基本错误,例如左recursion的使用(导致recursion下降parsing器LL语法中的无限recursion)。 然而,左recursion并不是一个真正难以追踪的bug,但是过度的前瞻可能会导致指数parsing时间。

  • 沉重的模板使用 – 虽然这有一定的优势,这会影响编译时间和代码大小。 另外,所有其他用户通常都必须看到语法定义,影响更多的编译时间。 我已经能够通过添加具有正确参数的显式模板实例化来将语法移动到.cpp文件,但这并不容易。

更新:我的回应仅限于我的精神经典,而不是精神V2的经验。 我仍然认为Spirit的模板很大,但现在我只是猜测。

在1.41的推动下,一个新版本的Spirit正在被释放,

经过很长一段时间的testing(精灵2.0超过2年),Spirit 2.1将会在即将发布的Boost 1.41版本发布。 代码现在非常稳定,可以用于生产代码。 我们正在努力完成Boost 1.41的文档。 您可以在这里查看文档的当前状态。 目前,您可以在Boost SVN中继中find代码和文档。 如果您有一个涉及Spirit的新项目,我们强烈build议现在开始使用Spirit 2.1。 请允许我引用Spirit邮件列表中的OvermindDL的post:

我可能开始听起来像一个bot,多频繁地说这个,但是Spirit.Classic是古老的,你应该切换到Spirit2.1,它可以做所有你做的事情更容易,更less的代码,它执行更快。 例如,Spirit2.1可以build立你的整个AST内联,没有怪异的覆盖,不需要事后build立等等,这一切都是一个又好又快的步骤。 你真的需要更新。 查看过去一天中的其他post,查看Spirit2.1的文档链接等。 Spirit2.1目前在Boost Trunk中,但是会在Boost 1.41正式发布,但是在其他方面完成。

对我来说,最大的问题是Spirit中的expression式(如编译器或debugging器所看到的)相当长(我在Spirit Classic中复制一个expression式的一部分 )。 这些表情吓倒了我。 当我使用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<char const*>, boost::spirit::classic::ref_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::clear_action> >, boost::spirit::classic::ref_actor<std::vector<int, std::allocator<int> >, 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<char>, boost::spirit::classic::chlit<char> >, boost::spirit::classic::positive<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit<char> >, boost::spirit::classic::chlit<char> > > > >, boost::spirit::classic::ref_value_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action<boost::spirit::classic::rule<boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, 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::vector<std::string, std::allocator<std::string> >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::action<boost::spirit::classic::uint_parser<unsigned int, 10, 1u, -1>, boost::spirit::classic::ref_value_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::push_back_action> > > > >, boost::spirit::classic::kleene_star<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, 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<char>, boost::spirit::classic::chlit<char> >, boost::spirit::classic::positive<boost::spirit::classic::alternative<boost::spirit::classic::alternative<boost::spirit::classic::alnum_parser, boost::spirit::classic::chlit<char> >, boost::spirit::classic::chlit<char> > > > >, boost::spirit::classic::ref_value_actor<std::vector<std::string, std::allocator<std::string> >, boost::spirit::classic::push_back_action> >, boost::spirit::classic::action<boost::spirit::classic::rule<boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, 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::vector<std::string, std::allocator<std::string> >, std::string, boost::spirit::classic::push_back_action> > >, boost::spirit::classic::contiguous<boost::spirit::classic::sequence<boost::spirit::classic::chlit<char>, boost::spirit::classic::action<boost::spirit::classic::uint_parser<unsigned int, 10, 1u, -1>, boost::spirit::classic::ref_value_actor<std::vector<int, std::allocator<int> >, boost::spirit::classic::push_back_action> > > > > > > > >, void ( )(char const , char const*)>, boost::spirit::classic::scanner<char const*, boost::spirit::classic::scanner_policies<boost::spirit::classic::skipper_iteration_policy<boost::spirit::classic::iteration_policy>, boost::spirit::classic::match_policy, boost::spirit::classic::action_policy> > >::type boost::spirit::classic::action<boost::spirit::classic::sequence<boost::spirit::classic::action<boost::spirit::classic::action<

这是我不喜欢的事情:

  • 文件是有限的。 有一个大的网页,“一切”是解释,但目前的解释缺乏细节。

  • 可怜的AST一代。 AST很less被解释,甚至在你碰到墙壁以了解AST修饰符如何工作之后,很难获得一个简单的操作AST(即很好地映射到问题域)

  • 即使对于“中等”大小的语法,它也会大大增加编译时间

  • 语法太重了。 在C / C ++中,你必须复制代码(即在声明和定义之间)是生活的一个事实。 然而,在boost :: spirit中,当你声明一个语法<>时,你必须重复3次:D(当你想要AST时,这是我想要的:D)

除此之外,考虑到C ++的局限性,我认为他们在parsing器方面做得非常好。 但我认为他们应该改进更多。 历史的页面描述了当前“静”精神之前存在的“dynamic”精神; 我想知道它有多快,多好的语法。

我想说的最大的问题是缺乏对语法问题的任何诊断或其他帮助。 如果你的语法是不明确的,parsing器可能不会parsing你所期望的,并且没有注意到这一点的好方法。