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

时间:2016-03-22 21:39:09

标签: c++ parsing boost boost-spirit boost-spirit-qi

如果有结构:

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

我希望解析&#39;分隔符&#39;和&#39;长度&#39;作为可选。但是,其中一个必须存在,如果存在,则另一个不存在。

例如:

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 个答案:

答案 0 :(得分:2)

你可以写出组合:

    >> (
            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(' ')事都是不必要的;只有在声明了一个队长时才使用lexemes;使用~char_代替字符集减法;使用{{1}而不是graph等。)。

char_ - space

完整代码:

<强> Live On Coliru

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

打印:

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

答案 1 :(得分:2)

因为它是2016年,所以也添加了X3示例。 Once again,采用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; }'

答案 2 :(得分:2)

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

您的start规则的属性存在一些问题。您要获取的属性为record,基本上为tuple<string,string,uint32_t,string>。让我们看看几个解析器的属性:

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

      

    属性:&#34;点亮(&#34;记录&#34;)&gt;&gt; char _(&#39; {&#39;)&gt;&gt;类型&gt;&gt;分隔符&gt;&gt;长度&gt;&gt;名称&gt;&gt;炭_(&#39;}&#39;)&#34;   tuple<char,string,string,uint32_t,string,char>
      正如您所看到的那样,您使用char(具有char属性)而不是char_(没有属性)会导致额外的两个litomit[char_]也可以起作用,但会有点傻。

  2. 让我们将char_更改为lit

      

    属性:&#34;点亮(&#34;记录&#34;)&gt;&gt;点亮(&#39; {&#39;)&gt;&gt;类型&gt;&gt;分隔符&gt;&gt;长度&gt;&gt;名称&gt;&gt;点亮(&#39;}&#39;)&#34;
      tuple<string,string,uint32_t,string>
      这就是我们想要的。

  3. lit的原始规则:

      

    属性:&#34;点亮(&#34;记录&#34;)&gt;&gt;点亮(&#39; {&#39;)&gt;&gt;类型&gt;&gt; (分隔符| attr(&#34;&#34;))&gt;&gt; (长度| attr(0))&gt;&gt;名称&gt;&gt;点亮(&#39;}&#39;)&#34;
      tuple<string,variant<string,char const (&)[1]>,variant<uint32_t,int>,string>
      由于|的分支不相同,因此您获得variant s而不是您想要的属性。 (在这个简单的例子中,一切都好像没有变体一样)

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

      

    属性:&#34;点亮(&#34;记录&#34;)&gt;&gt;点亮(&#39; {&#39;)&gt;&gt;类型&gt;&gt; (分隔符| attr(string()))&gt;&gt; (length | attr(uint32_t()))&gt;&gt;名称&gt;&gt;点亮(&#39;}&#39;)&#34;
      tuple<string,string,uint32_t,string>
      这适用于您想要的情况,但也缺少两种情况。

  5. sehe的方法:

      

    属性:&#34;点亮(&#34;记录&#34;)&gt;&gt;点亮(&#39; {&#39;)&gt;&gt;类型&gt;&gt; ((分隔符&gt;&gt; attr(uint32_t()))|(attr(string())&gt;&gt; length))&gt;&gt;名称&gt;&gt;点亮(&#39;}&#39;)&#34;
      tuple<string,tuple<string,uint32_t>,string>
      查看此合成属性,您可以看到需要创建param_t辅助结构以使record属性匹配。

  6. 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";
            }
        }
    }
    

答案 3 :(得分:1)

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

使用Nil-Param

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)

没有Nil-Param

只需要一个:

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