使用Boost.Spirit解析一个标记化的自由格式语法

时间:2013-04-24 15:29:15

标签: c++ boost boost-spirit callgrind boost-spirit-lex

我试图为callgrind工具的输出创建一个Boost.Spirit解析器,这是valgrind的一部分。 Callgrind输出一个特定于域的嵌入式编程语言(DSEL),它允许你做各种很酷的东西,比如合成计数器的自定义表达式,但它不容易解析。

我在https://gist.github.com/ned14/5452719#file-sample-callgrind-output放置了一些示例callgrind输出。我已经在https://gist.github.com/ned14/5452719#file-callgrindparser-hpphttps://gist.github.com/ned14/5452719#file-callgrindparser-cxx对Boost.Spirit lexer和解析器进行了最佳尝试。 Lexer部分很简单:它标记标记值,非空白文本,注释,行尾,整数,十六进制,浮点数和运算符(忽略示例代码中的标点符号,它们未被使用)。跳过空格。

到目前为止一切顺利。问题是解析标记化的输入流。我还没有尝试过主要的节,我仍然试图解析可能出现在文件中任何点的标记值。标记值如下所示:

tagtext: unknown series of tokens<eol>

它可以是自由形式的文本,例如

desc: I1 cache: 32768 B, 64 B, 8-way associative, 157 picosec hit latency

在这种情况下,您需要将标记集转换为字符串,即转换为iterator_range并提取。

然而,它可能是一种表达,例如

event: EPpsec = 316 Ir + 1120 I1mr + 1120 D1mr + 1120 D1mw + 1362 ILmr + 1362 DLmr + 1362 DLmw

这说明从现在开始,事件EPpsec将被合成为Ir乘以316加到I1mr乘以1120加到......等等。

我想在这里提出的一点是,标签值对需要作为任意的令牌集进行累积,然后进行后处理,以便我们将它们转换成以后的内容。

为此,Boost.Spirit的utree()类看起来正是我想要的,这就是示例代码所使用的内容。但是在VS2012上使用带有可变参数模板的11月CTP编译器,我现在看到了这个编译错误:

1>C:\Users\ndouglas.RIMNET\documents\visual studio 2012\Projects\CallgrindParser\boost\boost/range/iterator_range_core.hpp(56): error C2440: 'static_cast' : cannot convert from 'boost::spirit::detail::list::node_iterator<const boost::spirit::utree>' to 'base_iterator_type'
1>          No constructor could take the source type, or constructor overload resolution was ambiguous
1>          C:\Users\ndouglas.RIMNET\documents\visual studio 2012\Projects\CallgrindParser\boost\boost/range/iterator_range_core.hpp(186) : see reference to function template instantiation 'IteratorT boost::iterator_range_detail::iterator_range_impl<IteratorT>::adl_begin<const Range>(ForwardRange &)' being compiled
1>          with
1>          [
1>              IteratorT=base_iterator_type
1>  ,            Range=boost::spirit::utree
1>  ,            ForwardRange=boost::spirit::utree
1>          ]

...这表明我的base_iterator_type,它是一个Boost.Spirit multi_pass&lt;&gt;对于前向迭代器性质的istreambuf_iterator的包装,Boost.Spirit的utree()实现在某种程度上无法理解。事实是,我不确定这是我的坏代码还是坏的Boost.Spirit代码,看作line_pos_iterator&lt;&gt;未能正确指定其forward_iterator概念标记。

感谢过去的Stackoverflow帮助,我可以写一个纯粹的非标记化语法,但它会很脆弱。正确的解决方案是标记并使用能够相当任意输入的自由格式语法。让Boost.Spirit的Lex和Grammar在真实世界的例子中一起工作以实现这个而不是玩具示例的例子很少令人遗憾。因此,我们将非常感谢任何帮助。

尼尔

1 个答案:

答案 0 :(得分:3)

token属性公开一个变量,除了base-iterator范围之外,还可以_seume token_type typedef中声明的类型:

typedef lex::lexertl::token<base_iterator_type, mpl::vector<std::string, int, double>> token_type;

所以:stringintdouble。但请注意,当解析器实际使用该值时,强制只会延伸到其中一种可能的类型。

utree是一个非常通用的容器 [1] 。因此,当您在规则上公开spirit::utree属性,并且令牌值变量包含iterator_range时,它会尝试将其分配到utree对象中(这会失败,因为迭代器是......'时髦')。

获得所需行为的最简单方法是 强制 Qi将tag标记的属性解释为字符串,并拥有 分配给utree。因此,以下行构成了一个将使编译成功的修复:

    unknowntagvalue = qi::as_string[tok.tag] >> restofline;

备注

说完这一切之后,我的确会提出以下建议

  • 考虑使用Nabialek Trick来分发不同的延迟规则,具体取决于匹配的tag - 这使得无需处理原始utree s稍后

  • 您可能已成功专注boost::spirit::traits::assign_to_XXXXXX个特征(请参阅documentation

  • 考虑使用纯Qi解析器。虽然我可以“感觉”你的情绪“它会变得脆弱” [2] 但似乎你已经证明它将复杂性提高到如此程度以至于它可能没有净值:

    • 属性实现的意外方式(此问题)
    • line-pos迭代器的问题(这是常见问题,而AFAIR主要是不优雅的解决方案)
    • 关于例如的不灵活性ad-hoc调试(访问SA中的源数据),切换/禁用队长等。
    • 我个人的经验是,查看 lexer状态来驱动它们是没有用的,因为切换词法分析器状态只能从lexer token semantic actions可靠地工作,而通常,消歧会发生在 Qi阶段

但我分歧了:))


[1] ,例如它们具有非常轻量级“引用”迭代器范围的功能(例如,对于符号,或者为了避免将字符从源缓冲区复制到属性中,除非需要)

[2] 实际上,仅仅因为使用顺序词法分析器(扫描程序)大大减少了回溯机会的数量,因此它简化了解析器的心理模型。但是,您可以使用expectation points获得相同的效果。