【问题标题】:Boost.Spirit.Qi: How to return attributes with Nabialek trickBoost.Spirit.Qi:如何使用 Nabialek 技巧返回属性
【发布时间】:2013-07-17 01:21:57
【问题描述】:

按照几个教程(例如http://boost-spirit.com/home/articles/qi-example/nabialek-trick/),我想使用 Nabialek 技巧来拥有一个动态解析器​​。解析已经可以正常工作,但我没有得到传输的属性。像https://stackoverflow.com/a/9109972/2524462 这样的解释表明,属性应该是可能的,但不是参数。

这只是一个将字符串和数字解析为结构的小示例。这只是为了展示我的问题;这种方法应该在以后真正需要动态解析器​​的更大系统中使用。

问题:如何使用 Nabialek 技巧传输属性?

我不是精神专家,所以请多多包涵。我正在使用 gcc 4.8.1 和 boost 1.54。

#define BOOST_SPIRIT_DEBUG
#define BOOST_SPIRIT_USE_PHOENIX_V3
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>

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

//------------------------------------------------------------------------------
// Data structure
struct myline {
  myline()
      : _n(0), _s("") {
  }

  myline(int n, std::string s)
      : _n(n), _s(s) {
  }

  void set(int n, std::string s) {
    _n = n;
    _s = s;
  }

  int _n;
  std::string _s;
};

BOOST_FUSION_ADAPT_STRUCT(::myline, (int, _n) (std::string, _s))

//------------------------------------------------------------------------------
// Parser grammar
template<typename It, typename Skipper = qi::space_type>
struct parser: qi::grammar<It, myline(), Skipper> {
  parser()
      : parser::base_type(start) {
    using namespace qi;

    start = line;

    string %= qi::lexeme["'" >> *~qi::char_("'") >> "'"];

    one = (string >> "@" >> qi::int_)[_val = phx::construct<myline>(_2, _1)];
    two = (qi::int_ >> "@" >> string);

    keyword.add("one", &one)("two", &two);

    line = keyword[_a = _1] >> qi::lazy(*_a);

    on_error<fail>(
        start,
        std::cout << phx::val("Error! Expecting ") << _4
        << phx::val(" here: \"") << phx::construct<std::string>(_3, _2)
        << phx::val("\"\n"));

    BOOST_SPIRIT_DEBUG_NODES((start)(line)(one)(two))
  }

private:
  template<typename Attr> using Rule = qi::rule<It, Attr(), Skipper>;

  Rule<myline> start, one, two;
  qi::rule<It, myline, Skipper, qi::locals<Rule<myline>*> > line;

  Rule<std::string> string;

  qi::symbols<char, Rule<myline>*> keyword;
};

//------------------------------------------------------------------------------
int main() {
  for (const std::string input : std::vector<std::string> { "one 'test'@1",
                                                            "two 2@'test'" }) {
    auto f(std::begin(input)), l(std::end(input));
    const static parser<decltype(f)> p;

    myline parsed_script;
    bool ok = qi::phrase_parse(f, l, p, qi::space, parsed_script);

    if (!ok) {
      std::cout << "invalid input\n";
    }

    std::cout << parsed_script._n << ": " << parsed_script._s << std::endl;

    if (f != l) {
      std::cout << "unparsed: '" << std::string(f, l) << "'" << std::endl;
    }
  }
}

解析结果:

<start>
  <try>one 'test'@1</try>
  <line>
    <try>one 'test'@1</try>
    <one>
      <try> 'test'@1</try>
      <success></success>
      <attributes>[[1, [t, e, s, t]]]</attributes>
    </one>
    <success></success>
    <attributes>[]</attributes><locals>(0x43b0e0)</locals>
  </line>
  <success></success>
  <attributes>[[0, []]]</attributes>
</start>
<start>
  <try>two 2@'test'</try>
  <line>
    <try>two 2@'test'</try>
    <two>
      <try> 2@'test'</try>
      <success></success>
      <attributes>[[2, [t, e, s, t]]]</attributes>
    </two>
    <success></success>
    <attributes>[]</attributes><locals>(0x43b110)</locals>
  </line>
  <success></success>
  <attributes>[[0, []]]</attributes>
</start>

【问题讨论】:

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


    【解决方案1】:

    你在精神课上一直很关注:)

    有很多问题:

    1. line 规则的属性声明错误:

      qi::rule<It, myline, Skipper, qi::locals<Rule<myline>*> > line;
      

      需要

      qi::rule<It, myline(), Skipper, qi::locals<Rule<myline>*> > line;
      
    2. 在存在语义动作时禁止自动属性传播。有关更多信息,请参阅最近的答案:Boost.spirit: parsing number char and string。因此,您需要使用 %= 明确参与 Spirit 的自动规则行为:

      line = keyword[_a = _1] >> qi::lazy(*_a);
      

      需要

      // NOTE the %=
      line %= omit [ keyword[_a = _1] ] >> qi::lazy(*_a);
      

      注意事项:

      • %= 可以继续 string 规则(没有语义操作意味着自动属性传播)
      • 我们需要明确 omit[] 关键字匹配的结果,因为我们不能真正将Rule&lt;&gt;* 分配给我们的myline 属性

    这是固定版本:

    #define BOOST_SPIRIT_DEBUG
    #define BOOST_SPIRIT_USE_PHOENIX_V3
    #include <boost/fusion/adapted/struct.hpp>
    #include <boost/spirit/include/qi.hpp>
    #include <boost/spirit/include/phoenix.hpp>
    
    namespace qi = boost::spirit::qi;
    namespace phx = boost::phoenix;
    
    //------------------------------------------------------------------------------
    // Data structure
    struct myline {
      myline()
          : _n(0), _s("") {
      }
    
      myline(int n, std::string s)
          : _n(n), _s(s) {
      }
    
      void set(int n, std::string s) {
        _n = n;
        _s = s;
      }
    
      int _n;
      std::string _s;
    };
    
    BOOST_FUSION_ADAPT_STRUCT(::myline, (int, _n) (std::string, _s))
    
    //------------------------------------------------------------------------------
    // Parser grammar
    template<typename It, typename Skipper = qi::space_type>
    struct parser: qi::grammar<It, myline(), Skipper> {
      parser()
          : parser::base_type(start) {
        using namespace qi;
    
        start  = line;
    
        string = qi::lexeme["'" >> *~qi::char_("'") >> "'"];
    
        one    = (string >> "@" >> qi::int_) [_val           = phx::construct<myline>(_2, _1)];
        two    = (qi::int_ >> "@" >> string);
    
        keyword.add("one", &one)("two", &two);
    
        // NOTE the %=
        line %= omit [ keyword[_a = _1] ] >> qi::lazy(*_a);
    
        on_error<fail>(
            start,
            std::cout << phx::val("Error! Expecting ") << _4
            << phx::val(" here: \"") << phx::construct<std::string>(_3, _2)
            << phx::val("\"\n"));
    
        BOOST_SPIRIT_DEBUG_NODES((start)(line)(one)(two))
      }
    
    private:
      template<typename Attr> using Rule = qi::rule<It, Attr(), Skipper>;
    
      Rule<myline> start, one, two;
      qi::rule<It, myline(), Skipper, qi::locals<Rule<myline>* > > line;
    
      Rule<std::string> string;
    
      qi::symbols<char, Rule<myline>* > keyword;
    };
    
    //------------------------------------------------------------------------------
    int main() {
      for (const std::string input : std::vector<std::string> { "one 'test1'@1",
                                                                "two 2@'test2'" }) {
        auto f(std::begin(input)), l(std::end(input));
        const static parser<decltype(f)> p;
    
        myline parsed_script;
        bool ok = qi::phrase_parse(f, l, p, qi::space, parsed_script);
    
        if (!ok) {
          std::cout << "invalid input\n";
        }
    
        std::cout << parsed_script._n << ": " << parsed_script._s << std::endl;
    
        if (f != l) {
          std::cout << "unparsed: '" << std::string(f, l) << "'" << std::endl;
        }
      }
    }
    

    打印:

    <start>
      <try>one 'test1'@1</try>
      <line>
        <try>one 'test1'@1</try>
        <one>
          <try> 'test1'@1</try>
          <success></success>
          <attributes>[[1, [t, e, s, t, 1]]]</attributes>
        </one>
        <success></success>
        <attributes>[[1, [t, e, s, t, 1]]]</attributes><locals>(0x6386c0)</locals>
      </line>
      <success></success>
      <attributes>[[1, [t, e, s, t, 1]]]</attributes>
    </start>
    1: test1
    <start>
      <try>two 2@'test2'</try>
      <line>
        <try>two 2@'test2'</try>
        <two>
          <try> 2@'test2'</try>
          <success></success>
          <attributes>[[2, [t, e, s, t, 2]]]</attributes>
        </two>
        <success></success>
        <attributes>[[2, [t, e, s, t, 2]]]</attributes><locals>(0x6386f0)</locals>
      </line>
      <success></success>
      <attributes>[[2, [t, e, s, t, 2]]]</attributes>
    </start>
    2: test2
    

    【讨论】:

    • 查看固定样本live on Coliru
    • 非常感谢,效果很好。看来我还需要几节课;-)
    • 太糟糕了,Boost.Spirit 用户太少,无法获得更多支持。当你转世时你会得到它们:-)
    • TemplateRex,当 NicolBolas 能听到你的声音时,别提这事。我很确定他会demand a proof of your claim :)。此外,这已经非常受欢迎,所以我不抱怨@TemplateRex
    • @sehe 在某个时候我会学习 Boost.Spirit,毕竟在学习了量子力学和模板元编程之后,它有多难? ;-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-13
    • 2010-10-13
    相关资源
    最近更新 更多