代码之家  ›  专栏  ›  技术社区  ›  Alex

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

  •  4
  • Alex  · 技术社区  · 8 年前

    如果有结构:

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

    我希望将“分隔符”和“长度”解析为可选的。然而,其中一个必须存在,如果一个存在,另一个就不存在。

    例如:

    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;
    }
    
    4 回复  |  直到 8 年前
        1
  •  2
  •   sehe    8 年前

    你只需写出组合:

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

    完整代码:

    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)
    
        2
  •  2
  •   sehe    8 年前

    因为现在是2016年,还添加了一个X3示例。 Once again ,使用 variant 我发现这是典型的精神密码。

    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; }'
    
        3
  •  2
  •   cv_and_he    7 年前

    sehe的 first answer 是完美的(或者如果他在评论中纠正了他所意识到的),但我只是想补充一个问题的解释和一个可能的替代方案。下面的代码基于这个优秀的答案。

    您的 start 规则您想要获取的属性是 record 基本上是 tuple<string,string,uint32_t,string> 。让我们看看几个解析器的属性:

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

      属性:“lit(“record”)>>char_(“{”)&rgt;类型>分隔符>长度>名称>字符_(“}”)” tuple<char,string,string,uint32_t,string,char>
      如你所见,你还有两个额外的 char 导致你使用 char_ (具有char属性)而不是 lit (没有属性)。 omit[char_] 也可以工作,但会有点傻。

    2. 让我们改变一下 字符_ 点燃 :

      属性:“lit(“记录”)>>lit('{')>,类型>分隔符>长度>名称>gt;lit('}')”
      元组<字符串,字符串,uint32_t,字符串>
      这就是我们想要的。

    3. 您的原始规则 点燃 :

      属性:“lit(“record”)>>lit(‘{’)>)类型>(定界符|attr(“”))>,(长度|attr(0))<名称>>lit(‘}’)”
      tuple<string,variant<string,char const (&)[1]>,variant<uint32_t,int>,string>
      自从 | 不一样,你会 variant s而不是所需的属性。(在这个简单的例子中,一切都像没有变体一样工作)

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

      属性:“lit(“record”)>>lit(‘{’)>)类型>(定界符|attr(string()))>,(长度|attr(uint32_t())<名称>gt;lit(‘}’)”
      元组<字符串,字符串,uint32_t,字符串>
      这适用于您想要的情况,但也适用于两者都缺失的情况。

    5. sehe的方法:

      属性:“lit(“record”)>>lit('{')>)>,类型>gt;((分隔符>>attr(uint32_t()))|(attr)string()>:>length))>、name>lig('}')”
      tuple<string,tuple<string,uint32_t>,string>
      查看这个合成属性,您可以看到需要创建 param_t helper结构使 记录 属性匹配。

    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

    扁平方向.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> {};
            }
        }
    }
    

    主.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";
            }
        }
    }
    
        4
  •  1
  •   sehe    8 年前

    下面是解析的更典型的方法 variant<std::string, uint32_t> 因此AST反映只有一个可以存在:

    无参数

    同样的误解 in my first answer ,允许两个参数都是可选的:

    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