boost::spirit 解析为融合适应结构可选但独占

boost::spirit parsing into a fusion adapted structure optional but exclusive

如果有结构:

struct record
{
    std::string     type;
    std::string     delimiter;
    uint32_t        length;
    std::string     name;

    record()
    {
            type = "";
            delimiter = "";
            length = 0;
            name = "";
    }
};

使用 boost::fusion 和以下语法改编:

struct record_parser : qi::grammar<Iterator, record(), ascii::space_type>
{
    record_parser() : record_parser::base_type(start)
    {
        using qi::lit;
        using qi::uint_;
        using qi::lexeme;

        using ascii::char_;
        using ascii::blank;
        using ascii::string;
        using qi::attr;

        using qi::eps;

            type %= lexeme[+(char_ - (blank|char('(')))];
            delimiter_double_quote %= char('(') >> lexeme[char('"')  >> +(char_ - char('"'))  >> char('"') ] >> char(')');
            delimiter_single_quote %= char('(') >> lexeme[char('\'') >> +(char_ - char('\'')) >> char('\'')] >> char(')');
            delimiter %= (delimiter_double_quote | delimiter_single_quote);
            name %= lexeme[+(char_ - (blank|char(';')))] >> char(';');
            length %= (char('(') >> uint_ >> char(')'));

        start %=
            eps >
            lit("record")
            >> char('{')
            >>  type
            >>  (delimiter | attr("")) >> (length | attr(0))
            >>  name
            >>  char('}')
            ;
    }

    qi::rule<Iterator, std::string(), ascii::space_type> type;
    qi::rule<Iterator, std::string(), ascii::space_type> delimiter_double_quote;
    qi::rule<Iterator, std::string(), ascii::space_type> delimiter_single_quote;
    qi::rule<Iterator, std::string(), ascii::space_type> delimiter;
    qi::rule<Iterator, uint32_t(), ascii::space_type> length;
    qi::rule<Iterator, std::string(), ascii::space_type> name;
    qi::rule<Iterator, record(), ascii::space_type> start;
};

我希望将 'delimiter' 和 'length' 解析为可选。但是,其中之一必须存在,如果存在一个,则另一个不应该存在。

例如:

record { string(5) Alex; }
record { string("|") Alex; }

但不是:

record { string(5)("|") Alex; }
record { string Alex; }

我试过这样做,但是编译失败:

start %=
            eps >
            lit("record")
            >> char('{')
            >>  type
            >> ((delimiter >> attr(0)) | (attr("") >> length))
            >>  name
            >>  char('}')
            ;

提前感谢您的帮助。以下是完整的源代码:

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>

#include <string>

namespace client
{
    namespace qi = boost::spirit::qi;
    namespace ascii = boost::spirit::ascii;
    namespace phoenix = boost::phoenix;

    struct record
    {
        std::string     type;
        std::string     delimiter;
        uint32_t        length;
        std::string     name;

        record()
        {
                type = "";
                delimiter = "";
                length = 0;
                name = "";
        }
    };
}

BOOST_FUSION_ADAPT_STRUCT(
    client::record,
    (std::string, type)
    (std::string, delimiter)
    (uint32_t, length)
    (std::string, name)
)

namespace client
{
    template <typename Iterator>
    struct record_parser : qi::grammar<Iterator, record(), ascii::space_type>
    {
        record_parser() : record_parser::base_type(start)
        {
            using qi::lit;
            using qi::uint_;
            using qi::lexeme;

            using ascii::char_;
            using ascii::blank;
            using ascii::string;
            using qi::attr;

            using qi::eps;

                type %= lexeme[+(char_ - (blank|char('(')))];
                delimiter_double_quote %= char('(') >> lexeme[char('"')  >> +(char_ - char('"'))  >> char('"') ] >> char(')');
                delimiter_single_quote %= char('(') >> lexeme[char('\'') >> +(char_ - char('\'')) >> char('\'')] >> char(')');
                delimiter %= (delimiter_double_quote | delimiter_single_quote);
                name %= lexeme[+(char_ - (blank|char(';')))] >> char(';');
                length %= (char('(') >> uint_ >> char(')'));

            start %=
                eps >
                lit("record")
                >> char('{')
                >>  type
                >>  (delimiter | attr("")) >> (length | attr(0))
                >>  name
                >>  char('}')
                ;
        }

