Boost Spirit,在语义动作中获取迭代器
Boost Spirit, obtain iterator inside semantic action
在语义操作中我想要获取迭代器,最好是从第一个到最后一个解析字符的整个迭代器范围。当使用 raw
指令时,我可以简单地使用 _attr(context)
来获取它。我猜 _where(context)
会这样做,但它只是 returns 一个空范围,其开始迭代器指向已解析子字符串之后的字符。
示例代码:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <utility>
namespace x3 = boost::spirit::x3;
int main()
{
const auto action = [](auto &ctx)
{
auto range = x3::_where(ctx);
std::cout << range.size() << '\n';
std::cout << "range start: " << static_cast<const void*>(&*range.begin()) << '\n';
};
const auto rule = x3::int_[action];
const std::string input = "432";
std::cout << "string start: " << static_cast<const void*>(input.data()) << '\n';
int output;
x3::phrase_parse(input.begin(), input.end(), rule, x3::space, output);
std::cout << output << '\n';
}
输出
string start: 0x7ffd65f337c0
0
range start: 0x7ffd65f337c3
432
范围的长度为0,它的begin()指向字符串的末尾。当我扩展输入字符串时,范围涵盖剩余的未解析子字符串。
如何获取包含已解析子字符串的迭代器范围?
啊,看到你的代码让我想起了我过去所做的事情。
基本上可以
对 x3::rule<>
使用 on_error
处理,它将为您提供匹配的迭代器范围。请参阅示例:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <utility>
#include <iomanip>
namespace x3 = boost::spirit::x3;
namespace {
struct ehbase {
template <typename It, typename Attr, typename Ctx>
void on_success(It& f, It const& l, Attr const& attr, Ctx const& /*ctx*/) const {
std::cout << "on_succes: " << std::quoted(std::string(f, l)) << " -> " << attr << "\n";
}
};
struct rule_type : ehbase {};
}
int main() {
const auto rule = x3::rule<rule_type, int>{"rule"} = x3::int_;
for (std::string const input : { "q", "432", " 646 q" }) {
std::cout << "== " << std::quoted(input) << " ==\n";
auto f = begin(input), l = end(input);
int output;
if (x3::phrase_parse(f, l, rule, x3::space, output))
std::cout << "Parsed " << output << "\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n";
}
}
版画
== "q" ==
Parse failed
Remaining: "q"
== "432" ==
on_succes: "432" -> 432
Parsed 432
== " 646 q" ==
on_succes: "646" -> 646
Parsed 646
Remaining: "q"
On a slight tangent, you can add error-handling in the same vein:
template <typename It, typename Ctx>
x3::error_handler_result on_error(It f, It l, x3::expectation_failure<It> const& e, Ctx const& /*ctx*/) const {
std::cout << std::string(f,l) << "\n"
<< std::setw(1+std::distance(f, e.where())) << "^"
<< "-- expected: " << e.which() << "\n";
return x3::error_handler_result::fail;
}
If you have an expectation point in the parser:
const auto rule = x3::rule<rule_type, int>{"rule"} = x3::int_ > x3::eoi;
It now prints: Live On Coliru
== " 646 q" ==
646 q
^-- expected: eoi
Parse failed
Remaining: "646 q"
您可以使用 x3::raw[]
指令将迭代器范围公开为属性:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <utility>
#include <iomanip>
namespace x3 = boost::spirit::x3;
int main() {
for (std::string const input : { "q", "432", " 646 q" }) {
std::cout << "== " << std::quoted(input) << " ==\n";
auto action = [&input](auto& ctx) {
auto iters = x3::_attr(ctx);
std::cout
<< input << "\n"
<< std::setw(std::distance(input.begin(), iters.begin())) << ""
<< "^ matched: " << std::quoted(std::string(iters.begin(), iters.end())) << "\n";
};
const auto rule = x3::raw[x3::int_] [action];
auto f = begin(input), l = end(input);
if (x3::phrase_parse(f, l, rule, x3::space))
std::cout << "Parse succeeded\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n";
}
}
打印:
== "q" ==
Parse failed
Remaining: "q"
== "432" ==
432
^ matched: "432"
Parse succeeded
== " 646 q" ==
646 q
^ matched: "646"
Parse succeeded
Remaining: "q"
Again, slightly related, it can become a little more cumbersome to deal with attribute propagation in this approach:
const auto rule
= x3::rule<struct _rule, int, true> {"rule"}
= &x3::raw[x3::int_] [action] >> x3::int_;;
auto f = begin(input), l = end(input);
int output;
if (x3::phrase_parse(f, l, rule, x3::space, output))
为了减轻笨拙的属性传播,您可以编写一个自定义解析器组件,简单地包装另一个组件并添加您想要的逻辑:
template <typename SubjectParser>
struct verbose : x3::parser<verbose<SubjectParser> > {
explicit verbose(SubjectParser p, std::string name) : _subject(std::move(p)), _name(std::move(name)) {}
SubjectParser _subject;
std::string _name;
template <typename It, typename Ctx, typename... Other>
bool parse(It& f, It l, Ctx& ctx, Other&&... args) const {
auto saved = f;
auto ok = x3::as_parser(_subject).parse(f, l, ctx, std::forward<Other>(args)...);
if (ok) {
//optionally adjust for skipper
x3::skip_over(saved, l, ctx);
std::cout << "Debug: " << _name << " matched " << std::quoted(std::string(saved, f)) << "\n";
}
return ok;
}
};
现在像这样包装解析器表达式:
const auto rule = verbose {x3::int_, "YUMMY"};
结果如下:Live On Coliru
== "q" ==
Parse failed
Remaining: "q"
== "432" ==
Debug: YUMMY matched "432"
Parsed 432
== " 646 q" ==
Debug: YUMMY matched "646"
Parsed 646
Remaining: "q"
提炼出来,让我意识到规则调试可能是/您正在寻找的/。在这种情况下,只需使用 BOOST_SPIRIT_X3_DEBUG
可能就是您需要知道的:
#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <iomanip>
namespace x3 = boost::spirit::x3;
int main() {
const auto rule
= x3::rule<struct _rule, int> {"rule"}
= x3::int_;
for (std::string const input : { "q", "432", " 646 q" }) {
std::cout << "== " << std::quoted(input) << " ==\n";
auto f = begin(input), l = end(input);
int output;
if (x3::phrase_parse(f, l, rule, x3::space, output))
std::cout << "Parsed " << output << "\n";
else
std::cout << "Parse failed\n";
if (f!=l)
std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n";
}
}
打印:
== "q" ==
<rule>
<try>q</try>
<fail/>
</rule>
Parse failed
Remaining: "q"
== "432" ==
<rule>
<try>432</try>
<success></success>
<attributes>432</attributes>
</rule>
Parsed 432
== " 646 q" ==
<rule>
<try> 646 q</try>
<success> q</success>
<attributes>646</attributes>
</rule>
Parsed 646
Remaining: "q"
在语义操作中我想要获取迭代器,最好是从第一个到最后一个解析字符的整个迭代器范围。当使用 raw
指令时,我可以简单地使用 _attr(context)
来获取它。我猜 _where(context)
会这样做,但它只是 returns 一个空范围,其开始迭代器指向已解析子字符串之后的字符。
示例代码:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <utility>
namespace x3 = boost::spirit::x3;
int main()
{
const auto action = [](auto &ctx)
{
auto range = x3::_where(ctx);
std::cout << range.size() << '\n';
std::cout << "range start: " << static_cast<const void*>(&*range.begin()) << '\n';
};
const auto rule = x3::int_[action];
const std::string input = "432";
std::cout << "string start: " << static_cast<const void*>(input.data()) << '\n';
int output;
x3::phrase_parse(input.begin(), input.end(), rule, x3::space, output);
std::cout << output << '\n';
}
输出
string start: 0x7ffd65f337c0
0
range start: 0x7ffd65f337c3
432
范围的长度为0,它的begin()指向字符串的末尾。当我扩展输入字符串时,范围涵盖剩余的未解析子字符串。
如何获取包含已解析子字符串的迭代器范围?
啊,看到你的代码让我想起了我过去所做的事情。
基本上可以
对
x3::rule<>
使用on_error
处理,它将为您提供匹配的迭代器范围。请参阅示例:#include <boost/spirit/home/x3.hpp> #include <iostream> #include <utility> #include <iomanip> namespace x3 = boost::spirit::x3; namespace { struct ehbase { template <typename It, typename Attr, typename Ctx> void on_success(It& f, It const& l, Attr const& attr, Ctx const& /*ctx*/) const { std::cout << "on_succes: " << std::quoted(std::string(f, l)) << " -> " << attr << "\n"; } }; struct rule_type : ehbase {}; } int main() { const auto rule = x3::rule<rule_type, int>{"rule"} = x3::int_; for (std::string const input : { "q", "432", " 646 q" }) { std::cout << "== " << std::quoted(input) << " ==\n"; auto f = begin(input), l = end(input); int output; if (x3::phrase_parse(f, l, rule, x3::space, output)) std::cout << "Parsed " << output << "\n"; else std::cout << "Parse failed\n"; if (f!=l) std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n"; } }
版画
== "q" == Parse failed Remaining: "q" == "432" == on_succes: "432" -> 432 Parsed 432 == " 646 q" == on_succes: "646" -> 646 Parsed 646 Remaining: "q"
On a slight tangent, you can add error-handling in the same vein:
template <typename It, typename Ctx> x3::error_handler_result on_error(It f, It l, x3::expectation_failure<It> const& e, Ctx const& /*ctx*/) const { std::cout << std::string(f,l) << "\n" << std::setw(1+std::distance(f, e.where())) << "^" << "-- expected: " << e.which() << "\n"; return x3::error_handler_result::fail; }
If you have an expectation point in the parser:
const auto rule = x3::rule<rule_type, int>{"rule"} = x3::int_ > x3::eoi;
It now prints: Live On Coliru
== " 646 q" == 646 q ^-- expected: eoi Parse failed Remaining: "646 q"
您可以使用
x3::raw[]
指令将迭代器范围公开为属性:#include <boost/spirit/home/x3.hpp> #include <iostream> #include <utility> #include <iomanip> namespace x3 = boost::spirit::x3; int main() { for (std::string const input : { "q", "432", " 646 q" }) { std::cout << "== " << std::quoted(input) << " ==\n"; auto action = [&input](auto& ctx) { auto iters = x3::_attr(ctx); std::cout << input << "\n" << std::setw(std::distance(input.begin(), iters.begin())) << "" << "^ matched: " << std::quoted(std::string(iters.begin(), iters.end())) << "\n"; }; const auto rule = x3::raw[x3::int_] [action]; auto f = begin(input), l = end(input); if (x3::phrase_parse(f, l, rule, x3::space)) std::cout << "Parse succeeded\n"; else std::cout << "Parse failed\n"; if (f!=l) std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n"; } }
打印:
== "q" == Parse failed Remaining: "q" == "432" == 432 ^ matched: "432" Parse succeeded == " 646 q" == 646 q ^ matched: "646" Parse succeeded Remaining: "q"
Again, slightly related, it can become a little more cumbersome to deal with attribute propagation in this approach:
const auto rule = x3::rule<struct _rule, int, true> {"rule"} = &x3::raw[x3::int_] [action] >> x3::int_;; auto f = begin(input), l = end(input); int output; if (x3::phrase_parse(f, l, rule, x3::space, output))
为了减轻笨拙的属性传播,您可以编写一个自定义解析器组件,简单地包装另一个组件并添加您想要的逻辑:
template <typename SubjectParser> struct verbose : x3::parser<verbose<SubjectParser> > { explicit verbose(SubjectParser p, std::string name) : _subject(std::move(p)), _name(std::move(name)) {} SubjectParser _subject; std::string _name; template <typename It, typename Ctx, typename... Other> bool parse(It& f, It l, Ctx& ctx, Other&&... args) const { auto saved = f; auto ok = x3::as_parser(_subject).parse(f, l, ctx, std::forward<Other>(args)...); if (ok) { //optionally adjust for skipper x3::skip_over(saved, l, ctx); std::cout << "Debug: " << _name << " matched " << std::quoted(std::string(saved, f)) << "\n"; } return ok; } };
现在像这样包装解析器表达式:
const auto rule = verbose {x3::int_, "YUMMY"};
结果如下:Live On Coliru
== "q" == Parse failed Remaining: "q" == "432" == Debug: YUMMY matched "432" Parsed 432 == " 646 q" == Debug: YUMMY matched "646" Parsed 646 Remaining: "q"
提炼出来,让我意识到规则调试可能是/您正在寻找的/。在这种情况下,只需使用
BOOST_SPIRIT_X3_DEBUG
可能就是您需要知道的:#define BOOST_SPIRIT_X3_DEBUG #include <boost/spirit/home/x3.hpp> #include <iomanip> namespace x3 = boost::spirit::x3; int main() { const auto rule = x3::rule<struct _rule, int> {"rule"} = x3::int_; for (std::string const input : { "q", "432", " 646 q" }) { std::cout << "== " << std::quoted(input) << " ==\n"; auto f = begin(input), l = end(input); int output; if (x3::phrase_parse(f, l, rule, x3::space, output)) std::cout << "Parsed " << output << "\n"; else std::cout << "Parse failed\n"; if (f!=l) std::cout << "Remaining: " << std::quoted(std::string(f,l)) << "\n"; } }
打印:
== "q" == <rule> <try>q</try> <fail/> </rule> Parse failed Remaining: "q" == "432" == <rule> <try>432</try> <success></success> <attributes>432</attributes> </rule> Parsed 432 == " 646 q" == <rule> <try> 646 q</try> <success> q</success> <attributes>646</attributes> </rule> Parsed 646 Remaining: "q"