在几个问题中我看到了以下建议 精神 解析器生成器框架来自 boost.org, ,但是评论中却有使用 Spirit 的人抱怨不开心。请那些人站出来向我们其他人解释一下使用 Spirit 的缺点或缺点是什么?

有帮助吗?

解决方案

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

但他们的文档建议中小型解析器使用spirit。完整语言的解析器需要很长时间才能编译。我将列出三个原因。

  • 无扫描仪解析。虽然它相当简单,但当需要回溯时,它可能会减慢解析器的速度。不过它是可选的 - 可能会集成词法分析器,请参阅使用 Spirit 构建的 C 预处理器。约 300 行的语法(包括 .h 和 .cpp 文件)使用 GCC 编译(未优化)为 6M 的文件。内联和最大优化可将其降至约 1,7M。

  • 解析缓慢 - 没有对语法进行静态检查,既不提示需要过多的前瞻,也不验证基本错误,例如左递归的使用(这会导致递归下降解析器 LL 语法中的无限递归)。不过,左递归并不是一个很难追踪的错误,但过多的前瞻可能会导致指数解析时间。

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

更新:我的回答仅限于我对 Spirit classic 的体验,而不是 Spirit V2。我仍然期望 Spirit 很大程度上基于模板,但现在我只是猜测。

其他提示

在提升1.41中,一个新版本的精神正在发布,并且精神上出现了一些优势::经典:

  经过长时间的测试(超过2次)   精神2.0),精神2.1   最终会随着发布   即将发布的Boost 1.41版本。代码   现在非常稳定,已做好准备   生产代码。我们正在努力   及时完成文档   对于Boost 1.41。你可以偷看   文档的当前状态   这里。目前,您可以找到代码   和Boost SVN中的文档   树干。如果你有一个新项目   涉及精神,我们强烈推荐   现在从Spirit 2.1开始。请允许我   引用OvermindDL的帖子   精神邮件列表:

     
    
      

我可能听起来像机器人一样       我经常说这个,但是       Spirit.Classic是古老的,你应该       切换到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<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 的解释很差,即使你绞尽脑汁去了解 AST 修饰符是如何工作的,也很难获得一个易于操作的 AST(即能够很好地映射到问题域)

  • 它极大地增加了编译时间,即使对于“中等”大小的语法也是如此

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

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

我想说最大的问题是语法问题缺乏任何诊断或其他帮助。如果你的语法含糊不清,解析器可能无法解析你的预期,并且没有好的方法可以注意到它。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top