автоматическое объединение результатов синтаксического анализа в векторы

Я написал несколько правил для разбора чисел с плавающей запятой на два std :: vector чисел с плавающей запятой, которые, в свою очередь, хранятся в структуре:

Ввод данных:

#
# object name01
#

v  -1.5701 33.8087 0.3592
v  -24.0119 0.0050 21.7439
# a comment

vn 0.0000 0.5346 0.8451
vn 0.8331 0.5531 -0.0000
# another comment

Структура:

struct ObjParseData
{
    ObjParseData() : verts(), norms() {}

    std::vector<float> verts;
    std::vector<float> norms;
};

И соответствующий код синтаксического анализа:

struct objGram : qi::grammar<std::string::const_iterator, ObjParseData(), iso8859::space_type>
    {
        objGram() : objGram::base_type(start)
        {
            vertex  = 'v' >> qi::double_ >> qi::double_ >> qi::double_;
            normal  = "vn" >> qi::double_ >> qi::double_ >> qi::double_;
            comment = '#' >> qi::skip(qi::blank)[ *(qi::print) ];
            vertexList = *(vertex | comment);
            normalList = *(normal | comment);
            start = vertexList >> normalList;
        }

        qi::rule<std::string::const_iterator, ObjParseData(), iso8859::space_type> start;
        qi::rule<std::string::const_iterator, std::vector<float>(), iso8859::space_type> vertexList;
        qi::rule<std::string::const_iterator, std::vector<float>(), iso8859::space_type> normalList;
        qi::rule<std::string::const_iterator, std::vector<float>(), iso8859::space_type> vertex;
        qi::rule<std::string::const_iterator, std::vector<float>(), iso8859::space_type> normal;
        qi::rule<std::string::const_iterator, iso8859::space_type> comment;
    } objGrammar;


    ObjParseData resultData;

    std::string::const_iterator f = data.cbegin();
    bool res = qi::phrase_parse( f, data.cend(), objGrammar, iso8859::space, resultData );

И это работает. Он анализирует все числа с плавающей запятой, которым предшествует "v", в вектор verts структуры, а все числа с плавающей запятой, которым предшествует "vn", - в нормы. Это здорово, но я действительно не знаю, почему это работает.

Теперь, если я правильно понимаю, правило, определенное, как показано ниже, помещает все свои результаты в std :: vector float.

qi::rule<std::string::const_iterator, std::vector<float>(), iso8859::space_type> vertex;

Итак, глядя на код синтаксического анализа, показанный выше, и зная, что такое правило, как вершина, разбирается в std :: vector с плавающей запятой, очевидно, такое правило, как vertexList (показанное выше), объединяет результаты из вершины в один std :: vector с плавающей запятой. ? Увидев такое поведение, можно подумать, что можно просто записать эти два правила (vertex и vertexList) как одно, но, к сожалению, это не работает:

vertex  = *('v' >> qi::double_ >> qi::double_ >> qi::double_) | comment;
normal  = *("vn" >> qi::double_ >> qi::double_ >> qi::double_) | comment;
comment = '#' >> qi::skip(qi::blank)[ *(qi::print) ];
start = vertex >> normal;

Код компилируется, и qi :: фраза_parse возвращает успешный синтаксический анализ, но std :: vector в структуре больше не заполняется .. Что мне здесь не хватает?


person Krienie    schedule 11.07.2013    source источник
comment
Вы утверждаете, что vertex генерирует std :: vector ‹float› (), но comment нарушает это. Другими словами, у вас не может быть в одном правиле альтернативных синтаксических анализаторов, которые генерируют другой вывод.   -  person G. Civardi    schedule 11.07.2013
comment
@ G.Civardi правило комментариев выставляет qi::unused_type (без атрибута), поэтому это не имеет значения :)   -  person sehe    schedule 11.07.2013
comment
@sehe - Да, ты прав! По какой-то причине я подумал, что любой альтернативный синтаксический анализатор будет успешным - он должен быть совместим с выходным атрибутом правила - в данном случае std :: vector ‹float›.   -  person G. Civardi    schedule 11.07.2013


Ответы (1)


Вы не поместили группирующие скобки: раскрытие

    vertexList = *(vertex | comment);
    normalList = *(normal | comment);

