来自 Boost.Spirit 语法的属性:来自 std:vector 或 boost::variant 的错误
Attributes from Boost.Spirit grammar: error from std:vector of boost::variant
我得到了一个用于读取棋盘游戏(国际跳棋,official grammar)位置描述的工作解析器:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
namespace x3 = boost::spirit::x3;
auto const colon = x3::lit(':');
auto const comma = x3::lit(',');
auto const dash = x3::lit('-');
auto const dot = x3::lit('.');
auto const king = x3::char_('K');
auto const color = x3::char_("BW");
auto const num_sq = x3::int_;
auto const num_pc = -king >> num_sq; // Kxx means king on square xx, xx a man on that square
auto const num_rng = num_pc >> dash >> num_sq; // xx-yy means range of squares xx through yy (inclusive)
auto const num_seq = (num_rng | num_pc) % comma; // <--- attribute should be std::vector<boost::variant>
auto const ccn = colon >> color >> -num_seq;
auto const num_not = x3::repeat(2)[ccn]; // need to specify both white and black pieces
auto const fen = color >> num_not >> -dot;
现在我想从合成属性中提取值,所以我围绕 Boost.Fusion 等进行了样板舞蹈,
namespace ast {
struct num_pc { boost::optional<char> k; int sq; };
struct num_rng { boost::optional<char> k; int first, last; };
using rng_or_pc = boost::variant<num_rng, num_pc>;
struct num_seq { std::vector<rng_or_pc> sqrs; };
struct ccn { char c; boost::optional<num_seq> seq; };
struct num_not { std::vector<ccn> n; };
struct fen { char c; num_not n; };
} // namespace ast
BOOST_FUSION_ADAPT_STRUCT(ast::num_pc, (boost::optional<char>, k), (int, sq))
BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, (boost::optional<char>, k), (int, first), (int, last))
BOOST_FUSION_ADAPT_STRUCT(ast::num_seq, (std::vector<ast::rng_or_pc>, sqrs))
BOOST_FUSION_ADAPT_STRUCT(ast::ccn, (char, c), (boost::optional<ast::num_seq>, seq))
BOOST_FUSION_ADAPT_STRUCT(ast::num_not, (std::vector<ast::ccn>, n))
BOOST_FUSION_ADAPT_STRUCT(ast::fen, (char, c), (ast::num_not, n))
x3::rule<class num_pc_class, ast::num_pc > num_pc = "num_pc";
x3::rule<class num_rng_class, ast::num_rng> num_rng = "num_rng";
x3::rule<class num_seq_class, ast::num_seq> num_seq = "num_seq";
x3::rule<class ccn_class, ast::ccn > ccn = "ccn";
x3::rule<class num_not_class, ast::num_not> num_not = "num_not";
x3::rule<class fen_class, ast::fen > fen = "fen";
auto const colon = x3::lit(':');
auto const comma = x3::lit(',');
auto const dash = x3::lit('-');
auto const dot = x3::lit('.');
auto const king = x3::char_('K');
auto const color = x3::char_("BW");
auto const num_sq = x3::int_;
auto const num_pc_def = -king >> num_sq;
auto const num_rng_def = num_pc >> dash >> num_sq;
auto const num_seq_def = (num_rng | num_pc) % comma;
auto const ccn_def = colon >> color >> -num_seq;
auto const num_not_def = x3::repeat(2)[ccn];
auto const fen_def = color >> num_not >> -dot;
BOOST_SPIRIT_DEFINE(num_pc, num_rng, num_seq, ccn, num_not, fen)
但是,我收到一条错误消息说
error: static_assert failed "Attribute does not have the expected
size."
还有几页:
^ main.cpp:16:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from
'std::vector<boost::variant<ast::num_rng, ast::num_pc>,
std::allocator<boost::variant<ast::num_rng, ast::num_pc> > >
' to
'ast::num_seq' for 1st argument struct num_seq {
std::vector<rng_or_pc>
sqrs; };
^ main.cpp:16:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from
'std::vector<boost::variant<ast::num_rng, ast::num_pc>,
std::allocator<boost::variant<ast::num_rng, ast::num_pc> > >
' to
'const ast::num_seq' for 1st argument struct num_seq {
std::vector<rng_or_pc>
sqrs; };
问题: 这个错误是从哪里来的,如何解决?显然我的 num_seq
规则的综合属性不等于 std::vector<boost::variant>>
。我该如何纠正?
我花了一些时间试图理解语法。
I strongly suggest readable identifiers. It's very hard to understand what's going on, while I have the strong impression it's actually a really simple grammar
我建议使用如下所示的简化版本。
- 因为您的语法不使用递归,所以不需要规则和标记的解析器类型。
- 还为解析器工件使用命名空间。
- 考虑使用船长进行封装,而不是让调用者决定 (
x3::skip[]
)
添加几个助手可以打印 AST 进行验证:
template <typename T> std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
os << "{"; for (auto& el : v) os << el << " "; return os << "}";
}
std::ostream& operator<<(std::ostream& os, num_pc const& p) { if (p.k) os << p.k; return os << p.sq; }
std::ostream& operator<<(std::ostream& os, num_rng const& r) { return os << r.pc << "-" << r.last; }
std::ostream& operator<<(std::ostream& os, ccn const& o) { return os << o.c << " " << o.seq; }
std::ostream& operator<<(std::ostream& os, num_not const& nn) { return os << nn.n; }
我也会避免不必要地包装另一个向量:
using num_not = std::vector<ccn>;
使用现代 ADAPT 宏(根据定义,您使用的是 C++14):
BOOST_FUSION_ADAPT_STRUCT(ast::num_pc, k, sq)
BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, pc, last)
BOOST_FUSION_ADAPT_STRUCT(ast::ccn, c, seq)
BOOST_FUSION_ADAPT_STRUCT(ast::fen, c, n)
-
现场演示
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/optional.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <vector>
namespace ast {
struct num_pc {
boost::optional<char> k;
int sq;
};
struct num_rng {
num_pc pc;
int last;
};
using rng_or_pc = boost::variant<num_rng, num_pc>;
using num_seq = std::vector<rng_or_pc>;
struct ccn {
char c;
boost::optional<num_seq> seq;
};
using num_not = std::vector<ccn>;
struct fen {
char c;
num_not n;
};
template <typename T> std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
os << "{"; for (auto& el : v) os << el << " "; return os << "}";
}
std::ostream& operator<<(std::ostream& os, num_pc const& p) { if (p.k) os << p.k; return os << p.sq; }
std::ostream& operator<<(std::ostream& os, num_rng const& r) { return os << r.pc << "-" << r.last; }
std::ostream& operator<<(std::ostream& os, ccn const& o) { return os << o.c << " " << o.seq; }
}
BOOST_FUSION_ADAPT_STRUCT(ast::num_pc, k, sq)
BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, pc, last)
BOOST_FUSION_ADAPT_STRUCT(ast::ccn, c, seq)
BOOST_FUSION_ADAPT_STRUCT(ast::fen, c, n)
namespace FEN {
namespace x3 = boost::spirit::x3;
namespace grammar
{
using namespace x3;
template<typename T>
auto as = [](auto p) { return rule<struct _, T>{} = as_parser(p); };
uint_type const number {};
auto const color = char_("BW");
auto const num_pc = as<ast::num_pc> ( -char_('K') >> number );
auto const num_rng = as<ast::num_rng> ( num_pc >> '-' >> number );
auto const num_seq = as<ast::num_seq> ( (num_rng | num_pc) % ',' );
auto const ccn = as<ast::ccn> ( ':' >> color >> -num_seq );
auto const num_not = as<ast::num_not> ( repeat(2)[ccn] );
auto const fen = as<ast::fen> ( color >> num_not >> -lit('.') );
}
using grammar::fen;
}
int main() {
for (std::string const t : {
"B:W18,24,27,28,K10,K15:B12,16,20,K22,K25,K29",
"B:W18,19,21,23,24,26,29,30,31,32:B1,2,3,4,6,7,9,10,11,12",
"W:B1-20:W31-50", // initial position
"W:B:W", // empty board
"W:B1:W", // only black pieces
"W:B:W50" // only white pieces
}) {
auto b = t.begin(), e = t.end();
ast::fen data;
bool ok = phrase_parse(b, e, FEN::fen, FEN::x3::space, data);
std::cout << t << "\n";
if (ok) {
std::cout << "Parsed: " << boost::fusion::as_vector(data) << "\n";
} else {
std::cout << "Parse failed:\n";
std::cout << "\t on input: " << t << "\n";
}
if (b != e)
std::cout << "\t Remaining unparsed: '" << std::string(b, e) << '\n';
}
}
打印:
B:W18,24,27,28,K10,K15:B12,16,20,K22,K25,K29
Parsed: (B {W {18 24 27 28 K10 K15 } B {12 16 20 K22 K25 K29 } })
B:W18,19,21,23,24,26,29,30,31,32:B1,2,3,4,6,7,9,10,11,12
Parsed: (B {W {18 19 21 23 24 26 29 30 31 32 } B {1 2 3 4 6 7 9 10 11 12 } })
W:B1-20:W31-50
Parsed: (W {B {1-20 } W {31-50 } })
W:B:W
Parsed: (W {B -- W -- })
W:B1:W
Parsed: (W {B {1 } W -- })
W:B:W50
Parsed: (W {B -- W {50 } })
我得到了一个用于读取棋盘游戏(国际跳棋,official grammar)位置描述的工作解析器:
#include <boost/spirit/home/x3.hpp>
#include <iostream>
namespace x3 = boost::spirit::x3;
auto const colon = x3::lit(':');
auto const comma = x3::lit(',');
auto const dash = x3::lit('-');
auto const dot = x3::lit('.');
auto const king = x3::char_('K');
auto const color = x3::char_("BW");
auto const num_sq = x3::int_;
auto const num_pc = -king >> num_sq; // Kxx means king on square xx, xx a man on that square
auto const num_rng = num_pc >> dash >> num_sq; // xx-yy means range of squares xx through yy (inclusive)
auto const num_seq = (num_rng | num_pc) % comma; // <--- attribute should be std::vector<boost::variant>
auto const ccn = colon >> color >> -num_seq;
auto const num_not = x3::repeat(2)[ccn]; // need to specify both white and black pieces
auto const fen = color >> num_not >> -dot;
现在我想从合成属性中提取值,所以我围绕 Boost.Fusion 等进行了样板舞蹈,
namespace ast {
struct num_pc { boost::optional<char> k; int sq; };
struct num_rng { boost::optional<char> k; int first, last; };
using rng_or_pc = boost::variant<num_rng, num_pc>;
struct num_seq { std::vector<rng_or_pc> sqrs; };
struct ccn { char c; boost::optional<num_seq> seq; };
struct num_not { std::vector<ccn> n; };
struct fen { char c; num_not n; };
} // namespace ast
BOOST_FUSION_ADAPT_STRUCT(ast::num_pc, (boost::optional<char>, k), (int, sq))
BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, (boost::optional<char>, k), (int, first), (int, last))
BOOST_FUSION_ADAPT_STRUCT(ast::num_seq, (std::vector<ast::rng_or_pc>, sqrs))
BOOST_FUSION_ADAPT_STRUCT(ast::ccn, (char, c), (boost::optional<ast::num_seq>, seq))
BOOST_FUSION_ADAPT_STRUCT(ast::num_not, (std::vector<ast::ccn>, n))
BOOST_FUSION_ADAPT_STRUCT(ast::fen, (char, c), (ast::num_not, n))
x3::rule<class num_pc_class, ast::num_pc > num_pc = "num_pc";
x3::rule<class num_rng_class, ast::num_rng> num_rng = "num_rng";
x3::rule<class num_seq_class, ast::num_seq> num_seq = "num_seq";
x3::rule<class ccn_class, ast::ccn > ccn = "ccn";
x3::rule<class num_not_class, ast::num_not> num_not = "num_not";
x3::rule<class fen_class, ast::fen > fen = "fen";
auto const colon = x3::lit(':');
auto const comma = x3::lit(',');
auto const dash = x3::lit('-');
auto const dot = x3::lit('.');
auto const king = x3::char_('K');
auto const color = x3::char_("BW");
auto const num_sq = x3::int_;
auto const num_pc_def = -king >> num_sq;
auto const num_rng_def = num_pc >> dash >> num_sq;
auto const num_seq_def = (num_rng | num_pc) % comma;
auto const ccn_def = colon >> color >> -num_seq;
auto const num_not_def = x3::repeat(2)[ccn];
auto const fen_def = color >> num_not >> -dot;
BOOST_SPIRIT_DEFINE(num_pc, num_rng, num_seq, ccn, num_not, fen)
但是,我收到一条错误消息说
error: static_assert failed "Attribute does not have the expected size."
还有几页:
^ main.cpp:16:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from '
std::vector<boost::variant<ast::num_rng, ast::num_pc>, std::allocator<boost::variant<ast::num_rng, ast::num_pc> > >
' to 'ast::num_seq' for 1st argument struct num_seq {std::vector<rng_or_pc>
sqrs; };^ main.cpp:16:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from '
std::vector<boost::variant<ast::num_rng, ast::num_pc>, std::allocator<boost::variant<ast::num_rng, ast::num_pc> > >
' to 'const ast::num_seq' for 1st argument struct num_seq {std::vector<rng_or_pc>
sqrs; };
问题: 这个错误是从哪里来的,如何解决?显然我的 num_seq
规则的综合属性不等于 std::vector<boost::variant>>
。我该如何纠正?
我花了一些时间试图理解语法。
I strongly suggest readable identifiers. It's very hard to understand what's going on, while I have the strong impression it's actually a really simple grammar
我建议使用如下所示的简化版本。
- 因为您的语法不使用递归,所以不需要规则和标记的解析器类型。
- 还为解析器工件使用命名空间。
- 考虑使用船长进行封装,而不是让调用者决定 (
x3::skip[]
) 添加几个助手可以打印 AST 进行验证:
template <typename T> std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) { os << "{"; for (auto& el : v) os << el << " "; return os << "}"; } std::ostream& operator<<(std::ostream& os, num_pc const& p) { if (p.k) os << p.k; return os << p.sq; } std::ostream& operator<<(std::ostream& os, num_rng const& r) { return os << r.pc << "-" << r.last; } std::ostream& operator<<(std::ostream& os, ccn const& o) { return os << o.c << " " << o.seq; } std::ostream& operator<<(std::ostream& os, num_not const& nn) { return os << nn.n; }
我也会避免不必要地包装另一个向量:
using num_not = std::vector<ccn>;
使用现代 ADAPT 宏(根据定义,您使用的是 C++14):
BOOST_FUSION_ADAPT_STRUCT(ast::num_pc, k, sq) BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, pc, last) BOOST_FUSION_ADAPT_STRUCT(ast::ccn, c, seq) BOOST_FUSION_ADAPT_STRUCT(ast::fen, c, n)
-
现场演示
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/optional.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/variant.hpp>
#include <iostream>
#include <vector>
namespace ast {
struct num_pc {
boost::optional<char> k;
int sq;
};
struct num_rng {
num_pc pc;
int last;
};
using rng_or_pc = boost::variant<num_rng, num_pc>;
using num_seq = std::vector<rng_or_pc>;
struct ccn {
char c;
boost::optional<num_seq> seq;
};
using num_not = std::vector<ccn>;
struct fen {
char c;
num_not n;
};
template <typename T> std::ostream& operator<<(std::ostream& os, std::vector<T> const& v) {
os << "{"; for (auto& el : v) os << el << " "; return os << "}";
}
std::ostream& operator<<(std::ostream& os, num_pc const& p) { if (p.k) os << p.k; return os << p.sq; }
std::ostream& operator<<(std::ostream& os, num_rng const& r) { return os << r.pc << "-" << r.last; }
std::ostream& operator<<(std::ostream& os, ccn const& o) { return os << o.c << " " << o.seq; }
}
BOOST_FUSION_ADAPT_STRUCT(ast::num_pc, k, sq)
BOOST_FUSION_ADAPT_STRUCT(ast::num_rng, pc, last)
BOOST_FUSION_ADAPT_STRUCT(ast::ccn, c, seq)
BOOST_FUSION_ADAPT_STRUCT(ast::fen, c, n)
namespace FEN {
namespace x3 = boost::spirit::x3;
namespace grammar
{
using namespace x3;
template<typename T>
auto as = [](auto p) { return rule<struct _, T>{} = as_parser(p); };
uint_type const number {};
auto const color = char_("BW");
auto const num_pc = as<ast::num_pc> ( -char_('K') >> number );
auto const num_rng = as<ast::num_rng> ( num_pc >> '-' >> number );
auto const num_seq = as<ast::num_seq> ( (num_rng | num_pc) % ',' );
auto const ccn = as<ast::ccn> ( ':' >> color >> -num_seq );
auto const num_not = as<ast::num_not> ( repeat(2)[ccn] );
auto const fen = as<ast::fen> ( color >> num_not >> -lit('.') );
}
using grammar::fen;
}
int main() {
for (std::string const t : {
"B:W18,24,27,28,K10,K15:B12,16,20,K22,K25,K29",
"B:W18,19,21,23,24,26,29,30,31,32:B1,2,3,4,6,7,9,10,11,12",
"W:B1-20:W31-50", // initial position
"W:B:W", // empty board
"W:B1:W", // only black pieces
"W:B:W50" // only white pieces
}) {
auto b = t.begin(), e = t.end();
ast::fen data;
bool ok = phrase_parse(b, e, FEN::fen, FEN::x3::space, data);
std::cout << t << "\n";
if (ok) {
std::cout << "Parsed: " << boost::fusion::as_vector(data) << "\n";
} else {
std::cout << "Parse failed:\n";
std::cout << "\t on input: " << t << "\n";
}
if (b != e)
std::cout << "\t Remaining unparsed: '" << std::string(b, e) << '\n';
}
}
打印:
B:W18,24,27,28,K10,K15:B12,16,20,K22,K25,K29
Parsed: (B {W {18 24 27 28 K10 K15 } B {12 16 20 K22 K25 K29 } })
B:W18,19,21,23,24,26,29,30,31,32:B1,2,3,4,6,7,9,10,11,12
Parsed: (B {W {18 19 21 23 24 26 29 30 31 32 } B {1 2 3 4 6 7 9 10 11 12 } })
W:B1-20:W31-50
Parsed: (W {B {1-20 } W {31-50 } })
W:B:W
Parsed: (W {B -- W -- })
W:B1:W
Parsed: (W {B {1 } W -- })
W:B:W50
Parsed: (W {B -- W {50 } })