2014-11-20 2 views
1

Я пытаюсь написать синтаксический анализатор, который может принимать входной сигнал формы MATRIX.{variableName} = [1,2,3;4,5,6], где представление матрицы (матрица 2x3 в этом случае) как формат MATLAB (точка с запятой указывает на новую строку).Написание анализатора для ввода матрицы с помощью Boost Spirit

Первоначальная идея состояла в том, чтобы сохранить входной сигнал в 2d-векторном std для дальнейшей обработки данных. Это мой первый раз, когда я пишу синтаксический анализатор, и я несколько не знаю, что такое структура Духа.

Мое текущее (не очень интуитивное) решение для ввода должно быть чем-то вроде MATRIX (2,3) = [1,2,3,4,5,6], чтобы представить ту же матрицу, что и выше, и сохранить данные в одномерном векторе и использовать данные строки и столбца для последующей обработки (Я считаю, что это похоже на реализацию Эйгеном динамических матриц).

namespace client 
{ 
    namespace qi = boost::spirit::qi; 
    namespace ascii = boost::spirit::ascii; 
    namespace phoenix = boost::phoenix; 
    namespace fusion = boost::fusion; 

    template <typename Iterator> 
    bool parse_matrix(Iterator first, Iterator last, unsigned& rows, unsigned& cols, std::vector<double>& vals) 
    { 
     using qi::double_; 
     using qi::uint_; 
     using qi::_1; 
     using qi::lit; 
     using qi::phrase_parse; 
     using ascii::space; 
     using phoenix::push_back; 

     double rN = 0.0; 
     double iN = 0.0; 
     unsigned i=0; 
     rows = 0, cols = 0; 
     bool r = phrase_parse(first, last, 

      // Begin grammar 
      (
       lit("MATRIX") >> '(' >> uint_[phoenix::ref(rows) = _1] >> ',' >> uint_[phoenix::ref(cols) = _1] >> ')' >> '=' 
       >> '[' >> double_[push_back(phoenix::ref(vals),_1)] 
         >> *(',' >> double_[push_back(phoenix::ref(vals),_1)]) >> ']' 
       // | double_[ref(rN) = _1] 
      ), 
      // End grammar 

      space); 

     if (!r || first != last) // fail if we did not get a full match 
      return false; 
     if (vals.size() != (rows*cols)) 
      return false; 
     // c = std::complex<double>(rN, iN); 
     return r; 
    } 
} 

Я думал, может быть, было бы можно назвать такие функции, как добавление std::vector<double> к std::vector<std::vector<double> >, когда некоторые символы (такие как точка с запятой) в настоящее время анализируется. Это возможно? Или как я действительно продвигаю свою первоначальную идею?

ответ

1

Я хотел бы предложить:

  • Не используя семантические действия для распространения атрибутов. Вы можете использовать его, чтобы добавить критерии проверки (см Boost Spirit: "Semantic actions are evil"?)

  • Использование распространения автоматического атрибута, так что вы не должны передавать ссылки вокруг

  • Не проверяющего во время синтаксического анализа, если у вас есть нажав причины для Сделай так.

Минимальные жизнеспособный анализатор становится:

Live On Coliru

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

using It = boost::spirit::istream_iterator; 
using Row = std::vector<double>; 
using Mat = std::vector<Row>; 

int main() { 
    It f(std::cin>>std::noskipws), l; 

    Mat matrix; 
    std::string name; 

    { 
     using namespace boost::spirit::qi; 
     rule<It, std::string()> varname_ = char_("a-zA-Z_") >> *char_("a-zA-Z0-9_"); 

     if (phrase_parse(f, l, 
       lit("MATRIX") >> '.' >> '{' >> varname_ >> '}' >> '=' 
        >> '[' >> (int_ % ',' % ';') >> ']', 
       space, name, matrix)) 
     { 
      std::cout << "Parsed: variabled named '" << name << "' ["; 

      for(auto& row : matrix) 
       std::copy(row.begin(), row.end(), std::ostream_iterator<double>(std::cout<<"\n\t",", ")); 
      std::cout << "\n]\n"; 
     } else { 
      std::cout << "Parse failed\n"; 
     } 
    } 

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

Что можно увидеть печать следующего вывода для ввода "MATRIX.{variable_name}=[1,2,3;4,5,6]":

Parsed: variabled named 'variable_name' [ 
    1, 2, 3, 
    4, 5, 6, 
] 

Если вы хотите вначале поймать несогласованные длины строк, см., Например,этот ответ:

0

Вы должны сломать свои выражения на что-то вроде этого:

rows: DOUBLE | rows ',' DOUBLE 

columns: rows | rows ';' rows 

matrix: IDENTIFIER '.' '{' IDENTIFIER '}' '=' '[' columns ']' 

Теперь вы можете иметь vector<double> для ваших строк. A vector< vector<double> > для ваших столбцов и сохраните результат в своей матрице.

Конечно, когда вы получаете столбцы в своей матрице, вы должны проверить, что все строки имеют одинаковый размер. Это не требуется, но, очевидно, матрица , такая как [1,2;3,4,5], недействительна, хотя ее позволяет грамматика.

 Смежные вопросы

  • Нет связанных вопросов^_^