既然你真的想要解析一个语法,你应该考虑使用 Boost 的解析器生成器。
您只需自上而下地编写整个内容:
auto sentence = [](auto&& v, auto&& p) {
auto verb = lexeme [ no_case [ as_parser(v) ] ];
auto name = lexeme [ +graph ];
auto particle = lexeme [ no_case [ as_parser(p) ] ];
return confix(verb, particle) [ name ];
};
auto ask = sentence("ask", "to") >> lexeme[+char_];
auto notify = sentence("notify", "that") >> lexeme[+char_];
auto message = sentence("message", "that") >> lexeme[+char_];
auto command = ask | notify | message;
这是一个 Spirit X3 语法。将lexeme 读作“保留整个单词”(不要忽略空格)。
在这里,“名称”被认为是任何与预期粒子相关的内容¹
如果你只想返回匹配的原始字符串,这就足够了:
Live On Coliru
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/directive/confix.hpp>
namespace x3 = boost::spirit::x3;
namespace commands {
namespace grammar {
using namespace x3;
auto sentence = [](auto&& v, auto&& p) {
auto verb = lexeme [ no_case [ as_parser(v) ] ];
auto name = lexeme [ +graph ];
auto particle = lexeme [ no_case [ as_parser(p) ] ];
return confix(verb, particle) [ name ];
};
auto ask = sentence("ask", "to") >> lexeme[+char_];
auto notify = sentence("notify", "that") >> lexeme[+char_];
auto message = sentence("message", "that") >> lexeme[+char_];
auto command = ask | notify | message;
auto parser = raw [ skip(space) [ command ] ];
}
}
int main() {
for (std::string const input : {
"Ask peter to call me",
"Notify Jenna that I am going to be away",
"Message home that I am running late",
})
{
std::string matched;
if (parse(input.begin(), input.end(), commands::grammar::parser, matched))
std::cout << "Matched: '" << matched << "'\n";
else
std::cout << "No match in '" << input << "'\n";
}
}
打印:
Matched: 'Ask peter to call me'
Matched: 'Notify Jenna that I am going to be away'
Matched: 'Message home that I am running late'
奖金
当然,您实际上是想提取相关的信息。
我会这样做。让我们解析成一个结构体:
struct Command {
enum class Type { ask, message, notify } type;
std::string name;
std::string message;
};
让我们把main()写成:
commands::Command cmd;
if (parse(input.begin(), input.end(), commands::grammar::parser, cmd))
std::cout << "Matched: " << cmd.type << "|" << cmd.name << "|" << cmd.message << "\n";
else
std::cout << "No match in '" << input << "'\n";
Live On Coliru
#include <iostream>
#include <boost/fusion/adapted/struct.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/directive/confix.hpp>
namespace x3 = boost::spirit::x3;
namespace commands {
struct Command {
enum class Type { ask, message, notify } type;
std::string name;
std::string message;
friend std::ostream& operator<<(std::ostream& os, Type t) { return os << static_cast<int>(t); } // TODO
};
}
BOOST_FUSION_ADAPT_STRUCT(commands::Command, type, name, message)
namespace commands {
namespace grammar {
using namespace x3;
auto sentence = [](auto type, auto&& v, auto&& p) {
auto verb = lexeme [ no_case [ as_parser(v) ] ];
auto name = lexeme [ +graph ];
auto particle = lexeme [ no_case [ as_parser(p) ] ];
return attr(type) >> confix(verb, particle) [ name ];
};
using Type = Command::Type;
auto ask = sentence(Type::ask, "ask", "to") >> lexeme[+char_];
auto notify = sentence(Type::notify, "notify", "that") >> lexeme[+char_];
auto message = sentence(Type::message, "message", "that") >> lexeme[+char_];
auto command // = rule<struct command, Command> { }
= ask | notify | message;
auto parser = skip(space) [ command ];
}
}
int main() {
for (std::string const input : {
"Ask peter to call me",
"Notify Jenna that I am going to be away",
"Message home that I am running late",
})
{
commands::Command cmd;
if (parse(input.begin(), input.end(), commands::grammar::parser, cmd))
std::cout << "Matched: " << cmd.type << "|" << cmd.name << "|" << cmd.message << "\n";
else
std::cout << "No match in '" << input << "'\n";
}
}
打印
Matched: 0|peter|call me
Matched: 2|Jenna|I am going to be away
Matched: 1|home|I am running late
¹我不是英语语言学家,所以我不知道这是否是正确的语法术语:)