以增强精神来定义用子份子参数的解析器

Define parsers parameterized with sub-parsers in Boost Spirit

本文关键字:参数 增强 定义      更新时间:2023-10-16

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

一些典型格式:

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.

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

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

有可能用精神编写模板类型的sub-parser的模板解析器?

作为另一个答案几乎已经明确了,Qi已经有一种机制可以生成属性类型。

最终用户面对位的位是qi::auto_qi::auto_ Parser ,而不是 grammar

这具有不同的优势 [1]

  • 最重要的是,它允许用户在自己选择的 skipper 的语法内使用解析器,并可能使用qi::locals<>
  • 另外,auto_ Qi表达式终端已经定义,因此根本没有必要使用详细模板参数列表来实例化语法:
  • 最后,解析器返回一个表达模板,因此没有类型的呼吸器进行,并且以这种方式组合几个自动_解析器的效率并不比手动组成语法(而均包含qi::rule<>qi::grammar<> Incur conc_6 Incur性能开销)

让我们看看它的使用方式:

std::vector<std::pair<double, int> > parsed;
bool result_ = qi::phrase_parse(first, last, qi::auto_, qi::space, parsed);

您可以看到,这可容纳船长,并且"神奇地"选择与parsed匹配的解析器。现在,要从OP获取样品格式,您需要挂钩进入auto_ Parser的自定义点

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)   
 )               
]

并将解析的数据打印为:

Parsed:
 0.97 5 
 1.75 10 

请参阅 Live on Coliru

完整代码列表

#include <boost/fusion/adapted.hpp>
#include <boost/spirit/home/qi.hpp>
namespace qi = boost::spirit::qi;
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(lexeme [ lit("PAIR") ] >> '(' >> create_parser<T1>::call() >> ',' >> create_parser<T2>::call() >> ')');
        };
    template<typename TV, typename... TArgs>
        struct create_parser<std::vector<TV, TArgs...> >
        {
            PARSER_DEF(lexeme [ lit("VECTOR") ] >> '[' >> qi::omit[qi::uint_] >> ',' >> '(' >> create_parser<TV>::call() % ',' >> ')' >> ']' );
        };
    #undef PARSER_DEF
} } }
#include <boost/spirit/home/karma.hpp>
namespace karma = boost::spirit::karma;
int main()
{
    std::string const input("VECTOR[ 1 ,n"
                " (               n"
                "     PAIR (0.97, n"
                "           5),   n"
                "     PAIR (1.75,10)   n"
                " )               n"
            "]");
    std::cout << input << "nn";
    auto first = input.begin();
    auto last = input.end();
    std::vector<std::pair<double, int> > parsed;
    bool result_ = qi::phrase_parse(first, last, qi::auto_, qi::space, parsed);
    if (first!=last)
        std::cout << "Remaining unparsed input: '" << std::string(first, last) << "'n";
    if (result_)
        std::cout << "Parsed:n " << karma::format_delimited(karma::auto_ % karma::eol, " ", parsed) << "n";
    else
        std::cout << "Parsing did not succeedn";
}

[1] 潜在的缺点是定制点是固定的,因此您只能关联1 auto_带有任何类型的解析器。滚动自己的基本模板可为您提供更多的控制权,并使您(更多)可以轻松具有不同的"解析器风味"。但是,最终有可能两全其美,所以我首先要方便。

是的,这是可能的。我会以这种方式实施

#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;
}