        qi::rule<Iterator, std::string(), ascii::space_type> type;
        qi::rule<Iterator, std::string(), ascii::space_type> delimiter_double_quote;
        qi::rule<Iterator, std::string(), ascii::space_type> delimiter_single_quote;
        qi::rule<Iterator, std::string(), ascii::space_type> delimiter;
        qi::rule<Iterator, uint32_t(), ascii::space_type> length;
        qi::rule<Iterator, std::string(), ascii::space_type> name;
        qi::rule<Iterator, record(), ascii::space_type> start;
    };
}

////////////////////////////////////////////////////////////////////////////
//  Main program
////////////////////////////////////////////////////////////////////////////
int main()
{
    std::string storage = "record { string(5) Alex; }";

    using boost::spirit::ascii::space;
    typedef std::string::const_iterator iterator_type;
    typedef client::record_parser<iterator_type> record_parser;

    record_parser g; // Our grammar

        client::record rec;
        std::string::const_iterator iter = storage.begin();
        std::string::const_iterator end = storage.end();
        bool r = phrase_parse(iter, end, g, space, rec);

        if (r && iter == end)
        {
            std::cout << boost::fusion::tuple_open('[');
            std::cout << boost::fusion::tuple_close(']');
            std::cout << boost::fusion::tuple_delimiter(", ");

            std::cout << "-------------------------\n";
            std::cout << "Parsing succeeded\n";
            std::cout << "got: " << boost::fusion::as_vector(rec) << std::endl;
            std::cout << "\n-------------------------\n";
        }
        else
        {
                std::string::const_iterator some = iter+30;
                std::string context(iter, (some>end)?end:some);
                std::cout << "-------------------------\n";
                std::cout << "Parsing failed\n";
                std::cout << "stopped at -->" << context << "...\n";
                std::cout << "-------------------------\n";
        }

    return 0;
}

你可以直接写出组合:

    >> (
            delimiter >> attr(0)
         |  attr("")  >> length
         |  attr("")  >> attr(0)
    )

使其与自动属性传播一起工作的最佳方法是使用类似的 AST 结构:

namespace client {

    struct record {
        std::string type;

        struct param_t {
            std::string delimiter;
            uint32_t    length = 0;
        } param;

        std::string name;
    };
}

BOOST_FUSION_ADAPT_STRUCT(client::record::param_t, delimiter, length)
BOOST_FUSION_ADAPT_STRUCT(client::record, type, param, name)

完整演示 Live On Coliru

注意语法变得多么简单(所有这些 char(' ') 都是不必要的;只有在声明船长时才使用词素;使用 ~char_ 而不是字符集减法;使用 graph 而不是 char_ - space 等)。

type                   = +(graph - '(');
delimiter_double_quote = '"' >> +~char_('"') >> '"' ;
delimiter_single_quote = "'" >> +~char_("'") >> "'" ;
delimiter              = '(' >> (delimiter_double_quote | delimiter_single_quote) >> ')';
name                   = +(graph - ';');
length                 = '(' >> uint_ >> ')';

start = eps > lit("record") >> '{' 
    >> type
    >> (
            delimiter >> attr(0)
         |  attr("")  >> length
         |  attr("")  >> attr(0)
    )
    >>  name >> ';' >> '}'
    ;

完整代码:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>

#include <string>

namespace qi = boost::spirit::qi;

namespace client {

    struct record {
        std::string type;

        struct param_t {
            std::string delimiter;
            uint32_t    length = 0;
        } param;

        std::string name;
    };
}

BOOST_FUSION_ADAPT_STRUCT(client::record::param_t, delimiter, length)
BOOST_FUSION_ADAPT_STRUCT(client::record, type, param, name)

