boost::spirit::x3 中的通用解析器生成器

Generic parser generator in boost::spirit::x3

我正在尝试以 boost 精神编写通用解析器生成器。我想出了以下代码:

auto attr_to_val = [](auto& ctx) { _val(ctx) = boost::fusion::at_c<2>(_attr(ctx)); };

auto parser_gen = [](const std::string a, auto&& p) {
    return((boost::spirit::x3::string(a) >> boost::spirit::x3::blank >> p)[attr_to_val]);
};

并尝试像这样使用它:

int a;
auto action = [&a](auto& ctx) { a = _val(ctx); };
auto parser = (parser_gen("aaa", boost::spirit::x3::uint_))[action];
parse(bar.begin(), bar.end(), parser);

但是它给出了很多关于无法将 boost::fusion::deque 转换为 int 的错误。另一方面,当我像那样更改它时,恕我直言,这相当于上述模板代码的扩展:

auto pars = (
    boost::spirit::x3::string("aaa") >>
    boost::spirit::x3::blank >> boost::spirit::x3::uint_)[attr_to_val];

int a;
auto action = [&a](auto& ctx) { a = _val(ctx); };
parse(bar.begin(), bar.end(), pars);

一切都很好。我做错了什么,我怎样才能让 parser_gen 工作?

  1. 您不需要公开所有属性,大大简化了属性类型。

  2. 为了匹配字符串文字而不将其作为键公开(显然,无论如何您都不想要,因为您在语义操作中忽略了它),请使用 x3::lit("aaa") 而不是 x3::string("aaa")。在 x3 表达式中,裸 "aaa" 将自动解释为 x3::lit("aaa")(由于 x3::as_parser)。

  3. 更重要的是,你说 at_c<2> 暗示你也不希望 x3::blank 暴露。为什么不直接 x3::omit[x3::blank]?更好的是,考虑使用船长,并隐含它。

  4. action 中,您使用的是 x3::_val,这取决于声明的规则的属性(看不到 x3::rule?)或实际的绑定引用(你没有将任何东西传递给 x3::parse)。

    由于您的操作绑定到解析器参数,因此您似乎需要 its 属性,可以使用 x3::_attr() 查询。

    It seems you might be able to do without semantic actions altogether, see below

修复想法:

这结合了以上所有内容:

  • 使用船长(参见 Boost spirit skipper issues
  • 简化文字

看到了Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <iostream>

namespace x3 = boost::spirit::x3;

int main() {
    auto parser_gen = [=](std::string const a, auto&& p) {
        return x3::skip(x3::blank)[ x3::lit(a) >> p ];
    };

    for (std::string const bar : { "aaa 42", "aaa99", }) {
        int a;
        if (parse(begin(bar), end(bar), parser_gen("aaa", x3::uint_), a)) {
            std::cout << "Parsed " << a << "\n";
        } else {
            std::cout << "Failed\n";
        }
    }
}

版画

Parsed 42
Parsed 99
  • 使用检查强制性 whitespace/token 边界的 label() 助手(参见

看到了Live On Coliru

namespace {
    template <typename P>
    auto label_gen(P p) {
        return x3::omit[ x3::lexeme[ x3::as_parser(p) >> (&x3::punct | !x3::graph) ] ];
    }

    template <typename L, typename P> auto parser_gen(L l, P p) {
        return x3::skip(x3::blank)[ label_gen(l) >> p ];
    }
}

现在少打印一个匹配项:

Parsed 42
Failed

奖励:做有用的事情

所以,我猜您想以有用的方式组合这些 label/value 对中的多个,或许可以解释这些操作。现在,您可以从这个答案中翻页:.

实际上,我不会在此处复制该示例中的代码,但我认为它可能非常适用于您的用例。