使用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 cmd02n");
    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?

不能混合使用Boost Bind/Phoenix/Lambda/Qi/std占位符

实际上你不能在语义操作中使用Boost Bind。

您想使用use phoenix::bind with qi::_1。哦,在m_moduleName周围加入phoenix::cref()

除此之外,你根本不想在这种情况下使用难看的语义操作:

  • Boost Spirit:"语义行为是邪恶的"?
简化:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
namespace qi = boost::spirit::qi;
int main()
{
    std::string const str("mod01 cmd02n");
    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,例如缩放/自动化的方法

最新更新