如何用Boost Spirit X3将多CLI命令解析至对应AST?
Boost Spirit X3 多命令解析与AST映射实现
问题描述
我在CLI场景中需要接收多个带独立参数的命令,之前用临时正则表达式解析,现在想改用Boost Spirit X3优化实现。我写了一段C++代码尝试解析cmd1和cmd2,但不知道如何将解析结果存入对应的AST,也不清楚怎么在phrase_parse中指定多种AST类型。作为Boost Spirit新手,我从示例拼凑出实验程序,但卡在了这一步,希望实现cmd1和cmd2到对应AST的解析。
原实验代码:
#include <iostream> #include <tuple> #include <boost/fusion/adapted/std_tuple.hpp> #include <boost/fusion/include/std_tuple.hpp> #include <boost/fusion/adapted/std_pair.hpp> #include <boost/fusion/include/std_pair.hpp> #include <boost/spirit/home/x3.hpp> #include <boost/spirit/home/x3/char/char_parser.hpp> #include <fmt/format.h> namespace x3 = boost::spirit::x3; namespace client::ast { struct cmd1 { double param1; double param2; }; struct cmd2 { std::string param1; }; } BOOST_FUSION_ADAPT_STRUCT(client::ast::cmd1, param1, param2); BOOST_FUSION_ADAPT_STRUCT(client::ast::cmd2, param1); namespace parser { using x3::lit; using x3::lexeme; using x3::char_; using x3::double_; using x3::phrase_parse; using x3::ascii::space; x3::rule<class cmd1_class, client::ast::cmd1> const cmd1 = "cmd1"; x3::rule<class cmd2_class, client::ast::cmd2> const cmd2 = "cmd2"; auto const quoted_string = lexeme['"' >> +(char_ - '"') >> '"']; auto const cmd1_def = lit("cmd1") >> lit("param1") >> lit("=") >> double_ >> lit("param2") >> lit("=") >> double_; auto const cmd2_def = lit("cmd2") >> lit("param1") >> lit("=") >> quoted_string; BOOST_SPIRIT_DEFINE(cmd1, cmd2); } template <typename Iterator> bool parse_line(Iterator first, Iterator last) { using x3::lit; using x3::lexeme; using x3::char_; using x3::double_; using x3::phrase_parse; using x3::ascii::space; using parser::cmd1; using parser::cmd2; client::ast::cmd1 cmd1_ast; client::ast::cmd2 cmd2_ast; std::variant<client::ast::cmd1, client::ast::cmd2> cmd_ast; /// how do i parse the appropriate rule into the correct AST bool r = phrase_parse( first, // Start Iterator last, // End Iterator cmd1 | cmd2, space // The Skip-Parser /// cmd_ast); // The AST ); if (first != last) // fail if we did not get a full match return false; return r; } int main() { /// read a line from stdin while (true) { std::string line; std::getline(std::cin, line); if (line.empty()) break; fmt::print("line: {}\n", line); bool r = parse_line(line.begin(), line.end()); fmt::print("parse_line: {}\n", r); } return 0; }
解决实现
以下是修改后的完整代码,实现了将不同命令解析到对应AST并通过std::variant统一管理:
#include <iostream> #include <tuple> #include <variant> #include <boost/fusion/adapted/std_tuple.hpp> #include <boost/fusion/include/std_tuple.hpp> #include <boost/spirit/home/x3.hpp> #include <fmt/format.h> namespace x3 = boost::spirit::x3; namespace client::ast { struct cmd1 { double param1; double param2; }; struct cmd2 { std::string param1; }; // 用于打印AST的辅助函数 std::ostream& operator<<(std::ostream& os, const cmd1& c) { return os << "cmd1: param1=" << c.param1 << ", param2=" << c.param2; } std::ostream& operator<<(std::ostream& os, const cmd2& c) { return os << "cmd2: param1=\"" << c.param1 << "\""; } } BOOST_FUSION_ADAPT_STRUCT(client::ast::cmd1, param1, param2); BOOST_FUSION_ADAPT_STRUCT(client::ast::cmd2, param1); namespace parser { using x3::lit; using x3::lexeme; using x3::char_; using x3::double_; using x3::ascii::space; x3::rule<class cmd1_class, client::ast::cmd1> const cmd1 = "cmd1"; x3::rule<class cmd2_class, client::ast::cmd2> const cmd2 = "cmd2"; auto const quoted_string = lexeme['"' >> +(char_ - '"') >> '"']; auto const cmd1_def = lit("cmd1") >> lit("param1") >> lit("=") >> double_ >> lit("param2") >> lit("=") >> double_; auto const cmd2_def = lit("cmd2") >> lit("param1") >> lit("=") >> quoted_string; BOOST_SPIRIT_DEFINE(cmd1, cmd2); // 定义统一的命令解析规则,匹配cmd1或cmd2 auto const command = cmd1 | cmd2; } template <typename Iterator> bool parse_line(Iterator first, Iterator last, std::variant<client::ast::cmd1, client::ast::cmd2>& cmd_ast) { using parser::command; using x3::ascii::space; bool r = x3::phrase_parse( first, last, command, space, cmd_ast // 将解析结果存入variant ); // 确保整个输入都被解析完成 return r && first == last; } int main() { std::string line; while (std::getline(std::cin, line)) { if (line.empty()) break; fmt::print("输入行: {}\n", line); std::variant<client::ast::cmd1, client::ast::cmd2> cmd_ast; bool success = parse_line(line.begin(), line.end(), cmd_ast); if (success) { fmt::print("解析成功: "); // 访问variant中的具体命令类型 std::visit([](const auto& cmd) { std::cout << cmd << "\n"; }, cmd_ast); } else { fmt::print("解析失败\n"); } } return 0; }
关键说明
- 统一AST类型:使用
std::variant<client::ast::cmd1, client::ast::cmd2>作为所有命令的统一接收容器,Boost Spirit X3会自动根据匹配的规则将结果存入对应的variant分支。 - 解析规则绑定:通过
BOOST_FUSION_ADAPT_STRUCT宏将AST结构体的成员与解析规则中的元素顺序绑定,确保参数能正确映射到结构体字段。 - phrase_parse参数传递:在
phrase_parse的最后一个参数传入variant的引用,Spirit会处理类型匹配逻辑,将解析结果写入对应的AST类型。 - 结果访问:使用
std::visit遍历variant,对不同类型的命令执行对应的处理逻辑(示例中为打印AST内容)。
内容的提问来源于stack exchange,提问作者user3426633
相关产品推荐
相关产品推荐