namespace client {
    std::ostream& operator<<(std::ostream& os, record::param_t const& v) { return os << boost::fusion::as_vector(v); }
    std::ostream& operator<<(std::ostream& os, record const& v)          { return os << boost::fusion::as_vector(v); }
}

namespace client
{
    template <typename Iterator, typename Skipper = qi::ascii::space_type>
    struct record_parser : qi::grammar<Iterator, record(), Skipper>
    {
        record_parser() : record_parser::base_type(start)
        {
            using namespace qi;

            type                   = +(graph - '(');
            delimiter_double_quote = '"' >> +~char_('"') >> '"' ;
            delimiter_single_quote = "'" >> +~char_("'") >> "'" ;
            delimiter              = '(' >> (delimiter_double_quote | delimiter_single_quote) >> ')';
            name                   = +(graph - ';');
            length                 = '(' >> uint_ >> ')';

            start = eps > lit("record") >> '{' 
                >> type
                >> (
                        delimiter >> attr(0)
                     |  attr("")  >> length
                     |  attr("")  >> attr(0)
                )
                >>  name >> ';' >> '}'
                ;
        }
      private: 
        qi::rule<Iterator, record(),      Skipper> start;
        qi::rule<Iterator, uint32_t(),    Skipper> length;
        qi::rule<Iterator, std::string(), Skipper> delimiter;
        // lexemes
        qi::rule<Iterator, std::string()> type, delimiter_double_quote, delimiter_single_quote, name;
    };
}

int main()
{
    for (std::string const storage : {
                "record { string(5) Alex; }",
                "record { string(\"|\") Alex; }",
            })
    {
        typedef std::string::const_iterator iterator_type;
        typedef client::record_parser<iterator_type> record_parser;

        record_parser g; // Our grammar

        client::record rec;
        auto iter = storage.begin(), end = storage.end();
        bool r = phrase_parse(iter, end, g, qi::ascii::space, rec);

        if (r) {
            std::cout << "Parsing succeeded: " << rec << std::endl;
        } else {
            std::cout << "Parsing failed\n";
        }

        if (iter != end) {
            std::cout << "Remaining: '" << std::string(iter, end) << "'...\n";
        }
    }
}

打印:

Parsing succeeded: (string ( 5) Alex)
Parsing succeeded: (string (| 0) Alex)

这是解析 variant<std::string, uint32_t> 的更典型的方法,因此 AST 反映出只能存在一个:

使用零参数

一样的误解,允许两个参数都是可选的:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/optional/optional_io.hpp>

#include <string>

namespace qi = boost::spirit::qi;

namespace client {
    struct nil { friend std::ostream& operator<<(std::ostream& os, nil) { return os << "(nil)"; } };

    struct record {
        std::string type;
        boost::variant<nil, std::string, uint32_t> param;
        std::string name;
    };
}

BOOST_FUSION_ADAPT_STRUCT(client::record, type, param, name)

namespace client
{
    template <typename Iterator, typename Skipper = qi::ascii::space_type>
    struct record_parser : qi::grammar<Iterator, record(), Skipper>
    {
        record_parser() : record_parser::base_type(start)
        {
            using namespace qi;

            type                   = +(graph - '(');
            delimiter_double_quote = '"' >> +~char_('"') >> '"' ;
            delimiter_single_quote = "'" >> +~char_("'") >> "'" ;
            delimiter              = '(' >> (delimiter_double_quote | delimiter_single_quote) >> ')';
            name                   = +(graph - ';');
            length                 = '(' >> uint_ >> ')';

            start = eps > lit("record") >> '{' 
                >> type
                >> (delimiter | length | attr(nil{}))
                >> name >> ';' >> '}'
                ;
        }
      private: 
        qi::rule<Iterator, record(),      Skipper> start;
        qi::rule<Iterator, uint32_t(),    Skipper> length;
        qi::rule<Iterator, std::string(), Skipper> delimiter;
        // lexemes
        qi::rule<Iterator, std::string()> type, delimiter_double_quote, delimiter_single_quote, name;
    };
}

