如何捕获 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 ]
;
演示
#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"?
我有一个字符串文字解析器,我想将语义操作附加到解析器,以操纵解析后的值。似乎 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 ]
;
演示
#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"?