带有boost变量递归包装器的字符串解析器 [英] String parser with boost variant recursive wrapper

查看:153
本文介绍了带有boost变量递归包装器的字符串解析器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

下面的代码(改编自spirit qi mini_xml示例)不编译。与具有递归 boost :: variant 的属性的规则 brac 有关的错误。

然而,所有注释掉的版本的 brac 做编译。



我很想知道在这种情况下,简单的字符串解析器是如此特殊:

  #include< boost / config / warning_disable.hpp> 
#include< boost / spirit / include / qi.hpp>
#include< boost / spirit / include / phoenix_core.hpp>
#include< boost / spirit / include / phoenix_operator.hpp>
#include< boost / spirit / include / phoenix_fusion.hpp>
#include< boost / spirit / include / phoenix_stl.hpp>
#include< boost / spirit / include / phoenix_object.hpp>
#include< boost / fusion / include / adapt_struct.hpp>
#include< boost / variant / recursive_variant.hpp>

#include< string>
#include< vector>

命名空间客户端
{
命名空间fu​​sion = boost :: fusion;
namespace phoenix = boost :: phoenix;
namespace qi = boost :: spirit :: qi;
namespace ascii = boost :: spirit :: ascii;

struct ast_node;

typedef boost :: variant<
boost :: recursive_wrapper< ast_node>,
std :: string
>阿斯布兰奇

struct ast_node
{
std :: string text;
std :: vector< ast_branch>儿童;
};
}