int main()
{
    for (std::string const storage : {
                "record { string(5) Alex; }",
                "record { string(\"|\") Alex; }",
                "record { string Alex; }",
            })
    {
        typedef std::string::const_iterator iterator_type;
        typedef client::record_parser<iterator_type> record_parser;

        record_parser g; // Our grammar

        client::record rec;
        auto iter = storage.begin(), end = storage.end();
        bool r = phrase_parse(iter, end, g, qi::ascii::space, rec);

        if (r) {
            std::cout << "Parsing succeeded: " << boost::fusion::as_vector(rec) << std::endl;
        } else {
            std::cout << "Parsing failed\n";
        }

        if (iter != end) {
            std::cout << "Remaining: '" << std::string(iter, end) << "'...\n";
        }
    }
}

版画

Parsing succeeded: (string 5 Alex)
Parsing succeeded: (string | Alex)
Parsing succeeded: (string (nil) Alex)

没有零参数

正好需要一个:

Live On Coliru

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/optional/optional_io.hpp>

#include <string>

namespace qi = boost::spirit::qi;

namespace client {
    struct record {
        std::string type;
        boost::variant<std::string, uint32_t> param;
        std::string name;
    };
}

BOOST_FUSION_ADAPT_STRUCT(client::record, type, param, name)

namespace client
{
    template <typename Iterator, typename Skipper = qi::ascii::space_type>
    struct record_parser : qi::grammar<Iterator, record(), Skipper>
    {
        record_parser() : record_parser::base_type(start)
        {
            using namespace qi;

            type                   = +(graph - '(');
            delimiter_double_quote = '"' >> +~char_('"') >> '"' ;
            delimiter_single_quote = "'" >> +~char_("'") >> "'" ;
            delimiter              = '(' >> (delimiter_double_quote | delimiter_single_quote) >> ')';
            name                   = +(graph - ';');
            length                 = '(' >> uint_ >> ')';

            start = eps > lit("record") >> '{' 
                >> type
                >> (delimiter | length)
                >> name >> ';' >> '}'
                ;
        }
      private: 
        qi::rule<Iterator, record(),      Skipper> start;
        qi::rule<Iterator, uint32_t(),    Skipper> length;
        qi::rule<Iterator, std::string(), Skipper> delimiter;
        // lexemes
        qi::rule<Iterator, std::string()> type, delimiter_double_quote, delimiter_single_quote, name;
    };
}

int main()
{
    for (std::string const storage : {
                "record { string(5) Alex; }",
                "record { string(\"|\") Alex; }",
                "record { string Alex; }",
            })
    {
        typedef std::string::const_iterator iterator_type;
        typedef client::record_parser<iterator_type> record_parser;

        record_parser g; // Our grammar

        client::record rec;
        auto iter = storage.begin(), end = storage.end();
        bool r = phrase_parse(iter, end, g, qi::ascii::space, rec);

        if (r) {
            std::cout << "Parsing succeeded: " << boost::fusion::as_vector(rec) << std::endl;
        } else {
            std::cout << "Parsing failed\n";
        }

        if (iter != end) {
            std::cout << "Remaining: '" << std::string(iter, end) << "'...\n";
        }
    }
}

版画

Parsing succeeded: (string 5 Alex)
Parsing succeeded: (string | Alex)
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::spirit::qi::expectation_failure<__gnu_cxx::__normal_iterator<char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > > >'
  what():  boost::spirit::qi::expectation_failure

因为是2016年,所以也加了一个X3的例子。 ,采用 variant 方法,我发现这在 Spirit 代码中很典型。

namespace AST {
    struct record {
        std::string type;
        boost::variant<std::string, uint32_t> param;
        std::string name;
    };
}

BOOST_FUSION_ADAPT_STRUCT(AST::record, type, param, name)

namespace parser {
    using namespace x3;
    auto quoted = [](char q) { return q >> +~char_(q) >> q; };

    static auto const type      = +(graph - '(');
    static auto const delimiter = '(' >> (quoted('"') | quoted('\'')) >> ')';
    static auto const name      = +(graph - ';');
    static auto const length    = '(' >> uint_ >> ')';
    static auto const start     = lit("record") >> '{' >> type >> (delimiter | length) >> name >> ';' >> '}';
}

