在精神x3中使用布尔属性而不是可选

时间:2017-06-09 06:50:15

标签: c++ boost-spirit-x3

在我想要实现的语法中,有一些关键字,其中枚举很重要(将具体关键字的enum id存储在ast的节点内),甚至只存在相同的特定关键字 - 因此在布尔上下文中是可选的。我喜欢有一个自我表达的解析器表达式和ast节点,所以我提出了以下(可编译)解决方案:

#include <iostream>
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace x3 = boost::spirit::x3;

namespace ast {
    enum class keyword_token {
        UNSPECIFIED, FOO, BAR
    };
    struct buz {
        bool            foo;
        int             dummy;
    };
}

BOOST_FUSION_ADAPT_STRUCT( ast::buz, foo, dummy )

namespace boost { namespace spirit { namespace x3 { namespace traits {
    template <>
    inline void
    move_to(ast::keyword_token&& src, bool& dest) {
        dest = static_cast<bool>(src);
    }
} } } } // boost.spirit.x3.traits


namespace parser {
    auto const FOO = x3::rule<struct _, ast::keyword_token> { "FOO" } = 
        x3::lexeme[ x3::no_case[ "foo" ] >> !(x3::alnum | '_') ]
        >> x3::attr(ast::keyword_token::FOO);
    auto const buz = x3::rule<struct _, ast::buz> { "buz" } = 
        -FOO >> x3::int_;
}

int main() {
    for(std::string const str: {
           "FOO 42",
           "42"
    }) {
      auto iter = str.begin(), end = str.end();
      ast::buz attr;
      bool r = x3::phrase_parse(iter, end, parser::buz, x3::space, attr);

      std::cout << "parse '" << str << "': ";
      if (r && iter == end) {
        std::cout << "succeeded:\n";
        std::cout << (attr.foo ? "FOO " : "") << attr.dummy << "\n";
        std::cout << "\n";
      } else {
        std::cout << "*** failed ***\n";
      }
    }
    return 0;
}

这里ast的buz节点有一个布尔属性,解析器有'可选'语法。背后的想法是bool是默认可构造的,并且标准保证初始化为0,也就是假的。此外,我有一个keyword::UNSPECIFIED的后备解决方案(由于我不是100%确定枚举类)必须等于0 - 它不应该永远不会被触发 - 在x3内部评估为false最坏情况下的move_to(...)特征。

运行此解析阶段对于两个测试用例都按预期成功,但不期望第二个测试用例的属性;显然,'boolean as optional'方法不能按预期工作:

<buz>
  <try>FOO 42</try>
  <FOO>
    <try>FOO 42</try>
    <success> 42</success>
    <attributes>1</attributes>
  </FOO>
  <success></success>
  <attributes>[1, 42]</attributes>
</buz>
parse 'FOO 42': succeeded:
FOO 42

<buz>
  <try>42</try>
  <FOO>
    <try>42</try>
    <fail/>
  </FOO>
  <success></success>
  <attributes>[1, 42]</attributes>
</buz>
parse '42': succeeded:
FOO 42

调试模式显示合成属性[1, 42]。那么,我的考虑是否合情合理,如果是的话,如何解决它按预期工作? 可能还有另一个问题:没有定义BOOST_SPIRIT_X3_DEBUG我收到警告:

warning: 'attr' may be used uninitialized in this function
...
warning: 'attr.ast::buz::dummy' may be used uninitialized in this function

在cout线上。可能我不明白警告是正确的,因为ast :: buz默认是可构造的,我不想拥有默认值(false,0)。

蛮力解决方案就是写下这样的东西:

auto bool_attr = [](auto p) {
    return x3::omit[ p ] >> x3::attr(true) | x3::attr(false);
};

并在规则中使用它,但我更喜欢“可选”语法,而不是编写bool_attr(FOO) >> x3::int_等。

来源也在Coliru

1 个答案:

答案 0 :(得分:1)

llonesmiz得到does POD class object initialization require constructor?的观点;我必须显式编写ast节点的构造函数:

struct buz {
    bool            foo;
    int             dummy;

    buz() : foo{}, dummy{}
    { }
};

比属性符合预期:

parse 'FOO 42': succeeded:
FOO 42

parse '42': succeeded:
42

有了这个,上面提到的没有定义BOOST_SPIRIT_X3_DEBUG的警告也消失了,警告对我来说更有意义......