Boost Spirit Parser с вектором из трех строк, компилируемых в структуру, адаптация не работает

Я студент, и мне нужно написать синтаксический анализатор на C++ с помощью Boost-Library.

Поэтому я пишу грамматик в QI, потому что мне нужно разобрать структуру. Все идет нормально.

Я дам вам пример кода. Я думаю, что это проще, чем писать всю программу.

Описание: Итак, сначала мы берем txt-файл и читаем его, затем анализатор просматривает его и говорит: "Синтаксический анализ в порядке!" и разобрать в структуру. Наш вывод — это структура в консоли.

Это прекрасно работает, теперь несколько примеров кода. Здесь вы можете увидеть грамматику в Boost Spirit QI:

subject %= lexeme[lit("Fach: ") >> +(char_("a-zA-Z"))   >> lit("\n")]; //works!

        dozent %= lexeme[lit("Dozent: ") >> +(char_("a-zA-Z")) >> lit("\n")];

        date %= lexeme[lit("Datum: ") >> digit >> digit >> lit("-") >> digit >> digit >> lit("-") >> digit >> digit >> digit >> digit >> lit("\n")];

        count %= lexeme[lit("Anzahl: ") >> +digit >> lit("\n")];

        points %= lexeme[+digit >> lit("\t")];

        mark %= lexeme[digit >> lit("\n")];

        matnumber %= lexeme[(digit >> digit >> digit >> punct >> digit >> digit >> digit) >> lit("\t")];

        student %= matnumber >> points >> mark;

        start %=  subject >> dozent >> date >> count >> student;

Это прекрасно работает, правило для студента создает проблему, заключающуюся в том, что у нас есть элемент из трех частей. Matnumber, точки и метка. Чтобы вы могли представить, что я имею в виду, вот TXT-файл, который мы пытаемся разобрать:

Subject: Physics
Dozent: Wayne
Datum: 20-10-2014
Anzahl: 20
729.888 33  5
185.363 35  5

Последние две строки - правило студента. А в txt-файле у нас больше этих двух строк.

Чтобы мы могли принять эти строки как «студент», мы написали вектор в нашей структуре с typedef:

typedef boost::fusion::vector<string, string, string> student_t;

то мы будем использовать его в нашей структуре:

struct klausur
{
    string str_subject;
    string str_dozent;
    string str_date;
    string count;
    string matr_nr;
    string points;
    string mark;
    string ende;
    student_t student;

    void ToString()
    {
        cout << "Struct.Fach: " << str_subject << endl;
        cout << "Struct.Dozent: " << str_dozent << endl;
        cout << "Struct.Datum: " << str_date << endl;
        cout << "Struct.Anzahl: " << count << endl;
        cout << "Struct.Mat_Nr: " << matr_nr << endl;
        cout << "Struct.Punkte: " << points << endl;
        cout << "Struct.Note: " << mark << endl;
        cout << "Struct.Student<0>: " << vec::at_c<0>(student); 
        cout << "Struct.Student<1>: " << vec::at_c<1>(student);
        cout << "Struct.Student<2>: " << vec::at_c<2>(student);

    }
};

Затем у нас есть наш BOOST_ADAPT_STRUCT:

BOOST_FUSION_ADAPT_STRUCT(
client::klausur,
(string, str_subject)
(string, str_dozent)
(string, str_date)
(string, count)
(string, matr_nr)
(string, points)
(string, mark)
(student_t, student)

)

Вы видите, что у нас есть typedef.

А потом у нас есть свои правила в Грамматике.

    qi::rule<Iterator, string(), ascii::space_type> subject;
    qi::rule<Iterator, string(), ascii::space_type> dozent;
    qi::rule<Iterator, string(), ascii::space_type> date;
    qi::rule<Iterator, string(), ascii::space_type> count;
    qi::rule<Iterator, string(), ascii::space_type> matnumber;
    qi::rule<Iterator, string(), ascii::space_type> points;
    qi::rule<Iterator, string(), ascii::space_type> mark;
    qi::rule<Iterator, boost::fusion::vector<boost::fusion::vector<std::string, std::string, std::string> >()> student; 

И есть, надеюсь, последняя проблема для нашего проекта...

Мы не знаем, какой тип данных нужен qi:rule, чтобы BOOST_ADAPT... работал с ним нормально. Все остальные точки являются строками, но я не знаю, как реализовать собственный вектор, который мы создали.

Все остальные правила работают нормально и находятся в структуре позже, только вектор создает проблемы.

У кого-нибудь есть идеи по этому поводу? Я могу загрузить больше файлов и фрагментов кода, если вам нужно, но я все еще думаю, что это может быть просто небольшая проблема, которую я не вижу. Я искал много тем для повышения, но не нашел нужной вещи.

Я должен добавить информацию, что я только начинающий, поэтому, возможно, я не все правильно объяснил и... да. Надеюсь, вы это понимаете. К тому же мой английский не самый лучший...

Спасибо заранее за вашу помощь.

Уильям


