1

我一直在尝试为 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对 Boost.Spirit 词法分析器和解析器进行了目前的最佳尝试#file-callgrindparser-cxx。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<> 包装的 istreambuf_iterator 用于前向迭代器性质,Boost.Spirit 的 utree() 实现不知何故无法理解。问题是,我不确定这是我的错误代码还是错误的 Boost.Spirit 代码,因为 line_pos_iterator<> 未能正确指定其 forward_iterator 概念标签。

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

尼尔

4

1 回答 1

3

token 属性公开了一个变体,除了基本迭代器范围之外,它还可以 _assume token_typetypedef 中声明的类型:

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

所以stringintdouble。但是请注意,当解析器实际使用该值时,强制转换为一种可能的类型只会延迟发生。

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

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

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

笔记

说了这么多,我确实建议以下

  • 考虑使用来根据匹配Nabialek Trick来调度不同的惰性规则tag- 这使得以后无需处理 raw utrees

  • 您可能已经成功地专注于boost::spirit::traits::assign_to_XXXXXX特征(请参阅文档

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

    • 属性实现的意想不到的方式(这个问题)
    • line-pos 迭代器的问题(这是一个经常被问到的问题,而且 AFAIR 它大多有困难不优雅的解决方案)
    • 诸如临时调试(访问 SA 中的源数据)、切换/禁用跳过程序等方面的不灵活性。
    • 我个人的经验是,查看词法分析器状态来驱动这些并没有帮助,因为切换词法分析器状态只能从 可靠地工作lexer token semantic actions,而消歧通常会在Qi 阶段发生

但我有分歧:)


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

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

于 2013-04-24T19:55:48.313 回答