Реализация рекурсивных грамматик с Boost.Qi - PullRequest
3 голосов
/ 15 июня 2019

Я использую Boost.Spirit Qi для построения довольно сложной структуры из некоторых текстовых данных. Структура данных может быть рекурсивно определена, поэтому мне нужны две мои грамматики для ссылки друг на друга, и именно здесь возникают проблемы.

Например, у меня есть грамматика:

element = line | text | circle | box | composite_element
composite_element = 'C', int, int, '[', +element, ']'

Очевидно, мне нужно что-то подобное:

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_fusion.hpp>
#include <boost/spirit/include/phoenix_stl.hpp>
#include <tuple>
#include <boost/fusion/adapted/std_pair.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/spirit/include/qi_eol.hpp>
#include <boost/phoenix.hpp>
#include <vector>
#include <string>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace phoenix = boost::phoenix;

struct line {
    int x1;
    int y1;
    int x2;
    int y2;
    int color;
    int width;
    int capstyle;
    int dashstyle;
    int dashlength;
    int dashspace;
};

struct box {
    int x;
    int y;
    int width;
    int height;
    int color;
    int line_width;
    int capstyle;
    int dashstyle;
    int dashlength;
    int dashspace;
    int filltype;
    int fillwidth;
    int angle1;
    int pitch1;
    int angle2;
    int pitch2;
};

struct circle {
    int x;
    int y;
    int radius;
    int color;
    int line_width;
    int capstyle;
    int dashstyle;
    int dashlength;
};

struct text {
    int x;
    int y;
    int color;
    int size;
    int visibility;
    int show_name_value;
    int angle;
    int alignment;
    int num_lines;
    std::vector<std::string> lines;
};

struct composite_component;
using element_t = boost::variant<line, box, circle, text, boost::recursive_wrapper<composite_component>>;

struct composite_component {
    int x;
    int y;
    std::string basename;
    // only used if component is embedded
    // i. e. stores its definition within the schematic file
    std::vector<element_t> elements;
};

struct element {
    // some other fields
    // ...
    element_t element;
};

struct document {
    std::vector<element> elements;
};

BOOST_FUSION_ADAPT_STRUCT(line, x1, y1, x2, y2, color, width, capstyle, dashstyle, dashlength, dashspace)
BOOST_FUSION_ADAPT_STRUCT(box, x, y, width, height, color, line_width, capstyle, dashstyle, dashlength, dashspace, filltype, fillwidth, angle1, pitch1, angle2, pitch2)
BOOST_FUSION_ADAPT_STRUCT(circle, x, y, radius, color, line_width, capstyle, dashstyle, dashlength)
BOOST_FUSION_ADAPT_STRUCT(text, x, y, color, size, visibility, show_name_value, angle, alignment, num_lines, lines)
BOOST_FUSION_ADAPT_STRUCT(composite_component, x, y, basename, elements)
BOOST_FUSION_ADAPT_STRUCT(element, element)
BOOST_FUSION_ADAPT_STRUCT(document, elements)

template <typename Iterator, typename Attribute>
using rule = qi::rule<Iterator, Attribute, qi::blank_type>;

template <typename Iterator>
class composite_element_parser;

template <typename Iterator>
class element_parser : public qi::grammar<Iterator, element(), qi::blank_type> {
public:
    element_parser(): element_parser::base_type{start_rule_}
    {
        using qi::int_;
        using qi::repeat;
        using phoenix::val;
        using phoenix::construct;

        /* other definitions except of the 'line' is omitted in sake of simplicity */
        line_ = 'L' >> int_ >> int_ >> int_ >> int_ >> int_ >>
                    int_ >> int_ >> int_ >> int_ >> int_ >> qi::eol;
        // box = ...
        // circle = ...
        // text = ...
        start_rule_ = (line_ /* || embedded_component_ */) >> qi::eoi;
    }
private:
    rule<Iterator, element()> start_rule_;
    rule<Iterator, line()> line_;
    // here comes the problem - CIRCULAR REFERENCE to incompletely defined template
    // composite_element_parser<Iterator> embedded_component_;
};