person William Wallace    schedule 29.04.2015    source источник
comment
Почему нет шкипера на последнем правиле? Какие ошибки вы получаете? Вы пробовали правило‹итератор, student_t(), пробел›? Вы уверены, что student_t не находится в клиенте пространства имен? У вас возникли проблемы с разбором нескольких студентов? Ваше начальное правило содержит только одного ученика. Попробуйте клини стар.   -  person FRob    schedule 30.04.2015
comment
Эй, был скипер, но для проверки я его удалил. Я всегда получаю ошибку, что он не может разрешить оператор в более глубокой библиотечной функции. Я совершенно уверен, что student_t находится в клиенте пространства имен, это проблема? На данный момент был только один студент для тестирования. Обычно правило было бы .... +student. Заранее спасибо за помощь.   -  person William Wallace    schedule 01.05.2015


Ответы (1)


Spirit — это генератор парсеров. Похоже, вы ничего не анализируете (вы просто «извлекаете» последовательности символов, что больше похоже на токенизацию).

я бы сделал так:

  • использовать правильные типы данных
  • используйте blank для пропуска (не включает eol)
  • поместите ожидание eol в нужное место
  • расставить лексемы в нужном месте
  • сделать date_t собственный тип
  • сделать student_t собственный тип
  • ИСПРАВИТЬ правило использования std::vector<student_t>() вместо ~ fusion::vector<student_t>() (это была ошибка)
  • используйте operator<< для печати
  • используйте repeat(n) [ student >> eol ] для анализа ожидаемого количества строк учеников
  • используйте qi::locals, чтобы фактически передать ожидаемое количество студентов в repeat()

Жить на Coliru

#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <iostream>
#include <iomanip>

namespace qi = boost::spirit::qi;
namespace vec = boost::fusion;

struct student_t {
    std::string matr_nr;
    unsigned    points;
    int         mark;
};

struct date_t {
    unsigned dd, mm, yyyy;

    friend std::ostream& operator<<(std::ostream& os, date_t const& d) {
        std::ostream local(os.rdbuf());
        local << std::setw(2) << std::setfill('0') << d.dd <<
            "-" << std::setw(2) << std::setfill('0') << d.mm <<
            "-" << std::setw(4) << std::setfill('0') << d.yyyy;
        return os;
    }
};

BOOST_FUSION_ADAPT_STRUCT(student_t,
        (std::string,matr_nr)(unsigned,points)(int,mark))
BOOST_FUSION_ADAPT_STRUCT(date_t,
        (unsigned,dd)(unsigned,mm)(unsigned,yyyy))

struct klausur {
    std::string str_subject;
    std::string str_dozent;
    date_t date;

    unsigned count;
    std::vector<student_t>   students;

    friend std::ostream& operator<<(std::ostream& os, klausur const& k)
    {
        os << "Fach: "   << k.str_subject << '\n';
        os << "Dozent: " << k.str_dozent  << '\n';
        os << "Datum: "  << k.date        << '\n';
        os << "Anzahl: " << k.count       << '\n';
        for (auto& s : k.students) {
            os << "Mat_Nr: " << s.matr_nr << '\n';
            os << "Punkte: " << s.points  << '\n';
            os << "Note: "   << s.mark    << '\n';
        }
        return os;
    }
};

BOOST_FUSION_ADAPT_STRUCT(klausur,
        (std::string                     , str_subject)
        (std::string                     , str_dozent)
        (date_t                          , date)
        (unsigned                        , count)
        (std::vector<student_t>          , students)
    )

template <typename Iterator, typename Skipper = qi::ascii::blank_type>
struct grammar : qi::grammar<Iterator, klausur(), Skipper> {
    grammar() : grammar::base_type(start) {
        using namespace qi;
        subject   = "Fach:"   >> lexeme [ +~char_('\n') ] >> eol;
        dozent    = "Dozent:" >> lexeme [ +~char_('\n') ] >> eol;
        date      = "Datum:"  >> lexeme [uint_ >> '-' >> uint_ >> '-' >> uint_] >> eol;
        count     = "Anzahl:" >> uint_ >> eol;
        points    = uint_;
        mark      = int_parser<int, 10, 1, 1>(); // single base-10 digit

        // no clue about this format; what is it? Just a real number?
        matnumber = lexeme[digit >> digit >> digit >> punct >> digit >> digit >> digit];

        student   = matnumber >> points >> mark;

        _a_type expected;
        klausur_ %= subject
                 >> dozent
                 >> date
                 >> count            [ expected = _1 ]
                 >> repeat(expected) [ student >> (eol|eoi) ]
                 ;

        start     = klausur_;

        BOOST_SPIRIT_DEBUG_NODES((start)(klausur_)(student)(matnumber)(mark)(points)(count)(date)(dozent)(subject))
    }

  private:
    qi::rule<Iterator, klausur(), Skipper> start;
    qi::rule<Iterator, klausur(), Skipper, qi::locals<unsigned> > klausur_;

