我有一个工作解析器来阅读棋盘游戏的位置描述(国际草稿, 官方语法 ):
#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)住在科利鲁
但是,我接着说错了
错误:static_assert失败“属性没有预期的大小。”
并且几页下来:
^ main.cpp:16:8:注意:候选构造函数(隐式移动构造函数)不可行:没有来自' std::vector<boost::variant<ast::num_rng, ast::num_pc>, std::allocator<boost::variant<ast::num_rng, ast::num_pc> > >已知转换std::vector<boost::variant<ast::num_rng, ast::num_pc>, std::allocator<boost::variant<ast::num_rng, ast::num_pc> > >第一个参数struct num_seq { std::vector<rng_or_pc> sqrs; };
^ main.cpp:16:8:注意:候选构造函数(隐式复制构造函数)不可行:没有来自' std::vector<boost::variant<ast::num_rng, ast::num_pc>, std::allocator<boost::variant<ast::num_rng, ast::num_pc> > >已知转换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'代表第一个参数struct num_seq { std::vector<rng_or_pc> sqrs; };
问题 :此错误来自何处以及如何解决? 显然,我的num_seq规则的合成属性不等于std::vector<boost::variant>> 。 我怎么能纠正这个?
I got a working parser for reading position descriptions for a board game (international draughts, 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;Live On Coliru
Now I want to extract the values from the synthesized attributes, so I did the boilerplate dance around Boost.Fusion etc.,
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)Live On Coliru
However, I then get an error saying that
error: static_assert failed "Attribute does not have the expected size."
and a couple of pages down:
^ 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; };
Question: where is this error coming from, and how to resolve it? Apparently the synthesized attribute of my num_seq rule is not equal to std::vector<boost::variant>>. How can I correct this?
最满意答案
我花了一些时间试图理解语法。
我强烈建议可读的标识符。 很难理解发生了什么,而我有强烈的印象,它实际上是一个非常简单的语法
我建议使用如下所示的简化版本。
因为你的语法不使用递归,所以不需要规则和标记的解析器类型。 还为解析器工件使用命名空间。 考虑封装使用船长而不是让调用者决定( 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 } })I've spent some time trying to understand the grammar.
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
I suggest a simplification version shown below.
Because your grammar doesn't use recursion there's no real need for the rule and tagged parser types. Also use a namespace for the parser artefacts. Consider encapsulation the use of a skipper instead of letting the caller decide (x3::skip[])Add a few helpers to be able to print the AST for verification:
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; }I'd avoid wrapping the other vector unnecessarily too:
using num_not = std::vector<ccn>;Use the modern ADAPT macros (as you're using C++14 by definition):
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) -Live Demo
Live On Coliru
#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'; } }Prints:
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 } })更多推荐
发布评论