template <typename Iterator>
class composite_element_parser : public qi::grammar<Iterator, composite_component(), qi::blank_type> {
    public:
    composite_element_parser() : composite_element_parser::base_type{start_rule_}
    {
        using phoenix::at_c;
        using qi::int_;
        using phoenix::push_back;

        start_rule_ = "C" >> int_ >> int_ >>  qi::lexeme[(qi::char_)[at_c<2>(qi::_val) += qi::_1]]
                                                        >> -(
                                                           "[" >>
                                                           *(element_) [push_back(at_c<3>(qi::_val), qi::_1)] >>
                                                           "]"
                                                           );
    }
    private:
    rule<Iterator, composite_component()> start_rule_;
    element_parser<Iterator> element_;
};

template <typename Iterator>
class document_parser : public qi::grammar<Iterator, document(), qi::blank_type> {
public:
    document_parser() : document_parser::base_type{start_rule_}
    {

        using phoenix::at_c;
        using phoenix::push_back;
        using qi::_val;
        using qi::_0;
        using qi::_1;

        start_rule_ = +(element_)[push_back(at_c<0>(_val), _1)] >> qi::eoi;
    }
    private:
    rule<Iterator, document()> start_rule_;
    element_parser<Iterator> element_;
};

int main(int , char **) {
    document_parser<std::string::const_iterator> parser;
    document doc;
    const std::string text = "v 20180904 2\n"
                             "L 1 2 3 4 5 6 7 8 9 10\n"
                             "C 10 10 FOO\n"
                             "[ "
                             "L 1 2 3 4 5 6 7 8 9 10\n"
                             "]\n";
    bool r = qi::phrase_parse(text.cbegin(), text.cend(), parser, qi::blank, doc);
    std::cout << (r ? "OK" : "FAIL") << std::endl;

     return 0;
}

Определения правил для 'text', 'circle' и 'box' опущены. Обратите внимание на комментарий в приватном разделе определения element_parser - компилятор не сможет создать экземпляр неполного шаблона класса composite_element_parser<Iterator>. Что мне с этим делать? Очевидно, что я не могу иметь element_parser и composite_element_parser в качестве членов грамматики верхнего уровня (в моем случае document_parser) и передавать им ссылки / указатели друг другу в списке инициализатора конструкторов, потому что они неинициализированы в момент.

ОБНОВЛЕНИЕ: этот поток может быть распознан как дубликат Глубоко-рекурсивные грамматики ци (синтаксические анализаторы) с синтезированными и унаследованными атрибутами , но я действительно не могу понять утвержденный ответить.

Ответы [ 3 ]

3 голосов
/ 15 июня 2019

Я думаю, что в вашем примере нет необходимости использовать экземпляры грамматики.

Правила могут ссылаться друг на друга рекурсивно / циклически (потому что они ссылаются друг на друга по ссылке).Я бы использовал это.

Вы можете по-прежнему выделять грамматические классы (например, для разделения реализаций на модули компиляции), но вы просто хотите склеить их вместе вцентральное место, где у вас есть экземпляры, которые вы затем можете взаимно ссылаться (другими словами: классическое управление собственностью: если ни один из классов не может владеть объектами, пусть другой объект владеет ими обоими). ​​

В качестве альтернативы вы можете просто передать ссылки на дополнительные грамматики в конструкторы и сохранить их вместо экземпляров грамматики.

DEMO

Для удобства я применил второй подход¹.

