将已解析序列的每个元素传递给返回规则的属性类型的函数 [英] Passing each element of a parsed sequence to a function that returns a rule's attribute type

查看:72
本文介绍了将已解析序列的每个元素传递给返回规则的属性类型的函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想解析CSS颜色函数(为简单起见,所有参数都是0到255之间的数字)

I want to parse CSS color functions (for simplicity, all of the arguments are numbers between 0 and 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值.

I have a working hsl function implementation that converts the h, s, and l into rgb values.

我还具有rule来处理前两个功能:

I also have rules to handle the first two functions:

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.像这样:

The problem lies in the hsl functions. I cannot do a second BOOST_FUSION_ADAPT_STRUCT, so I thought of using semantic actions on the respective sequences of values where the semantic action would simply construct the color from the sequence. Something like this:

const auto color_hsl = rule<struct hsl, color, true>{"hsl"}
                   = (lit("hsl") >> '(' >> uint8 >> ',' >> uint8 >> ',' >> uint8 >> ')' >> attr(255))[color::hsl];

这行不通,否则我不会问这个问题. Boost.Spirit.X3都没有

This doesn't work, or I wouldn't be asking this question. Neither does Boost.Spirit.X3 have something in the sense of

[ qi::_val = phx::construct<color>(...), qi::_1, qi::_2, qi::_3, qi::_4) ];

似乎我想手动执行BOOST_FUSION_ADAPT_STRUCT的功能,但是要进行语义操作.这可能吗,我该如何处理?我知道序列的属性应该是具有四个解析值的融合向量"元组状实体.我想提取它们并将其填充到color::hsl中以生成规则的属性.

It seems I want to do manually what BOOST_FUSION_ADAPT_STRUCT does, but in a semantic action. Is this possible and how should I handle this? I know the attribute of the sequence should be an "fusion-vector" tuple-like entity with the four parsed values. I want to extract them and stuff them into color::hsl to generate the rule's attribute.

推荐答案

此处有许多提示.

我不能再进行一次BOOST_FUSION_ADAPT_STRUCT

I cannot do a second BOOST_FUSION_ADAPT_STRUCT

当然可以,请参见

Sure you can, see BOOST_FUSION_ADAPT_STRUCT_NAMED

在Qi中,这种一般形式似乎适用:

In Qi this general form seem to apply:

[ qi::_val = phxfunction(qi::_0) ]

您可以通过自己设置演员类型来进一步简化,以便您只需提供工厂行动"即可:[ factory(&Foo::makeBar) ].

You could further simplify that by making your own actor type so you could just supply your "factory action": [ factory(&Foo::makeBar) ].

如果使用fusion::apply¹的实现,则可以避免处理Fusion序列手册

If you throw in an implementation of fusion::apply¹ you can avoid ever dealing with the Fusion sequence manuallt

但是,您可能想了解一下-很好隐藏的-语义动作的属性兼容模式:

However, you might want to learn about this - very well hidden - attribute compatibility mode for semantic actions: BOOST_SPIRIT_ACTIONS_ALLOW_ATTR_COMPAT. Buried in that change log:

语义动作现在支持属性兼容性.这是一个重大更改,但是必须定义#define 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.

您可以通过更少的调整来获得所需的行为.

You might get the behaviour you desire with much less tweaking.

X3确实具有延展性.我们可以将上述factory助手的内容减少到最少:

X3 is really malleable. We can have that factory helper described above in as little as:

auto factory = [](auto f) {
    return [f](auto& ctx) {
        x3::_val(ctx) = my_apply(f, x3::_attr(ctx));
    };
};

我将快速撰写my_apply(对于前面所述的boost::fusion::apply):

I'll throw in a quick draft of my_apply (for the boost::fusion::apply described earlier):

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{}>{});
    }

现在我们可以使用解析器了:

Now we can have the parser:

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 ];
} 

并使用以下命令进行测试:

And test it with:

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;
        }
    }
}

打印:

在Coliru上直播

 ----- 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)

完整列表

在Coliru上直播

#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 (也实验)


¹ it seems not to exist. Of course you could copy to a std::tuple and use std::apply (also experimental)

这篇关于将已解析序列的每个元素传递给返回规则的属性类型的函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