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.

将状态注入规则

使用第一个的演示:

Live On Compiler Explorer

#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 的许多限制都消失了,因为它更容易编写规则。你会写出几乎相同但不同的东西:

Live On Compiler Explorer

#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));
    }
}

打印相同。

公平地说,这掩盖了一些/发生/对于给定示例不重要的复杂问题。但这就是非伪代码的好处:它有细节。如果您 运行 遇到任何更微妙的问题,我希望您能带着具体的代码回来 :)