在Boost Spirit中定义使用子解析器参数化的解析器

时间:2014-01-20 15:53:43

标签: c++ templates boost-spirit boost-spirit-qi parser-generator

我想将一些旧的手写解析代码转换为Boost Spirit,并在此过程中学习(更多)精神。旧代码使用流和模板来解析某些数据类型和某些容器的定义。

一些典型的格式:

VECTOR[number_of_items,(item_1, item_2 .... item_n)]
PAIR(p1, p2)
RECT[(left,top)-(right,bottom)]
Point( x, y )
Size( x, y )

解析函数是模板,其中项目类型为模板参数,并使用流作为输入,例如

 template<class T> std::istream& operator>>(std::Stream& in, std::vector<T>& v);

 template<class T1, class T2> std::istream& operator>>(std::istream& in, std::pair<T1, T2>& p);

 template<class T1, class T2> std::istream& operator>>(std::istream& in, RectType<T>& r);
 etc.

向量的解析器(流提取器)调用解析器以获取模板类型。

使用这些可以解析整数矩形,双矩形以及字符串和整数对的向量的定义。

是否可以使用Spirit编写为模板类型调用子解析器的模板化解析器?

2 个答案:

答案 0 :(得分:5)

由于几乎的其他答案已经明确,因此,在给定属性类型的情况下,Qi已经有了一种动态生成解析器的机制。

这里面向最终用户的位是qi::auto_qi::auto_ is a parser,而不是语法

这具有明显的优势 [1]

  • 最重要的是,它允许用户使用自己选择的队长在语法中使用解析器,也可以使用qi::locals<>
  • 此外,auto_ Qi表达式终端已经定义,因此不需要 at all 使用详细模板参数列表来实例化语法:
  • 最后,解析器返回一个表达式模板,因此没有类型擦除,因此以这种方式组合几个auto_ parsers并不比手动编写语法效率低(而是在{{1}中包装}和qi::rule<>会产生性能开销)

让我们看看它是如何使用的:

qi::grammar<>

正如您所看到的,这可以容纳一个队长,并且“神奇地”选择匹配std::vector<std::pair<double, int> > parsed; bool result_ = qi::phrase_parse(first, last, qi::auto_, qi::space, parsed); 的解析器。现在,要从OP中获取样本格式,您需要 hook into the customization point for the auto_ parser

parsed

这就是所有需要的。这是一个解析的演示:

namespace boost { namespace spirit { namespace traits {   
    // be careful copying expression templates. Boost trunk has `qi::copy` for this too, now
    #define PARSER_DEF(a) using type = decltype(boost::proto::deep_copy(a)); static type call() { return boost::proto::deep_copy(a); }

    template<typename T1, typename T2> 
        struct create_parser<std::pair<T1, T2> > 
        {
            PARSER_DEF('(' >> create_parser<T1>::call() >> ',' >> create_parser<T2>::call() >> ')');
        };

    template<typename TV, typename... TArgs>
        struct create_parser<std::vector<TV, TArgs...> >
        {
            PARSER_DEF('[' >> qi::omit[qi::uint_] >> ',' >> '(' >> create_parser<TV>::call() % ',' >> ')' >> ']' );
        };

    #undef PARSER_DEF
} } }

并将解析后的数据打印为:

VECTOR[ 1 ,
 (               
     PAIR (0.97, 
           5),   
     PAIR (1.75,10)   
 )               
]

查看 Live On Coliru

完整代码清单

Parsed:
 0.97 5 
 1.75 10 

[1] 潜在的缺点是定制点是固定的,因此您只能关联1 { {1}}任何类型的解析器。滚动您自己的基本模板可以让您获得更多控制,并使您(更多)轻松拥有不同的“解析器风格”。然而,最终它可以充分利用这两个世界,所以我首先是为了方便。

答案 1 :(得分:2)

是的,这是可能的。我会这样实现

#include <boost/spirit/home/qi.hpp>

namespace qi = boost::spirit::qi;

template < typename _Type, typename _Iterator, typename _Enable = void >
struct parser;

template < typename _Type, typename _Iterator >
struct parser < _Type, _Iterator, typename std::enable_if < std::is_arithmetic<_Type> ::value > ::type > :
    qi::grammar<_Iterator, _Type() >
{
    parser()
        : parser::base_type(impl)
    {
        impl = qi::create_parser<_Type>() ;
    }

    qi::rule<_Iterator, _Type()> impl;
};

template < typename _Iterator >
struct parser < double, _Iterator> :
    qi::grammar<_Iterator, double() >
{
    parser()
        : parser::base_type(impl)
    {
        impl = qi::double_;
    }

    qi::rule<_Iterator, double()> impl;
};

template < typename _First, typename _Second, typename _Iterator >
struct parser < std::pair<_First, _Second>, _Iterator> :
    qi::grammar<_Iterator, std::pair<_First, _Second>() >
{
    parser()
        : parser::base_type(impl)
    {
        impl =  qi::lit('(') >> first >> ',' >> second >> ')';
    }

    qi::rule<_Iterator, std::pair<_First, _Second>()> impl;

    parser<_First, _Iterator> first;
    parser<_Second, _Iterator> second;
};

template < typename _Type, typename _Alloc, typename _Iterator >
struct parser < std::vector<_Type, _Alloc>, _Iterator> :
    qi::grammar<_Iterator, std::vector<_Type, _Alloc>() >
{
    parser()
        : parser::base_type(impl)
    {
        impl = qi::lit('[') >> qi::omit[qi::uint_] >> ",(" >> item % ',' >> ")]";
    }

    qi::rule<_Iterator, std::vector<_Type, _Alloc>()> impl;

    parser<_Type, _Iterator> item;
};

用法示例:

int main(int agrc, char *argv[])
{
    typedef std::pair<double, int> pair;

    using string = std::string;

    string input_ = { "[1,((0.97,5),(1.75,10))]" };

    string::const_iterator iterator_ = input_.begin();
    string::const_iterator end_ = input_.end();

    std::vector<pair> pairs_;

    bool result_ = qi::parse(iterator_, end_, parser <std::vector<pair>, string::const_iterator> (), pairs_);

    return 0;
}
相关问题