Boost.Spirit qi值序列向量
Boost.Spirit qi value sequence vector
以下代码编译错误:
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:153:20: error: no matching conversion for static_cast from 'const char' to 'boost::fusion::vector<char,
std::vector<double, std::allocator<double> > >'
attr = static_cast<Attribute>(val);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
我不知道为什么,因为它按预期工作并更改为 auto grammar = boost::spirit::no_skip[drawto_commands];
。
moveto
和 lineto
解析的类型相同。
Qi 运算符 >> 具有类型规则 a: A, b: vector<A> --> (a >> b): vector<A>
,这应该使 drawto_commands
和 moveto_drawto_command_group
解析的类型相同。
我错过了什么?
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
typedef boost::fusion::vector<char, std::vector<double>> Arc;
template <typename P, typename T>
bool test_phrase_parser_attr(const std::string &string, P const& grammar, T& attr, bool full_match = true)
{
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::ascii::space;
auto f = string.begin();
auto l = string.end();
bool match = phrase_parse(f, l, grammar, space, attr);
return match && (!full_match || (f == l));
}
int main()
{
using boost::spirit::omit;
using boost::spirit::qi::ascii::char_;
using boost::spirit::qi::ascii::space;
using boost::spirit::qi::attr;
using boost::spirit::qi::double_;
using boost::spirit::qi::copy;
auto wsp = copy(omit[boost::spirit::ascii::space]);
auto comma_wsp = copy(omit[(char_(',') >> *wsp) | (+wsp >> -char_(',') >> *wsp)]);
auto coordinate = copy(double_);
auto coordinate_pair = copy(coordinate >> -comma_wsp >> coordinate);
auto closepath = copy(char_("Zz") >> attr(std::vector<double>()));
auto vertical_lineto = copy(char_("Vv") >> *wsp >> (coordinate % -comma_wsp));
auto lineto = copy(char_("Ll") >> *wsp >> (coordinate_pair % -comma_wsp));
auto moveto = copy(char_("Mm") >> *wsp >> (coordinate_pair % -comma_wsp));
auto drawto_command = copy(closepath | vertical_lineto | lineto);
auto drawto_commands = copy(*(*wsp >> drawto_command >> *wsp));
auto moveto_drawto_command_group = copy(moveto >> drawto_commands);
auto grammar = boost::spirit::no_skip[moveto_drawto_command_group];
std::vector<Arc> attribute;
std::string str;
std::cout << "*\n";
while (getline(std::cin, str))
{
if (str.empty())
break;
attribute = {};
bool r = test_phrase_parser_attr(str, grammar, attribute, true);
if (r)
{
std::cout << "Parsing succeeded, got: " << std::endl;
for (auto &command: attribute){
char line_type = boost::fusion::at_c<0>(command);
std::cout << line_type;
const std::vector<double> arguments = boost::fusion::at_c<1>(command);
for (size_t i = 0; i < arguments.size(); ++i)
{
std::cout << ' ' << arguments[i];
}
std::cout << std::endl;
}
}
else
{
std::cout << "Parsing failed\n";
}
}
}
`
好吧,就像经常发生的那样,我在看它和 SVG 规范,只是觉得更值得分享一些关于
的想法
- 风格
- 齐公约
- 先进的思想
您可能感兴趣。公平警告:我没有尝试解决您提出的问题。
具体你的类型
您似乎 "always auto" 在一个已经基于启发式的解析器框架中。有时事情 "don't magic out the right way" 我并不感到惊讶。假设你想继续使用 Qi,让我们有一个 Qi 解析器:
#define BOOST_SPIRIT_DEBUG
#include <string>
#include <iostream>
#include <boost/spirit/home/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace AST {
using Coordinates = std::vector<double>;
struct Arc {
char command;
Coordinates coordinates;
};
using PathData = std::vector<Arc>;
}
BOOST_FUSION_ADAPT_STRUCT(AST::Arc, command, coordinates)
namespace qi = boost::spirit::qi;
namespace Parsers {
template <typename It>
struct PathData : qi::grammar<It, AST::PathData()> {
PathData() : PathData::base_type(start) {
using namespace qi;
opt_comma = -lit(',');
coordinate = double_;
coordinate_pair = coordinate >> opt_comma >> coordinate;
moveto = char_("Mm") >> (coordinate_pair % opt_comma);
closepath = char_("Zz") >> attr(AST::Coordinates{});
vertical_lineto = char_("Vv") >> (coordinate % opt_comma);
lineto = char_("Ll") >> (coordinate_pair % opt_comma);
drawto_command = closepath | vertical_lineto | lineto;
drawto_commands = *drawto_command;
start = skip(space) [ moveto >> drawto_commands ];
BOOST_SPIRIT_DEBUG_NODES((opt_comma)(coordinate)(coordinate_pair)
(moveto)(closepath)(vertical_lineto)(lineto)(drawto_command)
(drawto_commands))
}
private:
using Skipper = qi::space_type;
qi::rule<It> opt_comma;
qi::rule<It, double()> coordinate;
qi::rule<It, AST::Coordinates(), Skipper> coordinate_pair;
qi::rule<It, AST::Arc(), Skipper> moveto, closepath, vertical_lineto, lineto, drawto_command;
qi::rule<It, AST::PathData(), Skipper> drawto_commands;
qi::rule<It, AST::PathData()> start;
};
}
template <typename P, typename T>
bool test_parse_attr(const std::string &text, P const& grammar, T& attr, bool full_match = true) {
return parse(text.cbegin(), text.cend(),
grammar >> (qi::eps(!full_match) | qi::eoi),
attr);
}
int main() {
const Parsers::PathData<std::string::const_iterator> grammar;
for (std::string const str : { "M 100 100 L 300 100 L 200 300 z" }) {
AST::PathData attribute;
if (test_parse_attr(str, grammar, attribute, true)) {
std::cout << "Parsing succeeded, got: " << std::endl;
for (auto &command: attribute) {
std::cout << command.command;
for (auto const& arg : command.coordinates) {
std::cout << ' ' << arg;
}
std::cout << std::endl;
}
} else {
std::cout << "Parsing failed\n";
}
}
}
版画
Parsing succeeded, got:
M 100 100
L 300 100
L 200 300
z
备注:
- Skipper 是解析器的责任,而不是调用者
不要干涉 fusion::vector
(甚至 tuple
),因此请保持您的代码可维护:
namespace AST {
using Coordinates = std::vector<double>;
struct Arc {
char command;
Coordinates coordinates;
};
using PathData = std::vector<Arc>;
}
以后:
for (auto &command: attribute) {
std::cout << command.command;
for (auto const& arg : command.coordinates) { std::cout << ' ' << arg; }
std::cout << std::endl;
}
它将所有可选的空格匹配推迟到 Skipper
。我知道这会改变行为(我们会解析 "L100,200" 而 "L 100,200" 是必需的)。如果你执意要诊断这个病例,拼出来:
command_letter = no_case [ char_(_r1) ] >> &(space|eoi);
moveto = command_letter('m') >> (coordinate_pair % opt_comma);
closepath = command_letter('z') >> attr(AST::Coordinates{});
vertical_lineto = command_letter('v') >> (coordinate % opt_comma);
lineto = command_letter('l') >> (coordinate_pair % opt_comma);
其中 command_letter
是采用继承属性的规则:
qi::rule<It, char(char)> command_letter;
更多类型的具体化
也许您还想具体说明您的 AST 类型。根据您的域逻辑,您可能真的不应该将所有参数都视为一个向量。
namespace AST {
using Coordinate = double;
using Coordinates = std::vector<Coordinate>;
struct Point { Coordinate x, y; };
using Points = std::vector<Point>;
namespace Cmds {
struct MoveTo { Points points; } ;
struct ClosePath { } ;
struct VerticalLineTo { Coordinates x; } ;
struct LineTo { Points points; } ;
}
using Cmd = boost::variant<
Cmds::MoveTo,
Cmds::ClosePath,
Cmds::VerticalLineTo,
Cmds::LineTo
>;
using PathData = std::vector<Cmd>;
}
全部改编:
BOOST_FUSION_ADAPT_STRUCT(AST::Point, x, y)
BOOST_FUSION_ADAPT_STRUCT(AST::Cmds::MoveTo, points)
BOOST_FUSION_ADAPT_STRUCT(AST::Cmds::LineTo, points)
您可以考虑 Nabialek Trick to parse them. See here for an example:
更高级的想法
也许使用 X3 可以更清晰地模拟您的原始代码组织:
#include <string>
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace AST {
using Coordinates = std::vector<double>;
struct Arc {
char command;
Coordinates coordinates;
};
using PathData = std::vector<Arc>;
}
BOOST_FUSION_ADAPT_STRUCT(AST::Arc, command, coordinates)
namespace x3 = boost::spirit::x3;
namespace Parsers {
using namespace x3;
auto const opt_comma = -lit(',');
auto const coordinate = double_;
auto const coordinate_pair = coordinate >> opt_comma >> coordinate;
template <typename T> auto as = [](auto p) { return rule<struct _, T>{} = p; };
auto const command_letter = [](auto p) { return lexeme [ no_case [ char_(p) ] >> &(space|eoi) ]; };
auto const moveto = command_letter('m') >> as<AST::Coordinates>(coordinate_pair % opt_comma);
auto const lineto = command_letter('l') >> as<AST::Coordinates>(coordinate_pair % opt_comma);
auto const vertical_lineto = command_letter('v') >> as<AST::Coordinates>(coordinate % opt_comma);
auto const closepath = command_letter('z') >> attr(AST::Coordinates{});
auto const drawto_command = as<AST::Arc>(closepath | vertical_lineto | lineto);
auto const drawto_commands = as<AST::PathData>(*drawto_command);
auto const path_data = as<AST::PathData>(skip(space) [ moveto >> drawto_commands ]);
}
template <typename P, typename T>
bool test_parse_attr(const std::string &text, P const& grammar, T& attr, bool full_match = true) {
return parse(
text.cbegin(), text.cend(),
grammar >> (x3::eps(!full_match) | x3::eoi),
attr
);
}
int main() {
for (std::string const str : { "M 100 100 L 300 100 L 200 300 z" }) {
AST::PathData attribute;
if (test_parse_attr(str, Parsers::path_data, attribute, true)) {
std::cout << "Parsing succeeded, got: " << std::endl;
for (auto &command: attribute) {
std::cout << command.command;
for (auto const& arg : command.coordinates) {
std::cout << ' ' << arg;
}
std::cout << std::endl;
}
} else {
std::cout << "Parsing failed\n";
}
}
}
同时打印:
Parsing succeeded, got:
M 100 100
L 300 100
L 200 300
z
以下代码编译错误:
/usr/include/boost/spirit/home/qi/detail/assign_to.hpp:153:20: error: no matching conversion for static_cast from 'const char' to 'boost::fusion::vector<char,
std::vector<double, std::allocator<double> > >'
attr = static_cast<Attribute>(val);
^~~~~~~~~~~~~~~~~~~~~~~~~~~
我不知道为什么,因为它按预期工作并更改为 auto grammar = boost::spirit::no_skip[drawto_commands];
。
moveto
和 lineto
解析的类型相同。
Qi 运算符 >> 具有类型规则 a: A, b: vector<A> --> (a >> b): vector<A>
,这应该使 drawto_commands
和 moveto_drawto_command_group
解析的类型相同。
我错过了什么?
#include <string>
#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
typedef boost::fusion::vector<char, std::vector<double>> Arc;
template <typename P, typename T>
bool test_phrase_parser_attr(const std::string &string, P const& grammar, T& attr, bool full_match = true)
{
using boost::spirit::qi::phrase_parse;
using boost::spirit::qi::ascii::space;
auto f = string.begin();
auto l = string.end();
bool match = phrase_parse(f, l, grammar, space, attr);
return match && (!full_match || (f == l));
}
int main()
{
using boost::spirit::omit;
using boost::spirit::qi::ascii::char_;
using boost::spirit::qi::ascii::space;
using boost::spirit::qi::attr;
using boost::spirit::qi::double_;
using boost::spirit::qi::copy;
auto wsp = copy(omit[boost::spirit::ascii::space]);
auto comma_wsp = copy(omit[(char_(',') >> *wsp) | (+wsp >> -char_(',') >> *wsp)]);
auto coordinate = copy(double_);
auto coordinate_pair = copy(coordinate >> -comma_wsp >> coordinate);
auto closepath = copy(char_("Zz") >> attr(std::vector<double>()));
auto vertical_lineto = copy(char_("Vv") >> *wsp >> (coordinate % -comma_wsp));
auto lineto = copy(char_("Ll") >> *wsp >> (coordinate_pair % -comma_wsp));
auto moveto = copy(char_("Mm") >> *wsp >> (coordinate_pair % -comma_wsp));
auto drawto_command = copy(closepath | vertical_lineto | lineto);
auto drawto_commands = copy(*(*wsp >> drawto_command >> *wsp));
auto moveto_drawto_command_group = copy(moveto >> drawto_commands);
auto grammar = boost::spirit::no_skip[moveto_drawto_command_group];
std::vector<Arc> attribute;
std::string str;
std::cout << "*\n";
while (getline(std::cin, str))
{
if (str.empty())
break;
attribute = {};
bool r = test_phrase_parser_attr(str, grammar, attribute, true);
if (r)
{
std::cout << "Parsing succeeded, got: " << std::endl;
for (auto &command: attribute){
char line_type = boost::fusion::at_c<0>(command);
std::cout << line_type;
const std::vector<double> arguments = boost::fusion::at_c<1>(command);
for (size_t i = 0; i < arguments.size(); ++i)
{
std::cout << ' ' << arguments[i];
}
std::cout << std::endl;
}
}
else
{
std::cout << "Parsing failed\n";
}
}
}
`
好吧,就像经常发生的那样,我在看它和 SVG 规范,只是觉得更值得分享一些关于
的想法- 风格
- 齐公约
- 先进的思想
您可能感兴趣。公平警告:我没有尝试解决您提出的问题。
具体你的类型
您似乎 "always auto" 在一个已经基于启发式的解析器框架中。有时事情 "don't magic out the right way" 我并不感到惊讶。假设你想继续使用 Qi,让我们有一个 Qi 解析器:
#define BOOST_SPIRIT_DEBUG
#include <string>
#include <iostream>
#include <boost/spirit/home/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace AST {
using Coordinates = std::vector<double>;
struct Arc {
char command;
Coordinates coordinates;
};
using PathData = std::vector<Arc>;
}
BOOST_FUSION_ADAPT_STRUCT(AST::Arc, command, coordinates)
namespace qi = boost::spirit::qi;
namespace Parsers {
template <typename It>
struct PathData : qi::grammar<It, AST::PathData()> {
PathData() : PathData::base_type(start) {
using namespace qi;
opt_comma = -lit(',');
coordinate = double_;
coordinate_pair = coordinate >> opt_comma >> coordinate;
moveto = char_("Mm") >> (coordinate_pair % opt_comma);
closepath = char_("Zz") >> attr(AST::Coordinates{});
vertical_lineto = char_("Vv") >> (coordinate % opt_comma);
lineto = char_("Ll") >> (coordinate_pair % opt_comma);
drawto_command = closepath | vertical_lineto | lineto;
drawto_commands = *drawto_command;
start = skip(space) [ moveto >> drawto_commands ];
BOOST_SPIRIT_DEBUG_NODES((opt_comma)(coordinate)(coordinate_pair)
(moveto)(closepath)(vertical_lineto)(lineto)(drawto_command)
(drawto_commands))
}
private:
using Skipper = qi::space_type;
qi::rule<It> opt_comma;
qi::rule<It, double()> coordinate;
qi::rule<It, AST::Coordinates(), Skipper> coordinate_pair;
qi::rule<It, AST::Arc(), Skipper> moveto, closepath, vertical_lineto, lineto, drawto_command;
qi::rule<It, AST::PathData(), Skipper> drawto_commands;
qi::rule<It, AST::PathData()> start;
};
}
template <typename P, typename T>
bool test_parse_attr(const std::string &text, P const& grammar, T& attr, bool full_match = true) {
return parse(text.cbegin(), text.cend(),
grammar >> (qi::eps(!full_match) | qi::eoi),
attr);
}
int main() {
const Parsers::PathData<std::string::const_iterator> grammar;
for (std::string const str : { "M 100 100 L 300 100 L 200 300 z" }) {
AST::PathData attribute;
if (test_parse_attr(str, grammar, attribute, true)) {
std::cout << "Parsing succeeded, got: " << std::endl;
for (auto &command: attribute) {
std::cout << command.command;
for (auto const& arg : command.coordinates) {
std::cout << ' ' << arg;
}
std::cout << std::endl;
}
} else {
std::cout << "Parsing failed\n";
}
}
}
版画
Parsing succeeded, got:
M 100 100
L 300 100
L 200 300
z
备注:
- Skipper 是解析器的责任,而不是调用者
不要干涉
fusion::vector
(甚至tuple
),因此请保持您的代码可维护:namespace AST { using Coordinates = std::vector<double>; struct Arc { char command; Coordinates coordinates; }; using PathData = std::vector<Arc>; }
以后:
for (auto &command: attribute) { std::cout << command.command; for (auto const& arg : command.coordinates) { std::cout << ' ' << arg; } std::cout << std::endl; }
它将所有可选的空格匹配推迟到
Skipper
。我知道这会改变行为(我们会解析 "L100,200" 而 "L 100,200" 是必需的)。如果你执意要诊断这个病例,拼出来:command_letter = no_case [ char_(_r1) ] >> &(space|eoi); moveto = command_letter('m') >> (coordinate_pair % opt_comma); closepath = command_letter('z') >> attr(AST::Coordinates{}); vertical_lineto = command_letter('v') >> (coordinate % opt_comma); lineto = command_letter('l') >> (coordinate_pair % opt_comma);
其中
command_letter
是采用继承属性的规则:qi::rule<It, char(char)> command_letter;
更多类型的具体化
也许您还想具体说明您的 AST 类型。根据您的域逻辑,您可能真的不应该将所有参数都视为一个向量。
namespace AST {
using Coordinate = double;
using Coordinates = std::vector<Coordinate>;
struct Point { Coordinate x, y; };
using Points = std::vector<Point>;
namespace Cmds {
struct MoveTo { Points points; } ;
struct ClosePath { } ;
struct VerticalLineTo { Coordinates x; } ;
struct LineTo { Points points; } ;
}
using Cmd = boost::variant<
Cmds::MoveTo,
Cmds::ClosePath,
Cmds::VerticalLineTo,
Cmds::LineTo
>;
using PathData = std::vector<Cmd>;
}
全部改编:
BOOST_FUSION_ADAPT_STRUCT(AST::Point, x, y)
BOOST_FUSION_ADAPT_STRUCT(AST::Cmds::MoveTo, points)
BOOST_FUSION_ADAPT_STRUCT(AST::Cmds::LineTo, points)
您可以考虑 Nabialek Trick to parse them. See here for an example:
更高级的想法
也许使用 X3 可以更清晰地模拟您的原始代码组织:
#include <string>
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
namespace AST {
using Coordinates = std::vector<double>;
struct Arc {
char command;
Coordinates coordinates;
};
using PathData = std::vector<Arc>;
}
BOOST_FUSION_ADAPT_STRUCT(AST::Arc, command, coordinates)
namespace x3 = boost::spirit::x3;
namespace Parsers {
using namespace x3;
auto const opt_comma = -lit(',');
auto const coordinate = double_;
auto const coordinate_pair = coordinate >> opt_comma >> coordinate;
template <typename T> auto as = [](auto p) { return rule<struct _, T>{} = p; };
auto const command_letter = [](auto p) { return lexeme [ no_case [ char_(p) ] >> &(space|eoi) ]; };
auto const moveto = command_letter('m') >> as<AST::Coordinates>(coordinate_pair % opt_comma);
auto const lineto = command_letter('l') >> as<AST::Coordinates>(coordinate_pair % opt_comma);
auto const vertical_lineto = command_letter('v') >> as<AST::Coordinates>(coordinate % opt_comma);
auto const closepath = command_letter('z') >> attr(AST::Coordinates{});
auto const drawto_command = as<AST::Arc>(closepath | vertical_lineto | lineto);
auto const drawto_commands = as<AST::PathData>(*drawto_command);
auto const path_data = as<AST::PathData>(skip(space) [ moveto >> drawto_commands ]);
}
template <typename P, typename T>
bool test_parse_attr(const std::string &text, P const& grammar, T& attr, bool full_match = true) {
return parse(
text.cbegin(), text.cend(),
grammar >> (x3::eps(!full_match) | x3::eoi),
attr
);
}
int main() {
for (std::string const str : { "M 100 100 L 300 100 L 200 300 z" }) {
AST::PathData attribute;
if (test_parse_attr(str, Parsers::path_data, attribute, true)) {
std::cout << "Parsing succeeded, got: " << std::endl;
for (auto &command: attribute) {
std::cout << command.command;
for (auto const& arg : command.coordinates) {
std::cout << ' ' << arg;
}
std::cout << std::endl;
}
} else {
std::cout << "Parsing failed\n";
}
}
}
同时打印:
Parsing succeeded, got:
M 100 100
L 300 100
L 200 300
z