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

Parsing a tokenized free form grammar with Boost.Spirit

本文关键字:自由 语法 一个 Spirit Boost      更新时间:2023-10-16

我一直在尝试创建一个Boost。用于调用grind工具输出的Spirit解析器,它是valgrind的一部分。Callgrind输出一种特定于领域的嵌入式编程语言(DSEL),它允许您做各种很酷的事情,比如为合成计数器定制表达式,但它不容易解析。

我在https://gist.github.com/ned14/5452719#file-sample-callgrind-output上放置了一些示例callgrind输出。我已经尽了最大的努力来提升。精神词法分析器和解析器在https://gist.github.com/ned14/5452719#file-callgrindparser-hpp和https://gist.github.com/ned14/5452719#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和extract。

也可以是表达式,例如

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使用十一月CTP编译器可变模板,我目前看到这个编译错误:

1>C:Usersndouglas.RIMNETdocumentsvisual studio 2012ProjectsCallgrindParserboostboost/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:Usersndouglas.RIMNETdocumentsvisual studio 2012ProjectsCallgrindParserboostboost/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。用于前向迭代器性质的istreambuf_iterator的Spirit multi_pass<> wrap在某种程度上不能被Boost理解。Spirit的utree()实现。问题是,我不确定这是我的错误代码还是错误的Boost。Spirit代码将line_pos_iterator<>视为未能正确指定其forward_iterator概念标签。

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

尼尔

token属性公开了一个变体,除了基本迭代器范围外,它还可以_假设在token_type typedef中声明的类型:

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

则:string, int, double。但是请注意,只有当解析器实际使用该值时,才会惰性地将强制转换为一种可能的类型。

utree s是一个非常通用的容器[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特征(见文档)

  • 考虑使用纯Qi解析器。虽然我可以"感受到"你的情绪,"它将变得脆弱"[2],但似乎你已经证明了它将复杂性提高到这样一个程度,以至于它可能没有净优点:

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

但是我偏离了:)


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

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