就是这样。调用代码几乎没有变化:

int main()
{
    for (std::string const storage : {
                "record { string(5) Alex; }",
                "record { string(\"|\") Alex; }",
                "record { string Alex; }",
            })
    {
        typedef std::string::const_iterator iterator_type;

        AST::record rec;
        auto iter = storage.begin(), end = storage.end();
        bool r = phrase_parse(iter, end, parser::start, x3::ascii::space, rec);

        if (r) {
            std::cout << "Parsing succeeded: " << boost::fusion::as_vector(rec) << std::endl;
        } else {
            std::cout << "Parsing failed\n";
        }

        if (iter != end) {
            std::cout << "Remaining: '" << std::string(iter, end) << "'\n";
        }
    }
}

一切都编译得更快,如果生成的代码在运行时至少快两倍,我也不会感到惊讶。

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

namespace x3 = boost::spirit::x3;

namespace AST {
    struct record {
        std::string type;
        boost::variant<std::string, uint32_t> param;
        std::string name;
    };
}

BOOST_FUSION_ADAPT_STRUCT(AST::record, type, param, name)

namespace parser {
    using namespace x3;
    auto quoted = [](char q) { return q >> +~char_(q) >> q; };

    static auto const type      = +(graph - '(');
    static auto const delimiter = '(' >> (quoted('"') | quoted('\'')) >> ')';
    static auto const name      = +(graph - ';');
    static auto const length    = '(' >> uint_ >> ')';
    static auto const start     = lit("record") >> '{' >> type >> (delimiter | length) >> name >> ';' >> '}';
}

#include <iostream>
#include <boost/fusion/include/io.hpp>
#include <boost/fusion/include/as_vector.hpp>
#include <boost/optional/optional_io.hpp>

int main()
{
    for (std::string const storage : {
                "record { string(5) Alex; }",
                "record { string(\"|\") Alex; }",
                "record { string Alex; }",
            })
    {
        typedef std::string::const_iterator iterator_type;

        AST::record rec;
        auto iter = storage.begin(), end = storage.end();
        bool r = phrase_parse(iter, end, parser::start, x3::ascii::space, rec);

        if (r) {
            std::cout << "Parsing succeeded: " << boost::fusion::as_vector(rec) << std::endl;
        } else {
            std::cout << "Parsing failed\n";
        }

        if (iter != end) {
            std::cout << "Remaining: '" << std::string(iter, end) << "'\n";
        }
    }
}

版画

Parsing succeeded: (string 5 Alex)
Parsing succeeded: (string | Alex)
Parsing failed
Remaining: 'record { string Alex; }'

sehe 的 是完美的(或者如果他纠正了他在评论中意识到的内容,那将是完美的),但我只是想添加对问题的解释和可能的替代方案。下面的代码基于那个出色的答案。

您的 start 规则的属性有几个问题。您要获取的属性是 record,基本上是 tuple<string,string,uint32_t,string>。来看看几个解析器的属性:

  1. 与您的原始规则类似(但更简单)的内容:

    Attribute of: "lit("record") >> char_('{') >> type >> delimiter >> length >> name >> char_('}')" tuple<char,string,string,uint32_t,string,char>
    As you can see you have two extra char caused b your use of char_(has an attribute of char) instead of lit(has no attribute). omit[char_] could also work, but would be a little silly.

  2. 让我们把char_改成lit

    Attribute of: "lit("record") >> lit('{') >> type >> delimiter >> length >> name >> lit('}')"
    tuple<string,string,uint32_t,string>
    Which is what we want.

  3. lit 的原始规则:

    Attribute of: "lit("record") >> lit('{') >> type >> (delimiter | attr("")) >> (length | attr(0)) >> name >> lit('}')"
    tuple<string,variant<string,char const (&)[1]>,variant<uint32_t,int>,string>
    Since the branches of | aren't identical, you get variants instead of the attribute you want. (In this simple case everything works as if there were no variants though)

  4. 让我们删除变体(因为它们在更复杂的场景中会导致错误):

    Attribute of: "lit("record") >> lit('{') >> type >> (delimiter | attr(string())) >> (length | attr(uint32_t())) >> name >> lit('}')"
    tuple<string,string,uint32_t,string>
    This works in the cases you want but also when both are missing.

  5. sehe的做法:

    Attribute of: "lit("record") >> lit('{') >> type >> ((delimiter >> attr(uint32_t())) | (attr(string()) >> length)) >> name >> lit('}')"
    tuple<string,tuple<string,uint32_t>,string>
    Looking at this synthesized attribute you can see the need to create the param_t helper struct to make your record attribute match.

