Files
scc/libs/argparse/src/optparse.c
zzy 088050c903 feat(argparse): 添加选择类型支持和错误处理优化
添加了 SCC_ARGPARSE_ERR_PNT_DEFAULT 错误类型用于默认操作处理,
实现了 scc_argparse_spec_setup_choices 函数支持枚举选择,
重构了错误处理流程使返回值更加一致。
修复了长选项名称匹配的逻辑错误。

feat(lexer): 添加换行符和注释符号的词法标记

新增 SCC_TOK_ENDLINE 和 SCC_TOK_SHARP 标记类型,
改进词法分析器对换行符和井号的识别处理。

feat(scc_core): 添加常用宏定义

添加 scc_min 和 scc_max 宏定义提供基础数值比较功能。

feat(main): 实现编译器主程序和命令行接口

创建主程序入口实现完整的编译流程,
集成预处理器、词法分析、语法分析和IR生成模块,
添加AST和IR输出功能支持调试查看中间表示。

chore(build): 配置项目构建依赖关系

创建 cbuild.toml 配置文件定义项目包信息和依赖库,
建立编译器各组件库之间的依赖关系管理。
2026-02-13 17:26:50 +08:00

222 lines
7.2 KiB
C

#include <optparse.h>
void scc_optparse_init(scc_optparse_t *parser, int argc, const char **argv) {
parser->argc = argc;
parser->argv = argv;
parser->opts = 0;
scc_optparse_reset(parser);
}
void scc_optparse_drop(scc_optparse_t *parser) { (void)parser; }
void scc_optparse_set(scc_optparse_t *parser, const scc_optparse_opt_t *opts) {
parser->opts = opts;
scc_optparse_reset(parser);
}
void scc_optparse_reset(scc_optparse_t *parser) {
parser->handle_positional = 0;
parser->current.opt = 0;
parser->current.count = 0;
parser->current.arg_pos = 1;
parser->current.opt_pos = 0;
}
static inline const char *str_chr(const char *s, char c) {
while (*s && *s != c)
s++;
return *s ? s : 0;
}
const scc_optparse_opt_t *
scc_optparse_get_long_name(const scc_optparse_opt_t *opts, const char *name,
const char end) {
if (*name == '\0')
return 0;
for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) {
if (!opt->long_name) {
continue;
}
int i;
for (i = 0; opt->long_name[i]; ++i) {
if (name[i] != opt->long_name[i]) {
break;
}
}
if (opt->long_name[i] == '\0' && (name[i] == '\0' || name[i] == end))
return opt;
}
return 0;
}
const scc_optparse_opt_t *
scc_optparse_get_short_name(const scc_optparse_opt_t *opts,
const char short_name) {
for (const scc_optparse_opt_t *opt = opts; opt->prefix; ++opt) {
if (opt->short_name == short_name) {
return opt;
}
}
return 0;
}
static inline void scc_optparse_parse_long_name(const scc_optparse_opt_t *opts,
scc_optparse_result_t *res,
const char *arg) {
// caller MUST use `--` start arg
const scc_optparse_opt_t *ret = 0;
const char *chr = str_chr(arg, '=');
if (chr) {
// --file=a.txt
ret = scc_optparse_get_long_name(opts, arg + 2, '=');
if (ret == 0) {
res->error = SCC_OPT_ERROR_NOT_FOUND_LONG_ARG;
} else {
res->opt = ret;
if (chr[1] == '\0') {
// TODO maybe can empty like -file=
res->error = SCC_OPT_ERROR_NOT_ENOUGH_ARGS;
}
res->value = chr + 1;
}
} else {
// --file a.txt
ret = scc_optparse_get_long_name(opts, arg + 2, '\0');
if (ret == 0) {
res->error = SCC_OPT_ERROR_NOT_FOUND_LONG_ARG;
} else {
res->opt = ret;
}
}
}
static inline void scc_optparse_parse_short_name(const scc_optparse_opt_t *opts,
scc_optparse_result_t *res,
const char *arg,
int arg_start) {
// const char *chr =
// str_chr(arg + arg_start, '='); // TODO maybe short can have -I=/usr
const scc_optparse_opt_t *ret =
scc_optparse_get_short_name(opts, arg[arg_start]);
if (ret == 0) {
res->error = SCC_OPT_ERROR_NOT_FOUND_SHORT_ARG;
} else {
res->opt = ret;
}
}
static inline void scc_optparse_parse_position_arg(scc_optparse_t *parser,
const char *arg,
scc_optparse_result_t *res) {
parser->current.arg_pos++;
res->value = arg;
}
static inline int scc_optparse_check(scc_optparse_t *parser,
scc_optparse_result_t *res) {
if (!res->opt || res->error)
return 0;
if (res->value)
parser->current.count += 1;
if (parser->current.count < res->opt->max_args && res->opt->max_args > 0)
parser->current.opt = res->opt;
if (parser->current.count < res->opt->min_args && !res->value)
return 1;
return 0;
}
int scc_optparse_parse(scc_optparse_t *parser, scc_optparse_result_t *res) {
const scc_optparse_opt_t *opts = parser->opts;
const char *arg = 0;
res->opt = 0;
res->value = 0;
res->error = SCC_OPT_ERROR_NONE;
if (parser->current.opt_pos != 0) {
arg = parser->argv[parser->current.arg_pos];
goto CONTINUE_SHORT_OPTION;
}
while (parser->current.arg_pos < parser->argc) {
arg = parser->argv[parser->current.arg_pos];
if (arg[0] == '\0' || parser->handle_positional) {
scc_optparse_parse_position_arg(parser, arg, res);
goto RETURN;
}
for (const scc_optparse_opt_t *opt = opts; opt && opt->prefix; ++opt) {
if (arg[0] != opt->prefix) {
continue;
}
parser->current.opt = 0;
parser->current.count = 0;
if (arg[1] == opt->prefix) {
// long option like --
if (arg[2] == '\0') {
// the `--`
res->value = arg;
parser->handle_positional = 1;
parser->current.arg_pos++;
goto RETURN;
}
scc_optparse_parse_long_name(opts, res, arg);
parser->current.arg_pos += 1;
if (scc_optparse_check(parser, res)) {
goto NEXT;
}
goto RETURN;
} else {
// short option like -
if (arg[1] == '\0') {
// the `-`
res->value = arg;
parser->current.arg_pos++;
goto RETURN;
}
parser->current.opt_pos = 1;
CONTINUE_SHORT_OPTION:
scc_optparse_parse_short_name(opts, res, arg,
parser->current.opt_pos++);
int have_next = arg[parser->current.opt_pos];
parser->current.opt_pos =
have_next ? parser->current.opt_pos : 0;
parser->current.arg_pos += have_next ? 0 : 1;
if (scc_optparse_check(parser, res)) {
if (have_next) {
res->value = arg + parser->current.opt_pos;
parser->current.opt_pos = 0;
parser->current.arg_pos += 1;
goto RETURN;
}
goto NEXT;
}
goto RETURN;
}
}
// position arg
if (parser->current.opt &&
parser->current.count < parser->current.opt->max_args) {
res->opt = parser->current.opt;
res->value = arg;
parser->current.count++;
parser->current.arg_pos++;
goto RETURN;
} else {
scc_optparse_parse_position_arg(parser, arg, res);
goto RETURN;
}
NEXT:
continue;
}
RETURN:
if (res->opt && res->opt->max_args > 0 &&
parser->current.count > res->opt->max_args) {
res->error = SCC_OPT_ERROR_TOO_MANY_ARGS;
}
// res->raw_arg = arg;
return res->opt != 0 || res->value != 0 || res->error != SCC_OPT_ERROR_NONE;
}