Разбор смешанных значений и пар ключ-значение с Boost.Spirit - PullRequest
3 голосов
/ 04 февраля 2012

У меня есть простая грамматика, состоящая из смешанных переменных ($(name)) и пар переменная-значение ($(name:value)).У меня есть рекурсивный парсер, закодированный вручную, но я заинтересован в его использовании в качестве упражнения для изучения Духа, которое мне понадобится для более сложных грамматик в конечном итоге (/ скоро).

В любом случае, набор возможных формЯ работаю с (упрощенно от полной грамматики):

$(variable)     // Uses simple look-up, recursion and inline replace
$(name:value)   // Inserts a new variable into the local lookup table

Мои текущие правила выглядят примерно так:

typedef std::map<std::string, std::string> dictionary;

template <typename Iterator>
bool parse_vars(Iterator first, Iterator last, dictionary & vars, std::string & output)
{
    using qi::phrase_parse;
    using qi::_1;
    using ascii::char_;
    using ascii::string;
    using ascii::space;
    using phoenix::insert;

    dictionary statevars;

    typedef qi::rule<Iterator, std::string()> string_rule;
    typedef qi::rule<Iterator, std::pair<std::string, std::string>()> pair_rule;

    string_rule state = string >> ':' >> string; // Error 3
    pair_rule variable = 
    (
        char_('$') >> '(' >> 
        (
            state[insert(phoenix::ref(statevars), _1)] |
            string[output += vars[_1]] // Error 1, will eventually need to recurse
        ) >> ')'
    ); // Error 2

    bool result = phrase_parse
    (
        first, last, 
        (
            variable % ','
        ), 
        space
    );

    return r;
}

Если это не было очевидно, я понятия не имеюкак работает Дух, и у документов есть все, кроме реальных объяснений, так что это примерно час объединения примеров.

Части, которые я особенно задаю вопрос, являются ведущими char_('$') в правиле variable , но удаление этого вызывает ошибку оператора сдвига (компилятор интерпретирует '$' >> '(' как сдвиг вправо).

При компиляции я получаю ошибки, связанные с правилом state , в частности, при созданиипара и поиск:

  1. ошибка C2679: двоичный файл '[': не найден оператор, который принимает правый операнд типа 'const boost :: spirit :: _ 1_type' (или нетприемлемый конвекrsion)
  2. ошибка C2512: 'boost :: spirit :: qi :: rule :: rule': не доступен соответствующий конструктор по умолчанию

Изменение поиска (vars[_1]) напростой += дает:

3 .ошибка C2665: 'boost :: spirit :: char_class :: classify :: is': ни одна из 15 перегрузок не может преобразовать все типы аргументов

Кажется, ошибка 1 связана с типом (атрибутом?)_1 заполнитель, но это должна быть строка, а равно при использовании для печати или объединения в выходную строку.2 кажется шумом, вызванным 1.

Ошибка 3, копающая стопку ошибок шаблона, кажется, связана с неспособностью превратить правило state в пару, которая кажетсястранно, поскольку оно почти точно соответствует одному из правил этого примера .

Как изменить правило variable для правильной обработки обеих форм ввода?

Ответы [ 2 ]

2 голосов
/ 05 февраля 2012

Несколько замечаний:

  1. Чтобы адаптировать std::pair (чтобы вы могли использовать его с картами), вы должны включить (как минимум)

    #include <boost/fusion/adapted/std_pair.hpp>
    
  2. Похоже, вы пытаетесь создать таблицу символов.Вы можете использовать qi::symbols для этого

  3. избегать смешивания генерации вывода с анализом, это излишне усложняет вопросы

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

Вот версия с фиксированным кодом, находящаяся довольно близко к ОП. Редактировать тоже протестировали, вывод ниже:

#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/fusion/adapted/std_pair.hpp>
#include <map>

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

typedef std::map<std::string, std::string> dictionary;

template <typename Iterator, typename Skipper = qi::space_type>
    struct parser : qi::grammar<Iterator, Skipper>
{
    parser(dictionary& statevars, std::string& output) : parser::base_type(start)
    {
        using namespace qi;
        using phx::insert; 

        with_initializer = +~char_(":)") >> ':' >> *~char_(")");

        simple           = +~char_(")");

        variable         = 
            "$(" >> (
                   with_initializer  [ insert(phx::ref(statevars), qi::_1) ] 
                 | simple            [ phx::ref(output) += phx::ref(statevars)[_1] ]
             ) >> ')';

        start = variable % ',';

        BOOST_SPIRIT_DEBUG_NODE(start);
        BOOST_SPIRIT_DEBUG_NODE(variable);
        BOOST_SPIRIT_DEBUG_NODE(simple);
        BOOST_SPIRIT_DEBUG_NODE(with_initializer);
    }

  private:
    qi::rule<Iterator, std::pair<std::string, std::string>(), Skipper> with_initializer;
    qi::rule<Iterator, std::string(), Skipper> simple;
    qi::rule<Iterator, Skipper> variable;
    qi::rule<Iterator, Skipper> start;
};

template <typename Iterator>
bool parse_vars(Iterator &first, Iterator last, dictionary & vars, std::string & output)
{
    parser<Iterator> p(vars, output);
    return qi::phrase_parse(first, last, p, qi::space);
}

int main()
{
    const std::string input = "$(name:default),$(var),$(name)";
    std::string::const_iterator f(input.begin());
    std::string::const_iterator l(input.end());

    std::string output;
    dictionary table;

    if (!parse_vars(f,l,table,output))
        std::cerr << "oops\n";
    if (f!=l)
        std::cerr << "Unparsed: '" << std::string(f,l) << "'\n";
    std::cout << "Output:   '" << output << "'\n";
}

Вывод:

Output:   'default'
0 голосов
/ 04 февраля 2012

вы должны иметь char _ ('$'), иначе >> is 'char' с обеих сторон - вам нужно иметь хотя бы один спиртовой тип, чтобы получить перегруженный оператор >>.

Вам также может понадобиться использовать _1 от Phoenix.

Также взгляните на: http://boost -spirit.com / home / Articles / qi-example / parsing-a-list-of-key-Value-пар, использующий-спирит-й /

...