See on Coliru a way to "calculate" the previous attributes.


可能的替代方法是使用 boost::fusion::flatten_view 的自定义指令。请记住,该指令的测试很少,因此我会推荐 sehe 展示的方法,但它似乎有效(至少在这种情况下)。

The example in this question with this directive on Wandbox

Several other examples where this directive can be useful

flatten_directive.hpp

#pragma once


#include <boost/spirit/home/qi/meta_compiler.hpp>
#include <boost/spirit/home/qi/skip_over.hpp>
#include <boost/spirit/home/qi/parser.hpp>
#include <boost/spirit/home/support/unused.hpp>
#include <boost/spirit/home/support/common_terminals.hpp>
#include <boost/spirit/home/qi/detail/attributes.hpp>
#include <boost/spirit/home/support/info.hpp>
#include <boost/spirit/home/support/handles_container.hpp>
#include <boost/fusion/include/flatten_view.hpp>
#include <boost/fusion/include/for_each.hpp>
#include <boost/fusion/include/zip_view.hpp>


namespace custom
{
    BOOST_SPIRIT_TERMINAL(flatten);
}

namespace boost {
    namespace spirit
    {
        ///////////////////////////////////////////////////////////////////////////
        // Enablers
        ///////////////////////////////////////////////////////////////////////////
        template <>
        struct use_directive<qi::domain, custom::tag::flatten> // enables flatten
            : mpl::true_ {};
    }
}

namespace custom
{


    template <typename Subject>
    struct flatten_directive : boost::spirit::qi::unary_parser<flatten_directive<Subject> >
    {
        typedef Subject subject_type;
        flatten_directive(Subject const& subject)
            : subject(subject) {}

        template <typename Context, typename Iterator>
        struct attribute
        {
            typedef boost::fusion::flatten_view<typename
                boost::spirit::traits::attribute_of<subject_type, Context, Iterator>::type>
                type;//the attribute of the directive is a flatten_view of whatever is the attribute of the subject
        };

        template <typename Iterator, typename Context
            , typename Skipper, typename Attribute>
            bool parse(Iterator& first, Iterator const& last
                , Context& context, Skipper const& skipper
                , Attribute& attr) const
        {
            Iterator temp = first;
            boost::spirit::qi::skip_over(first, last, skipper);
            typename boost::spirit::traits::attribute_of<subject_type, Context, Iterator>::type original_attr;
            if (subject.parse(first, last, context, skipper, original_attr))//parse normally
            {
                typename attribute<Context, Iterator>::type flattened_attr(original_attr);//flatten the attribute
                typedef boost::fusion::vector<Attribute&,typename attribute<Context,Iterator>::type&> sequences;
                boost::fusion::for_each(//assign to each element of Attribute the corresponding element of the flattened sequence
                    boost::fusion::zip_view<sequences>(
                        sequences(attr,flattened_attr)
                    )
                    ,
                    [](const auto& pair)//substitute with a functor with templated operator() to support c++98/03
                    {
                        boost::spirit::traits::assign_to(boost::fusion::at_c<1>(pair),boost::fusion::at_c<0>(pair));
                    }
                    ); 

                return true;
            }
            first = temp;
            return false;
        }

        template <typename Context>
        boost::spirit::info what(Context& context) const
        {
            return info("flatten", subject.what(context));

        }

