我有一个简单的解析器,可以解析整数列表或带引号的字符串。
如果我执行SIMPLE_CASE我将输入设为:
std::string input1 = "{ INT: 42, 24 STR: \"Smith\", \"John\" }";
它正确解析为my_record
,其中包含一个整数列表和一个 std::string 列表。
我想将此代码修改为通用代码,以便它可以以任意顺序获取零个或多个 INT 列表和零个或多个 STR 列表,并my_record
以正确的顺序填充它们。我想要我的第二个更通用的测试用例:
std::string input1 = "{ STR: \"Joe\" INT: 42, 24 STR: \"Smith\", \"John\" }";
解析为:
client::my_record expected1 { { 42, 24 }, {"Joe", "Smith", "John"} };
如果我运行,下面的代码可以正常工作:
/tmp$ g++ -DSIMPLE_CASE -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox
但我不确定在运行此程序时如何让一般情况下工作:
/tmp$ g++ -g -std=c++11 sandbox.cpp -o sandbox && ./sandbox
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
}