C++ 增强精神:将结果复制到字符串向量中

C++ 增强精神:将结果复制到字符串向量中,c++,boost,boost-spirit,C++,Boost,Boost Spirit,我想以以下形式解析函数(具有任意名称和任意参数数): 功能(再见,1,3,4,foo) 参数可以是逗号分隔的通用字符串。 我想复制函数名和字符串向量中的参数。 像这样 std::vector<std::string> F; std::string fun = "function(bye, 1, 3, 4, foo)"; // The parser must produce this vector from the example F[0] == "functio

我想以以下形式解析函数(具有任意名称和任意参数数):

功能(再见,1,3,4,foo)

参数可以是逗号分隔的通用字符串。 我想复制函数名和字符串向量中的参数。 像这样

   std::vector<std::string> F;
   std::string fun = "function(bye, 1, 3, 4, foo)";

// The parser must produce this vector from the example
    F[0] == "function"
    F[1] == "1"
    F[2] == "3"
    F[3] == "4"
    F[4] == "foo"
std::向量F;
std::string fun=“函数(再见,1,3,4,foo)”;
//解析器必须根据示例生成此向量
F[0]=“函数”
F[1]=“1”
F[2]=“3”
F[3]=“4”
F[4]=“foo”
在阅读了一些教程之后,我编写了以下代码,但它不起作用(从某种意义上说,它无法编译)

