用Boost.Spirit解析一个标记化的自由形式语法
Parsing a tokenized free form grammar with Boost.Spirit
我一直在尝试创建一个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
对象(这会失败,因为迭代器是…"时髦的")。
tag
令牌的属性解释为字符串,并将分配给utree
。因此,下面这行构成了一个使编译成功的修复:
unknowntagvalue = qi::as_string[tok.tag] >> restofline;
指出说了这么多,我确实建议如下
考虑使用
Nabialek Trick
来调度不同的延迟规则取决于匹配的tag
-这使得没有必要在 之后处理原始的你可能已经成功地专门化了
boost::spirit::traits::assign_to_XXXXXX
特征(见文档)考虑使用纯Qi解析器。虽然我可以"感受到"你的情绪,"它将变得脆弱"[2],但似乎你已经证明了它将复杂性提高到这样一个程度,以至于它可能没有净优点:
- 属性实现的意外方式(这个问题)
- 行序迭代器的问题(这是一个经常被问到的问题,而且它大多数都有困难的或不优雅的解决方案)
- 关于例如ad-hoc调试(访问SA中的源数据),切换/禁用跳过器等的不灵活性 我的个人经验是,查看词法器状态来驱动这些是没有帮助的,因为切换词法器状态只能从
lexer token semantic actions
可靠地工作,而通常,消歧将发生在Qi阶段
utree
s。但是我偏离了:)
[1]例如,它们提供了非常轻量级的迭代器范围'引用'功能(例如用于符号,或者避免将字符从源缓冲区复制到属性中,除非需要)
[2]实际上,只是因为使用顺序词法分析器(扫描器)大大减少了回溯的机会,所以它简化了解析器的心智模型。但是,您可以使用expectation points
来达到相同的效果。
- 1d 智能指针不适用于语法 (*)++
- 助记符和指向成员语法的指针
- 有人能分解一下这个c++模板的语法吗
- C++避免重复声明的语法是什么
- QMetaObject invokeMethod的基于函数指针的语法
- 这个语法std::class<>{}(arg1, arg2) 在C++中是什么意思?
- 如何针对特定情况调试和修复此双自由内存损坏问题
- 为什么包含windows.h会产生语法错误,从而阻止类的实例化?(C2146,C2065)
- 将自由函数绑定为类成员函数
- 静态数组的自由动态数组
- 单独定义模板化嵌套类方法的正确语法
- 共享指针和具有自定义删除程序的唯一指针之间的语法差异背后的任何原因
- 错误 C2760:语法错误:映射迭代器上意外的标记"标识符",预期的";"
- 调试和自由执行中的信号处理
- 为什么我会收到错误 C2143 语法错误:缺少"*"之前的';'?
- 奇怪的代码抛出编译错误模板< J,int aSize=10> C2143:语法错误:在"<"之前缺少";"
- 使用基类指针调用基类的值构造函数的语法是什么?
- C++ 上下文自由语法库
- 用Boost.Spirit解析一个标记化的自由形式语法
- 重载运算符== 作为具有模板化参数的自由函数的语法是什么?