如何解析没有前置标记的异构列表

How to parse a heterogeneous list of lists without a pre-tag?

本文关键字:异构 列表 何解析      更新时间:2023-10-16

我应该如何修改这个boost::spirit代码来解析范围或列表的任意组合?

请注意,这个问题与我之前的问题有些不同。 在这种情况下,我没有像 RANGE:LIST: 这样的特殊预标记来帮助我解析,所以我不确定我们是否需要在这里进行某种展望。

不过,我确实需要将解析分开,因为这样做可以帮助我将结果捕获到不同的数据结构中。

目标 让所有四个测试用例通过

编辑答案,似乎适用于更复杂的表达式

// #define BOOST_SPIRIT_DEBUG
#include <boost/foreach.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/tuple.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
typedef std::vector<boost::tuple<std::string,std::vector<int>>>   MY_TYPE;
template <typename Iterator, typename Skipper>
struct my_grammar :
qi::grammar<Iterator, MY_TYPE(), Skipper>
{
    my_grammar() :
        my_grammar::base_type( entries )
    {
        entries %=
            *(
                +(qi::char_ - '-')
                >> qi::lit( "->" )
                >> ( comma | range )
            )
            ;
        range %= '{' >> qi::int_ >> ':' >> qi::int_ >> ':' >> qi::int_ >> '}';
        comma %= '{' >> (qi::int_ % ',') >> '}';
        BOOST_SPIRIT_DEBUG_NODE( range   );
        BOOST_SPIRIT_DEBUG_NODE( comma   );
        BOOST_SPIRIT_DEBUG_NODE( entries );
    }
    qi::rule<Iterator, std::vector<int>(), Skipper> 
        range, comma;
    qi::rule<Iterator, MY_TYPE(), Skipper> 
        entries;
};
// -----------------------------------------------------------------------------
static void TryParse( const std::string& input, const std::string& label )
{
    MY_TYPE entries;
    auto it(input.cbegin()), end( input.cend() );
    my_grammar<decltype(it), qi::space_type> entry_grammar;
    if (qi::phrase_parse(it, end, entry_grammar, qi::space, entries)
            && it == end)
    {
        std::cerr << label << " SUCCESS" << std::endl;
    }
    else
    {
        std::cerr << label << " FAIL" << std::endl;
    }
}
int
main( int argc, char* argv[] )
{
    std::string range_first = "foo -> {1:9:1}nbar -> {1,2,3,4,5}n";
    std::string comma_first = "foo -> {1,2,3,4,5}nbar -> {1:9:1}n";
    std::string comma_only  = "foo -> {1,2,3,4,5}n";
    std::string range_only  = "foo -> {1:9:1}n";
    TryParse( range_first, "RANGE FIRST" );
    TryParse( comma_first, "COMMA FIRST" );
    TryParse( range_only,  "RANGE ONLY"  );
    TryParse( comma_only,  "COMMA ONLY"  );
}

编辑新输出(仍然无法处理异构列表)

/tmp$ g++ -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox 
COMMA FIRST FAIL
RANGE FIRST FAIL
COMMA ONLY SUCCESS
RANGE ONLY SUCCESS
/tmp$ 

编辑改进的代码(仍然无法处理异构列表)

