Files
scc/libs/argparse/src/argparse.c
zzy 28f65f8775 fix(argparse): 修复参数解析错误处理逻辑
当解析过程中出现错误时,应该继续循环而不是直接跳出,
确保完整的错误处理流程能够执行。

fix(ast2ir): 修复函数调用表达式中的类型检查

在每次迭代中重新获取指针以避免向量重分配问题,
并添加类型引用断言确保参数类型有效性。

fix(lexer): 添加词法分析器资源释放

在main函数中正确释放tok_ring资源,防止内存泄漏。

test: 更新测试用例输出字符串格式

将测试用例中的输出字符串从全小写更新为首字母大写,
保持代码风格一致性。
2026-05-25 00:46:42 +08:00

342 lines
11 KiB
C

#include <argparse.h>
void scc_argparse_init(scc_argparse_t *parser, const char *program_name,
const char *description) {
parser->prog_name = program_name;
parser->version = "0.1.0";
parser->description = description;
parser->epilog = nullptr;
parser->lang = SCC_ARGPARSE_LANG_EN;
parser->need_help = true;
parser->need_version = true;
parser->need_debug = false;
parser->need_error_add_help = false;
parser->need_error_add_usage = true;
scc_argparse_cmd_init(&parser->root_cmd, parser->prog_name,
parser->description);
}
scc_argparse_cmd_t *scc_argparse_get_root(scc_argparse_t *parser) {
return &parser->root_cmd;
}
void scc_argparse_drop(scc_argparse_t *parser) {
scc_argparse_cmd_drop(&parser->root_cmd);
}
static inline scc_argparse_cmd_t *is_subcommand(scc_argparse_cmd_t *cmd,
const char *name) {
if (!scc_vec_size(cmd->subcmds)) {
return nullptr;
}
scc_vec_foreach(cmd->subcmds, i) {
scc_argparse_cmd_t *subcmd = &scc_vec_at(cmd->subcmds, i);
if (scc_strcmp(subcmd->name, name) == 0) {
return subcmd;
}
}
return nullptr;
}
static inline void prepare_cmd(scc_optparse_t *optparse,
scc_optparse_opt_vec_t *opts,
scc_argparse_cmd_t *cmd) {
scc_vec_free(*opts);
scc_vec_init(*opts);
scc_vec_foreach(cmd->opts, i) {
scc_argparse_opt_t *opt = &scc_vec_at(cmd->opts, i);
int min_args = 0;
int max_args = 0;
// 根据选项类型推断 min_args 和 max_args
switch (opt->spec.value_type) {
case SCC_ARGPARSE_VAL_TYPE_STRING:
case SCC_ARGPARSE_VAL_TYPE_INT:
case SCC_ARGPARSE_VAL_TYPE_FLOAT:
min_args = 1;
max_args = 1;
break;
case SCC_ARGPARSE_VAL_TYPE_BOOL:
min_args = 0;
max_args = 0;
break;
case SCC_ARGPARSE_VAL_TYPE_COUNT:
min_args = 0;
max_args = 0;
break;
case SCC_ARGPARSE_VAL_TYPE_LIST: // 列表类型
min_args = 1;
max_args = 65535; // FIXME maybe INT_MAX ?
break;
case SCC_ARGPARSE_VAL_TYPE_ENUM:
min_args = 1;
max_args = 1;
break;
default:
min_args = 0;
max_args = 0;
break;
}
scc_vec_push(*opts, ((scc_optparse_opt_t){
.prefix = '-',
.short_name = opt->short_name,
.long_name = opt->long_name,
.min_args = min_args,
.max_args = max_args,
.user_data = opt,
}));
}
scc_vec_push(*opts, (scc_optparse_opt_t)SCC_OPTPARSE_OPT_END());
scc_optparse_set(optparse, opts->data);
}
static void push_help(scc_argparse_cmd_t *cmd) {
if (cmd == nullptr) {
return;
}
scc_vec_push(cmd->opts, ((scc_argparse_opt_t){
.short_name = 'h',
.long_name = "help",
.description = 0,
.spec.value_type = SCC_ARGPARSE_VAL_TYPE_BOOL,
}));
scc_vec_foreach(cmd->subcmds, i) {
push_help(&scc_vec_at(cmd->subcmds, i));
}
}
static void init_context(scc_argparse_context_t *ctx, scc_argparse_t *parser,
int argc, const char **argv) {
ctx->current_cmd = scc_argparse_get_root(parser);
if (parser->need_help) {
push_help(ctx->current_cmd);
}
scc_optparse_init(&ctx->optparse, argc, argv);
scc_vec_init(ctx->opts);
prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
ctx->positional_index = 0;
ctx->parsing_done = false;
}
static int transite_error(scc_argparse_context_t *ctx) {
scc_argparse_err_t error = SCC_ARGPARSE_ERR_NONE;
switch (ctx->result.error) {
case SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG:
case SCC_OPT_ERROR_NOT_FOUND_LONG_ARG:
error = SCC_ARGPARSE_ERR_UNKNOWN_ARG;
break;
case SCC_OPT_ERROR_NOT_ENOUGH_ARGS:
error = SCC_ARGPARSE_ERR_MISSING_VALUE;
break;
case SCC_OPT_ERROR_TOO_MANY_ARGS:
error = SCC_ARGPARSE_ERR_INVALID_ARG;
break;
default:
error = SCC_ARGPARSE_ERR_UNKNOWN_ERR;
break;
}
return error;
}
static int check_choices(const scc_argparse_spec_t *spec, const char *value,
int *out_index) {
if (!spec->choices.values || spec->choices.count == 0) {
return SCC_ARGPARSE_ERR_NONE;
}
if (value == nullptr) {
return SCC_ARGPARSE_ERR_MISSING_VALUE;
}
if (out_index)
*out_index = -1;
for (int i = 0; i < spec->choices.count; i += 1) {
if (scc_strcmp(value, spec->choices.values[i]) == 0) {
if (out_index)
*out_index = i;
return SCC_ARGPARSE_ERR_NONE;
}
}
return SCC_ARGPARSE_ERR_INVALID_VALUE;
}
static int validate_and_cleanup(scc_argparse_context_t *ctx,
scc_argparse_t *parser, int errcode) {
if (errcode != SCC_ARGPARSE_ERR_NONE) {
goto END;
}
// 检查必需的位置参数
scc_vec_foreach(ctx->current_cmd->args, i) {
scc_argparse_arg_t *arg = &scc_vec_at(ctx->current_cmd->args, i);
if (arg->spec.flag_required) {
// 检查是否已存储(非空)
if (arg->spec.store.str_store == nullptr ||
*arg->spec.store.str_store == nullptr) {
errcode = SCC_ARGPARSE_ERR_MISSING_ARG;
ctx->err_ctx = arg->name;
goto END;
}
}
}
scc_vec_foreach(ctx->current_cmd->opts, i) {
scc_argparse_opt_t *opt = &scc_vec_at(ctx->current_cmd->opts, i);
if (opt->spec.flag_required) {
int provided = 0;
if (opt->spec.flag_store_as_count) {
provided = (opt->spec.store.int_store &&
*opt->spec.store.int_store > 0);
} else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) {
provided =
(opt->spec.store.bool_store && *opt->spec.store.bool_store);
} else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) {
provided = (opt->spec.store.vec_store &&
scc_vec_size(*opt->spec.store.vec_store) > 0);
} else {
provided = (opt->spec.store.str_store &&
*opt->spec.store.str_store != nullptr);
}
if (!provided) {
errcode = SCC_ARGPARSE_ERR_MISSING_ARG;
goto END;
}
}
}
END:
if (errcode != SCC_ARGPARSE_ERR_NONE) {
scc_argparse_print_error(ctx, parser, errcode);
}
scc_vec_free(ctx->opts);
scc_optparse_drop(&ctx->optparse);
return errcode;
}
static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) {
scc_argparse_opt_t *opt = (scc_argparse_opt_t *)ctx->result.opt->user_data;
if (parser->need_help && scc_strcmp(opt->long_name, "help") == 0) {
scc_argparse_print_help(parser, ctx->current_cmd);
ctx->parsing_done = true;
return SCC_ARGPARSE_ERR_PNT_DEFAULT;
}
if (parser->need_version && scc_strcmp(opt->long_name, "version") == 0) {
// TODO default version print
}
if (opt->spec.flag_store_as_count) {
(*opt->spec.store.int_store)++;
}
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) {
*opt->spec.store.bool_store = true;
} else if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_ENUM) {
return check_choices(&opt->spec, ctx->result.value,
opt->spec.store.int_store);
}
if (!ctx->result.value) {
return SCC_ARGPARSE_ERR_NONE;
}
opt->spec.raw_value = ctx->result.value;
if (opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_LIST) {
scc_vec_push(*opt->spec.store.vec_store, ctx->result.value);
} else {
*opt->spec.store.str_store = ctx->result.value;
}
return SCC_ARGPARSE_ERR_NONE;
}
static int handle_positional_arg(scc_argparse_context_t *ctx,
scc_argparse_t *parser) {
(void)parser; // TODO
scc_argparse_cmd_t *subcmd =
is_subcommand(ctx->current_cmd, ctx->result.value);
if (subcmd != nullptr) {
ctx->current_cmd = subcmd;
prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
return SCC_ARGPARSE_ERR_NONE;
}
if (ctx->positional_index < (int)scc_vec_size(ctx->current_cmd->args)) {
scc_argparse_arg_t *arg =
&scc_vec_at(ctx->current_cmd->args, ctx->positional_index);
*arg->spec.store.str_store = ctx->result.value;
arg->spec.raw_value = ctx->result.value;
ctx->positional_index++;
} else {
ctx->parsing_done = true;
return SCC_ARGPARSE_ERR_UNKNOWN_ARG;
}
return SCC_ARGPARSE_ERR_NONE;
}
int scc_argparse_parse(scc_argparse_t *parser, int argc, const char **argv) {
scc_argparse_context_t ctx = {0};
init_context(&ctx, parser, argc, argv); // 初始化上下文
int errcode = SCC_ARGPARSE_ERR_NONE;
while (!ctx.parsing_done && errcode == SCC_ARGPARSE_ERR_NONE &&
scc_optparse_parse(&ctx.optparse, &ctx.result)) {
if (parser->need_debug) {
scc_printf("debug:[%c:%s:%d] %s\n",
ctx.result.opt ? ctx.result.opt->short_name : '-',
ctx.result.opt ? ctx.result.opt->long_name : "--",
ctx.result.error,
ctx.result.value ? ctx.result.value : "<nullptr>");
}
if (ctx.result.error) {
errcode = transite_error(&ctx);
continue;
}
if (ctx.result.opt != nullptr) {
errcode = handle_option(&ctx, parser);
} else if (ctx.result.value != nullptr) {
errcode = handle_positional_arg(&ctx, parser);
} else {
UNREACHABLE(); // 不应到达此处
}
}
return validate_and_cleanup(&ctx, parser, errcode);
}
void scc_argparse_cmd_init(scc_argparse_cmd_t *cmd, const char *name,
const char *description) {
cmd->name = name ? name : "cmd_name";
cmd->description = description ? description : "cmd_description";
scc_vec_init(cmd->args);
scc_vec_init(cmd->opts);
scc_vec_init(cmd->subcmds);
}
void scc_argparse_cmd_drop(scc_argparse_cmd_t *cmd) {
scc_vec_free(cmd->args);
scc_vec_free(cmd->opts);
scc_vec_foreach(cmd->subcmds, i) {
scc_argparse_cmd_drop(&scc_vec_at(cmd->subcmds, i));
}
scc_vec_free(cmd->subcmds);
}
void scc_argparse_cmd_add_arg(scc_argparse_cmd_t *cmd,
const scc_argparse_arg_t *arg) {
scc_vec_push(cmd->args, *arg);
}
void scc_argparse_cmd_add_opt(scc_argparse_cmd_t *cmd,
const scc_argparse_opt_t *opt) {
scc_vec_push(cmd->opts, *opt);
}
void scc_argparse_cmd_add_subcmd(scc_argparse_cmd_t *cmd,
const scc_argparse_cmd_t *subcmd) {
scc_vec_push(cmd->subcmds, *subcmd);
}