Парсер, который вы запрашиваете, очень сложен, так как должен решать несколько задач:
- обработать недостающие элементы в конце
- обрабатывать синтаксис "2 *" как замену отсутствующим элементам в конце
- правильно не только анализирует все допустимые входные данные, но также заполняет заданную структуру данных соответствующими значениями
Хитрость в том, чтобы использовать qi::attr
по-разному:
для предоставления значений по умолчанию для отсутствующих элементов:
qi::int_ | qi::attr(180)
т.е. либо соответствует целому числу, либо используйте значение по умолчанию 180
для предоставления всех оставшихся значений для синтаксиса "2 *" (как предложено @vines):
"2*" >> qi::attr(attr2)
т.е. если 2*
совпало, используйте значение по умолчанию attr2 (которое является fusion::vector
).
В целом, я придумала это решение, которое, кажется, хорошо разбирает и возвращает значения по умолчанию (даже если оно выглядит очень сложным):
#include <string>
#include <iostream>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/vector.hpp>
int main()
{
namespace qi = boost::spirit::qi;
namespace fusion = boost::fusion;
// the attribute passed to the parser has to match (in structure) the
// parser, requiring to create nested fusion::vector's
typedef fusion::vector<std::string, int> attribute1_type;
typedef fusion::vector<int, attribute1_type> attribute2_type;
typedef fusion::vector<int, attribute2_type> attribute3_type;
// overall attribute type
typedef fusion::vector<std::string, attribute3_type> attribute_type;
// initialize attributes with default values
attribute1_type attr1("male", 32);
attribute2_type attr2(80, attr1);
attribute3_type attr3(180, attr2);
qi::rule<std::string::iterator, std::string()> quoted_string =
"'" >> *~qi::char_("'") >> "'";
qi::rule<std::string::iterator, attribute_type(), qi::space_type> data =
qi::lit("Person") >> "{"
>> quoted_string
>> -( ("4*" >> qi::attr(attr3))
| (qi::int_ | qi::attr(180))
>> -( ("3*" >> qi::attr(attr2))
| (qi::int_ | qi::attr(80))
>> -( ("2*" >> qi::attr(attr1))
| (quoted_string | qi::attr("male"))
>> -( "1*"
| qi::int_
| qi::attr(32)
)
)
)
)
>> "}";
std::string in1 = "Person\n{ 'Tom' 188 80 'male' 32 }";
attribute_type fullattr1;
if (qi::phrase_parse(in1.begin(), in1.end(), data, qi::space, fullattr1))
std::cout << fullattr1 << std::endl;
std::string in2 = "Person\n{ 'Tom' 188 80 'male' }";
attribute_type fullattr2;
if (qi::phrase_parse(in2.begin(), in2.end(), data, qi::space, fullattr2))
std::cout << fullattr2 << std::endl;
std::string in3 = "Person\n{ 'Tom' 188 3* }";
attribute_type fullattr3;
if (qi::phrase_parse(in3.begin(), in3.end(), data, qi::space, fullattr3))
std::cout << fullattr3 << std::endl;
return 0;
}
Разделение правила на отдельные правила (как предлагает @vines) потребует многократного анализа входных данных, поэтому я использовал эту вложенную структуру последовательностей и альтернатив.