使用Boost Spirit中的子解析器定义解析器参数化 [英] Define parsers parameterized with sub-parsers in Boost Spirit

查看:202
本文介绍了使用Boost Spirit中的子解析器定义解析器参数化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I would like to convert some old hand-written parsing code to Boost Spirit and learn (more of) spirit in the process. The old code uses streams and templates to parse definitions for some data-types and some containers.

一些典型的格式:

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


$ b b

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

The parsing functions are templates with the type of the items as template parameter and use streams as input, e.g.

 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.

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

The parser (stream extractor) for vectors calls the parser for the templates type.

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

Using these it is possible to parse definitions of integer rectangles, double rectangles, and vectors of pairs of strings and integers.

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

Is it possible with Spirit to write templated parsers that call sub-parsers for the template type?

推荐答案

另一个回答几乎已经清楚了,Qi已经有了一个机制来生成解析器,给定一个属性类型。

As the other answer almost already made clear, Qi already has a mechanism to generate parsers on the fly, given an attribute type.

用户面对位在这里是 qi :: auto _ qi :: auto _ 解析器 ,而不是语法

The end-user facing bit here is qi::auto_. qi::auto_ is a parser, instead of a grammar.

这有明显的优势 [1]


  • 最重要的是,它允许用户在语法中使用自己选择的 也可以使用 qi :: locals<>

  • 此外, auto _ Qi表达式终端已定义,因此无需 使用详细模板参数列表实例化语法:

  • 最后,解析器返回一个表达式模板,因此没有进行类型擦除,因此以这种方式组合多个auto_解析器不是比手动构成语法效率低(而是在 qi :: rule<> qi :: grammar< code>产生性能开销)

  • Most of all, it allows users to use the parser inside a grammar with a skipper of their own choice as well as perhaps using qi::locals<>.
  • Also, the auto_ Qi expression terminal is already defined, so there is no need at all to instantiate a grammar using a verbose template argument list:
  • Finally, the parser returns an expression template, so there is no type-erasure going on, and combining several auto_ parsers in this way is therefore not less efficient than manually composing the grammar (whereas both wrapping in a qi::rule<> and qi::grammar<> incur performance overhead)

让我们看看它的用法:

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

正如你所看到的,这包含一个队长,以及'magically'选择匹配的解析器 parsed 。现在,要从OP中获取示例格式,您需要 钩入 auto _ 解析器

As you can see, this accomodates a skipper, as well as 'magically' selects the parser that matches parsed. Now, to get your sample format from the OP, you'd need to hook into the customization point for the 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
} } }

这是必要的。这里是一个演示,解析:

That's literally all that's needed. Here's a demo that parses:

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

并将解析的数据打印为:

And prints the parsed data as:

Parsed:
 0.97 5 
 1.75 10 

查看 Live on Coliru

See it 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 << "\n\n";

    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 succeed\n";
}






[1] 潜在的缺点是自定义点是固定的,因此您只能将1 auto _ 解析器与任何类型。滚动你自己的基本模板给你更多的控制,使你(更)容易有不同的解析器风格。但是,到最后可能有两个世界的最好的,所以我会为方便第一。


[1] A potential downside would be that the customization point is fixed, and hence you would only be able to associate 1 auto_ parser with any type. Rolling your own base template gives you more control and enables you to (more) easily have different 'parser flavours'. However, in the end it's possible to have the best of both worlds, so I'd go for convenience first.

这篇关于使用Boost Spirit中的子解析器定义解析器参数化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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