Boost.Spirit.X3 中的船长

Skippers in Boost.Spirit.X3

我正在尝试使用有点奇怪的语法为该语言编写一个解析器,并偶然发现了船长的问题,这让我觉得我不完全理解它们在 Boost.Spirit.X3 中的工作方式.

问题是对于某些规则,EOL 是有意义的(即我必须匹配行尾以确保语句正确),而对于其他规则则不是(因此可以跳过)。

因此,我决定为我的根规则使用以下船长定义:

namespace x3 = boost::spirit::x3;
namespace ch = x3::standard;

using ch::blank;
using x3::eol;

auto const skipper = comment | blank;

其中 comment 显然会跳过评论。换句话说,我在输入流中保留了 EOL。

现在,对于另一个规则,我想使用这样的定义:

auto const writable_property_declaration_def =
    skip(skipper | eol)
    [
        lit("#")
        > property_type
        > property_id
    ];

规则本身是另一个规则的一部分,实例化如下:

BOOST_SPIRIT_INSTANTIATE(property_declaration_type, iterator_type, context_type);

哪里

using skipper_type = decltype(skipper);

using iterator_type = std::string::const_iterator;
using phrase_context_type = x3::phrase_parse_context<skipper_type>::type;
using error_handler_type = x3::error_handler<iterator_type>;
using context_type = x3::context<x3::error_handler_tag, std::reference_wrapper<error_handler_type>, phrase_context_type>;

这似乎 没有 工作:没有跳过 EOL。

现在,我的问题如下:

期待您的回复! :)

您实际上并没有显示所有声明,因此还不完全清楚设置是如何进行的。那么让我快速模拟一下:

Live On Wandbox

#define BOOST_SPIRIT_X3_DEBUG
#include <iomanip>
#include <boost/spirit/home/x3.hpp>

namespace x3 = boost::spirit::x3;
namespace P {
    using namespace x3;
    static auto const comment = lexeme [ 
            "/*" >> *(char_ - "*/") >> "*/"
          | "//" >> *~char_("\r\n") >> eol
        ];

    static auto const skipper = comment | blank;

    static auto const property_type = lexeme["type"];
    static auto const property_id = lexeme["id"];

    auto const demo =
        skip(skipper | eol) [
            lit("#")
            > property_type
            > property_id
        ];
}

int main() {
    for (std::string const input : {
            "#type id",
            "#type\nid",
        })
    {
        std::cout << "==== " << std::quoted(input) << " ====" << std::endl;
        auto f = begin(input), l = end(input);
        if (parse(f, l, P::demo)) {
            std::cout << "Parsed successfully" << std::endl;
        } else {
            std::cout << "Failed" << std::endl;
        }

        if (f!=l) {
            std::cout << "Remaining input unparsed: " << std::quoted(std::string(f,l)) << std::endl;
        }
    }
}

如您所见,除非涉及规则声明,否则实际上没有问题:

==== "#type id" ====
Parsed successfully
==== "#type
id" ====
Parsed successfully

让我们从这里放大

static auto const demo_def =
    skip(skipper | eol) [
        lit("#")
        > property_type
        > property_id
    ];

static auto const demo = x3::rule<struct demo_> {"demo"} = demo_def;

还可以:Live On Wandbox

<demo>
  <try>#type id</try>
  <success></success>
</demo>
<demo>
  <try>#type\nid</try>
  <success></success>
</demo>
Parsed successfully
==== "#type
id" ====
Parsed successfully

所以,我们知道 x3::rule<> 实际上不是问题所在。这将是关于基于标签类型的静态调度(我认为也就是规则 ID,在这种情况下 struct demo_)。

直截了当:

static auto const demo_def =
    skip(skipper | eol) [
        lit("#")
        > property_type
        > property_id
    ];

static auto const demo = x3::rule<struct demo_> {"demo"};

BOOST_SPIRIT_DEFINE(demo)

还可以:Live On Wandbox

