提升平凡语法的编译错误 [英] Boost spirit compile error for trivial grammar

查看:88
本文介绍了提升平凡语法的编译错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am trying to compile a parser with the following rules:

else_statement =
    lit("else") > statement;

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

else_statement 的属性为声明,以及它使用的声明规则。 if_statement 的属性是一个结构,其成员分别为 expression statement 和可选的声明 boost :: optional< statement&)。

The attribute of else_statement is statement, as is the statement rule that it consumes. The attribute of if_statement is a struct with members respectively types expression, statement and an optional 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_node boost :: variant 的可能语句。

我希望如果存在 else_statement ,它将被放入 boost :: Optional< statement> ,因为 else_statement 的属性是 statement 。如果我在<$ c $中注释 lit( else)> ,这有用 c> else_statement 规则!但是随着 lit( else)的出现,奇怪的事情发生了:现在boost :: spirit试图适应陈述进入可选的语句(boost :: variant)的成员中,该语句显然不会编译,因为只需要A或B。

I'd expect that if an else_statement is present, it will be put into the boost::optional<statement>, since the attribute of else_statement is statement. And this does work if I comment out the lit("else") > in the else_statement rule! But with the lit("else") present something strange happens: now boost::spirit is trying to fit a statement into the member of the optional statement (the boost::variant) which obviously won't compile because that only takes an A or 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&)’

我在做什么错?我该如何解决?

What am I doing wrong? How can I solve this?

在显示错误的完整测试代码下面(并在 lit( else)> 已被注释掉)。

Below a complete test snippet that shows the error (and compiles when the lit("else") > is comment out).

// 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;
}


推荐答案

自动属性传播规则有一些由单个元素组成的Fusion序列出现问题。您可以在这里声明以下内容:

Automatic attribute propagation rules have some trouble with Fusion sequences that consist of a single element. You can work around it here by declaring:

rule<ast::statement_node> statement;

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

这有效: 在Coliru上直播

This works: Live On Coliru

更繁琐的解决方法是避免使用单元素融合序列那里。您可以在语句中添加一个虚拟字段:

The more tedious workaround is to avoid having a single-element fusion sequence there. You can add a dummy field to 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);

这也消除了混乱。

在魔盒上生活

// 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 规则 设置为相同的长度,也会得到同样的困惑:

Background

Note, though, that you get the same confusion back if you mistakenly make the else_statement rule also the same length:

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

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

Of course that doesn't actually make sense, but the error message DOES help in explaining what the real problem under the hood is (if the "upstream" fusion sequence appears "compatible" then it is "deconstructed" for propagation). The relevant comment in 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_);

这篇关于提升平凡语法的编译错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