【问题标题】:Skippers in Boost.Spirit.X3Boost.Spirit.X3 中的船长
【发布时间】:2019-12-28 15:20:17
【问题描述】:

我正在尝试为该语言编写一个语法有点奇怪的解析器,并偶然发现了一个与skippers有关的问题,这让我认为我并不完全理解它们在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 显然只是跳过了 cmets。换句话说,我在输入流中保留了 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。

现在,我的问题如下:

  • boost::spirit::x3::phrase_parse_context 和我使用的特定船长之间有什么联系?
  • skip(p)[a] 究竟是如何工作的?
  • 是否可以以某种方式定义底层规则,使其使用另一个船长,以便 X3 自行处理所有 EOL 而我不需要手动执行?

期待您的回复(-ies)! :)

【问题讨论】:

    标签: c++ boost boost-spirit boost-spirit-x3


    【解决方案1】:

    您实际上并没有显示所有声明,因此设置的方式并不完全清楚。所以让我快速模拟一些东西:

    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&lt;&gt; 实际上不是问题所在。这将是关于基于标签类型的静态调度(我认为,也就是规则 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

    • 规则.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)
      }
      
    • 规则.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
    

    最后的想法

    很遗憾,也许我无法重现您描述的症状。但是,我希望上面的一些步骤确实阐明了规则定义的单独链接实际上是如何与船长/上下文相关的。

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

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

    【讨论】:

    • 非常感谢您提供如此详细的分步分析!抱歉,我没有从我这边给你更多的背景信息。实际上,我的情况远比这复杂,我只是不知道什么是重要和相关的,什么不是。我将使用您提供的代码,然后再提出其他问题(如果有)或解决方案。但到现在为止,感觉就像我在一开始就错过了一些重要的事情,而问题肯定在我身边。
    • 所以,我检查了所有代码示例,它们确实按预期工作。然后,我从每个 EOL 的所有变通方法和手动匹配中清理了我的代码库,并使用示例对其进行了相应更新。它有效!对于那些想知道的人来说,已经很难说问题出在哪里,因为我离我最初的实现变体还很远。最有可能的是,我尝试匹配已经从输入流中消耗的 EOL。无论如何,@sehe,再次感谢!
    • 干杯。感谢您让我们知道。这也很有帮助,因为有时人们只需要增强信心,以便下次再看事物:)
    猜你喜欢
    • 2014-09-27
    • 2014-03-01
    • 2013-06-09
    • 1970-01-01
    • 2015-06-01
    • 2014-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多