Boost Spirit 结构 boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false> 中没有类型名称 'value_type'
Boost Spirit No type names 'value_type' in struct boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>
在此 之后,我将结构更改为 class 并添加了其他复制构造函数并重载了 = 运算符。我还为解析器添加了识别换行符、制表符等的功能。但是,由于一些错误,代码没有 运行。我知道如何修复它。下面是错误消息和代码的片段。
***/main.cpp:130:24: required from ‘Parser::BNF<Iterator>::BNF() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
****/main.cpp:156:52: required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:41:16: error: no type named ‘value_type’ in ‘struct boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>’
41 | struct is_container_of_ranges
| ^~~~~~~~~~~~~~~~~~~~~~
......
***/main.cpp:130:24: required from ‘Parser::BNF<Iterator>::BNF() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
***/main.cpp:156:52: required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:72:17: error: no matching function for call to ‘boost::spirit::traits::assign_to_attribute_from_iterators<boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>, __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, void>::call(const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>&, boost::spirit::traits::detail::is_container_of_ranges<boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false> >)’
72 | call(first, last, attr, detail::is_container_of_ranges<Attribute>());
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace Ast {
enum class TermType {
literal, rule_name
};
class Term {
private:
std::string data;
TermType term_type;
public:
Term() = default;
void set_data(std::string const &data) {
this->data = data;
}
std::string const &get_data() const {
return data;
}
void set_term_type(TermType term_type) {
this->term_type = term_type;
}
TermType const &get_term_type() const {
return term_type;
}
bool operator==(const Term &term) {
if (!data.compare(term.data) && term_type == term.term_type)
return true;
return false;
}
Term &operator=(const Term &term) {
if (this != &term) {
data = term.data;
term_type = term.term_type;
}
return *this;
}
Term(const Term &term) {
data = term.data;
term_type = term.term_type;
}
};
class List : public std::vector<Term> {
// attributes not required for parsing
// constructor, copy constructor, = operator overloading
};
using Expression = std::vector<List>;
class Rule {
private:
Term name; // lhs
Expression rhs;
public:
void set_rule_name(const Term &rule_name) {
this->name = rule_name;
}
Term const &get_rule_name() const {
return name;
}
void set_expression(const Expression &expression) {
rhs = expression;
}
Expression const &get_expression() const {
return rhs;
}
// constructor, copy constructor, = operator overloading
};
using Syntax = std::list<Rule>;
}
BOOST_FUSION_ADAPT_ADT(Ast::Term,
(obj.get_data(), obj.set_data(val))
(obj.get_term_type(), obj.set_term_type(val)))
BOOST_FUSION_ADAPT_ADT(Ast::Rule,
(obj.get_rule_name(), obj.set_rule_name(val))
(obj.get_expression(), obj.set_expression(val)))
namespace Parser {
template<typename Iterator>
struct BNF : qi::grammar<Iterator, Ast::Syntax()> {
BNF()
: BNF::base_type(start) {
using namespace qi;
_blank = blank;
unesc_char.add
("\a", '\a')
("\b", '\b')
("\f", '\f')
("\n", '\n')
("\r", '\r')
("\t", '\t')
("\v", '\v')
("\\", '\')
("\'", '\'')
("\\"", '\"');
_skipper = blank | (eol >> !skip(_blank.alias())[_rule]);
start = skip(_skipper.alias())[_rule % +eol];
_rule = _rule_name >> "::=" >> _expression;
_expression = _list % '|';
_list = +_term;
_term = _literal >> attr(Ast::TermType::literal)
| _rule_name;
_literal = unesc_char | '"' >> *(_character - '"') >> '"'
| "'" >> *(_character - "'") >> "'";
_character = alnum | char_("\"'| !#$%&()*+,./:;>=<?@]\^_`{}~[-");
_rule_name = '<' >> qi::raw[(alpha >> *(alnum | char_('-')))] >> '>'
>> attr(Ast::TermType::rule_name);
// clang-format on
BOOST_SPIRIT_DEBUG_NODES(
(_rule)(_expression)(_list)(_literal)(_character)(_rule_name))
}
private:
using Skipper = qi::rule<Iterator>;
Skipper _skipper, _blank;
qi::rule<Iterator, Ast::Syntax()> start;
qi::rule<Iterator, Ast::Rule(), Skipper> _rule;
qi::rule<Iterator, Ast::Expression(), Skipper> _expression;
qi::rule<Iterator, Ast::List(), Skipper> _list;
// lexemes
qi::rule<Iterator, Ast::Term()> _term;
qi::rule<Iterator, std::string()> _literal;
qi::rule<Iterator, Ast::Term()> _rule_name;
qi::rule<Iterator, char()> _character;
qi::symbols<char const, char const> unesc_char;
};
}
int main() {
Parser::BNF<std::string::const_iterator> const parser;
std::string const input = R"(<code> ::= <letter><digit> | <letter><digit><code>
<letter> ::= "a" | "b" | "c" | "d" | "e"
| "f" | "g" | "h" | "i"
<digit> ::= "0" | "1 \n 3" | "2" | "3 \t yy" |
"4"
)";
auto it = input.begin(), itEnd = input.end();
Ast::Syntax syntax;
if (parse(it, itEnd, parser, syntax)) {
for (auto &rule : syntax) {
std::cout << rule.get_rule_name().get_data() << " ::= ";
std::string sep;
for (auto &list : rule.get_expression()) {
std::cout << sep;
for (auto &term: list) { std::cout << term.get_data(); }
sep = " | ";
};
std::cout << "\n";
}
} else {
std::cout << "Failed\n";
}
if (it != itEnd)
std::cout << "Remaining: " << std::quoted(std::string(it, itEnd)) << "\n";
}
由于某些原因,属性兼容性规则在调用 setter 方法时不适用。您可以在顶部强制使用 as_string
问题:
_rule_name = '<' >> qi::as_string [ qi::raw[ (alpha >> *(alnum | char_('-'))) ] ] >>
'>' >> attr(Ast::TermType::rule_name);
侧面观察
2 月 27 日 23:35 :
My other concern is the inheritance from string class. I thought was bad to do that.
显然,你改变了主意。我真的不知道为什么。在这种情况下,您可能不应该这样做。快捷方式是“好的”,除非它们不是。参见例如More silent behaviour changes with C++20 three-way comparison and https://quuxplusone.github.io/blog/2018/12/11/dont-inherit-from-std-types/
另外,提防quasi-classes (PDF)。你能告诉我为什么结构不是更好吗?您只是重新定义了很多标准行为(copy ctor/assignment 应该是默认的,就像 C++20 上的 operator<=>
一样)。
在此
***/main.cpp:130:24: required from ‘Parser::BNF<Iterator>::BNF() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
****/main.cpp:156:52: required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:41:16: error: no type named ‘value_type’ in ‘struct boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>’
41 | struct is_container_of_ranges
| ^~~~~~~~~~~~~~~~~~~~~~
......
***/main.cpp:130:24: required from ‘Parser::BNF<Iterator>::BNF() [with Iterator = __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >]’
***/main.cpp:156:52: required from here
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:72:17: error: no matching function for call to ‘boost::spirit::traits::assign_to_attribute_from_iterators<boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>, __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >, void>::call(const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, const __gnu_cxx::__normal_iterator<const char*, std::__cxx11::basic_string<char> >&, boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false>&, boost::spirit::traits::detail::is_container_of_ranges<boost::fusion::extension::adt_attribute_proxy<Ast::Term, 0, false> >)’
72 | call(first, last, attr, detail::is_container_of_ranges<Attribute>());
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/adapted.hpp>
#include <iomanip>
namespace qi = boost::spirit::qi;
namespace Ast {
enum class TermType {
literal, rule_name
};
class Term {
private:
std::string data;
TermType term_type;
public:
Term() = default;
void set_data(std::string const &data) {
this->data = data;
}
std::string const &get_data() const {
return data;
}
void set_term_type(TermType term_type) {
this->term_type = term_type;
}
TermType const &get_term_type() const {
return term_type;
}
bool operator==(const Term &term) {
if (!data.compare(term.data) && term_type == term.term_type)
return true;
return false;
}
Term &operator=(const Term &term) {
if (this != &term) {
data = term.data;
term_type = term.term_type;
}
return *this;
}
Term(const Term &term) {
data = term.data;
term_type = term.term_type;
}
};
class List : public std::vector<Term> {
// attributes not required for parsing
// constructor, copy constructor, = operator overloading
};
using Expression = std::vector<List>;
class Rule {
private:
Term name; // lhs
Expression rhs;
public:
void set_rule_name(const Term &rule_name) {
this->name = rule_name;
}
Term const &get_rule_name() const {
return name;
}
void set_expression(const Expression &expression) {
rhs = expression;
}
Expression const &get_expression() const {
return rhs;
}
// constructor, copy constructor, = operator overloading
};
using Syntax = std::list<Rule>;
}
BOOST_FUSION_ADAPT_ADT(Ast::Term,
(obj.get_data(), obj.set_data(val))
(obj.get_term_type(), obj.set_term_type(val)))
BOOST_FUSION_ADAPT_ADT(Ast::Rule,
(obj.get_rule_name(), obj.set_rule_name(val))
(obj.get_expression(), obj.set_expression(val)))
namespace Parser {
template<typename Iterator>
struct BNF : qi::grammar<Iterator, Ast::Syntax()> {
BNF()
: BNF::base_type(start) {
using namespace qi;
_blank = blank;
unesc_char.add
("\a", '\a')
("\b", '\b')
("\f", '\f')
("\n", '\n')
("\r", '\r')
("\t", '\t')
("\v", '\v')
("\\", '\')
("\'", '\'')
("\\"", '\"');
_skipper = blank | (eol >> !skip(_blank.alias())[_rule]);
start = skip(_skipper.alias())[_rule % +eol];
_rule = _rule_name >> "::=" >> _expression;
_expression = _list % '|';
_list = +_term;
_term = _literal >> attr(Ast::TermType::literal)
| _rule_name;
_literal = unesc_char | '"' >> *(_character - '"') >> '"'
| "'" >> *(_character - "'") >> "'";
_character = alnum | char_("\"'| !#$%&()*+,./:;>=<?@]\^_`{}~[-");
_rule_name = '<' >> qi::raw[(alpha >> *(alnum | char_('-')))] >> '>'
>> attr(Ast::TermType::rule_name);
// clang-format on
BOOST_SPIRIT_DEBUG_NODES(
(_rule)(_expression)(_list)(_literal)(_character)(_rule_name))
}
private:
using Skipper = qi::rule<Iterator>;
Skipper _skipper, _blank;
qi::rule<Iterator, Ast::Syntax()> start;
qi::rule<Iterator, Ast::Rule(), Skipper> _rule;
qi::rule<Iterator, Ast::Expression(), Skipper> _expression;
qi::rule<Iterator, Ast::List(), Skipper> _list;
// lexemes
qi::rule<Iterator, Ast::Term()> _term;
qi::rule<Iterator, std::string()> _literal;
qi::rule<Iterator, Ast::Term()> _rule_name;
qi::rule<Iterator, char()> _character;
qi::symbols<char const, char const> unesc_char;
};
}
int main() {
Parser::BNF<std::string::const_iterator> const parser;
std::string const input = R"(<code> ::= <letter><digit> | <letter><digit><code>
<letter> ::= "a" | "b" | "c" | "d" | "e"
| "f" | "g" | "h" | "i"
<digit> ::= "0" | "1 \n 3" | "2" | "3 \t yy" |
"4"
)";
auto it = input.begin(), itEnd = input.end();
Ast::Syntax syntax;
if (parse(it, itEnd, parser, syntax)) {
for (auto &rule : syntax) {
std::cout << rule.get_rule_name().get_data() << " ::= ";
std::string sep;
for (auto &list : rule.get_expression()) {
std::cout << sep;
for (auto &term: list) { std::cout << term.get_data(); }
sep = " | ";
};
std::cout << "\n";
}
} else {
std::cout << "Failed\n";
}
if (it != itEnd)
std::cout << "Remaining: " << std::quoted(std::string(it, itEnd)) << "\n";
}
由于某些原因,属性兼容性规则在调用 setter 方法时不适用。您可以在顶部强制使用 as_string
问题:
_rule_name = '<' >> qi::as_string [ qi::raw[ (alpha >> *(alnum | char_('-'))) ] ] >>
'>' >> attr(Ast::TermType::rule_name);
侧面观察
2 月 27 日 23:35
My other concern is the inheritance from string class. I thought was bad to do that.
显然,你改变了主意。我真的不知道为什么。在这种情况下,您可能不应该这样做。快捷方式是“好的”,除非它们不是。参见例如More silent behaviour changes with C++20 three-way comparison and https://quuxplusone.github.io/blog/2018/12/11/dont-inherit-from-std-types/
另外,提防quasi-classes (PDF)。你能告诉我为什么结构不是更好吗?您只是重新定义了很多标准行为(copy ctor/assignment 应该是默认的,就像 C++20 上的 operator<=>
一样)。