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;
}