#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
命名空间客户端
{
名称空间qi=boost::spirit::qi;
名称空间ascii=boost::spirit::ascii;
///////////////////////////////////////////////////////////////////////////////
模板
结构命令语法分析器:qi::grammar
{
command_parser():command_parser::base_type(start)
{
使用qi::int_;
使用qi::lit;
使用qi::double;
使用气:词素;
使用ascii::char;
fn_name=+qi::char_uz(“a-zA-Z”);
string=+qi::char_uu(“a-zA-Z_u0-9”);
rec=*(亮(“,”>>字符串);
开始%=fn_name>>点亮(“”>>字符串>>rec>>点亮(“”);
}
qi::规则fn_名称;
qi::规则字符串;
qi::规则记录;
qi::规则开始;
};
}
////////////////////////////////////////////////////////////////////////////
//主程序
////////////////////////////////////////////////////////////////////////////
int
main()
{
名称空间qi=boost::spirit::qi;

std::cout我正在根据@sehe提出的建议更正我的答案。所有这些更正的功劳都归他。我在下面引用了您的行号。因此,第一个错误来自spirit,它说:

不兼容的\u开始\u规则: //如果您看到下面的断言失败,那么开始规则 //传递给语法的构造函数与不兼容 //语法(即,它使用不同的模板参数)

start
解析器的签名与解析器的签名不匹配

22. struct command_parser : qi::grammar<Iterator, std::vector<std::string>(), ascii::space_type>
43. qi::rule<Iterator, std::vector<std::string>, ascii::space_type> start;
现在它已编译,输出为:

fun
1
2345foo
我知道这不行,您希望用每个传递的参数填充向量。因此,您需要一个与属性和意图兼容的规则。使用
std::string
的kleene运算符将所有数据放在一个字符串中。因此,请使用您的属性:

41. qi::rule<Iterator, std::vector<std::string>(), ascii::space_type> rec;``
我犯的另一个错误是看到
%=
并将其称为列表运算符。从中,它是一个定义运算符。我不确定为什么有两个运算符,但在玩游戏时,似乎需要将
%=
与语义操作一起使用。下面是更正的代码:

#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 <iostream>
#include <string>

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

    template <typename Iterator>
    struct command_parser : qi::grammar<Iterator, std::vector<std::string>(), ascii::space_type>
    {
        command_parser() : command_parser::base_type(start)
        {
            using qi::int_;
            using qi::lit;
            using qi::double_;
            using qi::lexeme;
            using ascii::char_;

            fn_name = +qi::char_("a-zA-Z");
            string = +qi::char_("a-zA-Z_0-9");
            rec = *(lit(",") >> string);

            start %= fn_name >> lit("(") >> string >> rec >> lit(")");
        }
        qi::rule<Iterator, std::string()> fn_name;
        qi::rule<Iterator, std::string()> string;
        qi::rule<Iterator, std::vector<std::string>(), ascii::space_type> rec;
        qi::rule<Iterator, std::vector<std::string>(), ascii::space_type> start;
    };
}

int main()
{
    namespace qi = boost::spirit::qi;

    client::command_parser<std::string::iterator> CP;
    std::string cmd("function(1,2,3,4  , 5, foo) ");

    std::vector<std::string> VV;

    bool result = qi::phrase_parse(cmd.begin(), cmd.end(), CP, qi::ascii::space, VV);

    if (result) {
        for (auto sss : VV) {
            std::cout << sss << std::endl;
        }
    }
    else {
        std::cout << "Fail" << std::endl;
    }
    return 0;
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
命名空间客户端
{
名称空间qi=boost::spirit::qi;
名称空间ascii=boost::spirit::ascii;
模板
结构命令语法分析器:qi::grammar
{
command_parser():command_parser::base_type(start)
{
使用qi::int_;
使用qi::lit;
使用qi::double;
使用气:词素;
使用ascii::char;
fn_name=+qi::char_uz(“a-zA-Z”);
string=+qi::char_uu(“a-zA-Z_u0-9”);
rec=*(亮(“,”>>字符串);
开始%=fn_name>>点亮(“”>>字符串>>rec>>点亮(“”);
}
qi::规则fn_名称;
qi::规则字符串;
qi::规则记录;
qi::规则开始;
};
}
int main()
{
名称空间qi=boost::spirit::qi;
client::命令解析器CP;
stringcmd(“函数(1,2,3,4,5,foo)”;
std::向量VV;
bool result=qi::phrase_parse(cmd.begin(),cmd.end(),CP,qi::ascii::space,VV);
如果(结果){
用于(自动sss:VV){
std::cout字符串“%”,“>>”;
}
int main()
{
名称空间x3=boost::spirit::x3;
std::string cmd(“fun(1,2,3,4,5,foo)”);
attr VV;
auto it=cmd.begin();
bool result=phrase_parse(it,cmd.end(),parser::start,x3::space,VV);
如果(结果){
用于(自动sss:VV){

std::cout只是为了好玩,下面是我对这一语法的最简看法:

using CallList = std::vector<std::string>;

struct ParseError : std::runtime_error {
    ParseError() : std::runtime_error("ParseError") {}
};

// The parse implementation
CallList parse_function_call(std::string const& fun) {
    CallList elements;
    using namespace boost::spirit::qi;
    using It = decltype(begin(fun));
    static const rule<It, std::string()> identifier = alpha >> +(alnum | char_('_'));

    if (!phrase_parse(begin(fun), end(fun),
                identifier >> '(' >> -(lexeme[+~char_(",)")] % ",") >> ')' >> eoi,
                space, elements))
        throw ParseError{};
    return elements;
}
请注意,您的示例测试用例没有通过,但我认为这是测试用例中的一个错误

完整列表

"function(bye, 1, 3, 4, foo)": FAIL
 -- expected: {function,1,3,4,foo}
 -- actual:   {function,bye,1,3,4,foo}
"liar(pants on fire)": PASS
"liar('pants on fire')": PASS
"nullary()": PASS
"nullary(    )": PASS
"zerolength(a,,b)": PASS
"zerolength(a, ,b)": PASS
"noarglust": PASS
"": PASS
"()": PASS
"1(invalidfunctionname)": PASS
"foo(bar) BOGUS": PASS
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <experimental/iterator>
#include <variant>
#include <iomanip>

using CallList = std::vector<std::string>;

struct ParseError : std::runtime_error {
    ParseError() : std::runtime_error("ParseError") {}
};

// The parse implementation
CallList parse_function_call(std::string const& fun) {
    CallList elements;
    using namespace boost::spirit::qi;
    using It = decltype(begin(fun));
    static const rule<It, std::string()> identifier = alpha >> +(alnum | char_('_'));

    if (!phrase_parse(begin(fun), end(fun),
                identifier >> '(' >> -(lexeme[+~char_(",)")] % ",") >> ')' >> eoi,
                space, elements))
        throw ParseError{};
    return elements;
}

// just for test output
using TestResult = std::variant<CallList, ParseError>;

// exceptions are equivalent
static constexpr bool operator==(ParseError const&, ParseError const&)
    { return true; }

static inline std::ostream& operator<<(std::ostream& os, TestResult const& tr) {
    using namespace std;
    if (holds_alternative<ParseError>(tr)) {
        return os << "ParseError";
    } else {
        auto& list = get<CallList>(tr);
        copy(begin(list), end(list), std::experimental::make_ostream_joiner(os << "{", ","));
        return os << "}";
    }
}

TestResult try_parse(std::string const& fun) {
    try { return parse_function_call(fun); }
    catch(ParseError const& e) { return e; }
}

int main() {
    using namespace std;

    using Case = pair<std::string, TestResult>;

    for (auto const& [input, expected]: {
            Case("function(bye, 1, 3, 4, foo)", CallList{"function", "1", "3", "4", "foo"}),
            {"liar(pants on fire)", CallList{"liar", "pants on fire"}},
            {"liar('pants on fire')", CallList{"liar", "'pants on fire'"}},
            {"nullary()", CallList{"nullary"}},
            {"nullary(    )", CallList{"nullary"}},
            {"zerolength(a,,b)", ParseError{}},
            {"zerolength(a, ,b)", ParseError{}},
            {"noarglust", ParseError{}},
            {"", ParseError{}},
            {"()", ParseError{}},
            {"1(invalidfunctionname)", ParseError{}},
            {"foo(bar) BOGUS", ParseError{}},
        })
    {
        auto const actual = try_parse(input);
        bool const ok = (actual == expected);

        cout << std::quoted(input) << ": " << (ok? "PASS":"FAIL") << "\n";
        if (!ok) {
            std::cout << " -- expected: " << expected << "\n";
            std::cout << " -- actual:   " << actual << "\n";
        }
    }
}
/#定义BOOST_SPIRIT_调试
#包括
#包括
#包括
#包括
使用CallList=std::vector;
结构解析错误:std::runtime\u错误{
ParseError():std::runtime_error(“ParseError”){}
};
//解析实现
调用列表解析函数调用(std::string const&fun){
调用列表元素;
使用名称空间boost::spirit::qi;
使用它=decltype(开始(乐趣));
静态常量规则标识符=alpha>>+(alnum | char uz(“'));
如果(!短语解析(开始(乐趣),结束(乐趣),
标识符>>'('>>-(词素[+~char_(“,)”)],“>>”)'>>eoi,
空间、要素)
抛出语法错误{};
返回元素;
}
//仅用于测试输出
使用TestResult=std::variant;
//例外情况是等价的
静态constexpr bool运算符==(ParseError常量&,ParseError常量&)
{返回true;}

静态内联std::ostream&operator实际上,指定属性类型的首选方法是
T()
,而不是
T
。参数列表可以指定任何继承的属性。您建议的拼写方式就像“正确”一样实际上只是最近版本中添加的一个后备功能,对用户不太友好。但根据我的经验,
T
样式并不是在所有情况下都得到很好的支持。此外,您在标识符规则上保留了skipper,这会使解析变得混乱(其中
很有趣\
#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 <iostream>
#include <string>

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

    template <typename Iterator>
    struct command_parser : qi::grammar<Iterator, std::vector<std::string>(), ascii::space_type>
    {
        command_parser() : command_parser::base_type(start)
        {
            using qi::int_;
            using qi::lit;
            using qi::double_;
            using qi::lexeme;
            using ascii::char_;

            fn_name = +qi::char_("a-zA-Z");
            string = +qi::char_("a-zA-Z_0-9");
            rec = *(lit(",") >> string);

            start %= fn_name >> lit("(") >> string >> rec >> lit(")");
        }
        qi::rule<Iterator, std::string()> fn_name;
        qi::rule<Iterator, std::string()> string;
        qi::rule<Iterator, std::vector<std::string>(), ascii::space_type> rec;
        qi::rule<Iterator, std::vector<std::string>(), ascii::space_type> start;
    };
}

int main()
{
    namespace qi = boost::spirit::qi;

    client::command_parser<std::string::iterator> CP;
    std::string cmd("function(1,2,3,4  , 5, foo) ");

    std::vector<std::string> VV;

    bool result = qi::phrase_parse(cmd.begin(), cmd.end(), CP, qi::ascii::space, VV);

    if (result) {
        for (auto sss : VV) {
            std::cout << sss << std::endl;
        }
    }
    else {
        std::cout << "Fail" << std::endl;
    }
    return 0;
}
#include <boost/spirit/home/x3.hpp>
#include <iostream>
#include <vector>

//your attribute, could be more complex, might use namespace
using attr = std::vector<std::string>;

namespace parser {
    namespace x3 = boost::spirit::x3;

    const auto fn_name = +x3::char_("a-zA-Z");
    const auto string = +x3::char_("a-zA-Z_0-9");
    const auto start = x3::rule<struct _, attr>() = fn_name >> "(" >> string % ',' >> ")";
}

int main()
{
    namespace x3 = boost::spirit::x3;
    std::string cmd("fun(1,.2,3,4  , 5, foo) ");
    attr VV;
    auto it = cmd.begin();
    bool result = phrase_parse(it, cmd.end(), parser::start, x3::space, VV);

    if (result) {
        for (auto sss : VV) {
            std::cout << "-> " << sss << std::endl;
        }
    }
    else 
        std::cout << "Fail at" << std::endl;

    return 0;
}
using CallList = std::vector<std::string>;

struct ParseError : std::runtime_error {
    ParseError() : std::runtime_error("ParseError") {}
};

// The parse implementation
CallList parse_function_call(std::string const& fun) {
    CallList elements;
    using namespace boost::spirit::qi;
    using It = decltype(begin(fun));
    static const rule<It, std::string()> identifier = alpha >> +(alnum | char_('_'));

    if (!phrase_parse(begin(fun), end(fun),
                identifier >> '(' >> -(lexeme[+~char_(",)")] % ",") >> ')' >> eoi,
                space, elements))
        throw ParseError{};
    return elements;
}
// just for test output
using TestResult = std::variant<CallList, ParseError>;

// exceptions are equivalent
static constexpr bool operator==(ParseError const&, ParseError const&)
    { return true; }

static inline std::ostream& operator<<(std::ostream& os, TestResult const& tr) {
    using namespace std;
    if (holds_alternative<ParseError>(tr)) {
        return os << "ParseError";
    } else {
        auto& list = get<CallList>(tr);
        copy(begin(list), end(list), std::experimental::make_ostream_joiner(os << "{", ","));
        return os << "}";
    }
}

TestResult try_parse(std::string const& fun) {
    try { return parse_function_call(fun); }
    catch(ParseError const& e) { return e; }
}
for (auto const& [input, expected]: {
        Case("function(bye, 1, 3, 4, foo)", CallList{"function", "1", "3", "4", "foo"}),
        {"liar(pants on fire)", CallList{"liar", "pants on fire"}},
        {"liar('pants on fire')", CallList{"liar", "'pants on fire'"}},
        {"nullary()", CallList{"nullary"}},
        {"nullary(    )", CallList{"nullary"}},
        {"zerolength(a,,b)", ParseError{}},
        {"zerolength(a, ,b)", ParseError{}},
        {"noarglust", ParseError{}},
        {"", ParseError{}},
        {"()", ParseError{}},
        {"1(invalidfunctionname)", ParseError{}},
        {"foo(bar) BOGUS", ParseError{}},
    })
{
    auto const actual = try_parse(input);
    bool const ok = (actual == expected);

    cout << std::quoted(input) << ": " << (ok? "PASS":"FAIL") << "\n";
    if (!ok) {
        std::cout << " -- expected: " << expected << "\n";
        std::cout << " -- actual:   " << actual << "\n";
    }
}
"function(bye, 1, 3, 4, foo)": FAIL
 -- expected: {function,1,3,4,foo}
 -- actual:   {function,bye,1,3,4,foo}
"liar(pants on fire)": PASS
"liar('pants on fire')": PASS
"nullary()": PASS
"nullary(    )": PASS
"zerolength(a,,b)": PASS
"zerolength(a, ,b)": PASS
"noarglust": PASS
"": PASS
"()": PASS
"1(invalidfunctionname)": PASS
"foo(bar) BOGUS": PASS
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <experimental/iterator>
#include <variant>
#include <iomanip>

using CallList = std::vector<std::string>;

struct ParseError : std::runtime_error {
    ParseError() : std::runtime_error("ParseError") {}
};

// The parse implementation
CallList parse_function_call(std::string const& fun) {
    CallList elements;
    using namespace boost::spirit::qi;
    using It = decltype(begin(fun));
    static const rule<It, std::string()> identifier = alpha >> +(alnum | char_('_'));

    if (!phrase_parse(begin(fun), end(fun),
                identifier >> '(' >> -(lexeme[+~char_(",)")] % ",") >> ')' >> eoi,
                space, elements))
        throw ParseError{};
    return elements;
}

// just for test output
using TestResult = std::variant<CallList, ParseError>;

// exceptions are equivalent
static constexpr bool operator==(ParseError const&, ParseError const&)
    { return true; }

static inline std::ostream& operator<<(std::ostream& os, TestResult const& tr) {
    using namespace std;
    if (holds_alternative<ParseError>(tr)) {
        return os << "ParseError";
    } else {
        auto& list = get<CallList>(tr);
        copy(begin(list), end(list), std::experimental::make_ostream_joiner(os << "{", ","));
        return os << "}";
    }
}

TestResult try_parse(std::string const& fun) {
    try { return parse_function_call(fun); }
    catch(ParseError const& e) { return e; }
}

int main() {
    using namespace std;

    using Case = pair<std::string, TestResult>;

    for (auto const& [input, expected]: {
            Case("function(bye, 1, 3, 4, foo)", CallList{"function", "1", "3", "4", "foo"}),
            {"liar(pants on fire)", CallList{"liar", "pants on fire"}},
            {"liar('pants on fire')", CallList{"liar", "'pants on fire'"}},
            {"nullary()", CallList{"nullary"}},
            {"nullary(    )", CallList{"nullary"}},
            {"zerolength(a,,b)", ParseError{}},
            {"zerolength(a, ,b)", ParseError{}},
            {"noarglust", ParseError{}},
            {"", ParseError{}},
            {"()", ParseError{}},
            {"1(invalidfunctionname)", ParseError{}},
            {"foo(bar) BOGUS", ParseError{}},
        })
    {
        auto const actual = try_parse(input);
        bool const ok = (actual == expected);

        cout << std::quoted(input) << ": " << (ok? "PASS":"FAIL") << "\n";
        if (!ok) {
            std::cout << " -- expected: " << expected << "\n";
            std::cout << " -- actual:   " << actual << "\n";
        }
    }
}