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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 11:54:54