C++ c++;正则表达式:如何捕捉子匹配向量

C++ c++;正则表达式:如何捕捉子匹配向量,c++,regex,parsing,cookies,boost,C++,Regex,Parsing,Cookies,Boost,让我们假设它是ECMAScript、c++11的regexp或boost::xpressive 输入时,我有一个字符串,格式为:key1=value1;键2=值2;key3=value3依此类推,因此该函数的正则表达式为 ((w+)\=(w+)\;)*((\w)+\=(\w)+)\;? 我想将所有键和值放入两个std::vector 所以1,2,3,4号潜艇都有我需要的。并且存在问题-可能\1是一个数组。我没有看到任何接口,我既不能在C++中也不能得到Boo::XPixy。可能在这里,但不太明

让我们假设它是ECMAScript、c++11的regexp或boost::xpressive 输入时,我有一个字符串,格式为:
key1=value1;键2=值2;key3=value3
依此类推,因此该函数的正则表达式为

((w+)\=(w+)\;)*((\w)+\=(\w)+)\;?
我想将所有键和值放入两个std::vector
所以1,2,3,4号潜艇都有我需要的。并且存在问题-可能\1是一个数组。我没有看到任何接口,我既不能在C++中也不能得到Boo::XPixy。可能在这里,但不太明显。

尽管您从未回到需求的确切本质上来,但我想向您展示一下我提出的/只是/解析
设置Cookie
响应头的方法,如§4.2.2“设置Cookie语法”中所述

注意

  • 这是规范的直接翻译
  • 我试图严格遵守规范(关于日期格式、数字格式、区分大小写等)
  • 还要注意的是,这借鉴了Boost Spirit库的丰富经验,因此不可能每个人都以这种方式将规范转换为工作代码
  • 此外,通过遵守规范,许多现实生活中的cookie将被拒绝(例如,由于
    之后缺少空间)。要使其成为现实生活的证明,需要进行更多的调整(具体来说,更改skipper类型并将子解析器表达式指定为
    lexeme
    s)
  • 最后,关键点:这只是RFC中的一个。还有其他版本,它们指定了此处未涉及的变体。在现实生活中,HTTP客户端必须借助启发式来完善cookies的解密技术。它们通常根据版本切换到完全不同的解析代码

    轶事:就在今天,我追踪到我们软件中的一个bug,它被OpenJDK 6吞没了。毫无例外。你猜到了:由于不支持的功能。当然,Java 6很旧,但我们不会在下一个版本之前放弃对它的支持。所以……我们编写了更多的解析代码

变量1:无界属性列表 下面是最通用的代码版本,其中AST包含变量属性向量(如语法所示):

//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>

namespace qi = boost::spirit::qi;

#include <boost/date_time.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/operations.hpp>

namespace http {
    class datetime {
        using clock = boost::local_time::local_sec_clock;
        boost::local_time::local_date_time m_dt;
      public:
        datetime() : m_dt(clock::local_time(boost::local_time::time_zone_ptr())) {
        }

        friend std::ostream& operator<<(std::ostream& os, datetime const& o) {
            std::ostream imbued(os.rdbuf());
            imbued.imbue(std::locale(imbued.getloc(), new boost::local_time::local_time_facet("%a, %d %b %Y %H:%M:%S GMT")));
            imbued << o.m_dt;
            return os;
        }

        friend std::istream& operator>>(std::istream& is, datetime& o) {
            std::istream imbued(is.rdbuf());
            imbued.imbue(std::locale(std::locale::classic(), new boost::local_time::local_time_input_facet("%a, %d %b %Y %H:%M:%S GMT")));
            imbued >> o.m_dt;
            return is;
        }
    };
}

namespace ast {
    using rfc1123date = http::datetime; // rfc1123 formatting

    namespace attr {
        struct expires   { static constexpr char const* name() { return "expires";   } };
        struct max_age   { static constexpr char const* name() { return "max-age";   } };
        struct domain    { static constexpr char const* name() { return "domain";    } };
        struct path      { static constexpr char const* name() { return "path";      } };
        struct secure    { static constexpr char const* name() { return "secure";    } };
        struct httponly  { static constexpr char const* name() { return "httponly";  } };
        struct extension { static constexpr char const* name() { return "extension"; } };
    }

    template <typename tag, typename V = std::string>
    struct attribute {
        V value;
        attribute(V value = {}) : value(std::move(value)) {}
        friend std::ostream& operator<<(std::ostream& os, attribute const& attr) {
            return os << "[ " << tag::name() << "=" << attr.value << " ]";
        }
    };