嗯还有什么问题。也许如果有冲突的船长上下文?正在替换

    if (parse(f, l, P::demo)) {

    if (phrase_parse(f, l, P::demo, P::skipper)) {

还可以:Live On Wandbox

所以,也不是。好的,让我们尝试单独的实例化:

单独编译

Live On Wandbox

  • rule.h

    #pragma once
    #define BOOST_SPIRIT_X3_DEBUG
    #include <boost/spirit/home/x3.hpp>
    #include <boost/spirit/home/x3/support/utility/error_reporting.hpp>
    
    namespace x3 = boost::spirit::x3;
    namespace P {
        using namespace x3;
        static auto const comment = lexeme [ 
                "/*" >> *(char_ - "*/") >> "*/"
              | "//" >> *~char_("\r\n") >> eol
            ];
    
        static auto const skipper = comment | blank;
    
        using demo_type = x3::rule<struct demo_>;
        extern demo_type const demo;
    
        BOOST_SPIRIT_DECLARE(demo_type)
    }
    
  • rule.cpp

    #include "rule.h"
    #include <iostream>
    #include <iomanip>
    
    namespace P {
        using namespace x3;
    
        static auto const property_type = lexeme["type"];
        static auto const property_id = lexeme["id"];
    
        static auto const demo_def =
            skip(skipper | eol) [
                lit("#")
                > property_type
                > property_id
            ];
    
        struct demo_ {
            template<typename It, typename Ctx>
                x3::error_handler_result on_error(It f, It l, expectation_failure<It> const& ef, Ctx const&) const {
                    std::string s(f,l);
                    auto pos = std::distance(f, ef.where());
    
                    std::cout << "Expecting " << ef.which() << " at "
                        << "\n\t" << s
                        << "\n\t" << std::setw(pos) << std::setfill('-') << "" << "^\n";
    
                    return error_handler_result::fail;
                }
        };
    
        demo_type const demo {"demo"};
        BOOST_SPIRIT_DEFINE(demo)
    
        // for non-skipper invocation (x3::parse)
        using iterator_type = std::string::const_iterator;
        BOOST_SPIRIT_INSTANTIATE(demo_type, iterator_type, x3::unused_type)
    
        // for skipper invocation (x3::phrase_parse)
        using skipper_type = decltype(skipper);
        using phrase_context_type = x3::phrase_parse_context<skipper_type>::type;
        BOOST_SPIRIT_INSTANTIATE(demo_type, iterator_type, phrase_context_type)
    }
    
  • test.cpp

    #include "rule.h"
    #include <iostream>
    #include <iomanip>
    
    int main() {
        std::cout << std::boolalpha;
        for (std::string const input : {
                "#type id",
                "#type\nid",
            })
        {
            std::cout << "\n==== " << std::quoted(input) << " ====" << std::endl;
    
            {
                auto f = begin(input), l = end(input);
                std::cout << "With top-level skipper: " << phrase_parse(f, l, P::demo, P::skipper) << std::endl;
    
                if (f!=l) {
                    std::cout << "Remaining unparsed: " << std::quoted(std::string(f,l)) << std::endl;
                }
            }
            {
                auto f = begin(input), l = end(input);
                std::cout << "Without top-level skipper: " << parse(f, l, P::demo) << std::endl;
    
                if (f!=l) {
                    std::cout << "Remaining unparsed: " << std::quoted(std::string(f,l)) << std::endl;
                }
            }
        }
    }
    

打印预期的:

==== "#type id" ====
With top-level skipper: <demo>
  <try>#type id</try>
  <success></success>
</demo>
true
Without top-level skipper: <demo>
  <try>#type id</try>
  <success></success>
</demo>
true

==== "#type
id" ====
With top-level skipper: <demo>
  <try>#type\nid</try>
  <success></success>
</demo>
true
Without top-level skipper: <demo>
  <try>#type\nid</try>
  <success></success>
</demo>
true

或者,不启用调试:

==== "#type id" ====
With top-level skipper: true
Without top-level skipper: true

==== "#type
id" ====
With top-level skipper: true
Without top-level skipper: true

最后的想法

遗憾的是,我可能无法重现您描述的症状。但是,我希望上面的一些步骤确实阐明了规则定义的单独链接实际上是如何针对 skipper/contexts.

工作的

如果你的情况其实更复杂,我只能想到另一种情况X3的情况可能和QI的情况不一样。在齐国,一条规则静态地声明了它的船长。在 X3 中,船长严格来自上下文(并且规则可以限制支持的船长数量的唯一方法是通过分离实例化并将定义隐藏在单独的 TU 中)。

这意味着 很容易 意外地继承一个被覆盖的船长。例如,这可能是违反直觉的。嵌套规则。如果您有不同的船长,我建议不要完全依赖继承的船长上下文。