2013-10-06 2 views
2

Я хотел бы создать парсер, основанный на boost spirit qi, который сможет анализировать список целых значений. Это, очевидно, очень просто, и есть множество примеров. Список, хотя это немного умнее, чем через запятую, и это может выглядеть так:boost spirit qi соответствует нескольким элементам

17, 5, Фибоначчи (2, 4), 71, 99, диапазон (5, 7)

результат анализатор должен быть станд :: вектор со следующими значениями:

17, 5, 1, 2, 3, 71, 99, 5, 6, 7

Где Фибоначчи (2, 4) приводит к 1, 2, 3 и диапазон (5, 7) приводит к 5, 6, 7

Редактировать: Я ищу, если у меня уже есть парсеры, у которых есть атрибут ute int (скажем, int_) и парсеров, которые имеют атрибут std :: vector fibonacci и range, как я могу комбинировать результаты в одном парсере. Что-то вроде:

list %= *(int_ | elements [ fibonacci | range ]); 

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

Примечание: Я не ищу решение, которое включает в себя добавление функции как

list = *(int_[push_back(_val, _1)] | fibonacci[push_back(_val, _1)] | range[push_back(_val, _1)] ]); 

ответ

4

Вот simplist взятие: Live On Coliru

typedef std::vector<int64_t> data_t; 

value_list  = -value_expression % ','; 
value_expression = macro | literal; 
literal   = int_; 

macro   = (_functions > '(' > value_list > ')') 
    [ _pass = phx::bind(_1, _2, _val) ]; 

Где _functions является qi::symbols таблица функций:

qi::symbols<char, std::function<bool(data_t const& args, data_t& into)> > _functions; 

Теперь, обратите внимание, что входные "17, 5, fibonacci(2, 4), 71, 99, range(5, 7)" результаты в

parse success 
data: 17 5 1 2 3 71 99 5 6 7 

Но вы даже можете получить более фанк: "range(fibonacci(13, 14))" Результаты в:

parse success 
data: 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 

Как вы можете видеть, он печатает диапазон от [fib(13)..fib(14)] which is [233..377] (Wolfram Alpha) ,

Полный код (в том числе демо-реализаций fibonacci и range :)):

//#define BOOST_SPIRIT_DEBUG 
#define BOOST_SPIRIT_USE_PHOENIX_V3 
#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; 

typedef std::vector<int64_t> data_t; 

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

     value_list  = -value_expression % ','; 
     value_expression = macro | literal; 
     literal   = int_; 

     macro   = (_functions > '(' > value_list > ')') 
      [ _pass = phx::bind(_1, _2, _val) ]; 

     _functions.add("fibonacci", &fibonacci); 
     _functions.add("range", &range); 

     BOOST_SPIRIT_DEBUG_NODES((value_list)(value_expression)(literal)(macro)); 
    } 

    private: 
    static bool fibonacci(data_t const& args, data_t& into) { 
     // unpack arguments 
     if (args.size() != 2) 
      return false; 
     auto f = args[0], l = args[1]; 

     // iterate 
     uint64_t gen0 = 0, gen1 = 1, next = gen0 + gen1; 
     for(auto i = 0u; i <= l; ++i) 
     { 
      switch(i) { 
       case 0: if (i>=f) into.push_back(gen0); break; 
       case 1: if (i>=f) into.push_back(gen1); break; 
       default: 
        { 
         next = gen0 + gen1; 
         if (i>=f) into.push_back(next); 
         gen0 = gen1; 
         gen1 = next; 
         break; 
        } 
      } 
     } 

     // done 
     return true; 
    } 

    static bool range(data_t const& args, data_t& into) { 
     // unpack arguments 
     if (args.size() != 2) 
      return false; 
     auto f = args[0], l = args[1]; 

     if (l>f) 
      into.reserve(1 + l - f + into.size()); 
     for(; f<=l; ++f) 
      into.push_back(f); // to optimize 

     return true; 
    } 

    qi::rule<It, data_t(), Skipper> value_list ; 
    qi::rule<It, data_t(), Skipper> value_expression, macro; 
    qi::rule<It, int64_t(), Skipper> literal; 

    qi::symbols<char, std::function<bool(data_t const& args, data_t& into)> > _functions; 
}; 

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; 
    data_t 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(karma::auto_, ' ', data) << "\n"; 
     } 
     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() 
{ 
    assert(doParse("range(fibonacci(13, 14))")); 
} 
+0

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

+0

@gsf Это действительно очень мало, весь «трюк» находится в 'phx :: bind (_1, ...)', чтобы вызвать функцию «зарегистрирована» с помощью '_functions'. – sehe

+0

Теперь это быстро и грязно. Если вы что-то еще более развили, см. [Этот ответ] (http://stackoverflow.com/a/17013713/85371), который анализирует грамматику выражений, которая включает в себя оценку функции и использует представление AST. См. [Другой ответ там] (http://stackoverflow.com/a/17014063/85371) для подхода, который пропускает создание AST (как в этом ответе). – sehe