feat(argparse): 添加命令行参数约束和错误处理功能

- 添加了命令行参数约束相关数据结构定义
- 新增错误上下文字段用于更好的错误提示
- 实现了参数验证功能,包括必需参数检查和值选择验证
- 改进错误处理流程,支持添加帮助信息和使用说明
- 优化调试输出格式,增加更多错误场景的处理

fix(parser): 修复语义分析器资源释放问题

- 在scc_sema_drop函数中添加空指针检查,避免空指针释放导致崩溃

refactor(dump): 重构AST节点转储实现

- 将树形转储上下文重构为更简洁的数据结构
- 修改转储回调函数签名以支持更好的缓冲区管理
- 优化内存拷贝操作,提高转储性能

style(amd64): 移除未使用的变量声明

- 删除scc_mcode_amd64_mov_r64_m64_sib函数中未使用的disp8变量
This commit is contained in:
zzy
2026-04-11 11:42:31 +08:00
parent 053c6abf51
commit 630e22b73b
9 changed files with 136 additions and 99 deletions

View File

@@ -11,9 +11,13 @@
// 4. 支持配置文件解析
// 5. 支持国际化i18n
/// @brief 命令行参数约束
typedef struct scc_argparse_spec scc_argparse_spec_t;
/// @brief 命令行参数 eg. someting
typedef struct scc_argparse_arg scc_argparse_arg_t;
/// @brief 命令行参数选项 eg. -a --long
typedef struct scc_argparse_opt scc_argparse_opt_t;
/// @brief 命令集合
typedef struct scc_argparse_cmd scc_argparse_cmd_t;
typedef SCC_VEC(scc_argparse_arg_t) scc_argparse_arg_vec_t;
@@ -44,6 +48,7 @@ typedef enum scc_argparse_err {
} scc_argparse_err_t;
typedef SCC_VEC(const char *) scc_argparse_list_t;
// 约束规范结构体
struct scc_argparse_spec {
scc_argparse_val_type_t value_type; // 值类型
@@ -111,6 +116,8 @@ typedef struct {
cbool need_help;
cbool need_version;
cbool need_debug;
cbool need_error_add_help;
cbool need_error_add_usage;
} scc_argparse_t;
typedef SCC_VEC(scc_optparse_opt_t) scc_optparse_opt_vec_t;
@@ -119,10 +126,10 @@ typedef struct {
scc_argparse_cmd_t *current_cmd; // 当前正在解析的命令
scc_optparse_t optparse; // 底层解析器
scc_optparse_opt_vec_t opts; // 当前命令的选项列表
scc_optparse_result_t result;
int positional_index; // 当前处理的位置参数索引
cbool parsing_done; // 是否已完成解析
scc_argparse_lang_t lang;
scc_optparse_result_t result; // 底层解析结果
const void *err_ctx; // 错误上下文(期待的参数或者当前解析的参数)
int positional_index; // 当前处理的位置参数索引
cbool parsing_done; // 是否已完成解析
} scc_argparse_context_t;
void scc_argparse_init(scc_argparse_t *parser, const char *program_name,
@@ -144,7 +151,7 @@ void scc_argparse_cmd_add_subcmd(scc_argparse_cmd_t *cmd,
void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd);
void scc_argparse_print_error(scc_argparse_context_t *ctx,
scc_argparse_err_t err);
scc_argparse_t *parser, scc_argparse_err_t err);
static inline void scc_argparse_spec_init(scc_argparse_spec_t *spec) {
spec->value_type = SCC_ARGPARSE_VAL_TYPE_STRING;

View File

@@ -1,6 +1,10 @@
#ifndef __SCC_OPTPARSER_H__
#define __SCC_OPTPARSER_H__
#ifndef nullptr
#define nullptr ((void *)0)
#endif
typedef struct scc_optparse_opt {
char prefix;
char short_name;

View File

@@ -11,6 +11,8 @@ void scc_argparse_init(scc_argparse_t *parser, const char *program_name,
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);
}
@@ -37,9 +39,9 @@ static inline scc_argparse_cmd_t *is_subcommand(scc_argparse_cmd_t *cmd,
return nullptr;
}
static inline void parse_cmd(scc_optparse_t *optparse,
scc_optparse_opt_vec_t *opts,
scc_argparse_cmd_t *cmd) {
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) {
@@ -85,6 +87,7 @@ static inline void parse_cmd(scc_optparse_t *optparse,
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;
@@ -108,15 +111,12 @@ static void init_context(scc_argparse_context_t *ctx, scc_argparse_t *parser,
}
scc_optparse_init(&ctx->optparse, argc, argv);
scc_vec_init(ctx->opts);
parse_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
ctx->positional_index = 0;
ctx->parsing_done = false;
ctx->lang = parser->lang;
}
static int handle_parse_error(scc_argparse_t *parser,
scc_argparse_context_t *ctx) {
(void)parser; // TODO
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:
@@ -133,27 +133,68 @@ static int handle_parse_error(scc_argparse_t *parser,
error = SCC_ARGPARSE_ERR_UNKNOWN_ERR;
break;
}
scc_argparse_print_error(ctx, error);
return error;
}
static int check_choices(const scc_argparse_spec_t *spec, const char *value) {
if (!spec->choices.values || spec->choices.count == 0) {
return SCC_ARGPARSE_ERR_NONE;
}
for (int i = 0; i < spec->choices.count; i += 1) {
if (scc_strcmp(value, spec->choices.values[i]) == 0) {
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) {
(void)parser; // TODO
if (errcode == SCC_ARGPARSE_ERR_NONE) {
// 检查必需参数是否都已提供
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 (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;
scc_argparse_print_error(ctx, errcode);
break;
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;
@@ -191,26 +232,6 @@ static int handle_option(scc_argparse_context_t *ctx, scc_argparse_t *parser) {
*opt->spec.store.str_store = ctx->result.value;
}
// // opt value == nullptr or value != nullptr
// scc_argparse_opt_t *org_opt =
// (scc_argparse_opt_t *)opt_res.opt->user_data;
// if (parser->need_help) {
// if (scc_strcmp(org_opt->long_name, "help") == 0) {
// scc_argparse_print_help(parser, current_cmd);
// break;
// }
// }
// if (org_opt->spec.store_as_count) {
// *org_opt->spec.store.int_store += 1;
// }
// if (org_opt->spec.value_type == SCC_ARGPARSE_VAL_TYPE_BOOL) {
// *org_opt->spec.store.bool_store = true;
// }
// if (opt_res.value) {
// org_opt->spec.raw_value = opt_res.value;
// // TODO
// *org_opt->spec.store.str_store = opt_res.value;
// }
return SCC_ARGPARSE_ERR_NONE;
}
@@ -221,7 +242,7 @@ static int handle_positional_arg(scc_argparse_context_t *ctx,
is_subcommand(ctx->current_cmd, ctx->result.value);
if (subcmd != nullptr) {
ctx->current_cmd = subcmd;
parse_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
prepare_cmd(&ctx->optparse, &ctx->opts, ctx->current_cmd);
return SCC_ARGPARSE_ERR_NONE;
}
@@ -232,28 +253,9 @@ static int handle_positional_arg(scc_argparse_context_t *ctx,
arg->spec.raw_value = ctx->result.value;
ctx->positional_index++;
} else {
scc_argparse_print_error(ctx, SCC_ARGPARSE_ERR_UNKNOWN_ARG);
ctx->parsing_done = true;
return SCC_ARGPARSE_ERR_UNKNOWN_ARG;
}
// // position arg
// scc_argparse_cmd_t *cmd = is_subcommand(current_cmd,
// opt_res.value);
// if (cmd != nullptr) {
// current_cmd = cmd;
// parse_cmd(&optparse, &opts, current_cmd);
// } else {
// // TODO argument
// scc_vec_foreach(current_cmd->args, i) {
// scc_argparse_arg_t *arg = &scc_vec_at(current_cmd->args,
// i); if (*arg->spec.store.str_store == 0) {
// *arg->spec.store.str_store = opt_res.value;
// arg->spec.raw_value = opt_res.value;
// break;
// }
// }
// }
return SCC_ARGPARSE_ERR_NONE;
}
@@ -265,14 +267,15 @@ int scc_argparse_parse(scc_argparse_t *parser, int argc, const char **argv) {
while (!ctx.parsing_done &&
scc_optparse_parse(&ctx.optparse, &ctx.result)) {
if (parser->need_debug) {
scc_printf("[%c:%s:%d] %s\n",
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.error,
ctx.result.value ? ctx.result.value : "<nullptr>");
}
if (ctx.result.error) {
errcode = handle_parse_error(parser, &ctx);
errcode = transite_error(&ctx);
break;
}

View File

@@ -12,6 +12,7 @@ enum {
ARGPARSE_SHOW_CMD,
ARGPARSE_SHOW_HELP_MSG,
ARGPRASE_USING_HELP_HINT,
ARGPARSE_UNKNOWN_ARGUMENT,
ARGPARSE_INVALID_VALUE_FOR_OPTION,
ARGPARSE_OPTION_MISSING_VALUE,
@@ -28,10 +29,12 @@ static const char *fmt_en[] = {
[ARGPARSE_SHOW_CMD] = "Commands:\n",
[ARGPARSE_SHOW_HELP_MSG] = "Show this help message and exit",
[ARGPARSE_UNKNOWN_ARGUMENT] = "Unknown argument '%s'",
[ARGPRASE_USING_HELP_HINT] =
"Using '-h' or '--help' to get more information\n",
[ARGPARSE_UNKNOWN_ARGUMENT] = "Unknown argument '%s'\n",
[ARGPARSE_INVALID_VALUE_FOR_OPTION] = "Invalid value for option '%s'\n",
[ARGPARSE_OPTION_MISSING_VALUE] = "Option '%s' missing value\n",
[ARGPARSE_DID_YOU_MEAN] = "; Did you mean: '%s'?\n",
[ARGPARSE_DID_YOU_MEAN] = "Did you mean: '%s'?\n",
};
static const char *fmt_zh[] = {
@@ -44,10 +47,11 @@ static const char *fmt_zh[] = {
[ARGPARSE_SHOW_CMD] = "命令:\n",
[ARGPARSE_SHOW_HELP_MSG] = "显示帮助信息并退出",
[ARGPARSE_UNKNOWN_ARGUMENT] = "未知的参数 '%s'",
[ARGPRASE_USING_HELP_HINT] = "使用 '-h' 或者 '--help' 获取更多信息\n",
[ARGPARSE_UNKNOWN_ARGUMENT] = "未知的参数 '%s'\n",
[ARGPARSE_INVALID_VALUE_FOR_OPTION] = "无效的选项值 '%s'\n",
[ARGPARSE_OPTION_MISSING_VALUE] = "选项 '%s' 缺少值\n",
[ARGPARSE_DID_YOU_MEAN] = "; 您是不是想要输入: '%s'?\n",
[ARGPARSE_DID_YOU_MEAN] = "您是不是想要输入: '%s'?\n",
};
/**
@@ -95,7 +99,7 @@ static int scc_levenshtein(const char *s1, const char *s2) {
return dist;
}
void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
static void print_usage(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
const char **lines =
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
scc_printf(lines[ARGPARSE_USAGE],
@@ -109,6 +113,14 @@ void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
if (scc_vec_size(cmd->subcmds) > 0)
scc_printf(lines[ARGPARSE_COMMAND]);
scc_printf("\n\n");
}
void scc_argparse_print_help(scc_argparse_t *parser, scc_argparse_cmd_t *cmd) {
const char **lines =
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
print_usage(parser, cmd);
if (cmd->description)
scc_printf("%s\n\n", cmd->description);
@@ -197,28 +209,40 @@ const char *scc_argparse_find_similar_arg(scc_argparse_cmd_t *cmd,
}
void scc_argparse_print_error(scc_argparse_context_t *ctx,
scc_argparse_err_t err) {
const char **lines = (ctx->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
const char *optname = ctx->result.raw_arg;
scc_argparse_t *parser, scc_argparse_err_t err) {
const char **lines =
(parser->lang == SCC_ARGPARSE_LANG_ZH) ? fmt_zh : fmt_en;
switch (err) {
case SCC_ARGPARSE_ERR_INVALID_ARG:
case SCC_ARGPARSE_ERR_INVALID_VALUE:
scc_printf(lines[ARGPARSE_INVALID_VALUE_FOR_OPTION], optname);
scc_printf(lines[ARGPARSE_INVALID_VALUE_FOR_OPTION], ctx->err_ctx);
break;
case SCC_ARGPARSE_ERR_MISSING_ARG:
case SCC_ARGPARSE_ERR_MISSING_VALUE:
scc_printf(lines[ARGPARSE_OPTION_MISSING_VALUE], optname);
scc_printf(lines[ARGPARSE_OPTION_MISSING_VALUE], ctx->err_ctx);
break;
case SCC_ARGPARSE_ERR_UNKNOWN_ARG:
case SCC_ARGPARSE_ERR_UNKNOWN_VALUE:
scc_printf(lines[ARGPARSE_UNKNOWN_ARGUMENT], optname);
const char *similar_arg =
scc_argparse_find_similar_arg(ctx->current_cmd, optname);
scc_printf(lines[ARGPARSE_UNKNOWN_ARGUMENT], ctx->result.raw_arg);
const char *similar_arg = scc_argparse_find_similar_arg(
ctx->current_cmd, ctx->result.raw_arg);
if (similar_arg != 0) {
scc_printf(lines[ARGPARSE_DID_YOU_MEAN], similar_arg);
}
break;
// FACK ERROR
case SCC_ARGPARSE_ERR_PNT_DEFAULT:
return;
default:
scc_printf("Unknown error: %d\n", err);
break;
}
if (parser->need_help) {
scc_printf(lines[ARGPRASE_USING_HELP_HINT]);
}
if (parser->need_error_add_help) {
scc_argparse_print_help(parser, ctx->current_cmd);
} else if (parser->need_error_add_usage) {
print_usage(parser, ctx->current_cmd);
}
}

View File

@@ -109,4 +109,4 @@ int main(int argc, const char **argv, const char **envp) {
scc_argparse_drop(&argparse);
return ret;
}
}

View File

@@ -216,6 +216,6 @@ RETURN:
parser->current.count > res->opt->max_args) {
res->error = SCC_OPT_ERROR_TOO_MANY_ARGS;
}
// res->raw_arg = arg;
res->raw_arg = arg;
return res->opt != 0 || res->value != 0 || res->error != SCC_OPT_ERROR_NONE;
}