求助:用Boost Spirit在C++中解析嵌套If-Else语句生成XML
解决Boost Spirit嵌套If-ElseIf-Else解析问题
核心思路
嵌套条件语句的解析关键是递归规则定义和正确的分支结构匹配。首先明确语法结构:
- 基础结构:
If (条件) 语句块 [ElseIf (条件) 语句块]* [Else 语句块] EndIf; - 语句块可包含任意嵌套的If语句、循环、简单语句
步骤1:定义AST结构
设计能映射条件语句的AST类,确保支持嵌套存储:
#include <vector> #include <string> #include <variant> #include <optional> #include <memory> // 基础语句基类 struct Statement {}; // 简单语句(如赋值、表达式) struct SimpleStatement : Statement { std::string content; }; // For循环语句 struct ForLoop : Statement { std::string init; std::string condition; std::string increment; std::vector<std::unique_ptr<Statement>> body; }; // If-ElseIf-Else语句 struct IfStatement : Statement { std::string condition; std::vector<std::unique_ptr<Statement>> then_block; std::vector<std::pair<std::string, std::vector<std::unique_ptr<Statement>>>> elseif_blocks; std::optional<std::vector<std::unique_ptr<Statement>>> else_block; }; // 语句变体类型 using StatementVariant = std::variant< std::unique_ptr<SimpleStatement>, std::unique_ptr<ForLoop>, std::unique_ptr<IfStatement> >;
步骤2:Boost Spirit语法定义
使用递归规则处理嵌套,提前声明递归引用的规则:
#include <boost/spirit/include/qi.hpp> #include <boost/spirit/include/phoenix.hpp> #include <boost/fusion/include/adapt_struct.hpp> namespace qi = boost::spirit::qi; namespace phoenix = boost::phoenix; // 适配AST结构体,供Spirit自动映射 BOOST_FUSION_ADAPT_STRUCT(SimpleStatement, content) BOOST_FUSION_ADAPT_STRUCT(ForLoop, init, condition, increment, body) BOOST_FUSION_ADAPT_STRUCT(IfStatement, condition, then_block, elseif_blocks, else_block) template <typename Iterator> struct Grammar : qi::grammar<Iterator, std::vector<StatementVariant>(), qi::space_type> { Grammar() : Grammar::base_type(statements) { // 简单语句:匹配到分号结束,排除关键字避免冲突 simple_statement %= qi::lexeme[+(qi::char_ - ';' - qi::space - "If" - "ElseIf" - "Else" - "EndIf" - "For")] >> ';'; // For循环解析规则 for_loop %= "For" >> '(' >> qi::lexeme[+(qi::char_ - ')')] >> ')' >> '{' >> statements >> '}'; // 提前声明递归规则,解决循环引用问题 qi::rule<Iterator, StatementVariant(), qi::space_type> statement; qi::rule<Iterator, std::vector<StatementVariant>(), qi::space_type> statement_block; // If语句核心规则:支持嵌套、多ElseIf分支、可选Else if_statement %= "If" >> '(' >> qi::lexeme[+(qi::char_ - ')')] >> ')' >> '{' >> statements >> '}' >> *( "ElseIf" >> '(' >> qi::lexeme[+(qi::char_ - ')')] >> ')' >> '{' >> statements >> '}' ) >> -("Else" >> '{' >> statements >> '}') >> "EndIf;"; // 单个语句:匹配所有语句类型 statement %= simple_statement | for_loop | if_statement; // 语句块:多个语句的集合 statement_block %= +statement; // 顶层规则:解析多个语句 statements %= +statement; // 命名规则,方便错误提示 simple_statement.name("simple_statement"); for_loop.name("for_loop"); if_statement.name("if_statement"); statement.name("statement"); statements.name("statements"); // 错误处理(可选) qi::on_error<qi::fail>( statements, phoenix::ref(std::cout) << "解析失败,位置:" << phoenix::_4 << ",期望:" << phoenix::_3 << std::endl ); } qi::rule<Iterator, std::vector<StatementVariant>(), qi::space_type> statements; qi::rule<Iterator, SimpleStatement(), qi::space_type> simple_statement; qi::rule<Iterator, ForLoop(), qi::space_type> for_loop; qi::rule<Iterator, IfStatement(), qi::space_type> if_statement; };
步骤3:AST转XML实现
遍历AST结构,生成指定格式的XML:
#include <iostream> #include <sstream> void generate_xml(const StatementVariant& stmt, std::ostream& os, int indent = 0) { std::string indent_str(indent * 4, ' '); std::visit([&](const auto& s) { using T = std::decay_t<decltype(*s)>; if constexpr (std::is_same_v<T, SimpleStatement>) { os << indent_str << "<SimpleStatement>" << s->content << "</SimpleStatement>\n"; } else if constexpr (std::is_same_v<T, ForLoop>) { os << indent_str << "<ForLoop>\n"; os << indent_str << " <Init>" << s->init << "</Init>\n"; os << indent_str << " <Condition>" << s->condition << "</Condition>\n"; os << indent_str << " <Increment>" << s->increment << "</Increment>\n"; os << indent_str << " <Body>\n"; for (const auto& body_stmt : s->body) { generate_xml(*body_stmt, os, indent + 2); } os << indent_str << " </Body>\n"; os << indent_str << "</ForLoop>\n"; } else if constexpr (std::is_same_v<T, IfStatement>) { os << indent_str << "<IfStatement>\n"; os << indent_str << " <Condition>" << s->condition << "</Condition>\n"; os << indent_str << " <ThenBlock>\n"; for (const auto& then_stmt : s->then_block) { generate_xml(*then_stmt, os, indent + 2); } os << indent_str << " </ThenBlock>\n"; for (const auto& [elif_cond, elif_block] : s->elseif_blocks) { os << indent_str << " <ElseIfBlock>\n"; os << indent_str << " <Condition>" << elif_cond << "</Condition>\n"; os << indent_str << " <Block>\n"; for (const auto& elif_stmt : elif_block) { generate_xml(*elif_stmt, os, indent + 3); } os << indent_str << " </Block>\n"; os << indent_str << " </ElseIfBlock>\n"; } if (s->else_block) { os << indent_str << " <ElseBlock>\n"; for (const auto& else_stmt : *s->else_block) { generate_xml(*else_stmt, os, indent + 2); } os << indent_str << " </ElseBlock>\n"; } os << indent_str << "</IfStatement>\n"; } }, stmt); }
关键注意事项
- 递归规则声明:必须提前声明
statement规则,解决if_statement与statement的循环引用问题 - 关键字冲突避免:
simple_statement规则中排除关键字,防止错误匹配条件语句的开头 - 分支匹配逻辑:用
*匹配0或多个ElseIf分支,用-标记Else分支为可选 - 内存管理:使用
unique_ptr管理AST节点,避免内存泄漏
内容的提问来源于stack exchange,提问作者Dylan
相关产品推荐
相关产品推荐

