【问题标题】:Recursive rule in Spirit.X3Spirit.X3 中的递归规则
【发布时间】:2017-08-26 19:45:36
【问题描述】:

我想用 Boost.Spirit x3 解析递归语法,但由于模板实例化深度问题而失败。

语法如下:

value: int | float | char | tuple
int: "int: " int_
float: "float: " real_ 
char: "char: " char_
tuple: "tuple: [" value* "]"

这是一个包含的示例:

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <vector>
#include <variant>

struct value: std::variant<int,float,std::vector<value>>
{ 
    using std::variant<int,float,std::vector<value>>::variant;

    value& operator=(float) { return *this; } 
    value& operator=(int) { return *this; } 
    value& operator=(std::vector<value>) { return *this; } 
};

using namespace boost::fusion;
namespace x3 = boost::spirit::x3;

using x3::skip;
using x3::int_;
using x3::real_parser;
using x3::char_;

x3::rule<class value_, value> const value_ = "value";
x3::rule<class o_tuple_, std::vector<value>> o_tuple_ = "tuple";

using float_p = real_parser<float, x3::strict_real_policies<float>>;


const auto o_tuple__def = "tuple: " >> skip(boost::spirit::x3::space)["[" >> value_ % "," >> "]"];
BOOST_SPIRIT_DEFINE(o_tuple_)

const auto value__def
    = ("float: " >> float_p())
    | ("int: " >> int_)
    | o_tuple_
    ;

BOOST_SPIRIT_DEFINE(value_)

int main()
{
  std::string str;
  value val;

  using boost::spirit::x3::parse;
  auto first = str.cbegin(), last = str.cend();
  bool r = parse(first, last, value_, val);
}

如果注释了| o_tuple_ 行(例如,没有递归),则此方法有效。