    template <typename tag>
    struct attribute<tag, void> {
        //attribute(bool = true) {};
        friend std::ostream& operator<<(std::ostream& os, attribute const&) {
            return os << "[ attribute: present ]";
        }
    };

    using expires_av   = attribute<attr::expires, rfc1123date>;
    using max_age_av   = attribute<attr::max_age, int>;
    using domain_av    = attribute<attr::domain>;
    using path_av      = attribute<attr::path>;
    using secure_av    = attribute<attr::secure, void>;
    using httponly_av  = attribute<attr::httponly, void>;
    using extension_av = attribute<attr::extension>;

    using cookie_av = boost::variant<
            expires_av,
            max_age_av,
            domain_av,
            path_av,
            secure_av,
            httponly_av,
            extension_av
        >;

    struct cookie_pair {
        std::string name, value;
    };

    struct cookie {
        cookie_pair pair;
        std::list<cookie_av> attributes;
    };
}

BOOST_FUSION_ADAPT_STRUCT(ast::cookie_pair,
        (std::string, name)
        (std::string, value)
    )

BOOST_FUSION_ADAPT_STRUCT(ast::cookie,
        (ast::cookie_pair, pair)
        (std::list<ast::cookie_av>, attributes)
    )

namespace ast {
    static inline std::ostream& operator<<(std::ostream& os, std::list<cookie_av> const&v) {
        os << "{";
        std::copy(v.begin(), v.end(), std::ostream_iterator<cookie_av>(os, "; "));
        return os << "}";
    }
    static inline std::ostream& operator<<(std::ostream& os, cookie_pair const&v) { return os << boost::fusion::as_vector(v); }
    static inline std::ostream& operator<<(std::ostream& os, cookie      const&v) { return os << boost::fusion::as_vector(v); }
}

template <typename It>
struct set_cookie : qi::grammar<It, ast::cookie()> {
    set_cookie() : set_cookie::base_type(start) {
        using namespace qi;
        using boost::proto::deep_copy;

        /////////////////////////////////////////////////////////////////
        // RFC2616 2.2 token
#define RFC_CTLs "\x01-\x1f\x7f"
        constexpr char DQUOTE = '"';
        token = +(~char_(RFC_CTLs /*separators:*/ "()<>@,;:\\\"/[]?={} \t") - '\0');

        /////////////////////////////////////////////////////////////////
        // RFC6265 4.1.1. Syntax (set-cookie)

        set_cookie_header = no_case["set-cookie: "] >> set_cookie_string;
        set_cookie_string = cookie_pair >> *("; " >> cookie_av);
        cookie_pair       = cookie_name >> '=' >> cookie_value;
        cookie_name       = token;
        auto cookie_octet = deep_copy(char_("\x21" "\x23-\x2B" "\x2D-\x3A" "\x3C-\x5B" "\x5D-\x7E"));
        cookie_value      = *cookie_octet | (DQUOTE >> *cookie_octet >> DQUOTE);
        //                     ; US-ASCII characters excluding CTLs,
        //                     ; whitespace DQUOTE, comma, semicolon,
        //                     ; and backslash

        cookie_av         = expires_av
                          | max_age_av
                          | domain_av
                          | path_av
                          | secure_av
                          | httponly_av
                          | extension_av
                          ;
        expires_av        = no_case["expires="] >> sane_cookie_date;
        sane_cookie_date  = stream; // TODO <rfc1123_date, defined in [RFC2616], Section 3.3.1>
        max_age_av        = no_case["max-age="] >> !char_('0') >> uint_;
        //                     ; In practice, both expires_av and max_age_av
        //                     ; are limited to dates representable by the
        //                     ; user agent.
        // non_zero_digit    = %x31-39
        //                     ; digits 1 through 9
        domain_av         = no_case["domain="] >> domain_value;
        domain_value      = raw [ (alpha >> *(alpha|digit|'-')) % '.'];
        //                     ; defined in [RFC1034], Section 3.5, as
        //                     ; enhanced by [RFC1123], Section 2.1
        path_av           = no_case["path="] >> path_value;
        path_value        = *(~char_(RFC_CTLs ";") - '\0'); //  <any CHAR except CTLs or ";">
        secure_av         = no_case["secure"]   >> attr(ast::secure_av{});
        httponly_av       = no_case["httponly"] >> attr(ast::httponly_av{});
        extension_av      = as_string [*(~char_(RFC_CTLs ";") - '\0')]; // <any CHAR except CTLs or ";">

        start = set_cookie_header;

        BOOST_SPIRIT_DEBUG_NODES(
                (start)
                (set_cookie_header) (set_cookie_string)
                (cookie_pair) (cookie_name) (cookie_value) (token)

                (cookie_av)
                (expires_av) (sane_cookie_date)
                (max_age_av)
                (domain_av)  (domain_value)
                (path_av)    (path_value)
                (secure_av)
                (httponly_av)
                (extension_av)
            );
#undef RFC_CTLs
    }
  private:
    qi::rule<It, ast::cookie()>       start;
    qi::rule<It, std::string()>       token, cookie_name, cookie_value, domain_value, path_value;

