2.X3-解析器语义动作
Parser Semantic Actions(解析器语义动作)
前面的示例非常简单。它只是识别数据,但没有对数据执行任何操作。它只回答了一个问题:输入是否匹配? 现在,我们希望从已解析的内容中提取信息。例如,我们可能希望在成功匹配后存储已解析的数字。为此,您需要使用语义动作。
语义动作可以附加到语法规范的任何位置。这些动作是多态的函数对象,每当解析器成功识别输入的一部分时,它们就会被调用。假设您有一个解析器 p
和一个多态的C++函数对象 f
。您可以通过将函数对象 f
附加到解析器来使解析器在匹配输入时调用它:
p[f]
上述表达式将函数对象 f 与解析器 p 关联起来。f 应该是一个多态函数对象,其签名为:
template <typename Context>
void operator()(Context const& ctx) const;
我们也可以使用C++14中的通用lambda函数,形式如下:
[](auto& ctx) { /*...*/ }
从上下文中,我们可以提取相关信息:
Function | Description | Exmaple |
---|---|---|
_val | 对直接或间接调用parser p 的最内层规则的属性的引用 | _val(ctx) = “Gotya!” |
_where | 输入流的迭代器范围 | _where(ctx).begin() |
_attr | 对解析器 p 的属性的引用 | _val(ctx) += _attr(ctx) |
_pass | 一个用于强制解析器 p 失败的布尔标志的引用 | _pass(ctx) = false |
Examples of Semantic Actions
struct print_action
{
template <typename Context>
void operator()(Context const& ctx) const
{
std::cout << _attr(ctx) << std::endl;
}
};
需要注意的是,使用函数对象时,我们需要拥有一个带有 Context 参数的 operator()。如果我们不关心上下文,可以使用 unused_type。我们将在其他地方看到更多关于 unused_type 的内容。unused_type 是Spirit库提供的支持类。
所有示例都解析以下格式的输入:
"{NNN}
其中 NNN 是大括号内的整数(例如,{44})。
第一个示例展示了如何附加一个函数对象:
parse(first, last, '{' >> int_[print_action()] >> '}');
有什么新内容吗?int_ 是 double_ 的兄弟。我相信你可以猜到这个解析器是做什么的。
下一个示例展示了如何使用C++14 lambda函数:
auto f = [](auto& ctx){ std::cout << _attr(ctx) << std::endl; };
parse(first, last, '{' >> int_[f] >> '}');
Exmaple
#include <boost/spirit/home/x3.hpp>
#include <iostream>
// Presented are various ways to attach semantic actions
// * Using plain function pointer
// * Using simple function object
namespace client
{
namespace x3 = boost::spirit::x3;
using x3::_attr;
struct print_action
{
template <typename Context>
void operator()(Context const& ctx) const
{
std::cout << _attr(ctx) << std::endl;
}
};
}
int main()
{
using boost::spirit::x3::int_;
using boost::spirit::x3::parse;
using client::print_action;
{ // example using function object
char const *first = "{43}", *last = first + std::strlen(first);
parse(first, last, '{' >> int_[print_action()] >> '}');
}
{ // example using C++14 lambda
using boost::spirit::x3::_attr;
char const *first = "{44}", *last = first + std::strlen(first);
auto f = [](auto& ctx){ std::cout << _attr(ctx) << std::endl; };
parse(first, last, '{' >> int_[f] >> '}');
}
return 0;
}