【问题标题】:Best practice for boost spirit context-dependent grammar rule提升精神上下文相关语法规则的最佳实践
【发布时间】:2021-04-06 05:58:41
【问题描述】:

只是一个示例来澄清问题..(这是伪代码)

经典方式:为每条路径制定规则。所以从“开始”开始,选择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

【问题讨论】:

    标签: c++ boost-spirit


    【解决方案1】:

    您忘记指定 Spirit 的哪个版本(Qi 或 X3)(again?)。

    所以,这里是:

    灵气:继承属性

    您可以使用Inherited AttributesLocals 将状态注入到规则中。

    使用第一个演示:

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

    打印相同。

    公平地说,这掩盖了许多/发生/对给定样本不重要的复杂问题。但这就是非伪代码的好处:它有细节。如果你在路上遇到任何更微妙的问题,我希望你能用具体的代码回来:)

    【讨论】:

    • 很抱歉在这里使用伪代码..感谢您发布完整代码。我使用您的示例,似乎该属性自动继承到所有子规则。这个假设正确吗? inner2 = int_ % lit(_r1);inner1 = inner2;outer1 = 'X' &gt;&gt; inner1(':');outer2 = 'Y' &gt;&gt; inner1(';');start = skip(space)[outer1 | outer2];
    • 我不明白你的意思。我在我的代码或您的评论中没有看到 inner[12] 的子规则? (此外,不存在自动继承,您总是必须明确地传递它们;qi::locals 在这方面是不同的。)
    • 在上面的代码中(注释)我使用 inner1=inner2 但只使用附加属性调用 inner1 .. 无论如何似乎 inner2 也获得了属性
    • 啊。错过了。那是因为您不小心使用了复制构造函数而不是规则初始化。您应该使用inner1 = inner2(_r1);(或inner1= inner2.alias();,如果您没有继承属性)。这是“EDSL 是一种泄漏抽象”的陷阱之一。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-26
    • 1970-01-01
    • 2013-07-03
    相关资源
    最近更新 更多