        Subject subject;
    };
}//custom
 ///////////////////////////////////////////////////////////////////////////
 // Parser generators: make_xxx function (objects)
 ///////////////////////////////////////////////////////////////////////////
namespace boost {
    namespace spirit {
        namespace qi
        {
            template <typename Subject, typename Modifiers>
            struct make_directive<custom::tag::flatten, Subject, Modifiers>
            {
                typedef custom::flatten_directive<Subject> result_type;
                result_type operator()(unused_type, Subject const& subject, unused_type) const
                {
                    return result_type(subject);
                }
            };
        }
    }
}

namespace boost {
    namespace spirit {
        namespace traits
        {
            ///////////////////////////////////////////////////////////////////////////
            template <typename Subject>
            struct has_semantic_action<custom::flatten_directive<Subject> >
                : unary_has_semantic_action<Subject> {};

            ///////////////////////////////////////////////////////////////////////////
            template <typename Subject, typename Attribute, typename Context
                , typename Iterator>
            struct handles_container<custom::flatten_directive<Subject>, Attribute
                , Context, Iterator>
                : unary_handles_container<Subject, Attribute, Context, Iterator> {};
        }
    }
}

main.cpp

#include <boost/spirit/include/qi.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/fusion/include/flatten_view.hpp>
#include <boost/fusion/include/copy.hpp>
#include "flatten_directive.hpp"

#include <string>

namespace qi = boost::spirit::qi;

namespace client {

    struct record {
        std::string type;
        std::string delimiter;
        uint32_t length = 0;
        std::string name;
    };
}

BOOST_FUSION_ADAPT_STRUCT(client::record, type, delimiter, length, name)

namespace client {
    std::ostream& operator<<(std::ostream& os, record const& v) { return os << boost::fusion::tuple_open('[') << boost::fusion::tuple_close(']') << boost::fusion::tuple_delimiter(", ") << boost::fusion::as_vector(v); }
}

namespace client
{
    template <typename Iterator, typename Skipper = qi::ascii::space_type>
    struct record_parser : qi::grammar<Iterator, record(), Skipper>
    {
        record_parser() : record_parser::base_type(start)
        {
            using namespace qi;

            type = +(graph - '(');
            delimiter_double_quote = '"' >> +~char_('"') >> '"';
            delimiter_single_quote = "'" >> +~char_("'") >> "'";
            delimiter = '(' >> (delimiter_double_quote | delimiter_single_quote) >> ')';
            name = +(graph - ';');
            length = '(' >> uint_ >> ')';

            start =
                custom::flatten[
                    lit("record")
                    >> '{'
                    >> type
                    >> (
                        delimiter >> attr(uint32_t())//the attributes of both branches must be exactly identical
                        | attr(std::string("")) >> length//const char[1]!=std::string int!=uint32_t
                        )
                    >> name
                    >> ';'
                    >> '}'
                ]
                ;
        }
    private:
        qi::rule<Iterator, record(), Skipper> start;
        qi::rule<Iterator, uint32_t(), Skipper> length;
        qi::rule<Iterator, std::string(), Skipper> delimiter;
        // lexemes
        qi::rule<Iterator, std::string()> type, delimiter_double_quote, delimiter_single_quote, name;
    };
}

int main()
{
    for (std::string const storage : {
        "record { string(5) Alex; }",
        "record { string(\"|\") Alex; }",
        "record { string Alex; }",
        "record { string (\"|\")(5) Alex; }"

    })
    {
        typedef std::string::const_iterator iterator_type;
        typedef client::record_parser<iterator_type> record_parser;

        record_parser g; // Our grammar

        client::record rec;
        auto iter = storage.begin(), end = storage.end();
        bool r = phrase_parse(iter, end, g, qi::ascii::space, rec);

        if (r) {
            std::cout << "Parsing succeeded: " << rec << std::endl;
        }
        else {
            std::cout << "Parsing failed\n";
        }

        if (iter != end) {
            std::cout << "Remaining: '" << std::string(iter, end) << "'...\n";
        }
    }
}