You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:用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);
}

关键注意事项

  1. 递归规则声明:必须提前声明statement规则,解决if_statement与statement的循环引用问题
  2. 关键字冲突避免:simple_statement规则中排除关键字,防止错误匹配条件语句的开头
  3. 分支匹配逻辑:用*匹配0或多个ElseIf分支,用-标记Else分支为可选
  4. 内存管理:使用unique_ptr管理AST节点,避免内存泄漏

内容的提问来源于stack exchange,提问作者Dylan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:45:00