    qi::rule<Iterator, std::string()    , Skipper> subject;
    qi::rule<Iterator, std::string()    , Skipper> dozent;
    qi::rule<Iterator, date_t(),          Skipper> date;
    qi::rule<Iterator, unsigned()       , Skipper> count;
    qi::rule<Iterator, std::string()    , Skipper> matnumber;
    qi::rule<Iterator, unsigned()       , Skipper> points;
    qi::rule<Iterator, int()            , Skipper> mark;
    qi::rule<Iterator, student_t()      , Skipper> student;
};

int main() {
    using It = std::string::const_iterator;
    std::string const input =
R"(Fach: Physics
Dozent: Wayne
Datum: 20-10-2014
Anzahl: 2
729.888 33  5
185.363 35  5)";

    It f = input.begin(), l = input.end();

    grammar<It> g;
    klausur k;
    bool ok = qi::phrase_parse(f, l, g, qi::ascii::blank, k);

    if (ok) {
        std::cout << "Parse success\n";
        std::cout << k;
    } else {
        std::cout << "Parse failed\n";
    }

    if (f!=l) {
        std::cout << "Remaining input: '" << std::string(f,l) << "'\n";
    }
}

Выход:

Parse success
Fach: Physics
Dozent: Wayne
Datum: 20-10-2014
Anzahl: 2
Mat_Nr: 729.888
Punkte: 33
Note: 5
Mat_Nr: 185.363
Punkte: 35
Note: 5

А также 72 строки отладочного вывода:

<start>
  <try>Fach: Physics\nDozent</try>
  <klausur_>
    <try>Fach: Physics\nDozent</try>
    <subject>
      <try>Fach: Physics\nDozent</try>
      <success>Dozent: Wayne\nDatum:</success>
      <attributes>[[P, h, y, s, i, c, s]]</attributes>
    </subject>
    <dozent>
      <try>Dozent: Wayne\nDatum:</try>
      <success>Datum: 20-10-2014\nAn</success>
      <attributes>[[W, a, y, n, e]]</attributes>
    </dozent>
    <date>
      <try>Datum: 20-10-2014\nAn</try>
      <success>Anzahl: 2\n729.888 33</success>
      <attributes>[[20, 10, 2014]]</attributes>
    </date>
    <count>
      <try>Anzahl: 2\n729.888 33</try>
      <success>729.888 33  5\n185.36</success>
      <attributes>[2]</attributes>
    </count>
    <student>
      <try>729.888 33  5\n185.36</try>
      <matnumber>
        <try>729.888 33  5\n185.36</try>
        <success> 33  5\n185.363 35  5</success>
        <attributes>[[7, 2, 9, ., 8, 8, 8]]</attributes>
      </matnumber>
      <points>
        <try> 33  5\n185.363 35  5</try>
        <success>  5\n185.363 35  5</success>
        <attributes>[33]</attributes>
      </points>
      <mark>
        <try>  5\n185.363 35  5</try>
        <success>\n185.363 35  5</success>
        <attributes>[5]</attributes>
      </mark>
      <success>\n185.363 35  5</success>
      <attributes>[[[7, 2, 9, ., 8, 8, 8], 33, 5]]</attributes>
    </student>
    <student>
      <try>185.363 35  5</try>
      <matnumber>
        <try>185.363 35  5</try>
        <success> 35  5</success>
        <attributes>[[1, 8, 5, ., 3, 6, 3]]</attributes>
      </matnumber>
      <points>
        <try> 35  5</try>
        <success>  5</success>
        <attributes>[35]</attributes>
      </points>
      <mark>
        <try>  5</try>
        <success></success>
        <attributes>[5]</attributes>
      </mark>
      <success></success>
      <attributes>[[[1, 8, 5, ., 3, 6, 3], 35, 5]]</attributes>
    </student>
    <success></success>
    <attributes>[[[P, h, y, s, i, c, s], [W, a, y, n, e], [20, 10, 2014], 2, [[[7, 2, 9, ., 8, 8, 8], 33, 5], [[1, 8, 5, ., 3, 6, 3], 35, 5]]]]</attributes><locals>(2)</locals>
  </klausur_>
  <success></success>
  <attributes>[[[P, h, y, s, i, c, s], [W, a, y, n, e], [20, 10, 2014], 2, [[[7, 2, 9, ., 8, 8, 8], 33, 5], [[1, 8, 5, ., 3, 6, 3], 35, 5]]]]</attributes>
</start>
person sehe    schedule 30.04.2015
comment
Эй, спасибо за вашу быструю помощь и за код. Я пытаюсь протестировать его в своей визуальной студии 2012, и он всегда говорит, что я не могу использовать It в качестве директивы использования. Всю остальную информацию я буду тестировать и реализовывать в своей программе. Я не специалист в этом, поэтому думаю, что это займет некоторое время. Спасибо using It = std::string::const_iterator; это та часть, которая создает проблемы - person William Wallace; 01.05.2015
comment
Просто замените его на typedef :) - person sehe; 01.05.2015
comment
Теперь это работает. Спасибо :) typedef std::string::const_iterator It; Единственная оставшаяся ошибка заключается в том, что Visual Studio не позволяет использовать строки более чем в одну строку, я пытаюсь исправить это с помощью \n - person William Wallace; 01.05.2015