将已解析序列的每个元素传递给 returns 规则属性类型的函数
Passing each element of a parsed sequence to a function that returns a rule's attribute type
我想解析 CSS 颜色函数(为简单起见,所有参数都是 0 到 255 之间的数字)
rgb(r,g,b)
rgba(r,g,b,a)
hsl(h,s,l)
hsla(h,s,l,a)
进入
struct color
{
color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a) : red{r}, green{g}, blue{b}, alpha{a} {}
static color hsl(std::uint8_t h, std::uint8_t s, std::uint8_t l, std::uint8_t a) { ... }
std::uint8_t red;
std::uint8_t green;
std::uint8_t blue;
std::uint8_t alpha;
}
我有一个有效的 hsl
函数实现,可以将 h、s 和 l 转换为 rgb 值。
我还有 rule
s 来处理前两个函数:
constexpr auto uint8 = uint_parser<std::uint8_t>{};
const auto color_rgb = rule<struct rgb, color>{"rgb"}
= lit("rgb") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')' >> attr(255);
const auto color_rgba = rule<struct rgba, color>{"rgba"}
= lit("rgba") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')';
这个有用,因为我用过
BOOST_FUSION_ADAPT_STRUCT(color,
red, green, blue, alpha)
问题出在hsl
函数上。我不能做第二个 BOOST_FUSION_ADAPT_STRUCT
,所以我想到在相应的值序列上使用语义操作,其中语义操作将简单地从序列构造 color
。像这样:
const auto color_hsl = rule<struct hsl, color, true>{"hsl"}
= (lit("hsl") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')' >> attr(255))[color::hsl];
这个不行,不然我也不会问这个问题。 Boost.Spirit.X3 也没有
意义上的东西
[ qi::_val = phx::construct<color>(...), qi::_1, qi::_2, qi::_3, qi::_4) ];
看来我想手动执行 BOOST_FUSION_ADAPT_STRUCT
所做的操作,但是是在语义操作中。这可能吗?我应该如何处理?我知道序列的属性应该是一个具有四个解析值的 "fusion-vector" 类元组实体。我想提取它们并将它们填充到 color::hsl
中以生成规则的属性。
此处适用许多提示。
I cannot do a second BOOST_FUSION_ADAPT_STRUCT
在齐国这个一般形式似乎适用:
[ qi::_val = phxfunction(qi::_0) ]
您可以通过创建自己的 actor 类型来进一步简化它,这样您就可以只提供 "factory action": [ factory(&Foo::makeBar) ]
.
如果你投入 fusion::apply
¹ 的实现,你就可以避免处理 Fusion 序列 manuallt
但是,您可能想了解这个 - 隐藏得很好 - 语义操作的属性兼容模式:BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
。埋在那个更改日志中:
Semantic actions now support attribute compatibility. This is a breaking change but #define BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
must be defined in order for the new behavior to kick in. By default, the old behavior is still in place.
您可以通过更少的调整获得您想要的行为。
X3真是可塑。我们可以像上面描述的那样 factory
帮手:
auto factory = [](auto f) {
return [f](auto& ctx) {
x3::_val(ctx) = my_apply(f, x3::_attr(ctx));
};
};
我将快速草稿 my_apply
(针对前面描述的 boost::fusion::apply
):
namespace detail {
template <class F, class Sequence, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Sequence&& t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), boost::fusion::at_c<I>(std::forward<Sequence>(t))...);
}
}
template <class F, class Sequence>
constexpr decltype(auto) my_apply(F&& f, Sequence&& t)
{
return detail::apply_impl(
std::forward<F>(f), std::forward<Sequence>(t),
std::make_index_sequence<typename boost::fusion::result_of::size<std::remove_reference_t<Sequence> >::type{}>{});
}
现在我们可以拥有解析器了:
namespace parser {
using namespace x3;
constexpr auto uint8 = uint_parser<std::uint8_t>{};
auto factory = [](auto f) {
return [f](auto& ctx) {
x3::_val(ctx) = my_apply(f, x3::_attr(ctx));
};
};
const auto color_rgb = rule<struct rgb, ast::color>{"rgb"}
= lit("rgb") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> x3::attr(255u) >> ')';
const auto color_rgba = rule<struct rgba, ast::color>{"rgba"}
= lit("rgba") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')';
const auto color_hsl = rule<struct hsl, ast::color>{"hsl"}
= (lit("hsl") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> attr(255u) >> ')') [factory(ast::color::hsl)];
const auto color = skip(space) [ color_rgba | color_rgb | color_hsl ];
}
并测试它:
int main() {
for (std::string const input : {
"rgb(1,2,3)",
"rgba(4,5,6,7)",
"hsl(8,9,10)" })
{
std::cout << " ----- Parsng " << std::quoted(input) << " --------\n";
auto begin = input.begin(), end = input.end();
ast::color result;
bool success = parse(begin, end, parser::color, result);
if (success) {
std::cout << "parsed: ";
std::cout << result << "\n";
} else {
std::cout << "failed\n";
}
if (begin != end) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(begin, end)) << std::endl;
}
}
}
打印:
----- Parsng "rgb(1,2,3)" --------
parsed: rgba(1,2,3,255)
----- Parsng "rgba(4,5,6,7)" --------
parsed: rgba(4,5,6,7)
----- Parsng "hsl(8,9,10)" --------
TODO: implement static ast::color ast::color::hsl(uint8_t, uint8_t, uint8_t, uint8_t)(8,9,10,255)
parsed: rgba(8,9,10,255)
完整列表
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/fusion/include/size.hpp>
namespace x3 = boost::spirit::x3;
namespace ast {
using std::uint8_t;
struct color {
uint8_t red, green, blue, alpha;
color(uint8_t r=0, uint8_t g=0, uint8_t b=0, uint8_t a=255) : red{r}, green{g}, blue{b}, alpha{a} {}
static color hsl(uint8_t h, uint8_t s, uint8_t l, uint8_t a) {
std::cerr << "TODO: implement " << __PRETTY_FUNCTION__ << "(" << 1*h << "," << 1*s << "," << 1*l << "," << 1*a << ")\n";
return {h,s,l,a}; }
};
static inline std::ostream& operator<<(std::ostream& os, color const& c) {
return os << "rgba(" << 1*c.red << "," << 1*c.green << "," << 1*c.blue << "," << 1*c.alpha << ")";
}
}
BOOST_FUSION_ADAPT_STRUCT(ast::color, red, green, blue, alpha);
namespace {
namespace detail {
template <class F, class Sequence, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Sequence&& t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), boost::fusion::at_c<I>(std::forward<Sequence>(t))...);
}
}
template <class F, class Sequence>
constexpr decltype(auto) my_apply(F&& f, Sequence&& t)
{
return detail::apply_impl(
std::forward<F>(f), std::forward<Sequence>(t),
std::make_index_sequence<typename boost::fusion::result_of::size<std::remove_reference_t<Sequence> >::type{}>{});
}
}
namespace parser {
using namespace x3;
constexpr auto uint8 = uint_parser<std::uint8_t>{};
auto factory = [](auto f) {
return [f](auto& ctx) {
x3::_val(ctx) = my_apply(f, x3::_attr(ctx));
};
};
const auto color_rgb = rule<struct rgb, ast::color>{"rgb"}
= lit("rgb") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> x3::attr(255u) >> ')';
const auto color_rgba = rule<struct rgba, ast::color>{"rgba"}
= lit("rgba") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')';
const auto color_hsl = rule<struct hsl, ast::color>{"hsl"}
= (lit("hsl") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> attr(255u) >> ')') [factory(ast::color::hsl)];
const auto color = skip(space) [ color_rgba | color_rgb | color_hsl ];
}
int main() {
for (std::string const input : {
"rgb(1,2,3)",
"rgba(4,5,6,7)",
"hsl(8,9,10)" })
{
std::cout << " ----- Parsng " << std::quoted(input) << " --------\n";
auto begin = input.begin(), end = input.end();
ast::color result;
bool success = parse(begin, end, parser::color, result);
if (success) {
std::cout << "parsed: ";
std::cout << result << "\n";
} else {
std::cout << "failed\n";
}
if (begin != end) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(begin, end)) << std::endl;
}
}
}
¹ 它似乎不存在。当然,您可以复制到 std::tuple
并使用 std::apply
(also experimental)
我想解析 CSS 颜色函数(为简单起见,所有参数都是 0 到 255 之间的数字)
rgb(r,g,b)
rgba(r,g,b,a)
hsl(h,s,l)
hsla(h,s,l,a)
进入
struct color
{
color(std::uint8_t r, std::uint8_t g, std::uint8_t b, std::uint8_t a) : red{r}, green{g}, blue{b}, alpha{a} {}
static color hsl(std::uint8_t h, std::uint8_t s, std::uint8_t l, std::uint8_t a) { ... }
std::uint8_t red;
std::uint8_t green;
std::uint8_t blue;
std::uint8_t alpha;
}
我有一个有效的 hsl
函数实现,可以将 h、s 和 l 转换为 rgb 值。
我还有 rule
s 来处理前两个函数:
constexpr auto uint8 = uint_parser<std::uint8_t>{};
const auto color_rgb = rule<struct rgb, color>{"rgb"}
= lit("rgb") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')' >> attr(255);
const auto color_rgba = rule<struct rgba, color>{"rgba"}
= lit("rgba") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')';
这个有用,因为我用过
BOOST_FUSION_ADAPT_STRUCT(color,
red, green, blue, alpha)
问题出在hsl
函数上。我不能做第二个 BOOST_FUSION_ADAPT_STRUCT
,所以我想到在相应的值序列上使用语义操作,其中语义操作将简单地从序列构造 color
。像这样:
const auto color_hsl = rule<struct hsl, color, true>{"hsl"}
= (lit("hsl") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')' >> attr(255))[color::hsl];
这个不行,不然我也不会问这个问题。 Boost.Spirit.X3 也没有
意义上的东西[ qi::_val = phx::construct<color>(...), qi::_1, qi::_2, qi::_3, qi::_4) ];
看来我想手动执行 BOOST_FUSION_ADAPT_STRUCT
所做的操作,但是是在语义操作中。这可能吗?我应该如何处理?我知道序列的属性应该是一个具有四个解析值的 "fusion-vector" 类元组实体。我想提取它们并将它们填充到 color::hsl
中以生成规则的属性。
此处适用许多提示。
I cannot do a second BOOST_FUSION_ADAPT_STRUCT
在齐国这个一般形式似乎适用:
[ qi::_val = phxfunction(qi::_0) ]
您可以通过创建自己的 actor 类型来进一步简化它,这样您就可以只提供 "factory action":
[ factory(&Foo::makeBar) ]
.如果你投入
fusion::apply
¹ 的实现,你就可以避免处理 Fusion 序列 manuallt但是,您可能想了解这个 - 隐藏得很好 - 语义操作的属性兼容模式:
BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
。埋在那个更改日志中:Semantic actions now support attribute compatibility. This is a breaking change but #define
BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT
must be defined in order for the new behavior to kick in. By default, the old behavior is still in place.您可以通过更少的调整获得您想要的行为。
X3真是可塑。我们可以像上面描述的那样
factory
帮手:auto factory = [](auto f) { return [f](auto& ctx) { x3::_val(ctx) = my_apply(f, x3::_attr(ctx)); }; };
我将快速草稿
my_apply
(针对前面描述的boost::fusion::apply
):namespace detail { template <class F, class Sequence, std::size_t... I> constexpr decltype(auto) apply_impl(F&& f, Sequence&& t, std::index_sequence<I...>) { return std::invoke(std::forward<F>(f), boost::fusion::at_c<I>(std::forward<Sequence>(t))...); } } template <class F, class Sequence> constexpr decltype(auto) my_apply(F&& f, Sequence&& t) { return detail::apply_impl( std::forward<F>(f), std::forward<Sequence>(t), std::make_index_sequence<typename boost::fusion::result_of::size<std::remove_reference_t<Sequence> >::type{}>{}); }
现在我们可以拥有解析器了:
namespace parser {
using namespace x3;
constexpr auto uint8 = uint_parser<std::uint8_t>{};
auto factory = [](auto f) {
return [f](auto& ctx) {
x3::_val(ctx) = my_apply(f, x3::_attr(ctx));
};
};
const auto color_rgb = rule<struct rgb, ast::color>{"rgb"}
= lit("rgb") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> x3::attr(255u) >> ')';
const auto color_rgba = rule<struct rgba, ast::color>{"rgba"}
= lit("rgba") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')';
const auto color_hsl = rule<struct hsl, ast::color>{"hsl"}
= (lit("hsl") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> attr(255u) >> ')') [factory(ast::color::hsl)];
const auto color = skip(space) [ color_rgba | color_rgb | color_hsl ];
}
并测试它:
int main() {
for (std::string const input : {
"rgb(1,2,3)",
"rgba(4,5,6,7)",
"hsl(8,9,10)" })
{
std::cout << " ----- Parsng " << std::quoted(input) << " --------\n";
auto begin = input.begin(), end = input.end();
ast::color result;
bool success = parse(begin, end, parser::color, result);
if (success) {
std::cout << "parsed: ";
std::cout << result << "\n";
} else {
std::cout << "failed\n";
}
if (begin != end) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(begin, end)) << std::endl;
}
}
}
打印:
----- Parsng "rgb(1,2,3)" --------
parsed: rgba(1,2,3,255)
----- Parsng "rgba(4,5,6,7)" --------
parsed: rgba(4,5,6,7)
----- Parsng "hsl(8,9,10)" --------
TODO: implement static ast::color ast::color::hsl(uint8_t, uint8_t, uint8_t, uint8_t)(8,9,10,255)
parsed: rgba(8,9,10,255)
完整列表
#include <iostream>
#include <iomanip>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/fusion/include/size.hpp>
namespace x3 = boost::spirit::x3;
namespace ast {
using std::uint8_t;
struct color {
uint8_t red, green, blue, alpha;
color(uint8_t r=0, uint8_t g=0, uint8_t b=0, uint8_t a=255) : red{r}, green{g}, blue{b}, alpha{a} {}
static color hsl(uint8_t h, uint8_t s, uint8_t l, uint8_t a) {
std::cerr << "TODO: implement " << __PRETTY_FUNCTION__ << "(" << 1*h << "," << 1*s << "," << 1*l << "," << 1*a << ")\n";
return {h,s,l,a}; }
};
static inline std::ostream& operator<<(std::ostream& os, color const& c) {
return os << "rgba(" << 1*c.red << "," << 1*c.green << "," << 1*c.blue << "," << 1*c.alpha << ")";
}
}
BOOST_FUSION_ADAPT_STRUCT(ast::color, red, green, blue, alpha);
namespace {
namespace detail {
template <class F, class Sequence, std::size_t... I>
constexpr decltype(auto) apply_impl(F&& f, Sequence&& t, std::index_sequence<I...>)
{
return std::invoke(std::forward<F>(f), boost::fusion::at_c<I>(std::forward<Sequence>(t))...);
}
}
template <class F, class Sequence>
constexpr decltype(auto) my_apply(F&& f, Sequence&& t)
{
return detail::apply_impl(
std::forward<F>(f), std::forward<Sequence>(t),
std::make_index_sequence<typename boost::fusion::result_of::size<std::remove_reference_t<Sequence> >::type{}>{});
}
}
namespace parser {
using namespace x3;
constexpr auto uint8 = uint_parser<std::uint8_t>{};
auto factory = [](auto f) {
return [f](auto& ctx) {
x3::_val(ctx) = my_apply(f, x3::_attr(ctx));
};
};
const auto color_rgb = rule<struct rgb, ast::color>{"rgb"}
= lit("rgb") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> x3::attr(255u) >> ')';
const auto color_rgba = rule<struct rgba, ast::color>{"rgba"}
= lit("rgba") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')';
const auto color_hsl = rule<struct hsl, ast::color>{"hsl"}
= (lit("hsl") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> attr(255u) >> ')') [factory(ast::color::hsl)];
const auto color = skip(space) [ color_rgba | color_rgb | color_hsl ];
}
int main() {
for (std::string const input : {
"rgb(1,2,3)",
"rgba(4,5,6,7)",
"hsl(8,9,10)" })
{
std::cout << " ----- Parsng " << std::quoted(input) << " --------\n";
auto begin = input.begin(), end = input.end();
ast::color result;
bool success = parse(begin, end, parser::color, result);
if (success) {
std::cout << "parsed: ";
std::cout << result << "\n";
} else {
std::cout << "failed\n";
}
if (begin != end) {
std::cout << "Remaining unparsed: " << std::quoted(std::string(begin, end)) << std::endl;
}
}
}
¹ 它似乎不存在。当然,您可以复制到 std::tuple
并使用 std::apply
(also experimental)