Я изменил несколько вещей:

  • исправил тип composite_element::elements с std::vector<element_t> до std::vector<element>
  • Исправлено несколько вещей, касающихся пропуска пробелов:

    • при разборе блока '[' ... ']' не допускаются символы новой строки, которые показывает ваш ввод (см. skip(qi::space))
    • eol требуется только после line_, но ваш ввод показывает это после других элементов
    • синтаксический анализатор элементов неправильно требует eoi - что приводит к остановке синтаксического анализа после первого элемента (и сбой, если это не EOI).
    • Шкипер не обслуживается конечным пользователем.Мое кредо - скрыть это от звонящего, если только вы не хотите, чтобы звонящий действительно смог заменить шкипер
  • Дополнительное удобство start_rule_, которое кодирует шкипер, а также делаетубедитесь, что правила верхнего уровня для грамматики с пользой отображаются в выходных данных отладки (если все называется start_rule_, осталось мало информации)

  • Удалено все phoenix (см. Boost Spirit: «Семантические действия - это зло»? ):

    • Этот пример:

      start_rule_ = +(element_)[push_back(at_c<0>(_val), _1)] >> qi::eoi;
      

      Это то, что автоматическое распространение атрибутов уже делает, так что этодостаточно:

      start_rule_ = +elements_ >> qi::eoi;
      
    • Это:

      qi::lexeme[(qi::char_)[at_c<2>(qi::_val) += qi::_1]]
      

      Было несколько проблем: не хватает повторения (анализируется только 1 символ), это НЕуказать, какие символы принимаются, sp если повтор будет читать до EOI.Я подозреваю, что это то, что вы хотели:

      qi::lexeme[+qi::graph]
      

      См. Также Проблемы с шкиперами буста

  • Изменено || в | (см. Альтернативный парсер против Последовательный-или парсер ).

  • Может быть, еще, но я забыл?О да, я прокомментировал строку v.

Live On Coliru

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

struct line { int x1, y1, x2, y2, color, width, capstyle, dashstyle, dashlength, dashspace; };
struct box { int x, y, width, height, color, line_width, capstyle, dashstyle, dashlength, dashspace, filltype, fillwidth, angle1, pitch1, angle2, pitch2; };
struct circle { int x, y, radius, color, line_width, capstyle, dashstyle, dashlength; };
struct text { int x, y, color, size, visibility, show_name_value, angle, alignment, num_lines;
    std::vector<std::string> lines;
};

struct composite_component;
using element_t = boost::variant<line, box, circle, text, boost::recursive_wrapper<composite_component>>;

struct element {
    // ...
    element_t element;
};

struct composite_component {
    int x;
    int y;
    std::string basename;
    std::vector<element> elements;
};

struct document { std::vector<element> elements; };

BOOST_FUSION_ADAPT_STRUCT(line, x1, y1, x2, y2, color, width, capstyle, dashstyle, dashlength, dashspace)
BOOST_FUSION_ADAPT_STRUCT(box, x, y, width, height, color, line_width, capstyle, dashstyle, dashlength, dashspace, filltype, fillwidth, angle1, pitch1, angle2, pitch2)
BOOST_FUSION_ADAPT_STRUCT(circle, x, y, radius, color, line_width, capstyle, dashstyle, dashlength)
BOOST_FUSION_ADAPT_STRUCT(text, x, y, color, size, visibility, show_name_value, angle, alignment, num_lines, lines)
BOOST_FUSION_ADAPT_STRUCT(composite_component, x, y, basename, elements)
BOOST_FUSION_ADAPT_STRUCT(element, element)
BOOST_FUSION_ADAPT_STRUCT(document, elements)

template <typename Iterator, typename Attribute>
using blank_rule = qi::rule<Iterator, Attribute, qi::blank_type>;

template <typename Iterator>
struct composite_element_parser;

