当解析过程中出现错误时,应该继续循环而不是直接跳出, 确保完整的错误处理流程能够执行。 fix(ast2ir): 修复函数调用表达式中的类型检查 在每次迭代中重新获取指针以避免向量重分配问题, 并添加类型引用断言确保参数类型有效性。 fix(lexer): 添加词法分析器资源释放 在main函数中正确释放tok_ring资源,防止内存泄漏。 test: 更新测试用例输出字符串格式 将测试用例中的输出字符串从全小写更新为首字母大写, 保持代码风格一致性。
342 lines
11 KiB
C
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);
|
|
}
|