boost spirit 上下文相关语法规则的最佳实践
Best practice for boost spirit context-dependent grammar rule
只是澄清问题的示例..(这是伪代码)
经典方式:为每条路径制定规则。所以从“开始”开始,选择 outer_rule1 或 outer_rule2,然后从那里进入 inner_rule1 和 inner_rule2。您可以清楚地看到内部规则几乎相等。例如。关于语法的事情,其中特殊分隔的行是由符号“:”给出的,一旦由“;”给出,
inner_rule1 = a >> b >> ":"
inner_rule2 = a >> b >> ";"
outer_rule1 = "X" >> inner_rule1
outer_rule2 = "Z" >> inner_rule2
start=outer_rule1 | outer_rule2
您可以通过将分隔符放在顶层来解决这个问题
inner_rule1 = a >> b
inner_rule2 = a >> b
outer_rule1 = "X" >> inner_rule1 >> ":"
outer_rule2 = "Z" >> inner_rule2 >> ";"
start=outer_rule1 | outer_rule2
但是如果内部规则更复杂,分隔符也可能在嵌套规则中使用,现在使用相同的规则但交换分隔符变得很棘手...
complex_inner1= w >> ";"
complex_inner2= r >> ":"
inner_rule1 = a >> +complex_inner1
inner_rule2 = a >> +complex_inner2
outer_rule1 = "X" >> inner_rule1
outer_rule2 = "Z" >> inner_rule2
start=outer_rule1 | outer_rule2
问题是如何制作这样的东西,在这种情况下,例如使用自定义操作,但我们知道自定义操作不是最佳选择,尤其是在使用回溯时。
complex_inner1= w >> separator
complex_inner2= r >> separator
inner_rule1 = a[separator=";"] >> +complex_inner1
inner_rule2 = a[separator=":"] >> +complex_inner2
outer_rule1 = "X" >> inner_rule1
outer_rule2 = "Z" >> inner_rule2
start=outer_rule1 | outer_rule2
您忘记指定 Spirit 的版本(Qi 或 X3)()。
所以,这里是:
灵气:传承属性
您可以使用 Inherited Attributes or Locals.
将状态注入规则
使用第一个的演示:
#include <boost/spirit/include/qi.hpp>
#include <fmt/ranges.h>
namespace qi = boost::spirit::qi;
using Attr = std::vector<int>;
template <typename It>
struct Parser : qi::grammar<It, Attr()> {
Parser() : Parser::base_type(start) {
using namespace qi;
inner = int_ % lit(_r1);
outer1 = 'X' >> inner(':');
outer2 = 'Y' >> inner(';');
start = skip(space)[outer1 | outer2];
}
private:
qi::rule<It, Attr()> start;
qi::rule<It, Attr(), qi::space_type> outer1, outer2;
qi::rule<It, Attr(char), qi::space_type> inner;
};
int main() {
using It = std::string::const_iterator;
Parser<It> p;
for (std::string const& s : {
"",
" Y 7 ",
"X 7:-4:+99 ",
"Y 7 ; 42 ",
})
{
It f = begin(s), l = end(s);
Attr v;
bool ok = parse(f, l, p, v);
fmt::print("Parsed: {} {}, remaining: '{}'\n", ok, v,
std::string(f, l));
}
}
版画
Parsed: false {}, remaining: ''
Parsed: true {7}, remaining: ' '
Parsed: true {7, -4, 99}, remaining: ' '
Parsed: true {7, 42}, remaining: ' '
X3:函数组合
在 X3 中,Qi 的许多限制都消失了,因为它更容易编写规则。你会写出几乎相同但不同的东西:
#include <boost/spirit/home/x3.hpp>
#include <fmt/ranges.h>
namespace x3 = boost::spirit::x3;
using Attr = std::vector<int>;
namespace Parser {
using namespace x3;
auto inner = [](auto delim) { return int_ % delim; };
auto outer1 = 'X' >> inner(':');
auto outer2 = 'Y' >> inner(';');
auto start = skip(space)[outer1 | outer2];
} // namespace Parser
int main() {
using It = std::string::const_iterator;
for (std::string const& s : {
"",
" Y 7 ",
"X 7:-4:+99 ",
"Y 7 ; 42 ",
})
{
It f = begin(s), l = end(s);
Attr v;
bool ok = parse(f, l, Parser::start, v);
fmt::print("Parsed: {} {}, remaining: '{}'\n", ok, v,
std::string(f, l));
}
}
打印相同。
公平地说,这掩盖了一些/发生/对于给定示例不重要的复杂问题。但这就是非伪代码的好处:它有细节。如果您 运行 遇到任何更微妙的问题,我希望您能带着具体的代码回来 :)
只是澄清问题的示例..(这是伪代码)
经典方式:为每条路径制定规则。所以从“开始”开始,选择 outer_rule1 或 outer_rule2,然后从那里进入 inner_rule1 和 inner_rule2。您可以清楚地看到内部规则几乎相等。例如。关于语法的事情,其中特殊分隔的行是由符号“:”给出的,一旦由“;”给出,
inner_rule1 = a >> b >> ":"
inner_rule2 = a >> b >> ";"
outer_rule1 = "X" >> inner_rule1
outer_rule2 = "Z" >> inner_rule2
start=outer_rule1 | outer_rule2
您可以通过将分隔符放在顶层来解决这个问题
inner_rule1 = a >> b
inner_rule2 = a >> b
outer_rule1 = "X" >> inner_rule1 >> ":"
outer_rule2 = "Z" >> inner_rule2 >> ";"
start=outer_rule1 | outer_rule2
但是如果内部规则更复杂,分隔符也可能在嵌套规则中使用,现在使用相同的规则但交换分隔符变得很棘手...
complex_inner1= w >> ";"
complex_inner2= r >> ":"
inner_rule1 = a >> +complex_inner1
inner_rule2 = a >> +complex_inner2
outer_rule1 = "X" >> inner_rule1
outer_rule2 = "Z" >> inner_rule2
start=outer_rule1 | outer_rule2
问题是如何制作这样的东西,在这种情况下,例如使用自定义操作,但我们知道自定义操作不是最佳选择,尤其是在使用回溯时。
complex_inner1= w >> separator
complex_inner2= r >> separator
inner_rule1 = a[separator=";"] >> +complex_inner1
inner_rule2 = a[separator=":"] >> +complex_inner2
outer_rule1 = "X" >> inner_rule1
outer_rule2 = "Z" >> inner_rule2
start=outer_rule1 | outer_rule2
您忘记指定 Spirit 的版本(Qi 或 X3)(
所以,这里是:
灵气:传承属性
您可以使用 Inherited Attributes or Locals.
将状态注入规则使用第一个的演示:
#include <boost/spirit/include/qi.hpp>
#include <fmt/ranges.h>
namespace qi = boost::spirit::qi;
using Attr = std::vector<int>;
template <typename It>
struct Parser : qi::grammar<It, Attr()> {
Parser() : Parser::base_type(start) {
using namespace qi;
inner = int_ % lit(_r1);
outer1 = 'X' >> inner(':');
outer2 = 'Y' >> inner(';');
start = skip(space)[outer1 | outer2];
}
private:
qi::rule<It, Attr()> start;
qi::rule<It, Attr(), qi::space_type> outer1, outer2;
qi::rule<It, Attr(char), qi::space_type> inner;
};
int main() {
using It = std::string::const_iterator;
Parser<It> p;
for (std::string const& s : {
"",
" Y 7 ",
"X 7:-4:+99 ",
"Y 7 ; 42 ",
})
{
It f = begin(s), l = end(s);
Attr v;
bool ok = parse(f, l, p, v);
fmt::print("Parsed: {} {}, remaining: '{}'\n", ok, v,
std::string(f, l));
}
}
版画
Parsed: false {}, remaining: ''
Parsed: true {7}, remaining: ' '
Parsed: true {7, -4, 99}, remaining: ' '
Parsed: true {7, 42}, remaining: ' '
X3:函数组合
在 X3 中,Qi 的许多限制都消失了,因为它更容易编写规则。你会写出几乎相同但不同的东西:
#include <boost/spirit/home/x3.hpp>
#include <fmt/ranges.h>
namespace x3 = boost::spirit::x3;
using Attr = std::vector<int>;
namespace Parser {
using namespace x3;
auto inner = [](auto delim) { return int_ % delim; };
auto outer1 = 'X' >> inner(':');
auto outer2 = 'Y' >> inner(';');
auto start = skip(space)[outer1 | outer2];
} // namespace Parser
int main() {
using It = std::string::const_iterator;
for (std::string const& s : {
"",
" Y 7 ",
"X 7:-4:+99 ",
"Y 7 ; 42 ",
})
{
It f = begin(s), l = end(s);
Attr v;
bool ok = parse(f, l, Parser::start, v);
fmt::print("Parsed: {} {}, remaining: '{}'\n", ok, v,
std::string(f, l));
}
}
打印相同。
公平地说,这掩盖了一些/发生/对于给定示例不重要的复杂问题。但这就是非伪代码的好处:它有细节。如果您 运行 遇到任何更微妙的问题,我希望您能带着具体的代码回来 :)