template <typename Iterator>
struct element_parser : qi::grammar<Iterator, element()> {
    element_parser(): element_parser::base_type{start_rule_},
        embedded_component_(*this)
    {
        using qi::int_;

        /* other definitions except of the 'line' is omitted in sake of simplicity */
        line_ = 'L' >> int_ >> int_ >> int_ >> int_ >> int_ >>
                    int_ >> int_ >> int_ >> int_ >> int_;
        // box = ...
        // circle = ...
        // text = ...
        element_rule_ = (line_ | embedded_component_) >> qi::eol;
        start_rule_ = qi::skip(qi::blank) [ element_rule_ ];
        BOOST_SPIRIT_DEBUG_NODES((element_rule_)(line_));
    }
  private:
    qi::rule<Iterator, element()> start_rule_;
    blank_rule<Iterator, element()> element_rule_;
    blank_rule<Iterator, line()> line_;
    composite_element_parser<Iterator> embedded_component_;
};

template <typename Iterator>
struct composite_element_parser : qi::grammar<Iterator, composite_component()> {
    composite_element_parser(element_parser<Iterator> const& ep)
        : composite_element_parser::base_type{start_rule_},
          element_(ep)
    {
        using qi::int_;
        elements_ = -qi::skip(qi::space) [ '[' >> *element_ >> ']' ];
        composite_element_rule_ = 'C' >> int_ >> int_ >> qi::lexeme[+qi::graph] >> elements_;
        start_rule_ = qi::skip(qi::blank) [ composite_element_rule_ ];
        BOOST_SPIRIT_DEBUG_NODES((composite_element_rule_)(elements_));
    }
  private:
    qi::rule<Iterator, composite_component()> start_rule_;
    blank_rule<Iterator, composite_component()> composite_element_rule_;
    blank_rule<Iterator, std::vector<element>()> elements_;
    element_parser<Iterator> const& element_;
};

template <typename Iterator>
struct document_parser : qi::grammar<Iterator, document()> {
    document_parser() : document_parser::base_type{start_rule_}
    {
        document_rule_ = +element_ >> qi::eoi;
        start_rule_ = qi::skip(qi::blank) [ document_rule_ ];
        BOOST_SPIRIT_DEBUG_NODES((document_rule_));
    }
  private:
    qi::rule<Iterator, document()> start_rule_;
    blank_rule<Iterator, document()> document_rule_;
    element_parser<Iterator> element_;
};

int main(int , char **) {
    document_parser<std::string::const_iterator> parser;

    const std::string text = // "v 20180904 2\n"
         "L 1 2 3 4 5 6 7 8 9 10\n"
         "C 10 10 FOO\n"
         "[ "
         "    L 10 20 30 40 50 60 70 80 90 100\n"
         "]\n";

    document doc;
    bool r = qi::parse(text.cbegin(), text.cend(), parser, doc);
    std::cout << (r ? "OK" : "FAIL") << std::endl;
}

Prints

OK

И вывод отладки:

