【问题标题】:Boost spirit compile error for trivial grammar提升精神编译错误的琐碎语法
【发布时间】:2017-06-23 22:55:25
【问题描述】:

我正在尝试使用以下规则编译解析器:

else_statement =
    lit("else") > statement;

if_statement =
    lit("if") >> '(' >> expression >> ')' >> statement >> -else_statement;

else_statement 的属性是 statement,它使用的 statement 规则也是如此。 if_statement 的属性是一个结构体,其成员分别为expressionstatement 和可选的statement (boost::optional<statement>)。

使用以下BOOST_FUSION_ADAPT_STRUCT

BOOST_FUSION_ADAPT_STRUCT(ast::statement, m_statement_node)
BOOST_FUSION_ADAPT_STRUCT(ast::if_statement, m_condition, m_then, m_else)

其中m_statement_nodeboost::variant,表示可能的不同语句。

我希望如果存在else_statement,它将被放入boost::optional<statement>,因为else_statement 的属性是statement。如果我在else_statement 规则中注释掉lit("else") >,这确实 有效!但是随着lit("else") 的出现,一些奇怪的事情发生了:现在 boost::spirit 正试图将statement 放入可选的statement(boost::variant)的成员中,这显然是赢了'不编译,因为这只需要 A 或 B。

生成的编译错误如下所示:

/usr/include/boost/variant/variant.hpp:1534:38: error: no matching function for call to ‘boost::variant<ast::A, ast::B>::initializer::initialize(void*, const ast::statement&)’

我做错了什么?我该如何解决这个问题?

下面是显示错误的完整测试 sn-p(并在 lit("else") &gt; 被注释掉时编译)。

// File: so.cpp
// Compile as: g++ -std=c++11 so.cpp

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/optional/optional_io.hpp>
#include <iostream>
#include <string>
#include <vector>

namespace ast
{

struct A { int a; friend std::ostream& operator<<(std::ostream& os, A const&) { return os << "A"; } };
struct B { int b; friend std::ostream& operator<<(std::ostream& os, B const&) { return os << "B"; } };
struct expression { int e; friend std::ostream& operator<<(std::ostream& os, expression const&) { return os << "expression"; } };

using statement_node = boost::variant<A, B>;

struct statement
{
  statement_node m_statement_node;

  friend std::ostream& operator<<(std::ostream& os, statement const& statement)
      { return os << "STATEMENT:" << statement.m_statement_node; }
};

struct if_statement
{
  expression m_condition;
  statement m_then;
  boost::optional<statement> m_else;

  friend std::ostream& operator<<(std::ostream& os, if_statement const& if_statement)
  {
    os << "IF_STATEMENT:" << if_statement.m_condition << "; " << if_statement.m_then;
    if (if_statement.m_else)
      os << "; " << if_statement.m_else;
    return os;
  }
};

} // namespace ast

BOOST_FUSION_ADAPT_STRUCT(ast::expression, e)
BOOST_FUSION_ADAPT_STRUCT(ast::A, a)
BOOST_FUSION_ADAPT_STRUCT(ast::B, b)
BOOST_FUSION_ADAPT_STRUCT(ast::statement, m_statement_node)
BOOST_FUSION_ADAPT_STRUCT(ast::if_statement, m_condition, m_then, m_else)

namespace client
{

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;

template <typename Iterator>
class test_grammar : public qi::grammar<Iterator, ast::if_statement(), qi::space_type>
{
 private:
  template<typename T> using rule = qi::rule<Iterator, T(), qi::space_type>;

  rule<ast::A>                                     a;
  rule<ast::B>                                     b;
  rule<ast::statement>                             statement;
  rule<ast::statement>                             else_statement;
  rule<ast::if_statement>                          if_statement;
  rule<int>                                        expression;

 public:
  test_grammar() : test_grammar::base_type(if_statement, "result_grammar")
  {
    using namespace qi;

    statement =
      a | b;

    else_statement =
      lit("else") > statement;

    if_statement =
      lit("if") >> '(' >> expression >> ')' >> statement >> -else_statement;

    expression =
        int_;

    a = 'A';
    b = 'B';

    BOOST_SPIRIT_DEBUG_NODES(
        (statement)
        (else_statement)
        (if_statement)
        (expression)
        (a)
        (b)
    );
  }
};

} // namespace client

int main()
{
  std::string const input{"if (1) A B"};
  using iterator_type = std::string::const_iterator;
  using test_grammar = client::test_grammar<iterator_type>;
  namespace qi = boost::spirit::qi;

  test_grammar program;
  iterator_type iter{input.begin()};
  iterator_type const end{input.end()};
  ast::if_statement out;
  bool r = qi::phrase_parse(iter, end, program, qi::space, out);

  if (!r || iter != end)
  {
    std::cerr << "Parsing failed." << std::endl;
    return 1;
  }
  std::cout << "Parsed: " << out << std::endl;
}

