如何推广精神解析器采取列表以任意顺序? [英] How to generalize a spirit parser to take lists in arbitrary order?

查看:107
本文介绍了如何推广精神解析器采取列表以任意顺序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的解析器可以解析整数列表或引用字符串。

如果我在 SIMPLE_CASE ,我拿的输入是:

 的std ::字符串输入1 ={INT:42,24 STR:\\史密斯\\,\\约翰\\};

它正确地解析为 my_record ,其中包含整数列表和std列表::字符串。

欲修改此code键是通用的,因此,它可以取零或更多的INT列表和以任意顺序零个或更多个STR列表和他们塞进 my_record 在正确的顺序。我想我的第二,更通用的测试案例:

 的std ::字符串输入1 ={STR:\\乔\\INT:42,24 STR:\\史密斯\\,\\约翰\\};

为解析:

 客户端:: my_record expected1 {{42,24},{乔,史密斯,约翰}};

如果我运行code以下正常工作:

  / tmp目录$ G ++ -DSIMPLE_CASE -g -std = C ++ 11 sandbox.cpp -o沙箱和功放;&安培; ./sandbox

但我不知道怎么去一般的情况下运行时,此工作:

  / tmp目录$ g ++的-g -std = C ++ 11 sandbox.cpp -o沙箱和功放;&安培; ./sandbox

code为sandbox.cpp

 的#include<升压/配置/ warning_disable.hpp>
