如何捕获 boost::spirit::x3 解析器解析的值以在语义操作主体中使用?

How to capture the value parsed by a boost::spirit::x3 parser to be used within the body of a semantic action?

我有一个字符串文字解析器,我想将语义操作附加到解析器,以操纵解析后的值。似乎 boost::spirit::x3::_val() returns 在给定上下文时对解析值的引用,但由于某种原因,解析后的字符串总是作为空字符串进入语义动作的主体,这显然使它变得困难从中读取。虽然它是正确的字符串,但我已经通过检查地址来确定。任何人都知道我如何在附加到解析器的语义操作中引用解析值?这是我目前使用的解析器:

x3::lexeme[quote > *("\\"" >> x3::attr('\"') | ~x3::char_(quote)) > quote]

我想在它的末尾添加语义动作。提前致谢!

编辑:似乎每当我将任何语义操作附加到解析器时,该值就会无效。我想现在的问题是我如何才能在此之前访问该值?我只需要能够在将解析的字符串提供给 AST 之前对其进行操作。

在X3中,语义动作要简单得多。它们是只接受上下文的一元可调用对象。

然后您使用自由函数从上下文中提取信息:

  • x3::_val(ctx) 就像 qi::_val
  • x3::_attr(ctx) 类似于 qi::_0(或 qi::_1 对于简单解析器)
  • x3::_pass(ctx) 就像 qi::_pass

因此,要获得语义操作,您可以这样做:

   auto qstring 
    = x3::rule<struct rule_type, std::string> {"qstring"}
    = x3::lexeme[quote > *("\" >> x3::char_(quote) | ~x3::char_(quote)) > quote]
    ;

现在做一个非常奇怪的字符串规则,反转文本(在转义后)并且要求字符数是奇数:

auto odd_reverse = [](auto& ctx) {
    auto& attr = x3::_attr(ctx);
    auto& val  = x3::_val(ctx);
    x3::traits::move_to(attr, val);
    std::reverse(val.begin(), val.end());

    x3::_pass(ctx) = val.size() % 2 == 0;
};

auto odd_string 
    = x3::rule<struct odd_type, std::string> {"odd_string"}
    = qstring [ odd_reverse ]
    ;

演示

Live On Coliru

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

int main() {
    namespace x3 = boost::spirit::x3;

    auto constexpr quote = '"';
    auto qstring 
        = x3::rule<struct rule_type, std::string> {"qstring"}
        = x3::lexeme[quote > *("\" >> x3::char_(quote) | ~x3::char_(quote)) > quote]
        ;

    auto odd_reverse = [](auto& ctx) {
        auto& attr = x3::_attr(ctx);
        auto& val  = x3::_val(ctx);
        x3::traits::move_to(attr, val);
        std::reverse(val.begin(), val.end());

        x3::_pass(ctx) = val.size() % 2 == 0;
    };

    auto odd_string 
        = x3::rule<struct odd_type, std::string> {"odd_string"}
        = qstring [ odd_reverse ]
        ;

    for (std::string const input : { 
            R"("test \"hello\" world")",
            R"("test \"hello\" world!")",
    }) {
        std::string output;
        auto f = begin(input), l = end(input);
        if (x3::phrase_parse(f, l, odd_string, x3::blank, output)) {
            std::cout << "[" << output << "]\n";
        } else {
            std::cout << "Failed\n";
        }
        if (f != l) {
            std::cout << "Remaining unparsed: " << std::quoted(std::string(f,l)) << "\n";
        }
    }
}

打印

[dlrow "olleh" tset]
Failed
Remaining unparsed: "\"test \\"hello\\" world!\""

更新

补充问题:

EDIT: it seems that whenever I attach any semantic action in general to the parser, the value is nullified. I suppose the question now is how could I access the value before that happens? I just need to be able to manipulate the parsed string before it is given to the AST.

是的,如果附加一个动作,自动属性传播将被禁止。这在 Qi 中是相同的,您可以在其中分配规则 %= 而不是 = 以强制自动属性传播。

要在 X3 中获得相同的效果,请使用 x3::rule 的第三个模板参数:x3::rule<X, T, true> 以指示您想要自动传播。

真的,尽量不要打系统。实际上,自动转换系统比我愿意自己重新发现的要复杂得多,所以我通常 post-处理整个 AST 最多应用动作中的一些小调整。另见 Boost Spirit: "Semantic actions are evil"?