устранение подправил приводит к

    vertex     = *(('v'  >> qi::double_ >> qi::double_ >> qi::double_) | comment);
    normal     = *(("vn" >> qi::double_ >> qi::double_ >> qi::double_) | comment);

или, как я бы предпочел:

Полный рабочий образец (пожалуйста сделайте ваши образцы кода SSCCE в следующий раз? https://meta.stackexchange.com/questions/22754/sscce-how-to-provide-examples-for-programming-questions):

#include <iterator>
#include <fstream>
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/spirit/include/phoenix.hpp>

namespace qi    = boost::spirit::qi;
namespace karma = boost::spirit::karma;
namespace phx   = boost::phoenix;

struct ObjParseData
{
    ObjParseData() : verts(), norms() {}

    std::vector<float> verts;
    std::vector<float> norms;
};

BOOST_FUSION_ADAPT_STRUCT(ObjParseData, (std::vector<float>, verts)(std::vector<float>, norms))



template <typename It, typename Skipper = qi::space_type>
    struct parser : qi::grammar<It, ObjParseData(), Skipper>
{
    parser() : parser::base_type(start)
    {
        using namespace qi;


        vertex     = 'v'  >> qi::double_ >> qi::double_ >> qi::double_;
        normal     = "vn" >> qi::double_ >> qi::double_ >> qi::double_;
        comment    = '#' >> qi::skip(qi::blank)[ *(qi::print) ];
#if 0
        vertexList = *(vertex | comment);
        normalList = *(normal | comment);
        start      = vertexList >> normalList;
#else
        vertex     = *(comment | ('v'  >> qi::double_ >> qi::double_ >> qi::double_));
        normal     = *(comment | ("vn" >> qi::double_ >> qi::double_ >> qi::double_));
        start      = vertex >> normal;                                              
#endif

        BOOST_SPIRIT_DEBUG_NODE(start);
    }

  private:
    qi::rule<std::string::const_iterator, ObjParseData(), qi::space_type> start;
    qi::rule<std::string::const_iterator, std::vector<float>(), qi::space_type> vertexList;
    qi::rule<std::string::const_iterator, std::vector<float>(), qi::space_type> normalList;
    qi::rule<std::string::const_iterator, std::vector<float>(), qi::space_type> vertex;
    qi::rule<std::string::const_iterator, std::vector<float>(), qi::space_type> normal;
    qi::rule<std::string::const_iterator, qi::space_type> comment;
};

bool doParse(const std::string& input)
{
    typedef std::string::const_iterator It;
    auto f(begin(input)), l(end(input));

    parser<It, qi::space_type> p;
    ObjParseData data;

    try
    {
        bool ok = qi::phrase_parse(f,l,p,qi::space,data);
        if (ok)   
        {
            std::cout << "parse success\n";
            std::cout << "data: " << karma::format_delimited(
                    "v: " << karma::auto_ << karma::eol <<
                    "n: " << karma::auto_ << karma::eol, ' ', data);
        }
        else      std::cerr << "parse failed: '" << std::string(f,l) << "'\n";

        if (f!=l) std::cerr << "trailing unparsed: '" << std::string(f,l) << "'\n";
        return ok;
    } catch(const qi::expectation_failure<It>& e)
    {
        std::string frag(e.first, e.last);
        std::cerr << e.what() << "'" << frag << "'\n";
    }

    return false;
}

int main()
{
    std::ifstream ifs("input.txt", std::ios::binary);
    ifs.unsetf(std::ios::skipws);
    std::istreambuf_iterator<char> f(ifs), l;

    bool ok = doParse({ f, l });
}

Вывод:

parse success
data: v:  -1.57 33.809 0.359 -24.012 0.005 21.744 
 n:  0.0 0.535 0.845 0.833 0.553 0.0 
person sehe    schedule 11.07.2013
comment
Дерп, ты прав. Я их неуместил. Глупая ошибка. Спасибо за ответ. РЕДАКТИРОВАТЬ: О, и в следующий раз я сделаю свои примеры SSCCE. Я даже не слышал об этом раньше, но я буду делать это с этого момента - person Krienie; 11.07.2013
comment
Это не закон, но, особенно в тегах с меньшим интересом, вам нужно максимально снизить порог входа. Я активно слежу за boost-spirit, но я действительно не могу позволить себе 10 минут, чтобы преобразовать вопрос в компилируемый код во время работы. время. Так... - person sehe; 11.07.2013