Передать атрибут дочернего правила в духе буста

У меня есть два правила с одним и тем же атрибутом.

Можно ли передать атрибут правила matrix_ дочернему правилу matrixBlock_? Я хочу, чтобы директива повтора не создавала атрибут вектора формы< >. Вместо этого он должен просто продолжать запись в атрибут matrix_ (времена numBlocks). Я попытался передать атрибут как унаследованный атрибут в дочернее правило, и он компилируется (см. Ниже). Но я получаю несколько "призрачных" записей в моем векторе, которые происходят не от phoenix::push_back. Также это, кажется, не оптимальный путь для меня. Возможно ли нам автоматическое распространение атрибутов в matrixBlock_ вместо семантических действий?

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_(_val) ];
matrixBlock_ = *column[phoenix::push_back(_r1, _1)];

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, void(Matrix&), ascii::space_type> matrixBlock_;

Обновить

уточнить вопрос:

если я напишу правило без семантических действий, синтезированный атрибут matrix_ будет

vector< vector< columnT > >

-

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ matrixBlock_ ];
matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;

Я хочу, чтобы он имел тот же тип атрибута, что и matrixBlock_, одномерный массив.


Мое реальное решение - использовать только одно правило. (выглядит легко:-))

typedef vector<columnT> Matrix;
matrix_ = repeat(numBlocks)[ *column_[ phoenix::push_back(_val, _1) ] ];
//matrixBlock_ = *column;

qi::rule<Iterator, Matrix(), ascii::space_type> matrix_;
//qi::rule<Iterator, Matrix(), ascii::space_type> matrixBlock_;

Обновить

Я смог воспроизвести фантомные записи с этим кодом в vs2010 и повысить 1.46.1

http://liveworkspace.org/code/505091dc4631a379763567168a728e0c

Вывод был: 42, 45, -9, 3, 2, 1, 12, 34, 56, 0, 0, 0

Моя ошибка заключалась в использовании старой версии Boost. Нет фонтомов с 1.5.

Теперь у меня есть две рабочие версии моей грамматики. Можно ли изменить грамматику без использования семантического действия push_back?

1 ответ

Решение

обновленный

Отвечая на ваш отредактированный вопрос: Да, вы можете сделать это без семантических действий, выполнив просто:

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
    Parser() : Parser::base_type(matrix_)
    {
        matrixBlock_ = qi::lit(";") >> *qi::int_;
        matrix_      = qi::repeat(3)[ matrixBlock_ ];
    }
    qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

Обратите внимание, что вы можете проверить количество строк / столбцов. Посмотрите мой расширенный пример, который использует дополнительное семантическое действие, чтобы проверить это (обратите внимание на небольшое изменение от *int_ в +int_ чтобы избежать пустых строк):

#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;

typedef std::vector<int> Matrix;

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
    Parser() : Parser::base_type(matrix_)
    {
        using namespace qi;
        matrixBlock_ = lit(";") >> +int_ >> eps( 0 == (phx::size(_val) % 3));
        matrix_      = repeat(3)[ matrixBlock_ ];
    }
    qi::rule<It, Matrix(), qi::space_type> matrixBlock_, matrix_;
};

int main()
{
    std::string test = ";42 45 -9; 3 2 1; 12 34 56";

    std::string::const_iterator f(test.begin()), l(test.end());

    Parser<std::string::const_iterator> parser;
    Matrix m;

    if (qi::phrase_parse(f,l,parser,qi::space, m))
        std::cout << "Wokay\n";
    else
        std::cerr << "Uhoh\n";

    std::cout << karma::format(karma::auto_ % ", ", m) << "\n";
}

Старый ответ:

Да, вы можете использовать точки настройки Spirit для обработки вашего пользовательского типа как контейнера. Запись документации, которую я бы предложил для этого, находится здесь:

Вот простой пример, показывающий, как его использовать, live:

Дополнительное примечание в отношении "фантомных записей" в целом:

Обратите внимание, что есть несколько часто задаваемых вопросов, связанных с грамматикой возврата и атрибутами контейнера. Дело в том, что по соображениям производительности парсеры не будут отменять ("откатывать") изменения своих базовых контейнеров при возврате. Вы можете заставить это поведение, используя qi::hold но это может стоить усилий, чтобы изменить грамматику либо

  • избегать возврата или
  • зафиксировать атрибут на более поздней стадии (используя семантические действия)

Полный пример кода:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>

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

struct Matrix
{
   std::vector<int> data;
};

namespace boost { namespace spirit { namespace traits {
   template <>
      struct is_container<Matrix>
      {
      };

   template <typename Attrib>
      struct push_back_container<Matrix, Attrib>
      {
         static bool call(Matrix& c, Attrib const& val)
         {
            c.data.push_back(val);
            return true;
         }
      };

   template <>
      struct container_value<Matrix>
      {
         typedef int type;
      };
} } }

template<typename It>
struct Parser : qi::grammar<It, Matrix(), qi::space_type>
{
   Parser() : Parser::base_type(start)
   {
      start = *qi::int_;
   }
   qi::rule<It, Matrix(), qi::space_type> start;
};

int main()
{
   std::string test = "42 45 -9";

   std::string::const_iterator f(test.begin()),
      l(test.end());

   Parser<std::string::const_iterator> parser;
   Matrix m;

    if (qi::phrase_parse(f,l,parser,qi::space, m))
      std::cout << "Wokay\n";
   else
      std::cerr << "Uhoh\n";

   std::cout << karma::format(karma::auto_ % ", ", m.data) << "\n";
}

Выход:

Wokay
42, 45, -9

Обновить

Немного больше предыстории:

Конечно, для такого простого примера, как этот, который просто оборачивает стандартный поддерживаемый тип контейнера, было бы довольно легко вместо этого использовать адаптацию Fusion: ( http://liveworkspace.org/code/56aea8619867451a21cd49fddb1e93bd)

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/karma.hpp>
#include <boost/fusion/adapted/struct.hpp>

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

struct Matrix { std::vector<int> data; }; 
BOOST_FUSION_ADAPT_STRUCT(Matrix, (std::vector<int>, data));

int main()
{
    std::string test = "42 45 -9";
    std::string::const_iterator f(test.begin()), l(test.end());

    Matrix m;
    if (qi::phrase_parse(f,l, qi::eps >> *qi::int_, qi::space, m))
        std::cout << karma::format(karma::auto_ % ", ", m.data) << "\n";
}

Обратите внимание, что qi::eps необходим из-за ошибки (AFAICT) со структурами, которые содержат только один элемент данных. Смотрите, например, обсуждение здесь (и некоторые другие упоминания)

Другие вопросы по тегам