aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-08-31 12:51:47 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-08-31 12:51:47 -0400
commit07c5287760ca7a80c06dd80defe007ad009695fc (patch)
treeda8145974f100c7c8f96fbf73170d9991a1f75cf /src
parentc3bcb504a07823ec8a096d34d0f4fe8dc5b27196 (diff)
Add argument aliases so programs can use `func main(verbose|v=no)` to do
single-letter argument flags.
Diffstat (limited to 'src')
-rw-r--r--src/ast.h2
-rw-r--r--src/compile/cli.c47
-rw-r--r--src/compile/functions.c46
-rw-r--r--src/parse/functions.c20
-rw-r--r--src/tomo.c12
-rw-r--r--src/typecheck.c20
-rw-r--r--src/types.h2
7 files changed, 89 insertions, 60 deletions
diff --git a/src/ast.h b/src/ast.h
index 3fdff98c..bf22c9f6 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -65,7 +65,7 @@ typedef struct ast_list_s {
} ast_list_t;
typedef struct arg_ast_s {
- const char *name;
+ const char *name, *alias;
type_ast_t *type;
ast_t *value;
struct arg_ast_s *next;
diff --git a/src/compile/cli.c b/src/compile/cli.c
index 4a3be333..b082239d 100644
--- a/src/compile/cli.c
+++ b/src/compile/cli.c
@@ -2,6 +2,7 @@
#include "../environment.h"
#include "../stdlib/datatypes.h"
+#include "../stdlib/optionals.h"
#include "../stdlib/text.h"
#include "../stdlib/util.h"
#include "../typecheck.h"
@@ -25,6 +26,14 @@ static Text_t get_flag_options(type_t *t, const char *separator) {
}
}
+static OptionalText_t flagify(const char *name, bool prefix) {
+ if (!name) return NONE_TEXT;
+ Text_t flag = Text$from_str(name);
+ flag = Text$replace(flag, Text("_"), Text("-"));
+ if (prefix) flag = flag.length == 1 ? Texts("-", flag) : Texts("--", flag);
+ return flag;
+}
+
public
Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version) {
DeclareMatch(fn_info, fn_type, FunctionType);
@@ -49,20 +58,22 @@ 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) {
usage = Texts(usage, " ");
type_t *t = get_arg_type(main_env, arg);
- Text_t flag = Text$replace(Text$from_str(arg->name), Text("_"), Text("-"));
if (arg->default_val || arg->type->tag == OptionalType) {
- if (strlen(arg->name) == 1) {
- if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
- usage = Texts(usage, "[-", flag, "]");
- else usage = Texts(usage, "[-", flag, " ", get_flag_options(t, "|"), "]");
- } else {
- if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
- usage = Texts(usage, "[--", flag, "]");
- else if (t->tag == ListType) usage = Texts(usage, "[--", flag, " ", get_flag_options(t, "|"), "]");
- else usage = Texts(usage, "[--", flag, "=", get_flag_options(t, "|"), "]");
- }
+ OptionalText_t flag = flagify(arg->name, true);
+ assert(flag.length >= 0);
+ OptionalText_t alias_flag = flagify(arg->alias, true);
+ Text_t flags = alias_flag.length >= 0 ? Texts(flag, "|", alias_flag) : flag;
+ if (t->tag == BoolType || (t->tag == OptionalType && Match(t, OptionalType)->type->tag == BoolType))
+ usage = Texts(usage, "[", flags, "]");
+ else if (t->tag == ListType) usage = Texts(usage, "[", flags, " ", get_flag_options(t, "|"), "]");
+ else usage = Texts(usage, "[", flags, "=", get_flag_options(t, "|"), "]");
} else {
- if (t->tag == BoolType) usage = Texts(usage, "<--", flag, "|--no-", flag, ">");
+ OptionalText_t flag = flagify(arg->name, false);
+ assert(flag.length >= 0);
+ OptionalText_t alias_flag = flagify(arg->alias, true);
+ if (t->tag == BoolType)
+ usage = Texts(usage, "<--", flag, alias_flag.length >= 0 ? Texts("|", alias_flag) : EMPTY_TEXT,
+ "|--no-", flag, ">");
else if (t->tag == EnumType) usage = Texts(usage, get_flag_options(t, "|"));
else if (t->tag == ListType) usage = Texts(usage, "[", flag, "...]");
else usage = Texts(usage, "<", flag, ">");
@@ -76,9 +87,10 @@ 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) {
type_t *opt_type = arg->type->tag == OptionalType ? arg->type : Type(OptionalType, .type = arg->type);
- code = Texts(code, compile_declaration(opt_type, Texts("_$", arg->name)));
+ code = Texts(code, compile_declaration(opt_type, Texts("_$", Text$from_str(arg->name))));
if (arg->default_val) {
- Text_t default_val = compile(env, arg->default_val);
+ Text_t default_val =
+ arg->type ? compile_to_type(env, arg->default_val, arg->type) : compile(env, arg->default_val);
if (arg->type->tag != OptionalType) default_val = promote_to_optional(arg->type, default_val);
code = Texts(code, " = ", default_val);
} else {
@@ -92,7 +104,12 @@ 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, ",\n{", quoted_text(Text$replace(Text$from_str(arg->name), Text("_"), Text("-"))), ", ",
(arg->default_val || arg->type->tag == OptionalType) ? "false" : "true", ", ",
- compile_type_info(arg->type), ", &", Texts("_$", arg->name), "}");
+ compile_type_info(arg->type), ", &", Texts("_$", Text$from_str(arg->name)), "}");
+ if (arg->alias) {
+ code = Texts(code, ",\n{", quoted_text(Text$replace(Text$from_str(arg->alias), Text("_"), Text("-"))), ", ",
+ (arg->default_val || arg->type->tag == OptionalType) ? "false" : "true", ", ",
+ compile_type_info(arg->type), ", &", Texts("_$", Text$from_str(arg->name)), "}");
+ }
}
code = Texts(code, ");\n");
diff --git a/src/compile/functions.c b/src/compile/functions.c
index 665c7379..01de26e3 100644
--- a/src/compile/functions.c
+++ b/src/compile/functions.c
@@ -71,29 +71,31 @@ Text_t compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_
for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) {
int64_t i = 1;
// Find keyword:
- if (spec_arg->name) {
- for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
- if (call_arg->name && streq(call_arg->name, spec_arg->name)) {
- Text_t value;
- if (spec_arg->type->tag == IntType && call_arg->value->tag == Int) {
- value = compile_int_to_type(env, call_arg->value, spec_arg->type);
- } else if (spec_arg->type->tag == NumType && call_arg->value->tag == Int) {
- OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Int)->str);
- if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer");
- if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64)
- value = Text$from_str(String(hex_double(Num$from_int(int_val, false))));
- else value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f"));
- } else {
- env_t *arg_env = with_enum_scope(env, spec_arg->type);
- value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type);
- }
- Table$str_set(&used_args, call_arg->name, call_arg);
- if (code.length > 0) code = Texts(code, ", ");
- code = Texts(code, value);
- goto found_it;
- }
+ assert(spec_arg->name);
+ for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
+ if (!call_arg->name) continue;
+ if (!(streq(call_arg->name, spec_arg->name) || (spec_arg->alias && streq(call_arg->name, spec_arg->alias))))
+ continue;
+
+ Text_t value;
+ if (spec_arg->type->tag == IntType && call_arg->value->tag == Int) {
+ value = compile_int_to_type(env, call_arg->value, spec_arg->type);
+ } else if (spec_arg->type->tag == NumType && call_arg->value->tag == Int) {
+ OptionalInt_t int_val = Int$from_str(Match(call_arg->value, Int)->str);
+ if (int_val.small == 0) code_err(call_arg->value, "Failed to parse this integer");
+ if (Match(spec_arg->type, NumType)->bits == TYPE_NBITS64)
+ value = Text$from_str(String(hex_double(Num$from_int(int_val, false))));
+ else value = Text$from_str(String(hex_double((double)Num32$from_int(int_val, false)), "f"));
+ } else {
+ env_t *arg_env = with_enum_scope(env, spec_arg->type);
+ value = compile_maybe_incref(arg_env, call_arg->value, spec_arg->type);
}
+ Table$str_set(&used_args, call_arg->name, call_arg);
+ if (code.length > 0) code = Texts(code, ", ");
+ code = Texts(code, value);
+ goto found_it;
}
+
// Find positional:
for (arg_ast_t *call_arg = call_args; call_arg; call_arg = call_arg->next) {
if (call_arg->name) continue;
@@ -161,7 +163,7 @@ Text_t compile_function_call(env_t *env, ast_t *ast) {
args = new (arg_t, .name = a->name, .type = get_type(env, a->value), .next = args);
REVERSE_LIST(args);
code_err(ast,
- "This function's public signature doesn't match this call site.\n"
+ "This function's signature doesn't match this call site.\n"
"The signature is: ",
type_to_text(fn_t),
"\n"
diff --git a/src/parse/functions.c b/src/parse/functions.c
index 0779bb7b..37505ac5 100644
--- a/src/parse/functions.c
+++ b/src/parse/functions.c
@@ -13,8 +13,8 @@
#include "context.h"
#include "controlflow.h"
#include "errors.h"
-#include "functions.h"
#include "expressions.h"
+#include "functions.h"
#include "types.h"
#include "utils.h"
@@ -26,7 +26,7 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) {
type_ast_t *type = NULL;
typedef struct name_list_s {
- const char *name;
+ const char *name, *alias;
struct name_list_s *next;
} name_list_t;
@@ -37,19 +37,26 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) {
if (!name) break;
whitespace(pos);
+ const char *alias = NULL;
+ if (match(pos, "|")) {
+ whitespace(pos);
+ alias = get_id(pos);
+ if (!alias) parser_err(ctx, *pos, *pos, "I expected an argument alias after `|`");
+ }
+
if (match(pos, ":")) {
type = expect(ctx, *pos - 1, pos, parse_type, "I expected a type here");
- names = new (name_list_t, .name = name, .next = names);
+ names = new (name_list_t, .name = name, .alias = alias, .next = names);
whitespace(pos);
if (match(pos, "="))
default_val = expect(ctx, *pos - 1, pos, parse_term, "I expected a value after this '='");
break;
} else if (strncmp(*pos, "==", 2) != 0 && match(pos, "=")) {
default_val = expect(ctx, *pos - 1, pos, parse_term, "I expected a value after this '='");
- names = new (name_list_t, .name = name, .next = names);
+ names = new (name_list_t, .name = name, .alias = alias, .next = names);
break;
} else if (name) {
- names = new (name_list_t, .name = name, .next = names);
+ names = new (name_list_t, .name = name, .alias = alias, .next = names);
spaces(pos);
if (!match(pos, ",")) break;
} else {
@@ -64,7 +71,8 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) {
REVERSE_LIST(names);
for (; names; names = names->next)
- args = new (arg_ast_t, .name = names->name, .type = type, .value = default_val, .next = args);
+ args = new (arg_ast_t, .name = names->name, .alias = names->alias, .type = type, .value = default_val,
+ .next = args);
if (!match_separator(pos)) break;
}
diff --git a/src/tomo.c b/src/tomo.c
index d02cce74..70a15f59 100644
--- a/src/tomo.c
+++ b/src/tomo.c
@@ -289,7 +289,7 @@ int main(int argc, char *argv[]) {
if (files.length < 1) print_err("No file specified!");
- quiet = !verbose;
+ if (!compile_exe && !stop_at_transpile && !stop_at_obj_compilation) quiet = !verbose;
for (int64_t i = 0; i < files.length; i++) {
Path_t path = *(Path_t *)(files.data + i * files.stride);
@@ -404,7 +404,7 @@ void build_library(Path_t lib_dir) {
int status = pclose(prog);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE);
- if (!quiet) print("Compiled library:\t", shared_lib);
+ if (!quiet) print("Compiled library:\t", Path$relative_to(shared_lib, Path$current_dir()));
}
void install_library(Path_t lib_dir) {
@@ -726,7 +726,7 @@ void transpile_header(env_t *base_env, Path_t path) {
Text$print(header, h_code);
if (fclose(header) == -1) print_err("Failed to write header file: ", h_filename);
- if (!quiet) print("Transpiled header:\t", h_filename);
+ if (!quiet) print("Transpiled header:\t", Path$relative_to(h_filename, Path$current_dir()));
if (show_codegen.length > 0) xsystem(show_codegen, " <", h_filename);
}
@@ -765,7 +765,7 @@ void transpile_code(env_t *base_env, Path_t path) {
if (fclose(c_file) == -1) print_err("Failed to output C code to ", c_filename);
- if (!quiet) print("Transpiled code:\t", c_filename);
+ if (!quiet) print("Transpiled code:\t", Path$relative_to(c_filename, Path$current_dir()));
if (show_codegen.length > 0) xsystem(show_codegen, " <", c_filename);
}
@@ -781,7 +781,7 @@ void compile_object_file(Path_t path) {
Path$write(build_file(path, ".config"), config_summary, 0644);
- if (!quiet) print("Compiled object:\t", obj_file);
+ if (!quiet) print("Compiled object:\t", Path$relative_to(obj_file, Path$current_dir()));
}
Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t object_files, List_t extra_ldlibs) {
@@ -821,6 +821,6 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t
int status = pclose(runner);
if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) exit(EXIT_FAILURE);
- if (!quiet) print("Compiled executable:\t", exe_path);
+ if (!quiet) print("Compiled executable:\t", Path$relative_to(exe_path, Path$current_dir()));
return exe_path;
}
diff --git a/src/typecheck.c b/src/typecheck.c
index 828d2509..50df9327 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -104,7 +104,7 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) {
"longer exist on the stack.");
arg_t *type_args = NULL;
for (arg_ast_t *arg = fn->args; arg; arg = arg->next) {
- type_args = new (arg_t, .name = arg->name, .next = type_args);
+ type_args = new (arg_t, .name = arg->name, .alias = arg->alias, .next = type_args);
if (arg->type) type_args->type = parse_type_ast(env, arg->type);
else if (arg->value) type_args->type = get_type(env, arg->value);
@@ -397,8 +397,8 @@ void bind_statement(env_t *env, ast_t *statement) {
"\nTry using a @",
type_to_str(field_t), " pointer for this field.");
}
- fields = new (arg_t, .name = field_ast->name, .type = field_t, .default_val = field_ast->value,
- .next = fields);
+ fields = new (arg_t, .name = field_ast->name, .alias = field_ast->alias, .type = field_t,
+ .default_val = field_ast->value, .next = fields);
}
REVERSE_LIST(fields);
type->__data.StructType.fields = fields; // populate placeholder
@@ -453,8 +453,8 @@ void bind_statement(env_t *env, ast_t *statement) {
"\nTry using a @",
type_to_str(field_t), " pointer for this field.");
}
- fields = new (arg_t, .name = field_ast->name, .type = field_t, .default_val = field_ast->value,
- .next = fields);
+ fields = new (arg_t, .name = field_ast->name, .alias = field_ast->alias, .type = field_t,
+ .default_val = field_ast->value, .next = fields);
}
REVERSE_LIST(fields);
env_t *member_ns = namespace_env(env, String(def->name, "$", tag_ast->name));
@@ -586,7 +586,7 @@ type_t *get_function_def_type(env_t *env, ast_t *ast) {
env_t *scope = fresh_scope(env);
for (arg_ast_t *arg = arg_asts; arg; arg = arg->next) {
type_t *t = arg->type ? parse_type_ast(env, arg->type) : get_type(env, arg->value);
- args = new (arg_t, .name = arg->name, .type = t, .default_val = arg->value, .next = args);
+ args = new (arg_t, .name = arg->name, .alias = arg->alias, .type = t, .default_val = arg->value, .next = args);
set_binding(scope, arg->name, t, EMPTY_TEXT);
}
REVERSE_LIST(args);
@@ -918,7 +918,8 @@ type_t *get_type(env_t *env, ast_t *ast) {
return t; // Constructor
arg_t *arg_types = NULL;
for (arg_ast_t *arg = call->args; arg; arg = arg->next)
- arg_types = new (arg_t, .type = get_type(env, arg->value), .name = arg->name, .next = arg_types);
+ arg_types = new (arg_t, .type = get_type(env, arg->value), .name = arg->name, .alias = arg->alias,
+ .next = arg_types);
REVERSE_LIST(arg_types);
code_err(call->fn, "I couldn't find a type constructor for ",
type_to_text(Type(FunctionType, .args = arg_types, .ret = t)));
@@ -1399,7 +1400,7 @@ type_t *get_type(env_t *env, ast_t *ast) {
env_t *scope = fresh_scope(env); // For now, just use closed variables in scope normally
for (arg_ast_t *arg = lambda->args; arg; arg = arg->next) {
type_t *t = get_arg_ast_type(env, arg);
- args = new (arg_t, .name = arg->name, .type = t, .next = args);
+ args = new (arg_t, .name = arg->name, .alias = arg->alias, .type = t, .next = args);
set_binding(scope, arg->name, t, EMPTY_TEXT);
}
REVERSE_LIST(args);
@@ -1617,7 +1618,8 @@ bool is_valid_call(env_t *env, arg_t *spec_args, arg_ast_t *call_args, call_opts
for (arg_t *spec_arg = spec_args; spec_arg; spec_arg = spec_arg->next) {
if (!options.underscores && spec_arg->name[0] == '_') continue;
- if (!streq(call_arg->name, spec_arg->name)) continue;
+ if (!(streq(call_arg->name, spec_arg->name) || (spec_arg->alias && streq(call_arg->name, spec_arg->alias))))
+ continue;
type_t *spec_type = get_arg_type(env, spec_arg);
env_t *arg_scope = with_enum_scope(env, spec_type);
type_t *call_type = get_arg_ast_type(arg_scope, call_arg);
diff --git a/src/types.h b/src/types.h
index ed5628e8..5b5c4fb9 100644
--- a/src/types.h
+++ b/src/types.h
@@ -9,7 +9,7 @@
typedef struct type_s type_t;
typedef struct arg_s {
- const char *name;
+ const char *name, *alias;
type_t *type;
ast_t *default_val;
struct arg_s *next;