#包括LT&;升压/精神/有/ qi.hpp>
#包括LT&;升压/精神/有/ phoenix_core.hpp>
#包括LT&;升压/精神/有/ phoenix_operator.hpp>
#包括LT&;升压/精神/有/ phoenix_object.hpp>
#包括LT&;升压/融合/有/ adapt_struct.hpp>
#包括LT&;升压/融合/有/ io.hpp>#包括LT&;串GT;
#包括LT&;络合物GT;
#包括LT&;&算法GT;命名空间客户端
{
    命名空间补气=的boost ::精神::补气;
    命名空间ASCII =的boost ::精神:: ASCII;    结构my_record
    {
        的std ::矢量<&INT GT; m_ints;
        的std ::矢量<标准::字符串> m_strs;        布尔运算符==(const的my_record&安培;其他)常量
        {
            返回的std ::等于(m_ints.begin(),m_ints.end(),other.m_ints.begin())
                &功放;&安培;性病::等于(m_strs.begin(),m_strs.end(),other.m_strs.begin());
        }
        布尔运算符=(常量my_record&安培;其他)!常量
        {
            回报! ==操作符(其他);
        }
        朋友的std :: ostream的&放大器;运营商的LT;≤(的std :: ostream的和放大器; OS,常量my_record&安培; REC);
    };    的std :: ostream的&放大器;运营商的LT;≤(的std :: ostream的和放大器; OS,常量my_record&安培; REC)
    {
        对于(const的汽车和放大器; X:rec.m_ints)
            的std :: CERR<<点¯x所述&;&下; '';
        的std :: CERR<<的std :: ENDL;        对于(const的汽车和放大器; X:rec.m_strs)
            的std :: CERR<<点¯x所述&;&下; '';
        的std :: CERR<<的std :: ENDL;    }
}BOOST_FUSION_ADAPT_STRUCT(
    客户端:: my_record,
        (性病::矢量< INT>中m_ints)
        (性病::矢量<标准::字符串>中m_strs)
)命名空间客户端
{
    模板< typename的迭代器>
    结构employee_parser:补气::语法<迭代器,my_record(),ASCII ::空间类型>
    {
        employee_parser():employee_parser :: base_type(开始)
    {
        使用气:: int_;
        用气点燃::;
        使用气:: double_;
        使用气::词位;
        使用ASCII :: char_;        quoted_string%=语义[''>> +(char_ - ')GT;> ''];#IFDEF SIMPLE_CASE
        启动(%)=
            {
            >> int_list
            >>为str_list
            >> }
            ;
#其他
        //不知道如何处理这
        启动(%)=
            {
            >> *(int_list)//要零或更多的这些,以任何顺序
            >> *(为str_list)//要零或更多的这些,以任何顺序
            >> }
            ;
#万一        为str_list%=
                点亮(STR)>> quoted_string%','
                ;        int_list%=
                点亮(INT)>> int_%','
                ;
    }    齐::规则<迭代器,标准::字符串(),ASCII ::空间类型> quoted_string;
    齐::规则<迭代器,的std ::矢量<标准::字符串>(),ASCII ::空间类型>为str_list;
    齐::规则<迭代器,的std ::矢量<&INT GT;(),ASCII ::空间类型> int_list;    齐::规则<迭代器,my_record(),ASCII ::空间类型>开始;
    };
}静态INT
的TryParse(常量标准::字符串&放大器;输入,常量客户:: my_record&安培;预期)
{
    使用boost ::精神:: ASCII ::空间;
    客户端:: my_record REC;
    汽车ITER = input.begin(),结束= input.end();
    客户端:: employee_parser< decltype(ITER)GT; G;
    phrase_parse(ITER,最终,G,空间,REC);
    如果(ITER!=结束)
    {
        的std :: CERR<< 未能完全解析<<的std :: ENDL;
        返回-1;
    }否则如果(REC!=预期){
        的std :: CERR<< 意想不到的结果在解析<<的std :: ENDL;
        的std :: CERR<< REC;
        返回-1;
    }
    返回0;
}INT
主(INT ARGC,CHAR *的argv [])
{
#IFDEF SIMPLE_CASE
    客户端:: my_record expected1 {{42,24},{史密斯,约翰}},EMP;
    标准::字符串输入1 ={INT:42,24 STR:\\史密斯\\,\\约翰\\};
    返回的TryParse(输入1,expected1);
#其他
    客户端:: my_record expected1 {{42,24},{乔,史密斯,约翰}},EMP;
    标准::字符串输入1 ={STR:\\乔\\INT:42,24 STR:\\史密斯\\,\\约翰\\};
    返回的TryParse(输入1,expected1);
#万一}


解决方案

您的语法是错误的,

 启动(%)=
        {
        >> *(int_list)//要零或更多的这些,以任何顺序
        >> *(为str_list)//要零或更多的这些,以任何顺序
        >> }
        ;

这意味着接受任何数目 INT 第后跟任意数量的字符串的。你不能有 INT 字符串 INT 或任何其他组合。

您需要像

 启动(%)=
        {
         >> *(int_list //要零或更多的这些,以任何顺序
             |为str_list //要零或更多的这些,以任何顺序
             )
        >>
        }
        ;

但显然你需要的梅成你的数据结构,bewarned您可能需要使用语义动作。

另:

而我在这里,我不能让这个幻灯片:

 的std :: ostream的&放大器;运营商的LT;≤(的std :: ostream的和放大器; OS,常量my_record&安培; REC)
    {
        对于(const的汽车和放大器; X:rec.m_ints)
            的std :: CERR<<点¯x所述&;&下; '';
        的std :: CERR<<的std :: ENDL;        对于(const的汽车和放大器; X:rec.m_strs)
            的std :: CERR<<点¯x所述&;&下; '';
        的std :: CERR<<的std :: ENDL;    }

应straeming到操作系统这样的:

 的(常量汽车和放大器; X:rec.m_ints)
            OS<<点¯x所述&;&下; '';
        OS<<的'\\ n';

也尽量避免在流插入运算符 ENDL ING,使用 \\ n 如果你需要一个新行。

解决方案:

什么需要到底是使用凤功能的push_back和粘合剂。

 模板< typename的迭代器>
结构my_grammar
:补气::语法<迭代器,my_record(),ASCII ::空间类型> {    my_grammar()
    :my_grammar :: base_type(开始){        quoted_string%=补气::语义[''>> +(气:: char_ - )GT;> ''];        开始=齐亮::({)
                >>
                *(INT:>>气虚:: int_
                    [
                        PHX ::的push_back(
                            PHX :: at_c&℃,GT(
                                齐:: _ VAL
                            )
                            齐:: _ 1
                        )
                    ]%,
                 | STR:>> quoted_string
                     [
                        PHX ::的push_back(
                            PHX ::绑定(
                                &安培; my_record :: m_strs,
                                齐:: _ VAL
                            )
                            齐:: _ 1
                        )
                    ]%,
                 )
                >>
                }
                 ;
    }
    齐::规则<迭代器,标准::字符串(),ASCII ::空间类型> quoted_string;
    齐::规则<迭代器,my_record(),ASCII ::空间类型>开始;
};

整个code房源可以在这里看到:

http://ideone.com/XW18Z2

I have a simple parser which can parse lists of ints or quoted strings.

If I do the SIMPLE_CASE where I take the input to be:

std::string input1 = "{ INT: 42, 24 STR: \"Smith\", \"John\" }";

it parses correctly into my_record, which contains a list of ints and a list of std::string.

I want to modify this code to be generic so that it can take zero or more INT lists and zero or more STR lists in arbitrary order and stuff them into my_record in the proper order. I would like my second, more generic test case:

std::string input1 = "{ STR: \"Joe\" INT: 42, 24 STR: \"Smith\", \"John\" }";

to parse as:

client::my_record expected1 { { 42, 24 }, {"Joe", "Smith", "John"} }; 

The code below works fine if I run:

/tmp$ g++ -DSIMPLE_CASE -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox 

but I'm not sure how to get the general case to work when running this:

/tmp$ g++ -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox 

Code for sandbox.cpp

#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_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>

#include <string>
#include <complex>
#include <algorithm>

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

    struct my_record
    {
        std::vector<int>          m_ints;
        std::vector<std::string>  m_strs;

        bool operator==( const my_record& other ) const
        {
            return std::equal( m_ints.begin(), m_ints.end(), other.m_ints.begin() )
                && std::equal( m_strs.begin(), m_strs.end(), other.m_strs.begin() );
        }
        bool operator!=( const my_record& other ) const
        {
            return ! operator==( other );
        }
        friend std::ostream& operator<<( std::ostream& os, const my_record& rec );
    };

    std::ostream& operator<<( std::ostream& os, const my_record& rec )
    {
        for( const auto& x : rec.m_ints )
            std::cerr << x << ' ';
        std::cerr << std::endl;

        for( const auto& x : rec.m_strs )
            std::cerr << x << ' ';
        std::cerr << std::endl;

    }
}

BOOST_FUSION_ADAPT_STRUCT(
    client::my_record,
        (std::vector<int>,          m_ints)
        (std::vector<std::string>,  m_strs)
)

namespace client
{
    template <typename Iterator>
    struct employee_parser : qi::grammar<Iterator, my_record(), ascii::space_type>
    {
        employee_parser() : employee_parser::base_type(start)
    {
        using qi::int_;
        using qi::lit;
        using qi::double_;
        using qi::lexeme;
        using ascii::char_;

        quoted_string %= lexeme['"' >> +(char_ - '"') >> '"'];

#ifdef SIMPLE_CASE
        start %=
            '{'
            >>  int_list
            >>  str_list
            >>  '}'
            ;
#else
        // not sure how to approach this
        start %=
            '{'
            >>  *(int_list)  // want zero or more of these, in any order
            >>  *(str_list)  // want zero or more of these, in any order
            >>  '}'
            ;
#endif

        str_list %=
                lit( "STR:" ) >> quoted_string % ','    
                ;

        int_list %=
                lit( "INT:" ) >> int_ % ','
                ;
    }

    qi::rule<Iterator, std::string(), ascii::space_type>               quoted_string;
    qi::rule<Iterator, std::vector<std::string>(), ascii::space_type>  str_list;
    qi::rule<Iterator, std::vector<int>(),         ascii::space_type>  int_list;

    qi::rule<Iterator, my_record(), ascii::space_type>                 start;
    };
}

static int 
TryParse( const std::string& input, const client::my_record& expected )
{
    using boost::spirit::ascii::space;
    client::my_record                        rec;
    auto                                     iter = input.begin(), end = input.end();
    client::employee_parser<decltype(iter)>  g;
    phrase_parse( iter, end, g, space, rec );
    if ( iter!=end )
    {
        std::cerr << "failed to parse completely" << std::endl;
        return -1;
    } else if ( rec!=expected ) {
        std::cerr << "unexpected result in parse" << std::endl;
        std::cerr << rec;
        return -1;
    }
    return 0;
}

int 
main(int argc, char* argv[])
{
#ifdef SIMPLE_CASE
    client::my_record  expected1 { { 42, 24 }, {"Smith", "John"} }, emp;
    std::string        input1 = "{ INT: 42, 24 STR: \"Smith\", \"John\" }";
    return TryParse( input1, expected1 );
#else
    client::my_record  expected1 { { 42, 24 }, {"Joe", "Smith", "John"} }, emp;
    std::string        input1 = "{ STR: \"Joe\" INT: 42, 24 STR: \"Smith\", \"John\" }";
    return TryParse( input1, expected1 );
#endif

}

解决方案

You grammar is wrong,

    start %=
        '{'
        >>  *(int_list)  // want zero or more of these, in any order
        >>  *(str_list)  // want zero or more of these, in any order
        >>  '}'
        ;

This means accept any number of ints followed by any number of string. You can no have int, string, int, or any other combination.

You need something like

    start %=
        '{'
         >> *( int_list  // want zero or more of these, in any order
             | str_list  // want zero or more of these, in any order
             )
        >>  
        '}'
        ;

But obviously you need to plum that into you data structure, bewarned you may have to use semantic actions.

ALSO:

whilst I am here, I can't let this slide:

    std::ostream& operator<<( std::ostream& os, const my_record& rec )
    {
        for( const auto& x : rec.m_ints )
            std::cerr << x << ' ';
        std::cerr << std::endl;

        for( const auto& x : rec.m_strs )
            std::cerr << x << ' ';
        std::cerr << std::endl;

    }

should be straeming to os like:

        for( const auto& x : rec.m_ints )
            os << x << ' ';
        os << '\n';

Also try and avoid endling in stream insertion operator, use \n if you need a new line.

THE SOLUTION:

What was need in the end was to use phoenix functions, push_back and a binder.

template<typename Iterator>
struct my_grammar 
: qi::grammar<Iterator, my_record(), ascii::space_type> {

    my_grammar() 
    : my_grammar::base_type(start) {

        quoted_string %= qi::lexeme['"' >> +(qi::char_ - '"') >> '"'];

        start = qi::lit("{")
                >>
                *( "INT:" >> qi::int_     
                    [ 
                        phx::push_back(
                            phx::at_c<0>(
                                qi::_val
                            ), 
                            qi::_1
                        ) 
                    ] % ","
                 | "STR:" >> quoted_string
                     [ 
                        phx::push_back(
                            phx::bind(
                                &my_record::m_strs,
                                qi::_val
                            ), 
                            qi::_1
                        ) 
                    ] % ","
                 )
                >> 
                "}"
                 ;
    }
    qi::rule<Iterator, std::string(), ascii::space_type> quoted_string;
    qi::rule<Iterator, my_record(),   ascii::space_type>   start;
};

The whole code listing can be seen here:

http://ideone.com/XW18Z2

这篇关于如何推广精神解析器采取列表以任意顺序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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