这个小改动与我的Boost Spirit规则为什么需要升级C++编译器?

huangapple go评论94阅读模式
英文:

Why this small change with my boost spirit rule requires to upgrade C++ compiler?

问题

Here is the translated code portion:

我正在使用带有std/c++14设置的Visual Studio 2019。在Sehe的帮助下,这段代码可以成功编译,没有任何问题:
[**Working Code**](https://compiler-explorer.com/z/Y6sP5YY1P)

(请注意,我切换到了`boost::optional`并将`':'`添加到标识符字符集)

现在我想扩展语法,以便我可以解析这个语法:
```cpp
Class Complex Complex_Name (
  Inherit Class1:Variable1
  Inherit Class1:Variable2
);

并输出以下内容:

<class>
  <complex>
    <identifier>Complex_Name</identifier>
    <literal>" "</literal>
    <inherit>
      <identifier>Class1:Variable1</identifier>
    </inherit>
    <inherit>
      <identifier>Class1:Variable2</identifier>
    </inherit>
  </complex>
</class>

以下是我已经完成的工作:

  1. 添加新的Inherit结构体
struct Inherit {
  Id id;
};
  1. 将新的Inherit结构体添加到Class变体中
using Class = boost::variant<   
  Simple,                     
  Inherit,
  recursive_wrapper<Complex>, 
  recursive_wrapper<Container>
>;
  1. 现在更新规则以获取这些内容
id_      = raw[alpha >> *(alnum | '_' | ':')];  //将':'添加到标识符规则
...
type_      = simple_ | inherit_ | complex_ | container_;
...
inherit_ = lit("Inherit") >> id_
...
qi::rule<It, Ast::Inherit(), Skipper>     inherit_;
  1. 更新XML规则以生成内容
void apply(Node parent, Inherit const& inh) const {
  auto inherit_ = named_child(parent, "inherit");
  apply(inherit_, inh.id);
}

完整代码见上方的代码块。

当我使用当前std/c++14设置编译这个新更新时,它会引发此错误:

错误 C2440static_cast”:无法从“const T_”转换为“Attribute

即使我不应该这样做,我仍然尝试使用std/c++17选项进行编译,但仍然会出现相同的错误。但如果我使用Visual Studio 2022并选择std/c++20选项进行编译,那么它将成功编译并按预期工作。

Compiler Explorer中也会发生相同的情况。当我使用低于x86-64 clang 16.0.0选项时,会引发相同的错误。

演示新代码 在旧编译器下失败

如果我切换到使用x86-64 clang 16.0.0选项,它将成功编译并正常工作。

演示新代码 在最新编译器下正常工作

我添加的内容相当简单,但不确定为什么不支持。有人可以告诉我出了什么问题,以及为什么需要使用最新的编译器进行这个小改变吗?

谢谢
Dylan


<details>
<summary>英文:</summary>

I&#39;m using Visual Studio 2019 with std/c++14 setting. With the help from Sehe, this code compiled fine without any issue: 
[**Working Code**](https://compiler-explorer.com/z/Y6sP5YY1P)

(Note that I switched to `boost::optional` and added `&#39;:&#39;` to the identifier charset)

Now I want to extend the grammar so that I can parse this syntax

Class Complex Complex_Name (
Inherit Class1:Variable1
Inherit Class1:Variable2
);

and output the following:

<class>
<complex>
<identifier>Complex_Name</identifier>
<literal>" "</literal>
<inherit>
<identifier>Class1:Variable1</identifier>
</inherit>
<inherit>
<identifier>Class1:Variable2</identifier>
</inherit>
</complex>
</class>


Below is what I have done

 1. **Add New Inherit struct**

struct Inherit {
Id id;
};

 2. **Add the new Inherit struct into Class variant** 

using Class = boost::variant<
Simple,
Inherit,
recursive_wrapper<Complex>,
recursive_wrapper<Container>
>;

 3. **Now update the rule to pick this up**

id_ = raw[alpha >> *(alnum | '' | ':')]; // add ':' to identifier rule
...
type
= simple_ | inherit_ | complex_ | container_;
...
inherit_ = lit("Inherit") >> id_
...
qi::rule<It, Ast::Inherit(), Skipper> inherit_;


 4. **Update XML rule to generate the content**

void apply(Node parent, Inherit const& inh) const {
auto inherit_ = named_child(parent, "inherit");
apply(inherit_, inh.id);
}


**FULL CODE**

// #define BOOST_SPIRIT_DEBUG 1
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/qi.hpp>
#include <iomanip>

namespace qi = boost::spirit::qi;

namespace Ast {
using boost::recursive_wrapper;

template &lt;typename&gt; struct flavour_hack : std::char_traits&lt;char&gt; {};
template &lt;typename Tag&gt;
    using String = std::basic_string&lt;char, flavour_hack&lt;Tag&gt; &gt;;

using Id       = String&lt;struct TagId&gt;;
using Literal  = String&lt;struct TagLiteral&gt;;
using Datatype = String&lt;struct TagDatatype&gt;;
struct Base {
    Id      id;
    Literal literal;
};

using Enum = std::vector&lt;Id&gt;;

using Number = double;
using Value  = boost::variant&lt;Literal, Number, Id&gt;;

struct Simple : Base {
    boost::optional&lt;Enum&gt;     enumeration;
    boost::optional&lt;Datatype&gt; datatype;
    boost::optional&lt;Value&gt;    default_;
};

struct Complex;
struct Container;
struct Inherit {
    Id id;
};
using Class = boost::variant&lt;   
    Simple,                     
    Inherit,
    recursive_wrapper&lt;Complex&gt;, 
    recursive_wrapper&lt;Container&gt;
&gt;;

using Classes = std::vector&lt;Class&gt;;
struct Container : Base { Class   element; };
struct Complex   : Base { Classes members; };

using Task = std::vector&lt;Class&gt;;

} // namespace Ast

BOOST_FUSION_ADAPT_STRUCT(Ast::Simple, id, literal, enumeration, datatype, default_);
BOOST_FUSION_ADAPT_STRUCT(Ast::Complex, id, literal, members)
BOOST_FUSION_ADAPT_STRUCT(Ast::Container, id, literal, element)

namespace Parser {
template <typename It> struct Task : qi::grammar<It, Ast::Task()> {
Task() : Task::base_type(start) {
using namespace qi;

        start = skip(space)[task_];

        // lexemes:
        id_      = raw[alpha &gt;&gt; *(alnum | &#39;_&#39; | &#39;:&#39;)];
        literal_ = &#39;&quot;&#39; &gt; *(&#39;\\&#39; &gt;&gt; char_ | ~char_(&#39;&quot;&#39;)) &gt; &#39;&quot;&#39;;

        auto optlit = copy(literal_ | attr(std::string(&quot; &quot;))); // weird, but okay

        task_      = *class_ &gt; eoi;
        type_      = simple_ | inherit_ | complex_ | container_;
        class_     = lit(&quot;Class&quot;) &gt; type_ &gt; &#39;;&#39;;
        simple_    = lit(&quot;Simple&quot;) &gt;&gt; id_ &gt;&gt; optlit &gt;&gt; -enum_ &gt;&gt; -datatype_ &gt;&gt; -default_;
        inherit_   = lit(&quot;Inherit&quot;) &gt;&gt; id_;
        complex_   = lit(&quot;Complex&quot;) &gt;&gt; id_ &gt;&gt; optlit &gt;&gt; &#39;(&#39; &gt;&gt; *type_ &gt;&gt; &#39;)&#39;;
        container_ = lit(&quot;Container&quot;) &gt;&gt; id_ &gt;&gt; optlit &gt;&gt; &#39;(&#39; &gt;&gt; type_ &gt; &#39;)&#39;;
        enum_      = lit(&quot;enumeration&quot;) &gt;&gt; &#39;(&#39; &gt;&gt; -(id_ % &#39;,&#39;) &gt; &#39;)&#39;;
        datatype_  = lit(&quot;datatype&quot;) &gt;&gt; id_;
        value_     = literal_ | number_ | id_;
        number_    = double_;
        default_   = lit(&quot;Default&quot;) &gt;&gt; value_;

        BOOST_SPIRIT_DEBUG_NODES(
            (task_)(class_)(type_)(simple_)(complex_)(container_)(enum_)(datatype_)
            (default_)(id_)(literal_)(value_)(number_)
        )
    }

  private:
    qi::rule&lt;It, Ast::Task()&gt; start;

    using Skipper = qi::space_type;
    qi::rule&lt;It, Ast::Task(), Skipper&gt;      task_;
    qi::rule&lt;It, Ast::Class(), Skipper&gt;     class_, type_;
    qi::rule&lt;It, Ast::Simple(), Skipper&gt;    simple_;
    qi::rule&lt;It, Ast::Complex(), Skipper&gt;   complex_;
    qi::rule&lt;It, Ast::Container(), Skipper&gt; container_;
    qi::rule&lt;It, Ast::Enum(), Skipper&gt;      enum_;
    qi::rule&lt;It, Ast::Datatype(), Skipper&gt;  datatype_;
    qi::rule&lt;It, Ast::Value(), Skipper&gt;     default_;
    qi::rule&lt;It, Ast::Inherit(), Skipper&gt;     inherit_;

    // lexemes:
    qi::rule&lt;It, Ast::Id()&gt;      id_;
    qi::rule&lt;It, Ast::Literal()&gt; literal_;
    qi::rule&lt;It, Ast::Value()&gt;   value_;
    qi::rule&lt;It, Ast::Number()&gt;  number_;
};

}

#include <pugixml.hpp>
namespace Generate {
using namespace Ast;

struct XML {
    using Node = pugi::xml_node;

    // callable for variant visiting:
    template &lt;typename T&gt; void operator()(Node parent, T const&amp; node) const { apply(parent, node); }

  private:
    template &lt;typename... Ts&gt;
    void apply(Node parent, boost::variant&lt;Ts...&gt; const&amp; v) const {
        using std::placeholders::_1;
        boost::apply_visitor(std::bind(*this, parent, _1), v);
    }

    void apply(Node parent, Ast::Number const&amp; num) const {
        named_child(parent, &quot;num&quot;).text().set(num);
    }

    void apply(Node parent, Ast::Id const&amp; id) const {
        named_child(parent, &quot;identifier&quot;).text().set(id.c_str());
    }

    void apply(Node parent, Ast::Literal const&amp; literal) const {
        named_child(parent, &quot;literal&quot;).text().set(literal.c_str());
    }

    void apply(Node parent, Ast::Datatype const&amp; datatype) const {
        named_child(parent, &quot;datatype&quot;).text().set(datatype.c_str());
    }

    template &lt;typename T&gt; void apply(Node parent, boost::optional&lt;T&gt; const&amp; opt) const {
        if (opt)
            apply(parent, *opt);
    }

    void apply(Node parent, Simple const&amp; s) const {
        auto simple = named_child(parent, &quot;simple&quot;);
        apply(simple, s.id);
        apply(simple, s.literal);
        apply(simple, s.enumeration);
        apply(simple, s.datatype);
        if (s.default_.has_value()) {
            apply(named_child(simple, &quot;default&quot;), *s.default_);
        }
    }

    void apply(Node parent, Enum const&amp; e) const {
        auto enum_ = named_child(parent, &quot;enumeration&quot;);
        for (auto&amp; v : e)
            named_child(enum_, &quot;word&quot;).text().set(v.c_str());
    }

    void apply(Node parent, Inherit const&amp; inh) const {
        auto inherit = named_child(parent, &quot;inherit&quot;);
        apply(inherit, inh.id);
    }

    void apply(Node parent, Complex const&amp; c) const {
        auto complex_ = named_child(parent, &quot;complex&quot;);
        apply(complex_, c.id);
        apply(complex_, c.literal);
        for (auto&amp; m : c.members)
            apply(complex_, m);
    }

    void apply(Node parent, Container const&amp; c) const {
        auto cont = named_child(parent, &quot;container&quot;);
        apply(cont, c.id);
        apply(cont, c.literal);
        apply(cont, c.element);
    }

    void apply(Node parent, Task const&amp; t) const {
        auto task = named_child(parent, &quot;task&quot;);
        for (auto&amp; c : t)
            apply(task.append_child(&quot;class&quot;), c);
    }

  private:
    Node named_child(Node parent, std::string const&amp; name) const {
        auto child = parent.append_child();
        child.set_name(name.c_str());
        return child;
    }
};

} // namespace Generate

int main() {
using It = std::string_view::const_iterator;
static const Parser::Task<It> p;
static const Generate::XML to_xml;

for (std::string_view input : {
    R&quot;(
		Class Simple caption;
		Class Simple test enumeration(opt1, opt2, opt3, opt4);
		Class Simple my_var datatype restriction;
		Class Simple var2 Default 0;
		Class Complex complexType (
			Inherit Class1:Variable2
			Inherit Class1:Variable2
			Inherit Class2:Variable1
		);
	)&quot;
}) {
    try {
        Ast::Task t;

        if (qi::parse(begin(input), end(input), p, t)) {
            pugi::xml_document doc;
            to_xml(doc.root(), t);
            doc.print(std::cout, &quot;  &quot;, pugi::format_default);
            std::cout &lt;&lt; std::endl;
        } else {
            std::cout &lt;&lt; &quot; -&gt; INVALID&quot; &lt;&lt; std::endl;
        }
    } catch (qi::expectation_failure&lt;It&gt; const&amp; ef) {
        auto f    = begin(input);
        auto p    = ef.first - input.begin();
        auto bol  = input.find_last_of(&quot;\r\n&quot;, p) + 1;
        auto line = std::count(f, f + bol, &#39;\n&#39;) + 1;
        auto eol  = input.find_first_of(&quot;\r\n&quot;, p);

        std::cerr &lt;&lt; &quot; -&gt; EXPECTED &quot; &lt;&lt; ef.what_ &lt;&lt; &quot; in line:&quot; &lt;&lt; line &lt;&lt; &quot;\n&quot;
            &lt;&lt; input.substr(bol, eol - bol) &lt;&lt; &quot;\n&quot;
            &lt;&lt; std::setw(p - bol) &lt;&lt; &quot;&quot;
            &lt;&lt; &quot;^--- here&quot; &lt;&lt; std::endl;
    }
}

}


When I compile this new update with current std/c++14 settings, it throw this error

Error C2440 'static_cast': cannot convert from 'const T_' to 'Attribute'


Even though I&#39;m not suppose to, I still tried to compiled this with **std/c++17** options and get the same error.  But if I compile this with **std/c++20** option using Visual Studio 2022.  Then it compiled and work as expected.
Same thing happen with **Compiler Explorer**.  This will throw the same error when I&#39;m using anything below x86-64 clang 16.0.0 option.
[**DEMO NEW CODE**](https://compiler-explorer.com/z/qWT1od9E7) that fail using old compiler
If I switch to use x86-64 clang 16.0.0 option, it will compile and work just fine.
[**DEMO NEW CODE**](https://compiler-explorer.com/z/7eG6qrbvK) that work with latest compiler
What I added is fairly simple but not sure why it would not supported.  Can somebody tell me what&#39;s wrong and why I need to use the latest compiler for this small change.
Thanks
Dylan
</details>
# 答案1
**得分**: 1
以下是您提供的内容的中文翻译:
&gt; it would not supported
这是一种表述。它是支持的。你只是没有编写足够的代码。
C++20添加了聚合体的可构建性。由于您没有它,要么[将构造函数添加到 `Inherit`][1]:
Inherit(Id id = {}) {}
***要么*** [像其他AST节点一样适应它][2]:
BOOST_FUSION_ADAPT_STRUCT(Ast::Inherit, id)
两者都可以。其他注意事项:
- 具有讽刺意味的是,您正在使用 `std::string_view`,它只在C++17中可用。 &#175;\\_(ツ)_/&#175; 
- 您还忘记将 `inherit_` 添加到调试节点列表
- 看起来您似乎滥用了 `type_` 规则来表示与语义无关的内容:继承。现在您的语法将接受 `Class Inherit bogus;` 这样的输入 &#129335; 
实际上,假设OO式继承,我期望的是类似于
**[在编译器资源中查看](https://compiler-explorer.com/z/74f78GWWG)**
// #define BOOST_SPIRIT_DEBUG 1
#include &lt;boost/fusion/adapted.hpp&gt;
#include &lt;boost/spirit/include/qi.hpp&gt;
#include &lt;iomanip&gt;
namespace qi = boost::spirit::qi;
namespace Ast {
using boost::recursive_wrapper;
template &lt;typename&gt; struct flavour_hack : std::char_traits&lt;char&gt; {};
template &lt;typename Tag&gt;
using String = std::basic_string&lt;char, flavour_hack&lt;Tag&gt; &gt;;
using Id       = String&lt;struct TagId&gt;;
using Literal  = String&lt;struct TagLiteral&gt;;
using Datatype = String&lt;struct TagDatatype&gt;;
struct Base {
Id      id;
Literal literal;
};
using Ids  = std::vector&lt;Id&gt;;
using Enum = Ids;
using Number = double;
using Value  = boost::variant&lt;Literal, Number, Id&gt;;
struct Simple : Base {
boost::optional&lt;Enum&gt;     enumeration;
boost::optional&lt;Datatype&gt; datatype;
boost::optional&lt;Value&gt;    default_;
};
struct Complex;
struct Container;
;
using Class = boost::variant&lt;   
Simple,                     
recursive_wrapper&lt;Complex&gt;, 
recursive_wrapper&lt;Container&gt;
&gt;;
using Classes = std::vector&lt;Class&gt;;
struct Container : Base { Class element; };
struct Complex   : Base { Ids bases; Classes members; };
using Task = std::vector&lt;Class&gt;;
} // namespace Ast
BOOST_FUSION_ADAPT_STRUCT(Ast::Simple,    id, literal, enumeration, datatype, default_);
BOOST_FUSION_ADAPT_STRUCT(Ast::Complex,   id, literal, bases, members)
BOOST_FUSION_ADAPT_STRUCT(Ast::Container, id, literal, element)
namespace Parser {
template &lt;typename It&gt; struct Task : qi::grammar&lt;It, Ast::Task()&gt; {
Task() : Task::base_type(start) {
using namespace qi;
start = skip(space)[task_];
// 词法单元:
id_      = raw[alpha &gt;&gt; *(alnum | &#39;_&#39; | &#39;:&#39;)];
literal_ = &#39;&quot;&#39; &gt; *(&#39;\\&#39; &gt;&gt; char_ | ~char_(&#39;&quot;&#39;)) &gt; &#39;&quot;&#39;;
auto optlit = copy(literal_ | attr(std::string(&quot; &quot;))); // 奇怪,但可以
task_      = *class_ &gt; eoi;
type_      = simple_ | complex_ | container_;
class_     = lit(&quot;Class&quot;) &gt; type_ &gt; &#39;;&#39;;
simple_    = lit(&quot;Simple&quot;) &gt;&gt; id_ &gt;&gt; optlit &gt;&gt; -enum_ &gt;&gt; -datatype_ &gt;&gt; -default_;
inherit_   = lit(&quot;Inherit&quot;) &gt;&gt; id_;
complex_   = lit(&quot;Complex&quot;) &gt;&gt; id_ &gt;&gt; optlit &gt;&gt; &#39;(&#39; &gt;&gt; *inherit_ &gt;&gt; *type_ &gt;&gt; &#39;)&#39;;
container_ = lit(&quot;Container&quot;) &gt;&gt; id_ &gt;&gt; optlit &gt;&gt; &#39;(&#39; &gt;&gt; type_ &gt; &#39;)&#39;;
enum_      = lit(&quot;enumeration&quot;) &gt;&gt; &#39;(&#39; &gt;&gt; -(id_ % &#39;,&#39;) &gt; &#39;)&#39;;
datatype_  = lit(&quot;datatype&quot;) &gt;&gt; id_;
value_     = literal_ | number_ | id_;
number_    = double_;
default_   = lit(&quot;Default&quot;) &gt;&gt; value_;
BOOST_SPIRIT_DEBUG_NODES(
(task_)(class_)(type_)(simple_)(complex_)(container_)(enum_)(datatype_)
(default_)(id_)(literal_)(value_)(number_)(inherit_)
)
}
private:
qi::rule&lt;It, Ast::Task()&gt; start;
using Skipper = qi::space_type;
qi::rule&lt;It, Ast::Task(), Skipper&gt;      task_;
qi::rule&lt;It, Ast::Class(), Skipper&gt;     class_, type_;
qi::rule&lt;It, Ast::Simple(), Skipper&gt;    simple_;
qi::rule&lt;It, Ast::Complex(), Skipper&gt;   complex_;
qi::rule&lt;It, Ast::Container(), Skipper&gt; container_;
qi::rule&lt;It, Ast::Enum(), Skipper&gt;      enum_;
qi::rule&lt;It, Ast::Dat
<details>
<summary>英文:</summary>
&gt; it would not supported
That&#39;s framing. It&#39;s supported. You just don&#39;t write enough code.
C++20 adds () constructability of aggregates. Since you don&#39;t have it, either [add the constructor to `Inherit`][1]:
Inherit(Id id = {}) : id(std::move(id)) {}
***OR*** [adapt it like the other AST nodes][2]:
BOOST_FUSION_ADAPT_STRUCT(Ast::Inherit, id)
Both work. Other notes:
- ironically you were using `std::string_view` which [is c++17 only](https://en.cppreference.com/w/cpp/string/basic_string_view) &#175;\\_(ツ)_/&#175; 
- you also forgot to add `inherit_` to the debug node list
- it looks as though you&#39;re abusing the `type_` rule to express something semantically independent: inheritance. Now your grammar will accept `Class Inherit bogus;` as well &#129335; 
In fact, assuming OO-like inheritance I&#39;d expect something more like
**[Live On Compiler Explorer](https://compiler-explorer.com/z/74f78GWWG)**
// #define BOOST_SPIRIT_DEBUG 1
#include &lt;boost/fusion/adapted.hpp&gt;
#include &lt;boost/spirit/include/qi.hpp&gt;
#include &lt;iomanip&gt;
namespace qi = boost::spirit::qi;
namespace Ast {
using boost::recursive_wrapper;
template &lt;typename&gt; struct flavour_hack : std::char_traits&lt;char&gt; {};
template &lt;typename Tag&gt;
using String = std::basic_string&lt;char, flavour_hack&lt;Tag&gt; &gt;;
using Id       = String&lt;struct TagId&gt;;
using Literal  = String&lt;struct TagLiteral&gt;;
using Datatype = String&lt;struct TagDatatype&gt;;
struct Base {
Id      id;
Literal literal;
};
using Ids  = std::vector&lt;Id&gt;;
using Enum = Ids;
using Number = double;
using Value  = boost::variant&lt;Literal, Number, Id&gt;;
struct Simple : Base {
boost::optional&lt;Enum&gt;     enumeration;
boost::optional&lt;Datatype&gt; datatype;
boost::optional&lt;Value&gt;    default_;
};
struct Complex;
struct Container;
;
using Class = boost::variant&lt;   
Simple,                     
recursive_wrapper&lt;Complex&gt;, 
recursive_wrapper&lt;Container&gt;
&gt;;
using Classes = std::vector&lt;Class&gt;;
struct Container : Base { Class element; };
struct Complex   : Base { Ids bases; Classes members; };
using Task = std::vector&lt;Class&gt;;
} // namespace Ast
BOOST_FUSION_ADAPT_STRUCT(Ast::Simple,    id, literal, enumeration, datatype, default_);
BOOST_FUSION_ADAPT_STRUCT(Ast::Complex,   id, literal, bases, members)
BOOST_FUSION_ADAPT_STRUCT(Ast::Container, id, literal, element)
namespace Parser {
template &lt;typename It&gt; struct Task : qi::grammar&lt;It, Ast::Task()&gt; {
Task() : Task::base_type(start) {
using namespace qi;
start = skip(space)[task_];
// lexemes:
id_      = raw[alpha &gt;&gt; *(alnum | &#39;_&#39; | &#39;:&#39;)];
literal_ = &#39;&quot;&#39; &gt; *(&#39;\\&#39; &gt;&gt; char_ | ~char_(&#39;&quot;&#39;)) &gt; &#39;&quot;&#39;;
auto optlit = copy(literal_ | attr(std::string(&quot; &quot;))); // weird, but okay
task_      = *class_ &gt; eoi;
type_      = simple_ | complex_ | container_;
class_     = lit(&quot;Class&quot;) &gt; type_ &gt; &#39;;&#39;;
simple_    = lit(&quot;Simple&quot;) &gt;&gt; id_ &gt;&gt; optlit &gt;&gt; -enum_ &gt;&gt; -datatype_ &gt;&gt; -default_;
inherit_   = lit(&quot;Inherit&quot;) &gt;&gt; id_;
complex_   = lit(&quot;Complex&quot;) &gt;&gt; id_ &gt;&gt; optlit &gt;&gt; &#39;(&#39; &gt;&gt; *inherit_ &gt;&gt; *type_ &gt;&gt; &#39;)&#39;;
container_ = lit(&quot;Container&quot;) &gt;&gt; id_ &gt;&gt; optlit &gt;&gt; &#39;(&#39; &gt;&gt; type_ &gt; &#39;)&#39;;
enum_      = lit(&quot;enumeration&quot;) &gt;&gt; &#39;(&#39; &gt;&gt; -(id_ % &#39;,&#39;) &gt; &#39;)&#39;;
datatype_  = lit(&quot;datatype&quot;) &gt;&gt; id_;
value_     = literal_ | number_ | id_;
number_    = double_;
default_   = lit(&quot;Default&quot;) &gt;&gt; value_;
BOOST_SPIRIT_DEBUG_NODES(
(task_)(class_)(type_)(simple_)(complex_)(container_)(enum_)(datatype_)
(default_)(id_)(literal_)(value_)(number_)(inherit_)
)
}
private:
qi::rule&lt;It, Ast::Task()&gt; start;
using Skipper = qi::space_type;
qi::rule&lt;It, Ast::Task(), Skipper&gt;      task_;
qi::rule&lt;It, Ast::Class(), Skipper&gt;     class_, type_;
qi::rule&lt;It, Ast::Simple(), Skipper&gt;    simple_;
qi::rule&lt;It, Ast::Complex(), Skipper&gt;   complex_;
qi::rule&lt;It, Ast::Container(), Skipper&gt; container_;
qi::rule&lt;It, Ast::Enum(), Skipper&gt;      enum_;
qi::rule&lt;It, Ast::Datatype(), Skipper&gt;  datatype_;
qi::rule&lt;It, Ast::Value(), Skipper&gt;     default_;
qi::rule&lt;It, Ast::Id(), Skipper&gt;        inherit_;
// lexemes:
qi::rule&lt;It, Ast::Id()&gt;      id_;
qi::rule&lt;It, Ast::Literal()&gt; literal_;
qi::rule&lt;It, Ast::Value()&gt;   value_;
qi::rule&lt;It, Ast::Number()&gt;  number_;
};
}
#include &lt;pugixml.hpp&gt;
namespace Generate {
using namespace Ast;
struct XML {
using Node = pugi::xml_node;
// callable for variant visiting:
template &lt;typename T&gt; void operator()(Node parent, T const&amp; node) const { apply(parent, node); }
private:
template &lt;typename... Ts&gt;
void apply(Node parent, boost::variant&lt;Ts...&gt; const&amp; v) const {
using std::placeholders::_1;
boost::apply_visitor(std::bind(*this, parent, _1), v);
}
void apply(Node parent, Ast::Number const&amp; num) const {
named_child(parent, &quot;num&quot;).text().set(num);
}
void apply(Node parent, Ast::Id const&amp; id) const {
named_child(parent, &quot;identifier&quot;).text().set(id.c_str());
}
void apply(Node parent, Ast::Literal const&amp; literal) const {
named_child(parent, &quot;literal&quot;).text().set(literal.c_str());
}
void apply(Node parent, Ast::Datatype const&amp; datatype) const {
named_child(parent, &quot;datatype&quot;).text().set(datatype.c_str());
}
template &lt;typename T&gt; void apply(Node parent, boost::optional&lt;T&gt; const&amp; opt) const {
if (opt)
apply(parent, *opt);
}
void apply(Node parent, Simple const&amp; s) const {
auto simple = named_child(parent, &quot;simple&quot;);
apply(simple, s.id);
apply(simple, s.literal);
apply(simple, s.enumeration);
apply(simple, s.datatype);
if (s.default_.has_value()) {
apply(named_child(simple, &quot;default&quot;), *s.default_);
}
}
void apply(Node parent, Enum const&amp; e) const {
auto enum_ = named_child(parent, &quot;enumeration&quot;);
for (auto&amp; v : e)
named_child(enum_, &quot;word&quot;).text().set(v.c_str());
}
void apply(Node parent, Complex const&amp; c) const {
auto complex_ = named_child(parent, &quot;complex&quot;);
apply(complex_, c.id);
for (auto&amp; base : c.bases)
apply(named_child(complex_, &quot;inherit&quot;), base);
apply(complex_, c.literal);
for (auto&amp; m : c.members)
apply(complex_, m);
}
void apply(Node parent, Container const&amp; c) const {
auto cont = named_child(parent, &quot;container&quot;);
apply(cont, c.id);
apply(cont, c.literal);
apply(cont, c.element);
}
void apply(Node parent, Task const&amp; t) const {
auto task = named_child(parent, &quot;task&quot;);
for (auto&amp; c : t)
apply(task.append_child(&quot;class&quot;), c);
}
private:
Node named_child(Node parent, std::string const&amp; name) const {
auto child = parent.append_child();
child.set_name(name.c_str());
return child;
}
};
} // namespace Generate
int main() { 
using It = std::string::const_iterator;
static const Parser::Task&lt;It&gt; p;
static const Generate::XML to_xml;
for (std::string const input : {
R&quot;(
Class Simple caption;
Class Simple test enumeration(opt1, opt2, opt3, opt4);
Class Simple my_var datatype restriction;
Class Simple var2 Default 0;
Class Complex complexType (
Inherit Class1:Variable2
Inherit Class1:Variable2
Inherit Class2:Variable1
);
)&quot;
}) {
try {
Ast::Task t;
if (qi::parse(begin(input), end(input), p, t)) {
pugi::xml_document doc;
to_xml(doc.root(), t);
doc.print(std::cout, &quot;  &quot;, pugi::format_default);
std::cout &lt;&lt; std::endl;
} else {
std::cout &lt;&lt; &quot; -&gt; INVALID&quot; &lt;&lt; std::endl;
}
} catch (qi::expectation_failure&lt;It&gt; const&amp; ef) {
auto f    = begin(input);
auto p    = ef.first - input.begin();
auto bol  = input.find_last_of(&quot;\r\n&quot;, p) + 1;
auto line = std::count(f, f + bol, &#39;\n&#39;) + 1;
auto eol  = input.find_first_of(&quot;\r\n&quot;, p);
std::cerr &lt;&lt; &quot; -&gt; EXPECTED &quot; &lt;&lt; ef.what_ &lt;&lt; &quot; in line:&quot; &lt;&lt; line &lt;&lt; &quot;\n&quot;
&lt;&lt; input.substr(bol, eol - bol) &lt;&lt; &quot;\n&quot;
&lt;&lt; std::setw(p - bol) &lt;&lt; &quot;&quot;
&lt;&lt; &quot;^--- here&quot; &lt;&lt; std::endl;
}
}
}
Prints
```xml
&lt;task&gt;
&lt;class&gt;
&lt;simple&gt;
&lt;identifier&gt;caption&lt;/identifier&gt;
&lt;literal&gt; &lt;/literal&gt;
&lt;/simple&gt;
&lt;/class&gt;
&lt;class&gt;
&lt;simple&gt;
&lt;identifier&gt;test&lt;/identifier&gt;
&lt;literal&gt; &lt;/literal&gt;
&lt;enumeration&gt;
&lt;word&gt;opt1&lt;/word&gt;
&lt;word&gt;opt2&lt;/word&gt;
&lt;word&gt;opt3&lt;/word&gt;
&lt;word&gt;opt4&lt;/word&gt;
&lt;/enumeration&gt;
&lt;/simple&gt;
&lt;/class&gt;
&lt;class&gt;
&lt;simple&gt;
&lt;identifier&gt;my_var&lt;/identifier&gt;
&lt;literal&gt; &lt;/literal&gt;
&lt;datatype&gt;restriction&lt;/datatype&gt;
&lt;/simple&gt;
&lt;/class&gt;
&lt;class&gt;
&lt;simple&gt;
&lt;identifier&gt;var2&lt;/identifier&gt;
&lt;literal&gt; &lt;/literal&gt;
&lt;default&gt;
&lt;num&gt;0&lt;/num&gt;
&lt;/default&gt;
&lt;/simple&gt;
&lt;/class&gt;
&lt;class&gt;
&lt;complex&gt;
&lt;identifier&gt;complexType&lt;/identifier&gt;
&lt;inherit&gt;
&lt;identifier&gt;Class1:Variable2&lt;/identifier&gt;
&lt;/inherit&gt;
&lt;inherit&gt;
&lt;identifier&gt;Class1:Variable2&lt;/identifier&gt;
&lt;/inherit&gt;
&lt;inherit&gt;
&lt;identifier&gt;Class2:Variable1&lt;/identifier&gt;
&lt;/inherit&gt;
&lt;literal&gt; &lt;/literal&gt;
&lt;/complex&gt;
&lt;/class&gt;
&lt;/task&gt;

Note that

  • this doesn't incur a new AST node (since bases are just identifiers)
  • it doesn't accidentally accept Class x Inherit b;
  • it assumes that bases be listed before members (which is true in every programming language I can think of)

huangapple
  • 本文由 发表于 2023年6月9日 03:08:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76434997.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定