// #define BOOST_SPIRIT_DEBUG
#include <boost/foreach.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>
namespace qi = boost::spirit::qi;
template <typename Iterator, typename Skipper>
struct my_grammar :
qi::grammar<Iterator, std::vector<int>(), Skipper>
{
    my_grammar() :
        my_grammar::base_type(entries)
    {
        entries %= 
            *('{' >> qi::int_
            >> ( range_tail | comma_tail ))
        ;
        range_tail %= ':' >> qi::int_ >> ':' >> qi::int_ >> '}';
        comma_tail %= *( ',' >> qi::int_ ) >> '}';
        BOOST_SPIRIT_DEBUG_NODE(entries   );
        BOOST_SPIRIT_DEBUG_NODE(comma_tail);
        BOOST_SPIRIT_DEBUG_NODE(range_tail);
    }
    qi::rule<Iterator, std::vector<int>(), Skipper> entries, comma_tail, range_tail;
};
// -----------------------------------------------------------------------------
static void TryParse( const std::string& input, const std::string& label )
{
    std::vector<int> entries;
    auto it(input.cbegin()), end( input.cend() );
    my_grammar<decltype(it), qi::blank_type> entry_grammar;
    if (qi::phrase_parse(it, end, entry_grammar, qi::blank, entries)
            && it == end)
    {
        std::cerr << label << " SUCCESS" << std::endl;
    }
    else
    {
        std::cerr << label << " FAIL" << std::endl;
    }
}
int
main( int argc, char* argv[] )
{
    std::string range_first = "{3:5:7}n{1,2,3,4,5}";
    std::string comma_first = "{1,2,3,4,5}n{3:5:7}";
    std::string comma_only  = "{1,2,3,4,5}";
    std::string range_only  = "{3:5:7}";
    TryParse( comma_first, "COMMA FIRST" );
    TryParse( range_first, "RANGE FIRST" );
    TryParse( comma_only,  "COMMA ONLY"  );
    TryParse( range_only,  "RANGE ONLY"  );
}

原始代码

编译并运行

/tmp$ g++ -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox 
COMMA FIRST FAIL
RANGE FIRST FAIL
COMMA ONLY SUCCESS
RANGE ONLY FAIL
/tmp$ 

沙盒.cpp

// #define BOOST_SPIRIT_DEBUG
#include <boost/foreach.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iostream>

namespace qi = boost::spirit::qi;
typedef int Entry;
template <typename Iterator, typename Skipper>
struct my_grammar :
qi::grammar<Iterator, std::vector<Entry>(), Skipper>
{
    my_grammar() :
        my_grammar::base_type(entries)
    {
        entries %= 
            *( 
                '{' >>
                    ( comma_list | range_list )
                >> '}'
            )
        ;
        comma_list %= qi::int_ % ',';
        range_list %= qi::int_ >> ':' >> qi::int_ >> ':' >> qi::int_;
        BOOST_SPIRIT_DEBUG_NODE(entries);
        BOOST_SPIRIT_DEBUG_NODE(comma_list);
        BOOST_SPIRIT_DEBUG_NODE(range_list);
    }
    qi::rule<Iterator, std::vector<Entry>(), Skipper> entries, comma_list, range_list;
};
// -----------------------------------------------------------------------------
static void TryParse( const std::string& input, const std::string& label )
{
    std::vector<Entry> entries;
    auto it(input.cbegin()), end( input.cend() );
    my_grammar<decltype(it), qi::blank_type> entry_grammar;
    if (qi::phrase_parse(it, end, entry_grammar, qi::blank, entries)
            && it == end)
    {
        std::cerr << label << " SUCCESS" << std::endl;
    }
    else
    {
        std::cerr << label << " FAIL" << std::endl;
    }
}
int
main( int argc, char* argv[] )
{
    std::string range_first = "{3:5:7}n{1,2,3,4,5}";
    std::string comma_first = "{1,2,3,4,5}n{3:5:7}";
    std::string comma_only  = "{1,2,3,4,5}";
    std::string range_only  = "{3:5:7}";
    TryParse( range_first, "COMMA FIRST" );
    TryParse( comma_first, "RANGE FIRST" );
    TryParse( comma_only,  "COMMA ONLY"  );
    TryParse( range_only,  "RANGE ONLY"  );
}

使用通用代码中的前导数字。 在该数字之后有一个范围尾部或一个列表尾部。 列表尾部以 开头,范围尾部以 : 开头。

可以将 } 折叠到尾部,因此列表尾部是 (,number

)*},范围尾部是 :number:number},这使得空列表更易于解析。