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

使用boost::spirit QI能否强制函数参数的类型顺序与数量?

解决Boost Spirit中统一AST下的函数参数校验问题

要实现统一AST结构体+严格匹配参数数量/类型顺序的需求,核心是在解析阶段绑定每个函数的参数规则,既避免重复定义AST,又能过滤非法输入。以下是两种可行方案:

方案一:基于语义动作的动态校验

通过预定义每个函数的参数类型元数据,在解析后触发语义动作校验参数的数量与类型顺序,校验通过再填充统一AST。

1. 定义元数据与AST

#include <boost/spirit/home/x3.hpp>
#include <boost/variant.hpp>
#include <unordered_map>
#include <vector>
#include <string>

namespace x3 = boost::spirit::x3;

// 标记参数类型
enum class ParamType { Int, String };

// 存储每个函数的预期参数序列:函数名 -> 参数类型列表
const std::unordered_map<std::string, std::vector<ParamType>> func_specs = {
    {"Function1", {ParamType::Int, ParamType::String}},
    {"Function2", {ParamType::String, ParamType::Int, ParamType::String}}
};

// 统一AST结构体
struct FunctionTemplate {
    std::string name;
    std::vector<boost::variant<int, std::string>> params;
};

2. 带类型标记的参数解析规则

先解析单个参数并记录其类型,方便后续校验:

// 存储参数值+类型的临时结构体
struct TypedParam {
    boost::variant<int, std::string> value;
    ParamType type;
};

// 基础参数解析规则(假设字符串是带双引号的字面量)
auto int_parser = x3::int_;
auto string_parser = x3::lexeme['"' >> +(x3::char_ - '"') >> '"'];

// 绑定解析结果与类型标记
auto typed_int = int_parser[([](auto& ctx) {
    _val(ctx) = TypedParam{_attr(ctx), ParamType::Int};
})];
auto typed_string = string_parser[([](auto& ctx) {
    _val(ctx) = TypedParam{_attr(ctx), ParamType::String};
})];
auto any_typed_param = typed_int | typed_string;

3. 带校验的函数解析规则

匹配函数名后解析参数列表,通过语义动作完成校验:

auto func_template = x3::rule<class FuncTplRule, FunctionTemplate>{} = 
    (x3::raw[x3::lit("Function1") | x3::lit("Function2")] >> '(' >> (any_typed_param % ',') >> ')')
    [([](auto& ctx) {
        // 提取解析到的函数名和带类型参数
        auto& raw_name = _attr(ctx).first;
        std::string func_name(raw_name.begin(), raw_name.end());
        auto& parsed_params = _attr(ctx).second;

        // 查找当前函数的预期参数规则
        auto spec_it = func_specs.find(func_name);
        if (spec_it == func_specs.end()) {
            _pass(ctx) = false; // 未知函数,解析失败
            return;
        }
        auto& expected_types = spec_it->second;

        // 校验参数数量
        if (parsed_params.size() != expected_types.size()) {
            _pass(ctx) = false;
            return;
        }

        // 校验每个参数的类型顺序
        for (size_t i = 0; i < parsed_params.size(); ++i) {
            if (parsed_params[i].type != expected_types[i]) {
                _pass(ctx) = false;
                return;
            }
        }

        // 校验通过,填充统一AST
        FunctionTemplate result;
        result.name = func_name;
        for (auto& p : parsed_params) {
            result.params.push_back(p.value);
        }
        _val(ctx) = result;
    })];

方案二:绑定特定参数序列到函数名

如果函数数量可控,直接为每个函数定义严格匹配的参数序列规则,复用统一AST结构体,解析规则本身就保证参数合法性,无需额外校验:

// 定义每个函数的严格参数序列
auto func1_params = int_parser >> ',' >> string_parser;
auto func2_params = string_parser >> ',' >> int_parser >> ',' >> string_parser;

// 统一函数解析规则,分别绑定到AST
auto func_template = 
    (x3::lit("Function1") >> '(' >> func1_params >> ')')
    [([](auto& ctx) {
        FunctionTemplate ft;
        ft.name = "Function1";
        ft.params.push_back(std::get<0>(_attr(ctx)));
        ft.params.push_back(std::get<1>(_attr(ctx)));
        _val(ctx) = ft;
    })]
    | (x3::lit("Function2") >> '(' >> func2_params >> ')')
    [([](auto& ctx) {
        FunctionTemplate ft;
        ft.name = "Function2";
        ft.params.push_back(std::get<0>(_attr(ctx)));
        ft.params.push_back(std::get<1>(_attr(ctx)));
        ft.params.push_back(std::get<2>(_attr(ctx)));
        _val(ctx) = ft;
    })];

方案对比

  • 方案一适合函数数量极多的场景,只需维护func_specs元数据即可扩展,无需修改解析规则结构。
  • 方案二更直观,解析性能略优,适合函数数量中等的场景,规则本身就明确了参数要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 16:13:13