aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-10-18 15:58:02 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-10-18 15:58:02 -0400
commit3ce1743d09011304ceec8366bba8604026096fb1 (patch)
tree760f542c10968ae8655766a101430f48ac6c41b8
parent7e5a1fa68dbdc2aa034d9e22648837ef63e57d5e (diff)
Lazily initialize args
-rw-r--r--src/compile/cli.c42
-rw-r--r--src/stdlib/cli.c18
-rw-r--r--src/stdlib/cli.h13
-rw-r--r--src/tomo.c42
4 files changed, 61 insertions, 54 deletions
diff --git a/src/compile/cli.c b/src/compile/cli.c
index eec2f941..820f0e2e 100644
--- a/src/compile/cli.c
+++ b/src/compile/cli.c
@@ -86,7 +86,29 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c
}
for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
- code = Texts(code, compile_declaration(arg->type, Texts("_$", Text$from_str(arg->name))));
+ code = Texts(code, compile_declaration(arg->type, Texts("_$", Text$from_str(arg->name))), " = ",
+ compile_empty(arg->type), ";\n");
+ }
+
+ Text_t version_code = quoted_str(version);
+ code = Texts(code, "cli_arg_t cli_args[] = {\n");
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
+ code = Texts(code, "{", quoted_text(Text$replace(Text$from_str(arg->name), Text("_"), Text("-"))), ", &",
+ Texts("_$", Text$from_str(arg->name)), ", ", compile_type_info(arg->type),
+ arg->default_val ? Text("") : Text(", .required=true"),
+ arg->alias ? Texts(", .short_flag=", quoted_text(Text$from_str(arg->name)),
+ "[0]") // TODO: escape char properly
+ : Text(""),
+
+ "},\n");
+ }
+ code = Texts(code, "};\n");
+ code = Texts(code, "tomo_parse_args(argc, argv, ", usage_code, ", ", help_code, ", ", version_code,
+ ", sizeof(cli_args)/sizeof(cli_args[0]), cli_args);\n");
+
+ // Lazily initialize default values to prevent side effects
+ int64_t i = 0;
+ for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
if (arg->default_val) {
Text_t default_val;
if (arg->type) {
@@ -95,23 +117,11 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c
} else {
default_val = compile(env, arg->default_val);
}
- code = Texts(code, " = ", default_val);
- } else {
- code = Texts(code, " = ", compile_empty(arg->type));
+ code = Texts(code, "if (!cli_args[", i, "].populated) ", Texts("_$", Text$from_str(arg->name)), " = ",
+ default_val, ";\n");
}
- code = Texts(code, ";\n");
- }
-
- Text_t version_code = quoted_str(version);
- code = Texts(code, "tomo_parse_args(argc, argv, ", usage_code, ", ", help_code, ", ", version_code);
- for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
- code = Texts(code, ",\n{", quoted_text(Text$replace(Text$from_str(arg->name), Text("_"), Text("-"))), ", ",
- arg->alias ? Texts(quoted_text(Text$from_str(arg->name)), "[0]") // TODO: escape char properly
- : Text("'\\0'"),
- ", ", arg->default_val ? "false" : "true", ", ", compile_type_info(arg->type), ", &",
- Texts("_$", Text$from_str(arg->name)), "}");
+ i += 1;
}
- code = Texts(code, ");\n");
code = Texts(code, fn_name, "(");
for (arg_t *arg = fn_info->args; arg; arg = arg->next) {
diff --git a/src/stdlib/cli.c b/src/stdlib/cli.c
index 6e0a4b81..0f8bff4a 100644
--- a/src/stdlib/cli.c
+++ b/src/stdlib/cli.c
@@ -195,15 +195,14 @@ static bool pop_boolean_cli_flag(List_t *args, char short_flag, const char *flag
}
public
-void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, const char *version, int spec_len,
- cli_arg_t spec[spec_len]) {
- bool *parsed = GC_MALLOC_ATOMIC(sizeof(bool[spec_len]));
+void tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, const char *version, int spec_len,
+ cli_arg_t spec[spec_len]) {
List_t args = EMPTY_LIST;
for (int i = 1; i < argc; i++) {
List$insert(&args, &argv[i], I(0), sizeof(const char *));
}
for (int i = 0; i < spec_len; i++) {
- parsed[i] = pop_cli_flag(&args, spec[i].short_flag, spec[i].name, spec[i].dest, spec[i].type);
+ spec[i].populated = pop_cli_flag(&args, spec[i].short_flag, spec[i].name, spec[i].dest, spec[i].type);
}
bool show_help = false;
@@ -230,18 +229,19 @@ void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, const c
}
for (int i = 0; i < spec_len && before_double_dash.length > 0; i++) {
- if (!parsed[i]) {
- parsed[i] = pop_cli_positional(&before_double_dash, spec[i].name, spec[i].dest, spec[i].type, false);
+ if (!spec[i].populated) {
+ spec[i].populated =
+ pop_cli_positional(&before_double_dash, spec[i].name, spec[i].dest, spec[i].type, false);
}
}
for (int i = 0; i < spec_len && after_double_dash.length > 0; i++) {
- if (!parsed[i]) {
- parsed[i] = pop_cli_positional(&after_double_dash, spec[i].name, spec[i].dest, spec[i].type, true);
+ if (!spec[i].populated) {
+ spec[i].populated = pop_cli_positional(&after_double_dash, spec[i].name, spec[i].dest, spec[i].type, true);
}
}
for (int i = 0; i < spec_len; i++) {
- if (!parsed[i] && spec[i].required) print_err("Missing required flag: --", spec[i].name, "\n", usage);
+ if (!spec[i].populated && spec[i].required) print_err("Missing required flag: --", spec[i].name, "\n", usage);
}
List_t remaining_args = List$concat(before_double_dash, after_double_dash, sizeof(const char *));
diff --git a/src/stdlib/cli.h b/src/stdlib/cli.h
index 596409c1..5026edb3 100644
--- a/src/stdlib/cli.h
+++ b/src/stdlib/cli.h
@@ -9,17 +9,14 @@
typedef struct {
const char *name;
- char short_flag;
- bool required;
- const TypeInfo_t *type;
void *dest;
+ const TypeInfo_t *type;
+ char short_flag;
+ bool required, populated;
} cli_arg_t;
-void _tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, const char *version, int spec_len,
- cli_arg_t spec[spec_len]);
-#define tomo_parse_args(argc, argv, usage, help, version, ...) \
- _tomo_parse_args(argc, argv, usage, help, version, sizeof((cli_arg_t[]){__VA_ARGS__}) / sizeof(cli_arg_t), \
- (cli_arg_t[]){__VA_ARGS__})
+void tomo_parse_args(int argc, char *argv[], Text_t usage, Text_t help, const char *version, int spec_len,
+ cli_arg_t spec[spec_len]);
bool pop_cli_flag(List_t *args, char short_flag, const char *flag, void *dest, const TypeInfo_t *type);
bool pop_cli_positional(List_t *args, const char *flag, void *dest, const TypeInfo_t *type, bool allow_dashes);
diff --git a/src/tomo.c b/src/tomo.c
index 4552f401..6741374a 100644
--- a/src/tomo.c
+++ b/src/tomo.c
@@ -218,28 +218,28 @@ int main(int argc, char *argv[]) {
" --run|-r: run a program from ",
TOMO_PATH, "/share/tomo_" TOMO_VERSION "/installed\n");
Text_t help = Texts(Text("\x1b[1mtomo\x1b[m: a compiler for the Tomo programming language"), Text("\n\n"), usage);
- tomo_parse_args(argc, argv, usage, help, TOMO_VERSION, //
- {"run", 'r', false, List$info(&Path$info), &run_files}, //
- {"args", '\0', false, List$info(&Text$info), &args}, //
- {"format", 'F', false, List$info(&Path$info), &format_files}, //
- {"format-inplace", '\0', false, List$info(&Path$info), &format_files_inplace}, //
- {"transpile", 't', false, List$info(&Path$info), &transpile_files}, //
- {"compile-obj", 'c', false, List$info(&Path$info), &compile_objects}, //
- {"compile-exe", 'e', false, List$info(&Path$info), &compile_executables}, //
- {"library", 'L', false, List$info(&Path$info), &libraries}, //
- {"uninstall", 'u', false, List$info(&Text$info), &uninstall_libraries}, //
- {"verbose", 'v', false, &Bool$info, &verbose}, //
- {"version", 'V', false, &Bool$info, &show_version}, //
- {"install", 'I', false, &Bool$info, &should_install}, //
- {"prefix", '\0', false, &Bool$info, &show_prefix}, //
- {"quiet", 'q', false, &Bool$info, &quiet}, //
- {"show-codegen", 'C', false, &Text$info, &show_codegen}, //
- {"optimization", 'O', false, &Text$info, &optimization}, //
- {"force-rebuild", 'f', false, &Bool$info, &clean_build}, //
- {"source-mapping", 'm', false, &Bool$info, &source_mapping},
- {"changelog", '\0', false, &Bool$info, &show_changelog}, //
- );
+ cli_arg_t tomo_args[] = {
+ {"run", &run_files, List$info(&Path$info), .short_flag = 'r'}, //
+ {"args", &args, List$info(&Text$info)}, //
+ {"format", &format_files, List$info(&Path$info), .short_flag = 'F'}, //
+ {"format-inplace", &format_files_inplace, List$info(&Path$info)}, //
+ {"transpile", &transpile_files, List$info(&Path$info), .short_flag = 't'}, //
+ {"compile-obj", &compile_objects, List$info(&Path$info), .short_flag = 'c'}, //
+ {"compile-exe", &compile_executables, List$info(&Path$info), .short_flag = 'e'}, //
+ {"library", &libraries, List$info(&Path$info), .short_flag = 'L'}, //
+ {"uninstall", &uninstall_libraries, List$info(&Text$info), .short_flag = 'u'}, //
+ {"verbose", &verbose, &Bool$info, .short_flag = 'v'}, //
+ {"install", &should_install, &Bool$info, .short_flag = 'I'}, //
+ {"prefix", &show_prefix, &Bool$info}, //
+ {"quiet", &quiet, &Bool$info, .short_flag = 'q'}, //
+ {"show-codegen", &show_codegen, &Text$info, .short_flag = 'C'}, //
+ {"optimization", &optimization, &Text$info, .short_flag = 'O'}, //
+ {"force-rebuild", &clean_build, &Bool$info, .short_flag = 'f'}, //
+ {"source-mapping", &source_mapping, &Bool$info, .short_flag = 'm'},
+ {"changelog", &show_changelog, &Bool$info}, //
+ };
+ tomo_parse_args(argc, argv, usage, help, TOMO_VERSION, sizeof(tomo_args) / sizeof(tomo_args[0]), tomo_args);
if (show_prefix) {
print(TOMO_PATH);
return 0;