【问题讨论】:

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


    【解决方案1】:

    这是 X3 中递归的常见问题。还没有解决。

    我想我理解这个问题是因为x3::skip 改变了上下文对象¹。确实,删除它会使东西编译,并成功解析一些琐碎的测试用例:

    "float: 3.14",
    "int: 3.14",
    "tuple: [float: 3.14,int: 3]",
    

    但是,如果没有船长,显然以下内容无法解析:

    // the following _should_ have compiled with the original skip() configuration:
    "tuple: [ float: 3.14,\tint: 3 ]",
    

    现在,我冒昧地说,您可以通过在顶层应用船长来解决这个问题(这意味着实例化“循环”中涉及的所有规则的上下文都是相同的)。如果这样做,您将立即开始在输入中接受更灵活的空格:

    // the following would not have parsed with the original skip() configuration:
    "float:3.14",
    "int:3.14",
    "tuple:[float: 3.14,int: 3]",
    "tuple:[float:3.14,int:3]",
    "tuple: [ float:3.14,\tint:3 ]",
    

    即使编译成功,这些都不会用原始方法解析。

    需要什么

    这是我对代码所做的一些调整。

    1. 删除了无用的赋值运算符value::operator=(我不知道你为什么有它们)

    2. 添加代码以打印任何value 的调试转储:

      friend std::ostream& operator<<(std::ostream& os, base_type const& v) {
          struct {
              std::ostream& operator()(float const& f) const { return _os << "float:" << f; }
              std::ostream& operator()(int const& i)   const { return _os << "int:" << i; }
              std::ostream& operator()(std::vector<value> const& v) const { 
                  _os << "tuple: [";
                  for (auto& el : v) _os << el << ",";
                  return _os << ']';
              }
              std::ostream& _os;
          } vis { os };
      
          return std::visit(vis, v);
      }
      
    3. 去掉skipper,从: interpunction 中拆分出关键字:

      namespace x3 = boost::spirit::x3;
      
      x3::rule<struct value_class, value> const value_ = "value";
      x3::rule<struct o_tuple_class, std::vector<value> > o_tuple_ = "tuple";
      
      x3::real_parser<float, x3::strict_real_policies<float> > float_;
      
      const auto o_tuple__def = "tuple" >> x3::lit(':') >> ("[" >> value_ % "," >> "]");
      
      const auto value__def
          = "float" >> (':' >> float_)
          | "int" >> (':' >> x3::int_)
          | o_tuple_
          ;
      
      BOOST_SPIRIT_DEFINE(value_, o_tuple_)
      
    4. 现在,关键步骤:在顶层添加船长:

      const auto entry_point = x3::skip(x3::space) [ value_ ];
      
    5. 创建好的测试驱动main():

      int main()
      {
          for (std::string const str : {
                  "",
                  "float: 3.14",
                  "int: 3.14",
                  "tuple: [float: 3.14,int: 3]",
                  // the following _should_ have compiled with the original skip() configuration:
                  "tuple: [ float: 3.14,\tint: 3 ]",
                  // the following would not have parsed with the original skip() configuration:
                  "float:3.14",
                  "int:3.14",
                  "tuple:[float: 3.14,int: 3]",
                  "tuple:[float:3.14,int:3]",
                  "tuple: [ float:3.14,\tint:3 ]",
                  // one final show case for good measure
                  R"(
                  tuple: [
                     int  : 4,
                     float: 7e9,
                     tuple: [float: -inf],
      
      
                     int: 42
                  ])"
          }) {
              std::cout << "============ '" << str << "'\n";
      
              //using boost::spirit::x3::parse;
              auto first = str.begin(), last = str.end();
              value val;
      
              if (parse(first, last, parser::entry_point, val))
                  std::cout << "Parsed '" << val << "'\n";
              else
                  std::cout << "Parse failed\n";
      
              if (first != last)
                  std::cout << "Remaining input: '" << std::string(first, last) << "'\n";
          }
      }
      

    现场演示

    Live On Coliru

    //#define BOOST_SPIRIT_X3_DEBUG
    #include <iostream>
    #include <boost/fusion/adapted.hpp>
    #include <boost/spirit/home/x3.hpp>
    #include <string>
    #include <vector>
    #include <variant>
    
    struct value: std::variant<int,float,std::vector<value>>
    { 
        using base_type = std::variant<int,float,std::vector<value>>;
        using base_type::variant;
    
        friend std::ostream& operator<<(std::ostream& os, base_type const& v) {
            struct {
                std::ostream& operator()(float const& f) const { return _os << "float:" << f; }
                std::ostream& operator()(int const& i)   const { return _os << "int:" << i; }
                std::ostream& operator()(std::vector<value> const& v) const { 
                    _os << "tuple: [";
                    for (auto& el : v) _os << el << ",";
                    return _os << ']';
                }
                std::ostream& _os;
            } vis { os };
    
            return std::visit(vis, v);
        }
    };
    
    namespace parser {
        namespace x3 = boost::spirit::x3;
    
        x3::rule<struct value_class, value> const value_ = "value";
        x3::rule<struct o_tuple_class, std::vector<value> > o_tuple_ = "tuple";
    
        x3::real_parser<float, x3::strict_real_policies<float> > float_;
    
        const auto o_tuple__def = "tuple" >> x3::lit(':') >> ("[" >> value_ % "," >> "]");
    
        const auto value__def
            = "float" >> (':' >> float_)
            | "int" >> (':' >> x3::int_)
            | o_tuple_
            ;
    
        BOOST_SPIRIT_DEFINE(value_, o_tuple_)
    
        const auto entry_point = x3::skip(x3::space) [ value_ ];
    }
    
    int main()
    {
        for (std::string const str : {
                "",
                "float: 3.14",
                "int: 3.14",
                "tuple: [float: 3.14,int: 3]",
                // the following _should_ have compiled with the original skip() configuration:
                "tuple: [ float: 3.14,\tint: 3 ]",
                // the following would not have parsed with the original skip() configuration:
                "float:3.14",
                "int:3.14",
                "tuple:[float: 3.14,int: 3]",
                "tuple:[float:3.14,int:3]",
                "tuple: [ float:3.14,\tint:3 ]",
                // one final show case for good measure
                R"(
                tuple: [
                   int  : 4,
                   float: 7e9,
                   tuple: [float: -inf],
    
    
                   int: 42
                ])"
        }) {
            std::cout << "============ '" << str << "'\n";
    
            //using boost::spirit::x3::parse;
            auto first = str.begin(), last = str.end();
            value val;
    
            if (parse(first, last, parser::entry_point, val))
                std::cout << "Parsed '" << val << "'\n";
            else
                std::cout << "Parse failed\n";
    
            if (first != last)
                std::cout << "Remaining input: '" << std::string(first, last) << "'\n";
        }
    }
    

    打印

    ============ ''
    Parse failed
    ============ 'float: 3.14'
    Parsed 'float:3.14'
    ============ 'int: 3.14'
    Parsed 'int:3'
    Remaining input: '.14'
    ============ 'tuple: [float: 3.14,int: 3]'
    Parsed 'tuple: [float:3.14,int:3,]'
    ============ 'tuple: [ float: 3.14, int: 3 ]'
    Parsed 'tuple: [float:3.14,int:3,]'
    ============ 'float:3.14'
    Parsed 'float:3.14'
    ============ 'int:3.14'
    Parsed 'int:3'
    Remaining input: '.14'
    ============ 'tuple:[float: 3.14,int: 3]'
    Parsed 'tuple: [float:3.14,int:3,]'
    ============ 'tuple:[float:3.14,int:3]'
    Parsed 'tuple: [float:3.14,int:3,]'
    ============ 'tuple: [ float:3.14,  int:3 ]'
    Parsed 'tuple: [float:3.14,int:3,]'
    ============ '
                tuple: [
                   int  : 4,
                   float: 7e9,
                   tuple: [float: -inf],
    
    
                   int: 42
                ]'
    Parsed 'tuple: [int:4,float:7e+09,tuple: [float:-inf,],int:42,]'
    

    ¹ 其他指令也是如此,例如 x3::with&lt;&gt;。问题是上下文在每个实例化级别上得到扩展,而不是“修改”以恢复原始上下文类型,并结束实例化周期。

    【讨论】:

    • 哇,感谢您的回答。 operator= 的东西是因为我还尝试将递归更改为更简单的类型,例如 int_ 或 float_ 在我的原始代码中,然后它无法编译,因为它无法分配(但删除 skip 也解决了这个问题)
    猜你喜欢
    • 1970-01-01
    • 2013-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-05
    相关资源
    最近更新 更多