英文:
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>
以下是我已经完成的工作:
- 添加新的Inherit结构体
struct Inherit {
Id id;
};
- 将新的Inherit结构体添加到Class变体中
using Class = boost::variant<
Simple,
Inherit,
recursive_wrapper<Complex>,
recursive_wrapper<Container>
>;
- 现在更新规则以获取这些内容
id_ = raw[alpha >> *(alnum | '_' | ':')]; //将':'添加到标识符规则
...
type_ = simple_ | inherit_ | complex_ | container_;
...
inherit_ = lit("Inherit") >> id_
...
qi::rule<It, Ast::Inherit(), Skipper> inherit_;
- 更新XML规则以生成内容
void apply(Node parent, Inherit const& inh) const {
auto inherit_ = named_child(parent, "inherit");
apply(inherit_, inh.id);
}
完整代码见上方的代码块。
当我使用当前std/c++14设置编译这个新更新时,它会引发此错误:
错误 C2440“static_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'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 `':'` 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 <typename> struct flavour_hack : std::char_traits<char> {};
template <typename Tag>
using String = std::basic_string<char, flavour_hack<Tag> >;
using Id = String<struct TagId>;
using Literal = String<struct TagLiteral>;
using Datatype = String<struct TagDatatype>;
struct Base {
Id id;
Literal literal;
};
using Enum = std::vector<Id>;
using Number = double;
using Value = boost::variant<Literal, Number, Id>;
struct Simple : Base {
boost::optional<Enum> enumeration;
boost::optional<Datatype> datatype;
boost::optional<Value> default_;
};
struct Complex;
struct Container;
struct Inherit {
Id id;
};
using Class = boost::variant<
Simple,
Inherit,
recursive_wrapper<Complex>,
recursive_wrapper<Container>
>;
using Classes = std::vector<Class>;
struct Container : Base { Class element; };
struct Complex : Base { Classes members; };
using Task = std::vector<Class>;
} // 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 >> *(alnum | '_' | ':')];
literal_ = '"' > *('\\' >> char_ | ~char_('"')) > '"';
auto optlit = copy(literal_ | attr(std::string(" "))); // weird, but okay
task_ = *class_ > eoi;
type_ = simple_ | inherit_ | complex_ | container_;
class_ = lit("Class") > type_ > ';';
simple_ = lit("Simple") >> id_ >> optlit >> -enum_ >> -datatype_ >> -default_;
inherit_ = lit("Inherit") >> id_;
complex_ = lit("Complex") >> id_ >> optlit >> '(' >> *type_ >> ')';
container_ = lit("Container") >> id_ >> optlit >> '(' >> type_ > ')';
enum_ = lit("enumeration") >> '(' >> -(id_ % ',') > ')';
datatype_ = lit("datatype") >> id_;
value_ = literal_ | number_ | id_;
number_ = double_;
default_ = lit("Default") >> value_;
BOOST_SPIRIT_DEBUG_NODES(
(task_)(class_)(type_)(simple_)(complex_)(container_)(enum_)(datatype_)
(default_)(id_)(literal_)(value_)(number_)
)
}
private:
qi::rule<It, Ast::Task()> start;
using Skipper = qi::space_type;
qi::rule<It, Ast::Task(), Skipper> task_;
qi::rule<It, Ast::Class(), Skipper> class_, type_;
qi::rule<It, Ast::Simple(), Skipper> simple_;
qi::rule<It, Ast::Complex(), Skipper> complex_;
qi::rule<It, Ast::Container(), Skipper> container_;
qi::rule<It, Ast::Enum(), Skipper> enum_;
qi::rule<It, Ast::Datatype(), Skipper> datatype_;
qi::rule<It, Ast::Value(), Skipper> default_;
qi::rule<It, Ast::Inherit(), Skipper> inherit_;
// lexemes:
qi::rule<It, Ast::Id()> id_;
qi::rule<It, Ast::Literal()> literal_;
qi::rule<It, Ast::Value()> value_;
qi::rule<It, Ast::Number()> number_;
};
}
#include <pugixml.hpp>
namespace Generate {
using namespace Ast;
struct XML {
using Node = pugi::xml_node;
// callable for variant visiting:
template <typename T> void operator()(Node parent, T const& node) const { apply(parent, node); }
private:
template <typename... Ts>
void apply(Node parent, boost::variant<Ts...> const& v) const {
using std::placeholders::_1;
boost::apply_visitor(std::bind(*this, parent, _1), v);
}
void apply(Node parent, Ast::Number const& num) const {
named_child(parent, "num").text().set(num);
}
void apply(Node parent, Ast::Id const& id) const {
named_child(parent, "identifier").text().set(id.c_str());
}
void apply(Node parent, Ast::Literal const& literal) const {
named_child(parent, "literal").text().set(literal.c_str());
}
void apply(Node parent, Ast::Datatype const& datatype) const {
named_child(parent, "datatype").text().set(datatype.c_str());
}
template <typename T> void apply(Node parent, boost::optional<T> const& opt) const {
if (opt)
apply(parent, *opt);
}
void apply(Node parent, Simple const& s) const {
auto simple = named_child(parent, "simple");
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, "default"), *s.default_);
}
}
void apply(Node parent, Enum const& e) const {
auto enum_ = named_child(parent, "enumeration");
for (auto& v : e)
named_child(enum_, "word").text().set(v.c_str());
}
void apply(Node parent, Inherit const& inh) const {
auto inherit = named_child(parent, "inherit");
apply(inherit, inh.id);
}
void apply(Node parent, Complex const& c) const {
auto complex_ = named_child(parent, "complex");
apply(complex_, c.id);
apply(complex_, c.literal);
for (auto& m : c.members)
apply(complex_, m);
}
void apply(Node parent, Container const& c) const {
auto cont = named_child(parent, "container");
apply(cont, c.id);
apply(cont, c.literal);
apply(cont, c.element);
}
void apply(Node parent, Task const& t) const {
auto task = named_child(parent, "task");
for (auto& c : t)
apply(task.append_child("class"), c);
}
private:
Node named_child(Node parent, std::string const& 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"(
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
);
)"
}) {
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, " ", pugi::format_default);
std::cout << std::endl;
} else {
std::cout << " -> INVALID" << std::endl;
}
} catch (qi::expectation_failure<It> const& ef) {
auto f = begin(input);
auto p = ef.first - input.begin();
auto bol = input.find_last_of("\r\n", p) + 1;
auto line = std::count(f, f + bol, '\n') + 1;
auto eol = input.find_first_of("\r\n", p);
std::cerr << " -> EXPECTED " << ef.what_ << " in line:" << line << "\n"
<< input.substr(bol, eol - bol) << "\n"
<< std::setw(p - bol) << ""
<< "^--- here" << 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'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'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's wrong and why I need to use the latest compiler for this small change.
Thanks
Dylan
</details>
# 答案1
**得分**: 1
以下是您提供的内容的中文翻译:
> 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中可用。 ¯\\_(ツ)_/¯
- 您还忘记将 `inherit_` 添加到调试节点列表
- 看起来您似乎滥用了 `type_` 规则来表示与语义无关的内容:继承。现在您的语法将接受 `Class Inherit bogus;` 这样的输入 🤷
实际上,假设OO式继承,我期望的是类似于
**[在编译器资源中查看](https://compiler-explorer.com/z/74f78GWWG)**
// #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 <typename> struct flavour_hack : std::char_traits<char> {};
template <typename Tag>
using String = std::basic_string<char, flavour_hack<Tag> >;
using Id = String<struct TagId>;
using Literal = String<struct TagLiteral>;
using Datatype = String<struct TagDatatype>;
struct Base {
Id id;
Literal literal;
};
using Ids = std::vector<Id>;
using Enum = Ids;
using Number = double;
using Value = boost::variant<Literal, Number, Id>;
struct Simple : Base {
boost::optional<Enum> enumeration;
boost::optional<Datatype> datatype;
boost::optional<Value> default_;
};
struct Complex;
struct Container;
;
using Class = boost::variant<
Simple,
recursive_wrapper<Complex>,
recursive_wrapper<Container>
>;
using Classes = std::vector<Class>;
struct Container : Base { Class element; };
struct Complex : Base { Ids bases; Classes members; };
using Task = std::vector<Class>;
} // 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 <typename It> struct Task : qi::grammar<It, Ast::Task()> {
Task() : Task::base_type(start) {
using namespace qi;
start = skip(space)[task_];
// 词法单元:
id_ = raw[alpha >> *(alnum | '_' | ':')];
literal_ = '"' > *('\\' >> char_ | ~char_('"')) > '"';
auto optlit = copy(literal_ | attr(std::string(" "))); // 奇怪,但可以
task_ = *class_ > eoi;
type_ = simple_ | complex_ | container_;
class_ = lit("Class") > type_ > ';';
simple_ = lit("Simple") >> id_ >> optlit >> -enum_ >> -datatype_ >> -default_;
inherit_ = lit("Inherit") >> id_;
complex_ = lit("Complex") >> id_ >> optlit >> '(' >> *inherit_ >> *type_ >> ')';
container_ = lit("Container") >> id_ >> optlit >> '(' >> type_ > ')';
enum_ = lit("enumeration") >> '(' >> -(id_ % ',') > ')';
datatype_ = lit("datatype") >> id_;
value_ = literal_ | number_ | id_;
number_ = double_;
default_ = lit("Default") >> value_;
BOOST_SPIRIT_DEBUG_NODES(
(task_)(class_)(type_)(simple_)(complex_)(container_)(enum_)(datatype_)
(default_)(id_)(literal_)(value_)(number_)(inherit_)
)
}
private:
qi::rule<It, Ast::Task()> start;
using Skipper = qi::space_type;
qi::rule<It, Ast::Task(), Skipper> task_;
qi::rule<It, Ast::Class(), Skipper> class_, type_;
qi::rule<It, Ast::Simple(), Skipper> simple_;
qi::rule<It, Ast::Complex(), Skipper> complex_;
qi::rule<It, Ast::Container(), Skipper> container_;
qi::rule<It, Ast::Enum(), Skipper> enum_;
qi::rule<It, Ast::Dat
<details>
<summary>英文:</summary>
> it would not supported
That's framing. It's supported. You just don't write enough code.
C++20 adds () constructability of aggregates. Since you don'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) ¯\\_(ツ)_/¯
- you also forgot to add `inherit_` to the debug node list
- it looks as though you're abusing the `type_` rule to express something semantically independent: inheritance. Now your grammar will accept `Class Inherit bogus;` as well 🤷
In fact, assuming OO-like inheritance I'd expect something more like
**[Live On Compiler Explorer](https://compiler-explorer.com/z/74f78GWWG)**
// #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 <typename> struct flavour_hack : std::char_traits<char> {};
template <typename Tag>
using String = std::basic_string<char, flavour_hack<Tag> >;
using Id = String<struct TagId>;
using Literal = String<struct TagLiteral>;
using Datatype = String<struct TagDatatype>;
struct Base {
Id id;
Literal literal;
};
using Ids = std::vector<Id>;
using Enum = Ids;
using Number = double;
using Value = boost::variant<Literal, Number, Id>;
struct Simple : Base {
boost::optional<Enum> enumeration;
boost::optional<Datatype> datatype;
boost::optional<Value> default_;
};
struct Complex;
struct Container;
;
using Class = boost::variant<
Simple,
recursive_wrapper<Complex>,
recursive_wrapper<Container>
>;
using Classes = std::vector<Class>;
struct Container : Base { Class element; };
struct Complex : Base { Ids bases; Classes members; };
using Task = std::vector<Class>;
} // 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 <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 >> *(alnum | '_' | ':')];
literal_ = '"' > *('\\' >> char_ | ~char_('"')) > '"';
auto optlit = copy(literal_ | attr(std::string(" "))); // weird, but okay
task_ = *class_ > eoi;
type_ = simple_ | complex_ | container_;
class_ = lit("Class") > type_ > ';';
simple_ = lit("Simple") >> id_ >> optlit >> -enum_ >> -datatype_ >> -default_;
inherit_ = lit("Inherit") >> id_;
complex_ = lit("Complex") >> id_ >> optlit >> '(' >> *inherit_ >> *type_ >> ')';
container_ = lit("Container") >> id_ >> optlit >> '(' >> type_ > ')';
enum_ = lit("enumeration") >> '(' >> -(id_ % ',') > ')';
datatype_ = lit("datatype") >> id_;
value_ = literal_ | number_ | id_;
number_ = double_;
default_ = lit("Default") >> value_;
BOOST_SPIRIT_DEBUG_NODES(
(task_)(class_)(type_)(simple_)(complex_)(container_)(enum_)(datatype_)
(default_)(id_)(literal_)(value_)(number_)(inherit_)
)
}
private:
qi::rule<It, Ast::Task()> start;
using Skipper = qi::space_type;
qi::rule<It, Ast::Task(), Skipper> task_;
qi::rule<It, Ast::Class(), Skipper> class_, type_;
qi::rule<It, Ast::Simple(), Skipper> simple_;
qi::rule<It, Ast::Complex(), Skipper> complex_;
qi::rule<It, Ast::Container(), Skipper> container_;
qi::rule<It, Ast::Enum(), Skipper> enum_;
qi::rule<It, Ast::Datatype(), Skipper> datatype_;
qi::rule<It, Ast::Value(), Skipper> default_;
qi::rule<It, Ast::Id(), Skipper> inherit_;
// lexemes:
qi::rule<It, Ast::Id()> id_;
qi::rule<It, Ast::Literal()> literal_;
qi::rule<It, Ast::Value()> value_;
qi::rule<It, Ast::Number()> number_;
};
}
#include <pugixml.hpp>
namespace Generate {
using namespace Ast;
struct XML {
using Node = pugi::xml_node;
// callable for variant visiting:
template <typename T> void operator()(Node parent, T const& node) const { apply(parent, node); }
private:
template <typename... Ts>
void apply(Node parent, boost::variant<Ts...> const& v) const {
using std::placeholders::_1;
boost::apply_visitor(std::bind(*this, parent, _1), v);
}
void apply(Node parent, Ast::Number const& num) const {
named_child(parent, "num").text().set(num);
}
void apply(Node parent, Ast::Id const& id) const {
named_child(parent, "identifier").text().set(id.c_str());
}
void apply(Node parent, Ast::Literal const& literal) const {
named_child(parent, "literal").text().set(literal.c_str());
}
void apply(Node parent, Ast::Datatype const& datatype) const {
named_child(parent, "datatype").text().set(datatype.c_str());
}
template <typename T> void apply(Node parent, boost::optional<T> const& opt) const {
if (opt)
apply(parent, *opt);
}
void apply(Node parent, Simple const& s) const {
auto simple = named_child(parent, "simple");
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, "default"), *s.default_);
}
}
void apply(Node parent, Enum const& e) const {
auto enum_ = named_child(parent, "enumeration");
for (auto& v : e)
named_child(enum_, "word").text().set(v.c_str());
}
void apply(Node parent, Complex const& c) const {
auto complex_ = named_child(parent, "complex");
apply(complex_, c.id);
for (auto& base : c.bases)
apply(named_child(complex_, "inherit"), base);
apply(complex_, c.literal);
for (auto& m : c.members)
apply(complex_, m);
}
void apply(Node parent, Container const& c) const {
auto cont = named_child(parent, "container");
apply(cont, c.id);
apply(cont, c.literal);
apply(cont, c.element);
}
void apply(Node parent, Task const& t) const {
auto task = named_child(parent, "task");
for (auto& c : t)
apply(task.append_child("class"), c);
}
private:
Node named_child(Node parent, std::string const& 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<It> p;
static const Generate::XML to_xml;
for (std::string const input : {
R"(
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
);
)"
}) {
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, " ", pugi::format_default);
std::cout << std::endl;
} else {
std::cout << " -> INVALID" << std::endl;
}
} catch (qi::expectation_failure<It> const& ef) {
auto f = begin(input);
auto p = ef.first - input.begin();
auto bol = input.find_last_of("\r\n", p) + 1;
auto line = std::count(f, f + bol, '\n') + 1;
auto eol = input.find_first_of("\r\n", p);
std::cerr << " -> EXPECTED " << ef.what_ << " in line:" << line << "\n"
<< input.substr(bol, eol - bol) << "\n"
<< std::setw(p - bol) << ""
<< "^--- here" << std::endl;
}
}
}
Prints
```xml
<task>
<class>
<simple>
<identifier>caption</identifier>
<literal> </literal>
</simple>
</class>
<class>
<simple>
<identifier>test</identifier>
<literal> </literal>
<enumeration>
<word>opt1</word>
<word>opt2</word>
<word>opt3</word>
<word>opt4</word>
</enumeration>
</simple>
</class>
<class>
<simple>
<identifier>my_var</identifier>
<literal> </literal>
<datatype>restriction</datatype>
</simple>
</class>
<class>
<simple>
<identifier>var2</identifier>
<literal> </literal>
<default>
<num>0</num>
</default>
</simple>
</class>
<class>
<complex>
<identifier>complexType</identifier>
<inherit>
<identifier>Class1:Variable2</identifier>
</inherit>
<inherit>
<identifier>Class1:Variable2</identifier>
</inherit>
<inherit>
<identifier>Class2:Variable1</identifier>
</inherit>
<literal> </literal>
</complex>
</class>
</task>
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)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论