2015-07-14 3 views
1

Я строю парсер для выполнения команд, которые пользователь может ввести в командной строке. Первая часть команды - это модуль, к которой он принадлежит, вторая часть - это функция модуля для вызова.Порядок семантических действий с использованием Spirit (с ссылкой Phoenix)

К первому парсеру относится семантическое действие (с boost :: phoenix :: ref()), которое должно хранить имя модуля в переменной m_moduleName. К второму парсеру относится другое семантическое действие, которое вызывает функцию printParameters с прежней переменной в качестве параметра.

#include <boost/spirit/include/phoenix_core.hpp> 
#include <boost/spirit/include/phoenix_operator.hpp> 
#include <boost/spirit/home/qi.hpp> 
#include <boost/bind.hpp> 

#include <iostream> 
#include <string> 

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


void printParameters(const std::string & module, const std::string & command) 
{ 
    std::cout << "Module name during parse: " << module << std::endl; 
    std::cout << "Command name during parse: " << command << std::endl; 
} 


template <typename Iterator> 
struct myCommandParser : public qi::grammar<Iterator> 
{ 
    myCommandParser() : myCommandParser::base_type(start) 
    { 
     start = qi::as_string[+(~qi::char_(' '))][phoenix::ref(m_moduleName) = qi::_1] 
     >> qi::as_string[+(~qi::char_('\n'))][boost::bind(&printParameters, m_moduleName, ::_1)]; 
    }; 

    qi::rule<Iterator> start; 
    std::string m_moduleName; 
}; 


int main() 
{ 
    myCommandParser<std::string::const_iterator> commandGrammar; 

    commandGrammar.m_moduleName = std::string("initial_default"); 
    std::cout << "Module name before parsing: " << commandGrammar.m_moduleName << std::endl; 

    std::string str("mod01 cmd02\n"); 
    std::string::const_iterator first = str.begin(); 
    std::string::const_iterator last = str.end(); 
    qi::parse(first, last, commandGrammar); 

    std::cout << "Module name after parsing: " << commandGrammar.m_moduleName << std::endl; 
} 

Ожидаемый результат: В течение первого смыслового действия значение m_moduleName должен быть установлен в mod01, которые должны быть напечатаны во время функции printParameters.

Фактический результат (выход программы):

Module name before parsing: initial_default 
Module name during parse: 
Command name during parse: cmd02 
Module name after parsing: mod01 

При построении этого минимального примера, я заметил, что значение m_moduleName является пустым во время выполнения функции синтаксического анализа, хотя он был установлен на «initial_default "заранее.

Может кто-нибудь объяснить, что именно происходит здесь?

Почему значение пусто, а не mod01?

+0

Несмотря на то, что синтаксис настолько странный и волшебный, вам нужно иметь в виду, что назначение правила 'start' происходит внутри конструктора' myCommandParser'. [Пример] (http://coliru.stacked-crooked.com/a/d2de0d37cbc0b1d6). В конструкторе грамматики 'boost :: bind' генерирует объект, похожий на' [m_moduleName/* по текущему значению *] (const std :: string & command) {printParameters (m_moduleName, command); } '. Использование 'boost :: bind (& printParameters, boost :: cref (m_moduleName), :: _ 1)' [кажется, работает] (http: //coliru.stacked-crooked.ком/а/d81a32c98f40dd7c). – llonesmiz

+0

Но 'phx :: bind (& printParameters, phx :: cref (m_moduleName), qi :: _ 1)', как рекомендует sehe, вероятно, является лучшей альтернативой (например, если вам нужно передать два параметра вместо одного). [Пример] (http://coliru.stacked-crooked.com/a/48509abadc1255d9). – llonesmiz

ответ

1

Вы не можете смешивать заполнители Bind/Phoenix/Lambda/Qi/std.

Фактически вы не можете использовать Ускорение привязки внутри семантического действия.

Вы хотите использовать phoenix::bind с qi::_1. Aoh, и добавьте phoenix::cref() около m_moduleName.

Кроме того, вы не хотите использовать уродливые семантические действия в этом случае вообще:

Упрощенная:

Live On Coliru

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

namespace qi = boost::spirit::qi; 

int main() 
{ 
    std::string const str("mod01 cmd02\n"); 
    std::string moduleName, command; 
    qi::parse(str.begin(), str.end(), +~qi::char_(' ') >> +~qi::char_('\n'), moduleName, command); 

    std::cout << "Module name after parsing: " << moduleName << "\n"; 
    std::cout << "Command after parsing:  " << command << "\n"; 
} 

Печать

Module name after parsing: mod01 
Command after parsing:  cmd02 

См BOOST_FUSION_ADAPT_STRUCT и boost/fusion/adapted/std_pair.hpp, например, для способов масштабирования/автоматизации

+0

Добавлен образец http://coliru.stacked-crooked.com/a/ac5d4f8e4164149d – sehe

+0

Документация boost :: spirit явно заявляет, что использование boost :: bind внутри семантических действий действительно: http://www.boost.org /doc/libs/1_58_0/libs/spirit/doc/html/spirit/qi/tutorials/semantic_actions.html – ChristophK

+0

Я вижу, что этот ответ может быть полезен в некотором роде, но мой фактический вопрос состоял в том, чтобы объяснить, что здесь происходит, а не чтобы дать решение для того, что, по вашему мнению, может быть тем, что я хочу. Я хочу использовать семантические действия, хотя кто-то может подумать, что они злые. – ChristophK

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