    qi::rule<It, ast::cookie()>       set_cookie_header, set_cookie_string;
    qi::rule<It, ast::cookie_pair()>  cookie_pair;

    qi::rule<It, ast::cookie_av()>    cookie_av;

    qi::rule<It, ast::expires_av()>   expires_av;
    qi::rule<It, ast::rfc1123date()>  sane_cookie_date;

    qi::rule<It, ast::max_age_av()>   max_age_av; // non_zero_digit;
    qi::rule<It, ast::domain_av()>    domain_av;
    qi::rule<It, ast::path_av()>      path_av;

    qi::rule<It, ast::secure_av()>    secure_av;
    qi::rule<It, ast::httponly_av()>  httponly_av;
    qi::rule<It, ast::extension_av()> extension_av;
};

int main() {
    using It = std::string::const_iterator;
    for (std::string const s : {
            "Set-Cookie: name=value",
            "Set-Cookie: name=value; Path=/; Domain=domain.com",
            "set-cookie: name=value; path=/; domain=domain.com",

            //// not actually rfc 6265 conformant:
            //"Set-Cookie: name=value;path=/;domain=domain.com",

            // actually a wednesday:
            "Set-Cookie: name=value; path=/; domain=.mydomain.com; expires=Thu, 01-Jan-2070 00:00:10 GMT; comment=no_comment"
            })
    {
        It f = s.begin(), l = s.end();
        std::cout << "Parsing '" << s << "'\n";

        ast::cookie cookie;
        bool ok = qi::parse(f,l,set_cookie<It>(),cookie);

        if (ok) {
            std::cout << " -- Parse success: " << cookie << "\n";
        }
        else
            std::cout << " -- Parse failure\n";

        if (f!=l)
            std::cout << " -- Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
}
//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/optional/optional_io.hpp>

namespace qi = boost::spirit::qi;

#include <boost/date_time.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/operations.hpp>

namespace http {
    class datetime {
        using clock = boost::local_time::local_sec_clock;
        boost::local_time::local_date_time m_dt;
      public:
        datetime() : m_dt(clock::local_time(boost::local_time::time_zone_ptr())) {
        }

        friend std::ostream& operator<<(std::ostream& os, datetime const& o) {
            std::ostream imbued(os.rdbuf());
            imbued.imbue(std::locale(imbued.getloc(), new boost::local_time::local_time_facet("%a, %d %b %Y %H:%M:%S GMT")));
            imbued << o.m_dt;
            return os;
        }

        friend std::istream& operator>>(std::istream& is, datetime& o) {
            std::istream imbued(is.rdbuf());
            imbued.imbue(std::locale(std::locale::classic(), new boost::local_time::local_time_input_facet("%a, %d %b %Y %H:%M:%S GMT")));
            imbued >> o.m_dt;
            return is;
        }
    };
}

namespace ast {
    using boost::optional;
    using rfc1123date = http::datetime; // rfc1123 formatting

    struct expires_av {
        rfc1123date date;
        expires_av(rfc1123date date = {}) : date(date) {}
        friend std::ostream& operator<<(std::ostream& os, expires_av const& attr) {
            return os << "[ expires_av: " << attr.date << " ]";
        }
    };

    struct max_age_av {
        unsigned age;
        max_age_av(unsigned age = {}) : age(age) {}
        friend std::ostream& operator<<(std::ostream& os, max_age_av const& attr) {
            return os << "[ max_age_av: " << attr.age << " ]";
        }
    };
    struct string_attribute {
        std::string value;
        string_attribute(std::string value = "") : value(std::move(value)) {}
        friend std::ostream& operator<<(std::ostream& os, string_attribute const& attr) {
            return os << "[ string_attribute: " << attr.value << " ]";
        }
    };
    using domain_av = string_attribute;
    using path_av = string_attribute;
    using extension_av = string_attribute;

    struct cookie_av {
        optional<expires_av> expires;
        optional<max_age_av> max_age;
        optional<domain_av> domain;
        optional<path_av> path;
        bool secure   = false;
        bool httponly = false;
        optional<extension_av> extension;
    };

    struct cookie_pair {
        std::string name, value;
    };

    struct cookie {
        cookie_pair pair;
        cookie_av   attributes;
    };
}

BOOST_FUSION_ADAPT_STRUCT(ast::cookie_av,
        (optional<ast::expires_av>,expires)
        (optional<ast::max_age_av>,max_age)
        (optional<ast::domain_av>,domain)
        (optional<ast::path_av>,path)
        (bool,secure)
        (bool,httponly)
        (optional<ast::extension_av>,extension)
    )

BOOST_FUSION_ADAPT_STRUCT(ast::cookie_pair,
        (std::string, name)
        (std::string, value)
    )

BOOST_FUSION_ADAPT_STRUCT(ast::cookie,
        (ast::cookie_pair, pair)
        (ast::cookie_av, attributes)
    )

namespace ast {
    static inline std::ostream& operator<<(std::ostream& os, cookie_av   const&v) { return os << boost::fusion::as_vector(v); }
    static inline std::ostream& operator<<(std::ostream& os, cookie_pair const&v) { return os << boost::fusion::as_vector(v); }
    static inline std::ostream& operator<<(std::ostream& os, cookie      const&v) { return os << boost::fusion::as_vector(v); }
}

template <typename It>
struct set_cookie : qi::grammar<It, ast::cookie()> {
    set_cookie() : set_cookie::base_type(start) {
        using namespace qi;
        using boost::proto::deep_copy;

        /////////////////////////////////////////////////////////////////
        // RFC2616 2.2 token
#define RFC_CTLs "\x01-\x1f\x7f"
        constexpr char DQUOTE = '"';
        token = +(~char_(RFC_CTLs /*separators:*/ "()<>@,;:\\\"/[]?={} \t") - '\0');

        /////////////////////////////////////////////////////////////////
        // RFC6265 4.1.1. Syntax (set-cookie)

        set_cookie_header = no_case["set-cookie: "] >> set_cookie_string;
        set_cookie_string = cookie_pair >> -attributes;
        cookie_pair       = cookie_name >> '=' >> cookie_value;
        cookie_name       = token;
        auto cookie_octet = deep_copy(char_("\x21" "\x23-\x2B" "\x2D-\x3A" "\x3C-\x5B" "\x5D-\x7E"));
        cookie_value      = *cookie_octet | (DQUOTE >> *cookie_octet >> DQUOTE);
        //                     ; US-ASCII characters excluding CTLs,
        //                     ; whitespace DQUOTE, comma, semicolon,
        //                     ; and backslash

        attributes        = ("; " >> expires_av)
                          ^ ("; " >> max_age_av)
                          ^ ("; " >> domain_av)
                          ^ ("; " >> path_av)
                          ^ ("; " >> secure_av)
                          ^ ("; " >> httponly_av)
                          ^ ("; " >> extension_av)
                          ;
        expires_av        = no_case["expires="] >> sane_cookie_date;
        sane_cookie_date  = stream; // TODO <rfc1123_date, defined in [RFC2616], Section 3.3.1>
        max_age_av        = no_case["max-age="] >> !char_('0') >> uint_;
        //                     ; In practice, both expires_av and max_age_av
        //                     ; are limited to dates representable by the
        //                     ; user agent.
        // non_zero_digit    = %x31-39
        //                     ; digits 1 through 9
        domain_av         = no_case["domain="] >> domain_value;
        domain_value      = raw [ (alpha >> *(alpha|digit|'-')) % '.'];
        //                     ; defined in [RFC1034], Section 3.5, as
        //                     ; enhanced by [RFC1123], Section 2.1
        path_av           = no_case["path="] >> path_value;
        path_value        = *(~char_(RFC_CTLs ";") - '\0'); //  <any CHAR except CTLs or ";">
        secure_av         = no_case["secure"]   >> attr(true);
        httponly_av       = no_case["httponly"] >> attr(true);
        extension_av      = as_string [*(~char_(RFC_CTLs ";") - '\0')]; // <any CHAR except CTLs or ";">

        start = set_cookie_header;

        BOOST_SPIRIT_DEBUG_NODES(
                (start)
                (set_cookie_header) (set_cookie_string)
                (cookie_pair) (cookie_name) (cookie_value) (token)

                (attributes)
                (expires_av) (sane_cookie_date)
                (max_age_av)
                (domain_av)  (domain_value)
                (path_av)    (path_value)
                (secure_av)
                (httponly_av)
                (extension_av)
            );
#undef RFC_CTLs
    }
  private:
    qi::rule<It, ast::cookie()>       start;
    qi::rule<It, std::string()>       token, cookie_name, cookie_value, domain_value, path_value;

    qi::rule<It, ast::cookie()>       set_cookie_header, set_cookie_string;
    qi::rule<It, ast::cookie_pair()>  cookie_pair;

    qi::rule<It, ast::cookie_av()>    attributes;

    qi::rule<It, ast::expires_av()>   expires_av;
    qi::rule<It, ast::rfc1123date()>  sane_cookie_date;

    qi::rule<It, ast::max_age_av()>   max_age_av; // non_zero_digit;
    qi::rule<It, ast::domain_av()>    domain_av;
    qi::rule<It, ast::path_av()>      path_av;

    qi::rule<It, bool()>              secure_av, httponly_av;
    qi::rule<It, ast::extension_av()> extension_av;
};

int main() {
    using It = std::string::const_iterator;
    for (std::string const s : {
            "Set-Cookie: name=value",
            "Set-Cookie: name=value; Path=/; Domain=domain.com",
            "set-cookie: name=value; path=/; domain=domain.com",
            //// not actually rfc 6265 conformant:
            //"Set-Cookie: name=value;path=/;domain=domain.com",

            // actually a wednesday:
            "Set-Cookie: name=value; path=/; domain=.mydomain.com; expires=Thu, 01-Jan-2070 00:00:10 GMT; comment=no_comment"
            })
    {
        It f = s.begin(), l = s.end();
        std::cout << "Parsing '" << s << "'\n";

        ast::cookie cookie;
        bool ok = qi::parse(f,l,set_cookie<It>(),cookie);

        if (ok) {
            std::cout << " -- Parse success: " << cookie << "\n";
        }
        else
            std::cout << " -- Parse failure\n";

        if (f!=l)
            std::cout << " -- Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
}
/#定义BOOST_SPIRIT_调试
#包括
#包括
#包括
#包括
名称空间qi=boost::spirit::qi;
#包括
#包括
#包括
#包括
命名空间http{
类日期时间{
使用clock=boost::local_time::local_secu_时钟;
boost::local_time::local_date_time m_dt;
公众:
datetime():m_dt(时钟::本地时间(boost::本地时间::时区ptr()){
}
friend std::ostream&operator>o.m_dt;
回报是;
}
};
}
名称空间ast{
使用boost::可选;
使用rfc1123date=http::datetime;//rfc1123格式
struct\u av{
RFC1123日期;
expires_av(rfc1123date={}):date(date){}

friend std::奥斯特雷姆和运营商你确定没有键包含
=
,没有值包含
,没有不属于键/值的空白,等等吗?否则,一切都会变得更复杂。b)为什么要使用正则表达式?虽然它的代码可能略短一些,但要找到第一个
=
,调用substr,对
,以及大范围内的所有内容都一样循环是你看过一个吗?是的,当然我肯定:)我只是试着不让社区超负荷使用非常复杂的reg-exp。我试着解析Set Cookie:http头。当然我可以使用令牌迭代器,但这样我最终会编写RL降序解析器。我一直希望保持懒惰,让库为我工作。我肯定会使用提升工作精神你确定你在分析吗?这看起来更接近。饼干很容易被低估。。。
using domain_av = string_attribute;
using path_av = string_attribute;
using extension_av = string_attribute;

struct cookie_av {
    optional<expires_av> expires;
    optional<max_age_av> max_age;
    optional<domain_av> domain;
    optional<path_av> path;
    bool secure   = false;
    bool httponly = false;
    optional<extension_av> extension;
};

struct cookie_pair {
    std::string name, value;
};

struct cookie {
    cookie_pair pair;
    cookie_av   attributes;
};
//#define BOOST_SPIRIT_DEBUG
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/date_time/posix_time/posix_time_io.hpp>
#include <boost/optional/optional_io.hpp>

namespace qi = boost::spirit::qi;

#include <boost/date_time.hpp>
#include <boost/date_time/local_time/local_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/operations.hpp>

namespace http {
    class datetime {
        using clock = boost::local_time::local_sec_clock;
        boost::local_time::local_date_time m_dt;
      public:
        datetime() : m_dt(clock::local_time(boost::local_time::time_zone_ptr())) {
        }

        friend std::ostream& operator<<(std::ostream& os, datetime const& o) {
            std::ostream imbued(os.rdbuf());
            imbued.imbue(std::locale(imbued.getloc(), new boost::local_time::local_time_facet("%a, %d %b %Y %H:%M:%S GMT")));
            imbued << o.m_dt;
            return os;
        }

        friend std::istream& operator>>(std::istream& is, datetime& o) {
            std::istream imbued(is.rdbuf());
            imbued.imbue(std::locale(std::locale::classic(), new boost::local_time::local_time_input_facet("%a, %d %b %Y %H:%M:%S GMT")));
            imbued >> o.m_dt;
            return is;
        }
    };
}

namespace ast {
    using boost::optional;
    using rfc1123date = http::datetime; // rfc1123 formatting

    struct expires_av {
        rfc1123date date;
        expires_av(rfc1123date date = {}) : date(date) {}
        friend std::ostream& operator<<(std::ostream& os, expires_av const& attr) {
            return os << "[ expires_av: " << attr.date << " ]";
        }
    };

    struct max_age_av {
        unsigned age;
        max_age_av(unsigned age = {}) : age(age) {}
        friend std::ostream& operator<<(std::ostream& os, max_age_av const& attr) {
            return os << "[ max_age_av: " << attr.age << " ]";
        }
    };
    struct string_attribute {
        std::string value;
        string_attribute(std::string value = "") : value(std::move(value)) {}
        friend std::ostream& operator<<(std::ostream& os, string_attribute const& attr) {
            return os << "[ string_attribute: " << attr.value << " ]";
        }
    };
    using domain_av = string_attribute;
    using path_av = string_attribute;
    using extension_av = string_attribute;

    struct cookie_av {
        optional<expires_av> expires;
        optional<max_age_av> max_age;
        optional<domain_av> domain;
        optional<path_av> path;
        bool secure   = false;
        bool httponly = false;
        optional<extension_av> extension;
    };

    struct cookie_pair {
        std::string name, value;
    };

    struct cookie {
        cookie_pair pair;
        cookie_av   attributes;
    };
}

BOOST_FUSION_ADAPT_STRUCT(ast::cookie_av,
        (optional<ast::expires_av>,expires)
        (optional<ast::max_age_av>,max_age)
        (optional<ast::domain_av>,domain)
        (optional<ast::path_av>,path)
        (bool,secure)
        (bool,httponly)
        (optional<ast::extension_av>,extension)
    )

BOOST_FUSION_ADAPT_STRUCT(ast::cookie_pair,
        (std::string, name)
        (std::string, value)
    )

BOOST_FUSION_ADAPT_STRUCT(ast::cookie,
        (ast::cookie_pair, pair)
        (ast::cookie_av, attributes)
    )

namespace ast {
    static inline std::ostream& operator<<(std::ostream& os, cookie_av   const&v) { return os << boost::fusion::as_vector(v); }
    static inline std::ostream& operator<<(std::ostream& os, cookie_pair const&v) { return os << boost::fusion::as_vector(v); }
    static inline std::ostream& operator<<(std::ostream& os, cookie      const&v) { return os << boost::fusion::as_vector(v); }
}

template <typename It>
struct set_cookie : qi::grammar<It, ast::cookie()> {
    set_cookie() : set_cookie::base_type(start) {
        using namespace qi;
        using boost::proto::deep_copy;

        /////////////////////////////////////////////////////////////////
        // RFC2616 2.2 token
#define RFC_CTLs "\x01-\x1f\x7f"
        constexpr char DQUOTE = '"';
        token = +(~char_(RFC_CTLs /*separators:*/ "()<>@,;:\\\"/[]?={} \t") - '\0');

        /////////////////////////////////////////////////////////////////
        // RFC6265 4.1.1. Syntax (set-cookie)

        set_cookie_header = no_case["set-cookie: "] >> set_cookie_string;
        set_cookie_string = cookie_pair >> -attributes;
        cookie_pair       = cookie_name >> '=' >> cookie_value;
        cookie_name       = token;
        auto cookie_octet = deep_copy(char_("\x21" "\x23-\x2B" "\x2D-\x3A" "\x3C-\x5B" "\x5D-\x7E"));
        cookie_value      = *cookie_octet | (DQUOTE >> *cookie_octet >> DQUOTE);
        //                     ; US-ASCII characters excluding CTLs,
        //                     ; whitespace DQUOTE, comma, semicolon,
        //                     ; and backslash

        attributes        = ("; " >> expires_av)
                          ^ ("; " >> max_age_av)
                          ^ ("; " >> domain_av)
                          ^ ("; " >> path_av)
                          ^ ("; " >> secure_av)
                          ^ ("; " >> httponly_av)
                          ^ ("; " >> extension_av)
                          ;
        expires_av        = no_case["expires="] >> sane_cookie_date;
        sane_cookie_date  = stream; // TODO <rfc1123_date, defined in [RFC2616], Section 3.3.1>
        max_age_av        = no_case["max-age="] >> !char_('0') >> uint_;
        //                     ; In practice, both expires_av and max_age_av
        //                     ; are limited to dates representable by the
        //                     ; user agent.
        // non_zero_digit    = %x31-39
        //                     ; digits 1 through 9
        domain_av         = no_case["domain="] >> domain_value;
        domain_value      = raw [ (alpha >> *(alpha|digit|'-')) % '.'];
        //                     ; defined in [RFC1034], Section 3.5, as
        //                     ; enhanced by [RFC1123], Section 2.1
        path_av           = no_case["path="] >> path_value;
        path_value        = *(~char_(RFC_CTLs ";") - '\0'); //  <any CHAR except CTLs or ";">
        secure_av         = no_case["secure"]   >> attr(true);
        httponly_av       = no_case["httponly"] >> attr(true);
        extension_av      = as_string [*(~char_(RFC_CTLs ";") - '\0')]; // <any CHAR except CTLs or ";">

        start = set_cookie_header;

        BOOST_SPIRIT_DEBUG_NODES(
                (start)
                (set_cookie_header) (set_cookie_string)
                (cookie_pair) (cookie_name) (cookie_value) (token)

                (attributes)
                (expires_av) (sane_cookie_date)
                (max_age_av)
                (domain_av)  (domain_value)
                (path_av)    (path_value)
                (secure_av)
                (httponly_av)
                (extension_av)
            );
#undef RFC_CTLs
    }
  private:
    qi::rule<It, ast::cookie()>       start;
    qi::rule<It, std::string()>       token, cookie_name, cookie_value, domain_value, path_value;

    qi::rule<It, ast::cookie()>       set_cookie_header, set_cookie_string;
    qi::rule<It, ast::cookie_pair()>  cookie_pair;

    qi::rule<It, ast::cookie_av()>    attributes;

    qi::rule<It, ast::expires_av()>   expires_av;
    qi::rule<It, ast::rfc1123date()>  sane_cookie_date;

    qi::rule<It, ast::max_age_av()>   max_age_av; // non_zero_digit;
    qi::rule<It, ast::domain_av()>    domain_av;
    qi::rule<It, ast::path_av()>      path_av;

    qi::rule<It, bool()>              secure_av, httponly_av;
    qi::rule<It, ast::extension_av()> extension_av;
};

int main() {
    using It = std::string::const_iterator;
    for (std::string const s : {
            "Set-Cookie: name=value",
            "Set-Cookie: name=value; Path=/; Domain=domain.com",
            "set-cookie: name=value; path=/; domain=domain.com",
            //// not actually rfc 6265 conformant:
            //"Set-Cookie: name=value;path=/;domain=domain.com",

            // actually a wednesday:
            "Set-Cookie: name=value; path=/; domain=.mydomain.com; expires=Thu, 01-Jan-2070 00:00:10 GMT; comment=no_comment"
            })
    {
        It f = s.begin(), l = s.end();
        std::cout << "Parsing '" << s << "'\n";

        ast::cookie cookie;
        bool ok = qi::parse(f,l,set_cookie<It>(),cookie);

        if (ok) {
            std::cout << " -- Parse success: " << cookie << "\n";
        }
        else
            std::cout << " -- Parse failure\n";

        if (f!=l)
            std::cout << " -- Remaining unparsed: '" << std::string(f,l) << "'\n";
    }
}