BOOST_FUSION_ADAPT_STRUCT(
client :: ast_node,
(std :: string,text)
(std :: vector< client :: ast_branch> ;,children)


命名空间客户端
{
template< typename Iterator>
struct ast_node_grammar
:qi :: grammar< Iterator,ast_branch(),ascii :: space_type>
{
ast_node_grammar()
:ast_node_grammar :: base_type(brac)
{
使用qi :: _ 1;
使用qi :: _ val;
using ascii :: char_;
using ascii :: string;

name%= * char_;

brac%= string(no way);
// brac = string(works)[_ val = _1];
// brac%= string(this)| string(works);
// brac%= name; // works
// brac%= * char_; // works
}
qi :: rule< Iterator,std :: string()>名称;
qi :: rule< Iterator,ast_branch(),ascii :: space_type>布拉克
};
}


int main(int argc,char ** argv)
{
typedef client :: ast_node_grammar< std :: string :: const_iterator> ; ast_node_grammar;
ast_node_grammar gram
client :: ast_branch ast;

std :: string text(dummy);
using boost :: spirit :: ascii :: space;
std :: string :: const_iterator iter = text.begin();
std :: string :: const_iterator end = text.end();
bool r = phrase_parse(iter,end,gram,space,ast);
return r? 0:1;
}

错误讯息的一部分:

  /usr/include/boost/spirit/home/qi/detail/assign_to.hpp:38:17:错误:不匹配'boost :: variant< 
boost :: recursive_wrapper< client :: ast_node>,basic_string< char>
> :: variant(
const __normal_iterator< const char *,basic_string< char>&& const __normal_iterator<
const char *,basic_string< char>&提前感谢。



<解决方案

我建议的问题是属性兼容性。与文档相反,ascii :: string解析器显示为显示迭代器范围而不是字符串

  name = string(no way); 

没有问题,因为ascii :: string公开的属性可以强制转换为规则的属性类型没有任何困难。



但是, brac 规则的属性类型是 ast_branch ,这只是其中一个可能包含的类型的变体。因此, ast_branch 类型有几个构造函数,Spirit不清楚哪一个对于这个特定的转换是足够的。



有几种方法(除了已经显示的方法):




  • 使用 attr_cast

      brac = qi :: attr_cast(string(no way)); 


  • 使用 as_string

      brac = qi :: as_string [string(no way)]; 


  • 使用自订点

      namespace boost {namespace spirit {namespace traits {
    template< typename It>
    struct assign_to_attribute_from_iterators< client :: ast_branch,It>
    {
    static void call(It const& f,It const& l,client :: ast_branch& val)
    {
    val = std :: string(f, ;
    }
    };
    }}}




这些都有相同的效果:让Spirit意识到要使用的属性转换。



这是一个完整的工作示例,显示所有三个:

  // #define BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT 
#include< boost / config / warning_disable.hpp>
#include< boost / spirit / include / qi.hpp>
#include< boost / fusion / adapted.hpp>
#include< boost / spirit / include / phoenix.hpp>
#include< boost / variant / recursive_variant.hpp>

#include< string>
#include< vector>

命名空间客户端
{
命名空间fu​​sion = boost :: fusion;
namespace phoenix = boost :: phoenix;
namespace qi = boost :: spirit :: qi;
namespace ascii = boost :: spirit :: ascii;

struct ast_node;

typedef boost :: variable<
boost :: recursive_wrapper< ast_node>,
std :: string
>阿斯布兰奇

struct ast_node
{
std :: string text;
std :: vector< ast_branch>儿童;
};
}

namespace boost {namespace spirit {namespace traits {
template< typename It>
struct assign_to_attribute_from_iterators< client :: ast_branch,It>
{
static void call(It const& f,It const& l,client :: ast_branch& val)
{
val = std :: string(f, ;
}
};
}}}

BOOST_FUSION_ADAPT_STRUCT(
client :: ast_node,
(std :: string,text)
(std :: vector& :ast_branch> children)


命名空间客户端
{
template< typename Iterator>
struct ast_node_grammar:qi :: grammar< Iterator,ast_branch(),ascii :: space_type>
{
ast_node_grammar()
:ast_node_grammar :: base_type(brac)
{
使用qi :: _ 1;
使用qi :: _ val;
using ascii :: char_;
using ascii :: string;

name%= * char_;

brac = string(works);
brac = string(works)[_ val = _1];
brac%= string(this)| string(works);
brac%= name; // works
brac%= * char_; // works

brac = qi :: as_string [string(no way)];
brac = qi :: attr_cast(string(no way));
}
qi :: rule< Iterator,std :: string()>名称;
qi :: rule< Iterator,ast_branch(),ascii :: space_type>布拉克
};
}


int main(int argc,char ** argv)
{
typedef client :: ast_node_grammar< std :: string :: const_iterator> ; ast_node_grammar;
ast_node_grammar gram
client :: ast_branch ast;

std :: string text(dummy);
using boost :: spirit :: ascii :: space;
std :: string :: const_iterator iter = text.begin();
std :: string :: const_iterator end = text.end();
bool r = phrase_parse(iter,end,gram,space,ast);
return r? 0:1;
}


The code below (adapted from spirit qi mini_xml example) does not compile. There is an error related to the rule brac that has an attribute of an recursive boost::variant.
However, all commented out versions of brac do compile.

I am very curious to know what makes the simple string parser so special in this case:

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/variant/recursive_variant.hpp>

#include <string>
#include <vector>

namespace client
{
   namespace fusion = boost::fusion;
   namespace phoenix = boost::phoenix;
   namespace qi = boost::spirit::qi;
   namespace ascii = boost::spirit::ascii;

   struct ast_node;

   typedef boost::variant<
      boost::recursive_wrapper<ast_node>,
      std::string
   > ast_branch;

   struct ast_node
   {
      std::string text;
      std::vector<ast_branch> children;
   };
}

BOOST_FUSION_ADAPT_STRUCT(
      client::ast_node,
      (std::string, text)
      (std::vector<client::ast_branch>, children)
)

namespace client
{
   template <typename Iterator>
      struct ast_node_grammar
      : qi::grammar<Iterator, ast_branch(), ascii::space_type>
      {
         ast_node_grammar()
            : ast_node_grammar::base_type(brac)
         {
            using qi::_1;
            using qi::_val;
            using ascii::char_;
            using ascii::string;

            name %= *char_;

            brac %= string("no way") ;
//            brac = string("works")[_val = _1] ;
//            brac %= string("this") | string("works");
//            brac %= name ; // works
//            brac %= *char_ ; // works
         }
         qi::rule<Iterator, std::string()> name;
         qi::rule<Iterator, ast_branch(), ascii::space_type> brac;
      };
}


int main(int argc, char **argv)
{
   typedef client::ast_node_grammar<std::string::const_iterator> ast_node_grammar;
   ast_node_grammar gram;
   client::ast_branch ast;

   std::string text("dummy");
   using boost::spirit::ascii::space;
   std::string::const_iterator iter = text.begin();
   std::string::const_iterator end = text.end();
   bool r = phrase_parse(iter, end, gram, space, ast);
   return r ? 0 : 1;
}

Part of the error message:

/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:38:17: error: No match for ‘boost::variant<
        boost::recursive_wrapper<client::ast_node>, basic_string<char> 
>::variant(
        const __normal_iterator<const char *, basic_string<char> > &, const __normal_iterator<
            const char *, basic_string<char> > &)’

Thank's in advance.

解决方案

I'd suggest the problem is Attribute compatibility. Contrary to the documentation, the ascii::string parser appears to expose an iterator range instead of a string.

name = string("no way");

is no issue, because the attribute exposed by ascii::string can be coerced into the rule's attribute type without any difficulty.

However, the brac rule's attribute type is ast_branch, which is only a variant with one of it's possible contained types. Therefore, the ast_branch type has several constructors, and it isn't clear to Spirit which one is adequate for this particular conversion.

There are several ways about this (besides the approaches you already showed):

  • use attr_cast

    brac = qi::attr_cast( string("no way") );
    

  • use as_string

    brac = qi::as_string[ string("no way") ];
    

  • use customization points

    namespace boost { namespace spirit { namespace traits {
        template <typename It>
            struct assign_to_attribute_from_iterators<client::ast_branch, It>
            {
                static void call(It const& f, It const& l, client::ast_branch& val)
                {
                    val = std::string(f, l);
                }
            };
    }}}
    

Each of these have the same effect: make Spirit realize what attribute conversion to use.

Here is a full working sample showing all three:

// #define BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/variant/recursive_variant.hpp>

#include <string>
#include <vector>

namespace client
{
   namespace fusion  = boost::fusion;
   namespace phoenix = boost::phoenix;
   namespace qi      = boost::spirit::qi;
   namespace ascii   = boost::spirit::ascii;

   struct ast_node;

   typedef boost::variant<
      boost::recursive_wrapper<ast_node>,
      std::string
   > ast_branch;

   struct ast_node
   {
      std::string text;
      std::vector<ast_branch> children;
   };
}

namespace boost { namespace spirit { namespace traits {
    template <typename It>
        struct assign_to_attribute_from_iterators<client::ast_branch, It>
        {
            static void call(It const& f, It const& l, client::ast_branch& val)
            {
                val = std::string(f, l);
            }
        };
}}}

BOOST_FUSION_ADAPT_STRUCT(
      client::ast_node,
      (std::string, text)
      (std::vector<client::ast_branch>, children)
)

namespace client
{
    template <typename Iterator>
        struct ast_node_grammar : qi::grammar<Iterator, ast_branch(), ascii::space_type>
    {
        ast_node_grammar()
            : ast_node_grammar::base_type(brac)
        {
            using qi::_1;
            using qi::_val;
            using ascii::char_;
            using ascii::string;

            name %= *char_;

            brac = string("works");
            brac = string("works")[_val = _1] ;
            brac %= string("this") | string("works");
            brac %= name ; // works
            brac %= *char_ ; // works

            brac = qi::as_string[ string("no way") ];
            brac = qi::attr_cast( string("no way") );
        }
        qi::rule<Iterator, std::string()> name;
        qi::rule<Iterator, ast_branch(), ascii::space_type> brac;
    };
}


int main(int argc, char **argv)
{
   typedef client::ast_node_grammar<std::string::const_iterator> ast_node_grammar;
   ast_node_grammar gram;
   client::ast_branch ast;

   std::string text("dummy");
   using boost::spirit::ascii::space;
   std::string::const_iterator iter = text.begin();
   std::string::const_iterator end = text.end();
   bool r = phrase_parse(iter, end, gram, space, ast);
   return r ? 0 : 1;
}

这篇关于带有boost变量递归包装器的字符串解析器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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