2013-11-18 2 views
3

Я хочу разбирать специальные конструкции и отбрасывать остальные. Но я не хочу использовать шкипера.Boost Spirit Qi: Опустить элемент в парсеры Kleene Star

Я хочу получить вектор этих конструкций, поэтому я использую парсер Kleene Star в качестве основного правила. Но каждый раз, когда что-то отбрасывается, встроенный элемент по умолчанию вставляется в вектор.

Настоящий пример. Он просто ищет строку Test и выбрасывает остальных, по крайней мере, это план. Но каждый раз, когда правило garbage преуспевает, он добавляет построенный по умолчанию элемент к вектору в правиле all, давая результат 7 insteat of 1. Как я могу сказать, что Spirit просто добавляет к вектору, если правило item преуспевает?

#define BOOST_SPIRIT_USE_PHOENIX_V3 

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

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

#include <iostream> 
#include <string> 
#include <vector> 

namespace qi = boost::spirit::qi; 

struct container { 
    std::string name; 
    bool  dummy; 
}; 
BOOST_FUSION_ADAPT_STRUCT(::container, 
          (std::string, name) 
          (bool, dummy)) 

int main() { 
    typedef std::string::const_iterator iterator; 

    qi::rule<iterator, std::vector<container>()> all; 
    qi::rule<iterator, container()> item; 
    qi::rule<iterator, std::string()> string_rule; 
    qi::rule<iterator> garbage; 

    all = *(garbage | item); 
    garbage = qi::char_ - qi::lit("Test"); 
    string_rule = qi::string("Test"); 
    item = string_rule >> qi::attr(true); 

    std::vector<container> ast; 

    std::string input = "blaTestbla"; 

    iterator first = input.begin(); 
    iterator last = input.end(); 

    bool result = qi::parse(first, last, all, ast); 
    if (result) { 
    result = first == last; 
    } 

    if (result) { 
    std::cout << "Parsed " << ast.size() << " element(s)" << std::endl; 
    } else { 
    std::cout << "failure" << std::endl; 
    } 

} 

ответ

3

Поскольку ответ sehe был более или меньше для образовательных целей, у нас сейчас несколько таких lutions:

*garbage >> -(item % *garbage) >> *garbage 

*garbage >> *(item >> *garbage) 

all = *(garbage | item[phx::push_back(qi::_val,qi::_1)]); 

И решение от cv_and_he:

#define BOOST_SPIRIT_USE_PHOENIX_V3 

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

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

#include <iostream> 
#include <string> 
#include <vector> 

namespace qi = boost::spirit::qi; 

struct container { 
    std::string name; 
    bool  dummy; 
}; 
BOOST_FUSION_ADAPT_STRUCT(::container, 
          (std::string, name) 
          (bool, dummy)) 

struct container_vector { //ADDED 
    std::vector<container> data; 
}; 

namespace boost{ namespace spirit{ namespace traits //ADDED 
{ 
    template <> 
    struct is_container<container_vector> : boost::mpl::true_ {}; 

    template <> 
    struct container_value<container_vector> { 
     typedef optional<container> type; 
    }; 

    template <> 
    struct push_back_container<container_vector,optional<container> > { 
     static bool call(container_vector& cont, const optional<container>& val) { 
      if(val) 
       cont.data.push_back(*val); 
      return true; 
     } 
    }; 
}}} 

int main() { 
    typedef std::string::const_iterator iterator; 

    qi::rule<iterator, container_vector()> all; //CHANGED 
    qi::rule<iterator, container()> item; 
    qi::rule<iterator, std::string()> string_rule; 
    qi::rule<iterator> garbage; 

    all = *(garbage | item); 
    garbage = qi::char_ - qi::lit("Test"); 
    string_rule = qi::string("Test"); 
    item = string_rule >> qi::attr(true); 

    container_vector ast;  //CHANGED 

    std::string input = "blaTestbla"; 

    iterator first = input.begin(); 
    iterator last = input.end(); 

    bool result = qi::parse(first, last, all, ast); 
    if (result) { 
    result = first == last; 
    } 

    if (result) { 
    std::cout << "Parsed " << ast.data.size() << " element(s)" << std::endl; //CHANGED 
    } else { 
    std::cout << "failure" << std::endl; 
    } 

} 

Хотя я не хотел использовать шкипера я закончил с:

start = qi::skip(garbage.alias())[*item]; 

Это последнее решение было самым быстрым (на 1-2%) в моих ненаучных тестах, используя c-файлы ядра Linux с моими правилами производства.

+0

Еще впечатляющий ответ. Я забыл некоторые из этих вещей. +1 +1 снова. – sehe

2

Быстрое исправление (не обязательно самый производительный) будет

all   = -(item - garbage) % +garbage; 

Он печатает:

Parsed 3 element(s) 

Посмотри Live on Coliru

+1

Спасибо, но он должен найти только один элемент. Но ваш ответ заставил меня задуматься; моя проблема заключалась в разном типе распространения '|' и '>>'. Это приводит к правилу '* garbage >> - (item% * garbage) >> * мусор или более короткий' * мусор >> * (item >> * garbage) ', которые дают желаемый результат. –

+0

@Mike Cheers, именно это я и собирался начать с вас. – sehe

+0

:-D (ну еще текст, так как только смайлик не разрешен) –

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

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