<document_rule_>
  <try>L 1 2 3 4 5 6 7 8 9 </try>
  <element_rule_>
    <try>L 1 2 3 4 5 6 7 8 9 </try>
    <line_>
      <try>L 1 2 3 4 5 6 7 8 9 </try>
      <success>\nC 10 10 FOO\n[     L</success>
      <attributes>[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]</attributes>
    </line_>
    <success>C 10 10 FOO\n[     L </success>
    <attributes>[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]]</attributes>
  </element_rule_>
  <element_rule_>
    <try>C 10 10 FOO\n[     L </try>
    <line_>
      <try>C 10 10 FOO\n[     L </try>
      <fail/>
    </line_>
    <composite_element_rule_>
      <try>C 10 10 FOO\n[     L </try>
      <elements_>
        <try>\n[     L 10 20 30 40</try>
        <element_rule_>
          <try>L 10 20 30 40 50 60 </try>
          <line_>
            <try>L 10 20 30 40 50 60 </try>
            <success>\n]\n</success>
            <attributes>[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]</attributes>
          </line_>
          <success>]\n</success>
          <attributes>[[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]</attributes>
        </element_rule_>
        <element_rule_>
          <try>]\n</try>
          <line_>
            <try>]\n</try>
            <fail/>
          </line_>
          <composite_element_rule_>
            <try>]\n</try>
            <fail/>
          </composite_element_rule_>
          <fail/>
        </element_rule_>
        <success>\n</success>
        <attributes>[[[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]]</attributes>
      </elements_>
      <success>\n</success>
      <attributes>[[10, 10, [F, O, O], [[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]]]</attributes>
    </composite_element_rule_>
    <success></success>
    <attributes>[[[10, 10, [F, O, O], [[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]]]]</attributes>
  </element_rule_>
  <element_rule_>
    <try></try>
    <line_>
      <try></try>
      <fail/>
    </line_>
    <composite_element_rule_>
      <try></try>
      <fail/>
    </composite_element_rule_>
    <fail/>
  </element_rule_>
  <success></success>
  <attributes>[[[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], [[10, 10, [F, O, O], [[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]]]]]]</attributes>
</document_rule_>

usually Обычно я этого не делаю, так как это вызывает проблемы при жизни и я считаю ссылочные членызапах кода, если только для недолговечных функторных объектов

3 голосов
/ 15 июня 2019

Исходя из моего более раннего ответа , где я показал подход, обмениваясь ссылками, я упростил этот ответ до следующего:

template <typename Iterator>
struct document_parser : qi::grammar<Iterator, document()> {
    document_parser() : document_parser::base_type{start_}
    {
        using namespace qi;

        line_              = 'L' >> auto_;
        box_               = 'B' >> auto_;
        circle_            = 'S' >> auto_;
        // text            = 'T' >> ...;
        element_           = (line_ | box_ | circle_ | composite_element_) >> eol;
        elements_          = -skip(space) [ '[' >> skip(blank) [*element_] >> ']' ];
        composite_element_ = 'C' >> int_ >> int_ >> lexeme[+graph] >> elements_;

        document_          = +element_ >> eoi;

        start_ = skip(blank) [ document_ ];
        BOOST_SPIRIT_DEBUG_NODES((document_)(element_)(composite_element_)(elements_)(line_));
    }
  private:
    qi::rule<Iterator, document()> start_;
    qi::rule<Iterator, document(),             qi::blank_type> document_;
    qi::rule<Iterator, element(),              qi::blank_type> element_;
    qi::rule<Iterator, line(),                 qi::blank_type> line_;
    qi::rule<Iterator, box(),                  qi::blank_type> box_;
    qi::rule<Iterator, circle(),               qi::blank_type> circle_;
    qi::rule<Iterator, composite_component(),  qi::blank_type> composite_element_;
    qi::rule<Iterator, std::vector<element>(), qi::blank_type> elements_;
};

Обратите внимание, что теперь он также разбирает прямоугольники и кружки.Вы можете прочитать о Автоматический парсер для этой магии.

Посмотреть Live On Wandbox

#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>

namespace qi = boost::spirit::qi;

struct line { int x1, y1, x2, y2, color, width, capstyle, dashstyle, dashlength, dashspace; };
struct box { int x, y, width, height, color, line_width, capstyle, dashstyle, dashlength, dashspace, filltype, fillwidth, angle1, pitch1, angle2, pitch2; };
struct circle { int x, y, radius, color, line_width, capstyle, dashstyle, dashlength; };
struct text { int x, y, color, size, visibility, show_name_value, angle, alignment, num_lines;
    std::vector<std::string> lines;
};

struct composite_component;
using element_t = boost::variant<line, box, circle, text, boost::recursive_wrapper<composite_component>>;

struct element {
    // ...
    element_t element;
};

struct composite_component {
    int x;
    int y;
    std::string basename;
    std::vector<element> elements;
};

struct document { std::vector<element> elements; };

BOOST_FUSION_ADAPT_STRUCT(line, x1, y1, x2, y2, color, width, capstyle, dashstyle, dashlength, dashspace)
BOOST_FUSION_ADAPT_STRUCT(box, x, y, width, height, color, line_width, capstyle, dashstyle, dashlength, dashspace, filltype, fillwidth, angle1, pitch1, angle2, pitch2)
BOOST_FUSION_ADAPT_STRUCT(circle, x, y, radius, color, line_width, capstyle, dashstyle, dashlength)
BOOST_FUSION_ADAPT_STRUCT(text, x, y, color, size, visibility, show_name_value, angle, alignment, num_lines, lines)
BOOST_FUSION_ADAPT_STRUCT(composite_component, x, y, basename, elements)
BOOST_FUSION_ADAPT_STRUCT(element, element)
BOOST_FUSION_ADAPT_STRUCT(document, elements)

template <typename Iterator>
struct document_parser : qi::grammar<Iterator, document()> {
    document_parser() : document_parser::base_type{start_}
    {
        using namespace qi;

        line_              = 'L' >> auto_;
        box_               = 'B' >> auto_;
        circle_            = 'S' >> auto_;
        // text            = 'T' >> ...;
        element_           = (line_ | box_ | circle_ | composite_element_) >> eol;
        elements_          = -skip(space) [ '[' >> skip(blank) [*element_] >> ']' ];
        composite_element_ = 'C' >> int_ >> int_ >> lexeme[+graph] >> elements_;

        document_          = +element_ >> eoi;

        start_ = skip(blank) [ document_ ];
        BOOST_SPIRIT_DEBUG_NODES((document_)(element_)(composite_element_)(elements_)(line_)(box_)(circle_));
    }
  private:
    qi::rule<Iterator, document()> start_;
    qi::rule<Iterator, document(),             qi::blank_type> document_;
    qi::rule<Iterator, element(),              qi::blank_type> element_;
    qi::rule<Iterator, line(),                 qi::blank_type> line_;
    qi::rule<Iterator, box(),                  qi::blank_type> box_;
    qi::rule<Iterator, circle(),               qi::blank_type> circle_;
    qi::rule<Iterator, composite_component(),  qi::blank_type> composite_element_;
    qi::rule<Iterator, std::vector<element>(), qi::blank_type> elements_;
};

int main(int , char **) {
    document_parser<std::string::const_iterator> parser;

    const std::string text = // "v 20180904 2\n"
         "L 1 2 3 4 5 6 7 8 9 10\n"
         "C 10 10 FOO\n"
         "[ "
         "    L 10 20 30 40 50 60 70 80 90 100\n"
         "]\n";

    document doc;
    bool r = qi::parse(text.cbegin(), text.cend(), parser, doc);
    std::cout << (r ? "OK" : "FAIL") << std::endl;
}

Печать

OK

И отладочный вывод:

<document_>
  <try>L 1 2 3 4 5 6 7 8 9 </try>
  <element_>
    <try>L 1 2 3 4 5 6 7 8 9 </try>
    <line_>
      <try>L 1 2 3 4 5 6 7 8 9 </try>
      <success>\nC 10 10 FOO\n[     L</success>
      <attributes>[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]</attributes>
    </line_>
    <success>C 10 10 FOO\n[     L </success>
    <attributes>[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]]]</attributes>
  </element_>
  <element_>
    <try>C 10 10 FOO\n[     L </try>
    <line_>
      <try>C 10 10 FOO\n[     L </try>
      <fail/>
    </line_>
    <box_>
      <try>C 10 10 FOO\n[     L </try>
      <fail/>
    </box_>
    <circle_>
      <try>C 10 10 FOO\n[     L </try>
      <fail/>
    </circle_>
    <composite_element_>
      <try>C 10 10 FOO\n[     L </try>
      <elements_>
        <try>\n[     L 10 20 30 40</try>
        <element_>
          <try>     L 10 20 30 40 5</try>
          <line_>
            <try>     L 10 20 30 40 5</try>
            <success>\n]\n</success>
            <attributes>[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]</attributes>
          </line_>
          <success>]\n</success>
          <attributes>[[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]</attributes>
        </element_>
        <element_>
          <try>]\n</try>
          <line_>
            <try>]\n</try>
            <fail/>
          </line_>
          <box_>
            <try>]\n</try>
            <fail/>
          </box_>
          <circle_>
            <try>]\n</try>
            <fail/>
          </circle_>
          <composite_element_>
            <try>]\n</try>
            <fail/>
          </composite_element_>
          <fail/>
        </element_>
        <success>\n</success>
        <attributes>[[[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]]</attributes>
      </elements_>
      <success>\n</success>
      <attributes>[[10, 10, [F, O, O], [[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]]]</attributes>
    </composite_element_>
    <success></success>
    <attributes>[[[10, 10, [F, O, O], [[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]]]]</attributes>
  </element_>
  <element_>
    <try></try>
    <line_>
      <try></try>
      <fail/>
    </line_>
    <box_>
      <try></try>
      <fail/>
    </box_>
    <circle_>
      <try></try>
      <fail/>
    </circle_>
    <composite_element_>
      <try></try>
      <fail/>
    </composite_element_>
    <fail/>
  </element_>
  <success></success>
  <attributes>[[[[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]], [[10, 10, [F, O, O], [[[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]]]]]]]]</attributes>
</document_>
3 голосов
/ 15 июня 2019

Обычно вы просто не разделяете грамматику таким образом.Но если вы действительно хотите, есть несколько способов:

  1. Создать грамматики отдельно и назначить грамматики для rule заполнитель извне:

    #include <boost/spirit/include/qi.hpp>
    
    namespace qi = boost::spirit::qi;
    
    template <typename Iterator>
    struct grammar1 : qi::grammar<Iterator, int()>
    {
        grammar1() : grammar1::base_type{start_}
        {
            start_ = '[' >> outer >> ']';
        }
    
        qi::rule<Iterator, int()> outer;
    
    private:
        qi::rule<Iterator, int()> start_;
    };
    
    template <typename Iterator>
    struct grammar2 : qi::grammar<Iterator, int()>
    {
        grammar2() : grammar2::base_type{start_}
        {
            start_ = outer | qi::int_;
        }
    
        qi::rule<Iterator, int()> outer;
    
    private:
        qi::rule<Iterator, int()> start_;
    };
    
    int main()
    {
        char const* s = "[[123]]", * e = s + std::strlen(s);
        grammar2<char const*> g2;
        grammar1<char const*> g1;
        g2.outer = g1;
        g1.outer = g2;
        int value = 0;
        if (qi::parse(s, e, g1, value))
            std::cout << value << '\n';
        else
            std::cout << "failed\n";
    }
    

    https://wandbox.org/permlink/QhA18pIZwVlQ2osi

  2. Динамически создайте одну грамматику в другой и передайте ей ссылку на предыдущую:

    #include <boost/spirit/include/qi.hpp>
    
    namespace qi = boost::spirit::qi;
    
    template <typename Iterator>
    struct grammar2;
    
    template <typename Iterator>
    struct grammar1 : qi::grammar<Iterator, int()>
    {
        grammar1()
            : grammar1::base_type{start_}
        {
            outer_ = std::make_unique<grammar2<Iterator>>(start_);
            start_ = '[' >> *outer_ >> ']';  // NOTE: it is not a kleen star!
        }
    
    private:
        std::unique_ptr<grammar2<Iterator>> outer_;
        qi::rule<Iterator, int()> start_;
    };
    
    template <typename Iterator>
    struct grammar2 : qi::grammar<Iterator, int()>
    {
        explicit grammar2(qi::rule<Iterator, int()> const& outer)
            : grammar2::base_type{start_}
        {
            start_ = outer | qi::int_;
        }
    
    private:
        qi::rule<Iterator, int()> start_;
    };
    
    int main()
    {
        char const* s = "[[123]]", * e = s + std::strlen(s);
        grammar1<char const*> const g1;
        int value = 0;
        if (qi::parse(s, e, g1, value))
            std::cout << value << '\n';
        else
            std::cout << "failed\n";
    }
    

    https://wandbox.org/permlink/hJz3v1ApK8GCkquS

...