Spirit.X3中的递归规则 [英] Recursive rule in Spirit.X3

查看:63
本文介绍了Spirit.X3中的递归规则的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to parse a recursive grammar with Boost.Spirit x3, but it fails with a template instantiation depth problem.

语法如下:

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

以下是一个包​​含的示例:

Here is a contained example:

#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_进行了注释(例如,没有递归),则此方法有效.

This works if the line | o_tuple_ is commented (eg no recursion).

推荐答案

这是X3中递归的常见问题.尚未解决.

This is a common problem with recursive in X3. It's yet unresolved.

我认为我理解问题是因为x3::skip更改了上下文对象¹.确实,删除该东西可以使其编译并成功解析一些琐碎的测试用例:

I think I understand the issue is because of x3::skip alters the context object¹. Indeed, dropping that makes the thing compile, and successfully parse some trivial test cases:

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

但是,很明显,以下内容在没有船长的情况下不会解析:

However, obviously the following do not parse without the skipper:

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

现在,我敢冒险通过在顶层应用跳过器来摆脱这个问题(这意味着实例化循环"中涉及的所有规则的上下文都是相同的).如果这样做,您将立即开始在输入中接受更灵活的空格:

Now, I venture that you can get rid of the problem by applying the skipper at the top level (which means that the context is identical for all rules involved in the instantiation "cycle"). If you do, you will at once start accepting more flexible whitespace in the input:

// 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 ]",

即使编译成功,这些方法都无法解析原始方法.

None of these would have parsed with the original approach, even if it had compiled successfully.

这是我对代码进行的一些调整.

Here's some of the tweaks I made to the code.

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

  1. removed the impotent assignment operators value::operator= (I don't know why you had them)

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

add code to print a debug dump of any 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);
}

  • 拖放船长并从:插入点中分离出关键字:

  • Drop the skipper and split out keywords from : 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_)
    

  • 现在,关键步骤:在顶层添加船长:

  • Now, the crucial step: add the skipper at toplevel:

    const auto entry_point = x3::skip(x3::space) [ value_ ];
    

  • 创建不错的测试驱动程序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";
        }
    }
    

  • 实时演示

    查看 在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<>.问题在于上下文在每个实例化级别上得到了 extended ,而不是被修改"以恢复原始上下文类型,并结束了实例化周期.


    ¹ other directives do too, like x3::with<>. The problem would be that the context gets extended on each instantiation level, instead of "modified" to get the original context type back, and ending the instantiation cycle.

    这篇关于Spirit.X3中的递归规则的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