【问题讨论】:

    标签: c++11 compiler-errors boost-spirit


    【解决方案1】:

    自动属性传播规则对于由单个元素组成的 Fusion 序列存在一些问题。您可以通过声明来解决它:

    rule<ast::statement_node> statement;
    

    (从ast::statement 更改为ast::statement_node)。

    这有效:Live On Coliru

    替代解决方法

    更繁琐的解决方法是避免在此处使用单元素融合序列。您可以向statement 添加一个虚拟字段:

    struct statement
    {
        statement_node m_statement_node;
        int dummy;
    
        friend std::ostream& operator<<(std::ostream& os, statement const& statement)
        { return os << "STATEMENT:" << statement.m_statement_node; }
    };
    
    BOOST_FUSION_ADAPT_STRUCT(ast::statement, m_statement_node, dummy)
    

    然后为其添加一个值:

    statement = (a | b) >> attr(42);
    

    这也消除了混乱。

    Live On Wandbox

    // File: so.cpp
    // Compile as: g++ -std=c++11 so.cpp
    //#define BOOST_SPIRIT_DEBUG
    
    #include <boost/spirit/include/qi.hpp>
    #include <boost/fusion/include/adapt_struct.hpp>
    #include <boost/fusion/include/std_pair.hpp>
    #include <boost/optional/optional_io.hpp>
    #include <iostream>
    #include <string>
    #include <vector>
    
    namespace ast
    {
    
        struct A { int a; friend std::ostream& operator<<(std::ostream& os, A const&) { return os << "A"; } };
        struct B { int b; friend std::ostream& operator<<(std::ostream& os, B const&) { return os << "B"; } };
        struct expression { int e; friend std::ostream& operator<<(std::ostream& os, expression const&) { return os << "expression"; } };
    
        using statement_node = boost::variant<A, B>;
    
        struct statement
        {
            statement_node m_statement_node;
            int dummy;
    
            friend std::ostream& operator<<(std::ostream& os, statement const& statement)
            { return os << "STATEMENT:" << statement.m_statement_node; }
        };
    
        struct if_statement
        {
            expression m_condition;
            statement m_then;
            boost::optional<statement> m_else;
    
            friend std::ostream& operator<<(std::ostream& os, if_statement const& if_statement)
            {
                os << "IF_STATEMENT:" << if_statement.m_condition << "; " << if_statement.m_then;
                if (if_statement.m_else)
                    os << "; " << if_statement.m_else;
                return os;
            }
        };
    
    } // namespace ast
    
        BOOST_FUSION_ADAPT_STRUCT(ast::expression, e)
        BOOST_FUSION_ADAPT_STRUCT(ast::A, a)
        BOOST_FUSION_ADAPT_STRUCT(ast::B, b)
        BOOST_FUSION_ADAPT_STRUCT(ast::statement, m_statement_node, dummy)
        BOOST_FUSION_ADAPT_STRUCT(ast::if_statement, m_condition, m_then, m_else)
    
        namespace client
    {
    
        namespace qi = boost::spirit::qi;
        namespace ascii = boost::spirit::ascii;
    
        template <typename Iterator>
            class test_grammar : public qi::grammar<Iterator, ast::if_statement(), qi::space_type>
        {
            private:
                template<typename T> using rule = qi::rule<Iterator, T(), qi::space_type>;
    
                rule<ast::A>            a;
                rule<ast::B>            b;
                rule<ast::statement> statement;
                rule<ast::statement> else_statement;
                rule<ast::if_statement> if_statement;
                rule<int>               expression;
    
            public:
                test_grammar() : test_grammar::base_type(if_statement, "result_grammar")
            {
                using namespace qi;
    
                statement = (a | b) >> attr(42);
    
                else_statement = lit("else") > statement;
    
                if_statement = lit("if") >> '(' >> expression >> ')' >> statement >> -else_statement;
    
                expression = int_;
    
                a = 'A' >> attr(1);
                b = 'B' >> attr(2);
    
                BOOST_SPIRIT_DEBUG_NODES( (statement) (else_statement) (if_statement) (expression) (a) (b));
            }
        };
    
    } // namespace client
    
    int main()
    {
        for (std::string const input : {
                    "if (1) A else B",
                }) 
        {
            using iterator_type = std::string::const_iterator;
            using test_grammar  = client::test_grammar<iterator_type>;
            namespace qi        = boost::spirit::qi;
    
            test_grammar program;
            iterator_type iter = input.begin(), end = input.end();
            ast::if_statement out;
            bool r = qi::phrase_parse(iter, end, program, qi::space, out);
    
            if (!r || iter != end)
            {
                std::cerr << "Parsing failed." << std::endl;
                return 1;
            }
            std::cout << "Parsed: " << out << std::endl;
        }
    }
    

    打印

    Parsed: IF_STATEMENT:expression; STATEMENT:A;  STATEMENT:B
    

    背景

    但请注意,如果您错误地将else_statement 规则设置为相同的长度,您会得到同样的困惑:

    else_statement = lit("else") > statement > attr(42); // this is wrong
    

    当然这实际上没有意义,但错误消息确实有助于解释真正的问题是什么(如果“上游”融合序列看起来“兼容”,那么它就被“解构”以进行传播) . qi/nonterminal/rule.hpp 中的相关注释:

    // do up-stream transformation, this integrates the results
    // back into the original attribute value, if appropriate
    traits::post_transform(attr_param, attr_);
    

    【讨论】:

    • 非常感谢您的快速回答!使用“使else_statement 相同长度”是指与语句相同数量的元素(2)吗?还是它们也必须是同一类型?
    • 我认为它只是长度,但“错误的启发式”只会在单元素(子)序列变平时发生。在大多数情况下,这会导致最佳的自动属性兼容性效果。可悲的是,所有足够复杂的“魔法”都会导致像这样的毛茸茸的边缘情况
    猜你喜欢
    • 1970-01-01
    • 2014-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多