【问题标题】:Order of semantic actions using Spirit (with Phoenix reference)使用 Spirit 的语义动作顺序(带有 Phoenix 参考)
【发布时间】:2015-10-03 03:44:52
【问题描述】:

我正在构建一个解析器来执行用户可以在命令行上输入的命令。命令的第一部分是它所属的模块,第二部分是要调用的模块的函数。

附加到第一个解析器的是一个语义操作(使用 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

在构建这个最小示例时,我注意到在执行 parse 函数期间 m_moduleName 的值是 empty,尽管它已预先设置为“initial_default”。

有人能解释一下这里到底发生了什么吗?

为什么值是空的而不是 mod01

【问题讨论】:

  • 尽管语法如此奇怪和神奇,但您需要记住start 规则的赋值发生在myCommandParser 构造函数中。 Example。在语法构造函数boost::bind 生成一个类似于[m_moduleName/*by current value*](const std::string&amp; command){ printParameters(m_moduleName, command); } 的对象。使用boost::bind(&amp;printParameters, boost::cref(m_moduleName), ::_1)seems to work
  • 但是 phx::bind(&amp;printParameters, phx::cref(m_moduleName), qi::_1) sehe 建议可能是更好的选择(例如,如果您需要传递两个参数而不是一个)。 Example.

标签: c++ boost-spirit-qi ref boost-phoenix


【解决方案1】:

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

事实上,你不能在语义动作中使用 Boost Bind。

您想将phoenix::bindqi::_1 一起使用。哦,在m_moduleName 周围加上phoenix::cref()

除此之外,您根本不想在这种情况下使用丑陋的语义操作:

简化:

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 例如扩展/自动化的方法

【讨论】:

  • boost::spirit 的文档明确指出在语义操作中使用 boost::bind 是有效的:boost.org/doc/libs/1_58_0/libs/spirit/doc/html/spirit/qi/…
  • 我看到这个答案在某种程度上可能会有所帮助,但我的实际问题是解释这里发生了什么,而不是为你认为可能是我想要的提供解决方案。我确实想使用语义动作,即使有人可能认为它们是邪恶的。
  • 当然,您可以使用 boost bind 来绑定原始语义操作,但这不是您想要/需要的(它不能像这样工作)。此外,如果您仔细阅读,您会注意到我从未说过语义行为是邪恶的
  • 也许您可以向我们喊出您想要实现的目标。我可以对语义动作大喊大叫。但我需要先知道你需要做什么
  • 请注意,在森林段落中,o 准确地描述了您需要做什么才能按原样修复语义操作。相信我,在发布之前我已经测试过了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-13
  • 2013-02-06
  • 1970-01-01
  • 1970-01-01
  • 2013-03-18
  • 2016-02-06
相关资源
最近更新 更多