【问题标题】:boost::spirit default semantic action and string compositionboost::spirit 默认语义动作和字符串组合
【发布时间】:2016-11-02 00:57:24
【问题描述】:

我有以下代码:

#include <gtest/gtest.h>
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_eps.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/spirit/include/phoenix_bind.hpp>
#include <boost/spirit/include/classic_core.hpp>
#include <boost/spirit/include/classic_confix.hpp>
#include <boost/spirit/include/classic_chset.hpp>
#include <boost/spirit/include/classic_utility.hpp>
#include <boost/fusion/include/cons.hpp>
#include <boost/fusion/include/at_c.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/bind.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/variant.hpp>

namespace spi = boost::spirit;
namespace qi = boost::spirit::qi;

TEST(TestBoost, cpp_comment)
{
    using qi::char_;
    using qi::omit;
    using qi::eoi;
    typedef std::string::const_iterator iter;

    const std::string example = "/* this should be ignored */";
    auto b = std::begin(example);
    auto e = std::end(example);

    qi::rule<iter, std::string()> cpp_comment = char_('/') >> char_('/') >> *(char_ - '\n') >> (char_('\n') | omit[eoi]);
    qi::rule<iter, std::string()> c_comment = char_('/') >> char_('*') >> *(char_ - "*/") >> char_('*') >> char_('/');
    qi::rule<iter, std::string()> shell_comment = char_('#') >> *(char_ - '\n') >> (char_('\n') | omit[eoi]);
    qi::rule<iter, std::string()> comment = cpp_comment
            | c_comment
            | shell_comment
            ;

    std::string result;

    EXPECT_TRUE(qi::parse(b, e, comment, result));
    EXPECT_EQ(b, e);
    EXPECT_EQ(result, example);
}

失败并出现以下错误:

[----------] 1 test from TestBoost
[ RUN      ] TestBoost.cpp_comment
tests/spirit.cpp:56: Failure
      Expected: result
      Which is: "//* this should be ignored */"
To be equal to: example
      Which is: "/* this should be ignored */"
[  FAILED  ] TestBoost.cpp_comment (0 ms)
[----------] 1 test from TestBoost (0 ms total)

我不明白为什么。可能在 boost 文档中的某个地方提到了这种行为,但我找不到它。有人知道为什么会这样吗?

如果我这样写语义动作:

qi::rule<iter, std::string()> comment = cpp_comment[spi::_val = spi::_1]
            | c_comment[spi::_val = spi::_1]
            | shell_comment[spi::_val = spi::_1]
            ;

或者这个

qi::rule<iter, std::string()> comment = cpp_comment[spi::_val += spi::_1]
            | c_comment[spi::_val += spi::_1]
            | shell_comment[spi::_val += spi::_1]
            ;

它有效,但我真的很想知道为什么原始代码不起作用。

【问题讨论】:

  • 请不要在你的最小代码中使用不必要的东西,比如谷歌测试或几十个包含。

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


【解决方案1】:

这是回溯容器属性的经典问题:

这里的想法是使用qi::hold,或者在这种情况下使用qi::raw 更好,因为看起来您想将整个匹配的输入序列公开为属性:

qi::rule<iter, std::string()>
      cpp_comment   = "//" >> *~char_('\n')   >> (eol|eoi),
      c_comment     = "/*" >> *(char_ - "*/") >> "*/",
      shell_comment = '#'  >> *~char_('\n')   >> (eol|eoi),
      comment       = qi::raw [ cpp_comment | c_comment | shell_comment ];

Live On Coliru

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

namespace qi = boost::spirit::qi;

void test() {
    using qi::char_;
    using qi::eol;
    using qi::eoi;

    std::string const example = "/* this should be ignored */";

    qi::rule<std::string::const_iterator, std::string()>
          cpp_comment   = "//" >> *~char_('\n')   >> (eol|eoi),
          c_comment     = "/*" >> *(char_ - "*/") >> "*/",
          shell_comment = '#'  >> *~char_('\n')   >> (eol|eoi),
          comment       = qi::raw [ cpp_comment | c_comment | shell_comment ];

    std::string result;

    bool ok = qi::parse(std::begin(example), std::end(example), comment >> eoi, result);
    assert(ok);

    std::cout << "expected: " << example << "\n";
    std::cout << "actual:   " << result << "\n";
    assert(result == example);
}

int main() {
    test();
}

打印

expected: /* this should be ignored */
actual:   /* this should be ignored */

【讨论】:

  • 刚刚意识到:使用raw,您无需在子规则上合成std::string
  • 非常感谢您提供这么好的答案。我刚刚意识到,在我复杂的语法中,可能有很多地方会出现这种效果,这是一个可怕的想法。除了替代运营商,还有其他已知的运营商可能发生这种情况吗?
  • 它可能发生在具有非原子分配的容器属性上。查看链接资源:)
  • 好的,我会再检查一次。我还有最后一个问题:如果我在上面的示例中添加新行(即“/* 这应该\n 被忽略 */),那么我只会得到部分匹配。为什么?我的假设是它匹配 cpp_comment,这就是为什么我改变了我的语法。
  • @Sasa 我不明白你的意思。这是一场比赛:coliru.stacked-crooked.com/a/62dad53319011f2c
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-11
  • 2013-02-06
  • 1970-01-01
  • 2015-11-24
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
相关资源
最近更新 更多