diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/ast.h | 2 | ||||
| -rw-r--r-- | src/compile/cli.c | 47 | ||||
| -rw-r--r-- | src/compile/functions.c | 46 | ||||
| -rw-r--r-- | src/parse/functions.c | 22 | ||||
| -rw-r--r-- | src/stdlib/cli.c | 389 | ||||
| -rw-r--r-- | src/stdlib/cli.h | 21 | ||||
| -rw-r--r-- | src/stdlib/stdlib.c | 374 | ||||
| -rw-r--r-- | src/stdlib/stdlib.h | 15 | ||||
| -rw-r--r-- | src/stdlib/tomo.h | 1 | ||||
| -rw-r--r-- | src/tomo.c | 12 | ||||
| -rw-r--r-- | src/typecheck.c | 20 | ||||
| -rw-r--r-- | src/types.h | 2 |
12 files changed, 503 insertions, 448 deletions
@@ -67,7 +67,7 @@ typedef struct ast_list_s { typedef struct arg_ast_s { file_t *file; const char *start, *end; - 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 b50519b7..6cb085b7 100644 --- a/src/parse/functions.c +++ b/src/parse/functions.c @@ -27,7 +27,7 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) { typedef struct name_list_s { const char *start, *end; - const char *name; + const char *name, *alias; struct name_list_s *next; } name_list_t; @@ -39,19 +39,29 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) { const char *name_start = *pos; whitespace(ctx, 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"); whitespace(ctx, pos); if (match(pos, "=")) default_val = expect(ctx, *pos - 1, pos, parse_term, "I expected a value after this '='"); - names = new (name_list_t, .start = name_start, .end = *pos, .name = name, .next = names); + names = + new (name_list_t, .start = name_start, .end = *pos, .name = name, .alias = alias, .next = names); 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, .start = name_start, .end = *pos, .name = name, .next = names); + names = + new (name_list_t, .start = name_start, .end = *pos, .name = name, .alias = alias, .next = names); break; } else if (name) { - names = new (name_list_t, .start = name_start, .end = *pos, .name = name, .next = names); + names = + new (name_list_t, .start = name_start, .end = *pos, .name = name, .alias = alias, .next = names); spaces(pos); if (!match(pos, ",")) break; } else { @@ -66,8 +76,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, .start = names->start, .end = names->end, .name = names->name, .type = type, - .value = default_val, .next = args); + args = new (arg_ast_t, .start = names->start, .end = names->end, .name = names->name, .alias = names->name, + .type = type, .value = default_val, .next = args); if (!match_separator(ctx, pos)) break; } diff --git a/src/stdlib/cli.c b/src/stdlib/cli.c new file mode 100644 index 00000000..62174f19 --- /dev/null +++ b/src/stdlib/cli.c @@ -0,0 +1,389 @@ +// Comman-line argument parsing + +#include <ctype.h> +#include <execinfo.h> +#include <fcntl.h> +#include <gc.h> +#include <math.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/param.h> +#include <time.h> + +#include "../config.h" +#include "bools.h" +#include "bytes.h" +#include "cli.h" +#include "integers.h" +#include "metamethods.h" +#include "nums.h" +#include "optionals.h" +#include "paths.h" +#include "print.h" +#include "stdlib.h" +#include "tables.h" +#include "text.h" +#include "util.h" + +static bool is_numeric_type(const TypeInfo_t *info) { + return (info == &Num$info || info == &Num32$info || info == &Int$info || info == &Int64$info || info == &Int32$info + || info == &Int16$info || info == &Int8$info || info == &Byte$info); +} + +static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) { + if (!arg) return false; + + if (info->tag == OptionalInfo && streq(arg, "none")) return true; + + while (info->tag == OptionalInfo) + info = info->OptionalInfo.type; + + if (info == &Int$info) { + OptionalInt_t parsed = Int$from_str(arg); + if (parsed.small != 0) *(OptionalInt_t *)dest = parsed; + return parsed.small != 0; + } else if (info == &Int64$info) { + OptionalInt64_t parsed = Int64$parse(Text$from_str(arg), NULL); + if (!parsed.is_none) *(OptionalInt64_t *)dest = parsed; + return !parsed.is_none; + } else if (info == &Int32$info) { + OptionalInt32_t parsed = Int32$parse(Text$from_str(arg), NULL); + if (!parsed.is_none) *(OptionalInt32_t *)dest = parsed; + return !parsed.is_none; + } else if (info == &Int16$info) { + OptionalInt16_t parsed = Int16$parse(Text$from_str(arg), NULL); + if (!parsed.is_none) *(OptionalInt16_t *)dest = parsed; + return !parsed.is_none; + } else if (info == &Int8$info) { + OptionalInt8_t parsed = Int8$parse(Text$from_str(arg), NULL); + if (!parsed.is_none) *(OptionalInt8_t *)dest = parsed; + return !parsed.is_none; + } else if (info == &Byte$info) { + OptionalByte_t parsed = Byte$parse(Text$from_str(arg), NULL); + if (!parsed.is_none) *(OptionalByte_t *)dest = parsed; + return !parsed.is_none; + } else if (info == &Bool$info) { + OptionalBool_t parsed = Bool$parse(Text$from_str(arg), NULL); + if (parsed != NONE_BOOL) *(OptionalBool_t *)dest = parsed; + return parsed != NONE_BOOL; + } else if (info == &Num$info) { + OptionalNum_t parsed = Num$parse(Text$from_str(arg), NULL); + if (!isnan(parsed)) *(OptionalNum_t *)dest = parsed; + return !isnan(parsed); + } else if (info == &Num32$info) { + OptionalNum32_t parsed = Num32$parse(Text$from_str(arg), NULL); + if (!isnan(parsed)) *(OptionalNum32_t *)dest = parsed; + return !isnan(parsed); + } else if (info == &Path$info) { + *(OptionalPath_t *)dest = Path$from_str(arg); + return true; + } else if (info->tag == TextInfo) { + *(OptionalText_t *)dest = Text$from_str(arg); + return true; + } else if (info->tag == EnumInfo) { + for (int t = 0; t < info->EnumInfo.num_tags; t++) { + NamedType_t named = info->EnumInfo.tags[t]; + size_t len = strlen(named.name); + if (strncmp(arg, named.name, len) == 0 && (arg[len] == '\0' || arg[len] == ':')) { + *(int32_t *)dest = (t + 1); + + // Simple tag (no associated data): + if (!named.type || (named.type->tag == StructInfo && named.type->StructInfo.num_fields == 0)) + return true; + + // Single-argument tag: + if (arg[len] != ':') print_err("Invalid value for ", t, ".", named.name, ": ", arg); + size_t offset = sizeof(int32_t); + if (named.type->align > 0 && offset % (size_t)named.type->align > 0) + offset += (size_t)named.type->align - (offset % (size_t)named.type->align); + if (!parse_single_arg(named.type, arg + len + 1, dest + offset)) return false; + return true; + } + } + print_err("Invalid value for ", info->EnumInfo.name, ": ", arg); + } else if (info->tag == StructInfo) { + if (info->StructInfo.num_fields == 0) return true; + else if (info->StructInfo.num_fields == 1) return parse_single_arg(info->StructInfo.fields[0].type, arg, dest); + + Text_t t = generic_as_text(NULL, false, info); + print_err("Unsupported multi-argument struct type for argument parsing: ", t); + } else if (info->tag == ListInfo) { + print_err("List arguments must be specified as `--flag ...` not `--flag=...`"); + } else if (info->tag == TableInfo) { + print_err("Table arguments must be specified as `--flag ...` not `--flag=...`"); + } else { + Text_t t = generic_as_text(NULL, false, info); + print_err("Unsupported type for argument parsing: ", t); + } + return false; +} + +static List_t parse_list(const TypeInfo_t *item_info, int n, char *args[]) { + int64_t padded_size = item_info->size; + if ((padded_size % item_info->align) > 0) + padded_size = padded_size + item_info->align - (padded_size % item_info->align); + + List_t items = { + .stride = padded_size, + .length = n, + .data = GC_MALLOC((size_t)(padded_size * n)), + }; + for (int i = 0; i < n; i++) { + bool success = parse_single_arg(item_info, args[i], items.data + items.stride * i); + if (!success) print_err("Couldn't parse argument: ", args[i]); + } + return items; +} + +// Arguments take the form key=value, with a guarantee that there is an '=' +static Table_t parse_table(const TypeInfo_t *table, int n, char *args[]) { + const TypeInfo_t *key = table->TableInfo.key, *value = table->TableInfo.value; + int64_t padded_size = key->size; + if ((padded_size % value->align) > 0) padded_size = padded_size + value->align - (padded_size % value->align); + int64_t value_offset = padded_size; + padded_size += value->size; + if ((padded_size % key->align) > 0) padded_size = padded_size + key->align - (padded_size % key->align); + + List_t entries = { + .stride = padded_size, + .length = n, + .data = GC_MALLOC((size_t)(padded_size * n)), + }; + for (int i = 0; i < n; i++) { + char *key_arg = args[i]; + char *equals = strchr(key_arg, '='); + assert(equals); + char *value_arg = equals + 1; + *equals = '\0'; + + bool success = parse_single_arg(key, key_arg, entries.data + entries.stride * i); + if (!success) print_err("Couldn't parse table key: ", key_arg); + + success = parse_single_arg(value, value_arg, entries.data + entries.stride * i + value_offset); + if (!success) print_err("Couldn't parse table value: ", value_arg); + + *equals = '='; + } + return Table$from_entries(entries, table); +} + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wstack-protector" +#endif +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 populated_args[spec_len]; + bool used_args[argc]; + memset(populated_args, 0, sizeof(populated_args)); + memset(used_args, 0, sizeof(used_args)); + for (int i = 1; i < argc;) { + if (argv[i][0] == '-' && argv[i][1] == '-') { + if (argv[i][2] == '\0') { // "--" signals the rest of the arguments are literal + used_args[i] = true; + i += 1; + break; + } + + for (int s = 0; s < spec_len; s++) { + const TypeInfo_t *non_opt_type = spec[s].type; + while (non_opt_type->tag == OptionalInfo) + non_opt_type = non_opt_type->OptionalInfo.type; + + if (non_opt_type == &Bool$info && strncmp(argv[i], "--no-", strlen("--no-")) == 0 + && strcmp(argv[i] + strlen("--no-"), spec[s].name) == 0) { + *(OptionalBool_t *)spec[s].dest = false; + populated_args[s] = true; + used_args[i] = true; + goto next_arg; + } + + if (strncmp(spec[s].name, argv[i] + 2, strlen(spec[s].name)) != 0) continue; + + char after_name = argv[i][2 + strlen(spec[s].name)]; + if (after_name == '\0') { // --foo val + used_args[i] = true; + if (non_opt_type->tag == ListInfo) { + int num_args = 0; + while (i + 1 + num_args < argc) { + if (argv[i + 1 + num_args][0] == '-') break; + used_args[i + 1 + num_args] = true; + num_args += 1; + } + populated_args[s] = true; + *(OptionalList_t *)spec[s].dest = + parse_list(non_opt_type->ListInfo.item, num_args, &argv[i + 1]); + } else if (non_opt_type->tag == TableInfo) { + int num_args = 0; + while (i + 1 + num_args < argc) { + if (argv[i + 1 + num_args][0] == '-' || !strchr(argv[i + 1 + num_args], '=')) break; + used_args[i + 1 + num_args] = true; + num_args += 1; + } + populated_args[s] = true; + *(OptionalTable_t *)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i + 1]); + } else if (non_opt_type == &Bool$info) { // --flag + populated_args[s] = true; + *(OptionalBool_t *)spec[s].dest = true; + } else { + if (i + 1 >= argc) print_err("Missing argument: ", argv[i], "\n", usage); + used_args[i + 1] = true; + populated_args[s] = parse_single_arg(spec[s].type, argv[i + 1], spec[s].dest); + if (!populated_args[s]) + print_err("Couldn't parse argument: ", argv[i], " ", argv[i + 1], "\n", usage); + } + goto next_arg; + } else if (after_name == '=') { // --foo=val + used_args[i] = true; + populated_args[s] = + parse_single_arg(spec[s].type, 2 + argv[i] + strlen(spec[s].name) + 1, spec[s].dest); + if (!populated_args[s]) print_err("Couldn't parse argument: ", argv[i], "\n", usage); + goto next_arg; + } else { + continue; + } + } + + if (streq(argv[i], "--help")) { + print(help); + exit(0); + } + if (streq(argv[i], "--version")) { + print(version); + exit(0); + } + print_err("Unrecognized argument: ", argv[i], "\n", usage); + } else if (argv[i][0] == '-' && argv[i][1] && argv[i][1] != '-' && !isdigit(argv[i][1])) { // Single flag args + used_args[i] = true; + for (char *f = argv[i] + 1; *f; f++) { + char flag[] = {'-', *f, 0}; + for (int s = 0; s < spec_len; s++) { + if (spec[s].name[0] != *f || strlen(spec[s].name) > 1) continue; + + const TypeInfo_t *non_opt_type = spec[s].type; + while (non_opt_type->tag == OptionalInfo) + non_opt_type = non_opt_type->OptionalInfo.type; + + if (f[1] == '=') { + populated_args[s] = parse_single_arg(spec[s].type, f + 2, spec[s].dest); + if (!populated_args[s]) print_err("Couldn't parse argument: ", argv[i], "\n", usage); + f += strlen(f) - 1; + } else if (non_opt_type->tag == ListInfo) { + if (f[1]) print_err("No value provided for ", flag, "\n", usage); + int num_args = 0; + while (i + 1 + num_args < argc) { + if (argv[i + 1 + num_args][0] == '-') break; + used_args[i + 1 + num_args] = true; + num_args += 1; + } + populated_args[s] = true; + *(OptionalList_t *)spec[s].dest = + parse_list(non_opt_type->ListInfo.item, num_args, &argv[i + 1]); + } else if (non_opt_type->tag == TableInfo) { + int num_args = 0; + while (i + 1 + num_args < argc) { + if (argv[i + 1 + num_args][0] == '-' || !strchr(argv[i + 1 + num_args], '=')) break; + used_args[i + 1 + num_args] = true; + num_args += 1; + } + populated_args[s] = true; + *(OptionalTable_t *)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i + 1]); + } else if (non_opt_type == &Bool$info) { // -f + populated_args[s] = true; + *(OptionalBool_t *)spec[s].dest = true; + } else if (is_numeric_type(non_opt_type) && (f[1] == '-' || f[1] == '.' || isdigit(f[1]))) { // -O3 + size_t len = strspn(f + 1, "-0123456789._"); + populated_args[s] = + parse_single_arg(spec[s].type, String(string_slice(f + 1, len)), spec[s].dest); + if (!populated_args[s]) print_err("Couldn't parse argument: ", argv[i], "\n", usage); + f += len; + } else { + if (f[1] || i + 1 >= argc) print_err("No value provided for ", flag, "\n", usage); + used_args[i + 1] = true; + populated_args[s] = parse_single_arg(spec[s].type, argv[i + 1], spec[s].dest); + if (!populated_args[s]) + print_err("Couldn't parse argument: ", argv[i], " ", argv[i + 1], "\n", usage); + } + goto next_flag; + } + + if (*f == 'h') { + print(help); + exit(0); + } + print_err("Unrecognized flag: ", flag, "\n", usage); + next_flag:; + } + } else { + // Handle positional args later + i += 1; + continue; + } + + next_arg: + while (used_args[i] && i < argc) + i += 1; + } + + // Get remaining positional arguments + bool ignore_dashes = false; + for (int i = 1, s = 0; i < argc; i++) { + if (!ignore_dashes && streq(argv[i], "--")) { + ignore_dashes = true; + continue; + } + if (used_args[i]) continue; + + while (populated_args[s]) { + next_non_bool_flag: + ++s; + if (s >= spec_len) print_err("Extra argument: ", argv[i], "\n", usage); + } + + const TypeInfo_t *non_opt_type = spec[s].type; + while (non_opt_type->tag == OptionalInfo) + non_opt_type = non_opt_type->OptionalInfo.type; + + // You can't specify boolean flags positionally + if (non_opt_type == &Bool$info) goto next_non_bool_flag; + + if (non_opt_type->tag == ListInfo) { + int num_args = 0; + while (i + num_args < argc) { + if (!ignore_dashes && (argv[i + num_args][0] == '-' && !isdigit(argv[i + num_args][1]))) break; + used_args[i + num_args] = true; + num_args += 1; + } + populated_args[s] = true; + *(OptionalList_t *)spec[s].dest = parse_list(non_opt_type->ListInfo.item, num_args, &argv[i]); + } else if (non_opt_type->tag == TableInfo) { + int num_args = 0; + while (i + num_args < argc) { + if ((argv[i + num_args][0] == '-' && !isdigit(argv[i + num_args][1])) + || !strchr(argv[i + num_args], '=')) + break; + used_args[i + num_args] = true; + num_args += 1; + } + populated_args[s] = true; + *(OptionalTable_t *)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i]); + } else { + populated_args[s] = parse_single_arg(spec[s].type, argv[i], spec[s].dest); + } + + if (!populated_args[s]) print_err("Invalid value for ", spec[s].name, ": ", argv[i], "\n", usage); + } + + for (int s = 0; s < spec_len; s++) { + if (!populated_args[s] && spec[s].required) { + if (spec[s].type->tag == ListInfo) *(OptionalList_t *)spec[s].dest = (List_t){}; + else if (spec[s].type->tag == TableInfo) *(OptionalTable_t *)spec[s].dest = (Table_t){}; + else print_err("The required argument '", spec[s].name, "' was not provided\n", usage); + } + } +} +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif diff --git a/src/stdlib/cli.h b/src/stdlib/cli.h new file mode 100644 index 00000000..a2058754 --- /dev/null +++ b/src/stdlib/cli.h @@ -0,0 +1,21 @@ +// Command line argument parsing + +#pragma once + +#include <stdbool.h> + +#include "datatypes.h" +#include "types.h" + +typedef struct { + const char *name; + bool required; + const TypeInfo_t *type; + void *dest; +} 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__}) diff --git a/src/stdlib/stdlib.c b/src/stdlib/stdlib.c index b215ac3b..bf36ca0d 100644 --- a/src/stdlib/stdlib.c +++ b/src/stdlib/stdlib.c @@ -1,12 +1,10 @@ // Built-in functions -#include <ctype.h> #include <errno.h> #include <execinfo.h> #include <fcntl.h> #include <gc.h> #include <locale.h> -#include <math.h> #include <signal.h> #include <stdbool.h> #include <stdint.h> @@ -15,18 +13,13 @@ #include <time.h> #include "../config.h" -#include "bools.h" #include "files.h" -#include "integers.h" #include "metamethods.h" -#include "nums.h" #include "optionals.h" -#include "paths.h" #include "print.h" #include "siphash.h" #include "stacktrace.h" #include "stdlib.h" -#include "tables.h" #include "text.h" #include "util.h" @@ -76,349 +69,6 @@ void tomo_init(void) { sigaction(SIGILL, &sigact, (struct sigaction *)NULL); } -static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest) { - if (!arg) return false; - - if (info->tag == OptionalInfo && streq(arg, "none")) return true; - - while (info->tag == OptionalInfo) - info = info->OptionalInfo.type; - - if (info == &Int$info) { - OptionalInt_t parsed = Int$from_str(arg); - if (parsed.small != 0) *(OptionalInt_t *)dest = parsed; - return parsed.small != 0; - } else if (info == &Int64$info) { - OptionalInt64_t parsed = Int64$parse(Text$from_str(arg), NULL); - if (!parsed.is_none) *(OptionalInt64_t *)dest = parsed; - return !parsed.is_none; - } else if (info == &Int32$info) { - OptionalInt32_t parsed = Int32$parse(Text$from_str(arg), NULL); - if (!parsed.is_none) *(OptionalInt32_t *)dest = parsed; - return !parsed.is_none; - } else if (info == &Int16$info) { - OptionalInt16_t parsed = Int16$parse(Text$from_str(arg), NULL); - if (!parsed.is_none) *(OptionalInt16_t *)dest = parsed; - return !parsed.is_none; - } else if (info == &Int8$info) { - OptionalInt8_t parsed = Int8$parse(Text$from_str(arg), NULL); - if (!parsed.is_none) *(OptionalInt8_t *)dest = parsed; - return !parsed.is_none; - } else if (info == &Bool$info) { - OptionalBool_t parsed = Bool$parse(Text$from_str(arg), NULL); - if (parsed != NONE_BOOL) *(OptionalBool_t *)dest = parsed; - return parsed != NONE_BOOL; - } else if (info == &Num$info) { - OptionalNum_t parsed = Num$parse(Text$from_str(arg), NULL); - if (!isnan(parsed)) *(OptionalNum_t *)dest = parsed; - return !isnan(parsed); - } else if (info == &Num32$info) { - OptionalNum32_t parsed = Num32$parse(Text$from_str(arg), NULL); - if (!isnan(parsed)) *(OptionalNum32_t *)dest = parsed; - return !isnan(parsed); - } else if (info == &Path$info) { - *(OptionalPath_t *)dest = Path$from_str(arg); - return true; - } else if (info->tag == TextInfo) { - *(OptionalText_t *)dest = Text$from_str(arg); - return true; - } else if (info->tag == EnumInfo) { - for (int t = 0; t < info->EnumInfo.num_tags; t++) { - NamedType_t named = info->EnumInfo.tags[t]; - size_t len = strlen(named.name); - if (strncmp(arg, named.name, len) == 0 && (arg[len] == '\0' || arg[len] == ':')) { - *(int32_t *)dest = (t + 1); - - // Simple tag (no associated data): - if (!named.type || (named.type->tag == StructInfo && named.type->StructInfo.num_fields == 0)) - return true; - - // Single-argument tag: - if (arg[len] != ':') print_err("Invalid value for ", t, ".", named.name, ": ", arg); - size_t offset = sizeof(int32_t); - if (named.type->align > 0 && offset % (size_t)named.type->align > 0) - offset += (size_t)named.type->align - (offset % (size_t)named.type->align); - if (!parse_single_arg(named.type, arg + len + 1, dest + offset)) return false; - return true; - } - } - print_err("Invalid value for ", info->EnumInfo.name, ": ", arg); - } else if (info->tag == StructInfo) { - if (info->StructInfo.num_fields == 0) return true; - else if (info->StructInfo.num_fields == 1) return parse_single_arg(info->StructInfo.fields[0].type, arg, dest); - - Text_t t = generic_as_text(NULL, false, info); - print_err("Unsupported multi-argument struct type for argument parsing: ", t); - } else if (info->tag == ListInfo) { - print_err("List arguments must be specified as `--flag ...` not `--flag=...`"); - } else if (info->tag == TableInfo) { - print_err("Table arguments must be specified as `--flag ...` not `--flag=...`"); - } else { - Text_t t = generic_as_text(NULL, false, info); - print_err("Unsupported type for argument parsing: ", t); - } - return false; -} - -static List_t parse_list(const TypeInfo_t *item_info, int n, char *args[]) { - int64_t padded_size = item_info->size; - if ((padded_size % item_info->align) > 0) - padded_size = padded_size + item_info->align - (padded_size % item_info->align); - - List_t items = { - .stride = padded_size, - .length = n, - .data = GC_MALLOC((size_t)(padded_size * n)), - }; - for (int i = 0; i < n; i++) { - bool success = parse_single_arg(item_info, args[i], items.data + items.stride * i); - if (!success) print_err("Couldn't parse argument: ", args[i]); - } - return items; -} - -// Arguments take the form key=value, with a guarantee that there is an '=' -static Table_t parse_table(const TypeInfo_t *table, int n, char *args[]) { - const TypeInfo_t *key = table->TableInfo.key, *value = table->TableInfo.value; - int64_t padded_size = key->size; - if ((padded_size % value->align) > 0) padded_size = padded_size + value->align - (padded_size % value->align); - int64_t value_offset = padded_size; - padded_size += value->size; - if ((padded_size % key->align) > 0) padded_size = padded_size + key->align - (padded_size % key->align); - - List_t entries = { - .stride = padded_size, - .length = n, - .data = GC_MALLOC((size_t)(padded_size * n)), - }; - for (int i = 0; i < n; i++) { - char *key_arg = args[i]; - char *equals = strchr(key_arg, '='); - assert(equals); - char *value_arg = equals + 1; - *equals = '\0'; - - bool success = parse_single_arg(key, key_arg, entries.data + entries.stride * i); - if (!success) print_err("Couldn't parse table key: ", key_arg); - - success = parse_single_arg(value, value_arg, entries.data + entries.stride * i + value_offset); - if (!success) print_err("Couldn't parse table value: ", value_arg); - - *equals = '='; - } - return Table$from_entries(entries, table); -} - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wstack-protector" -#endif -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 populated_args[spec_len]; - bool used_args[argc]; - memset(populated_args, 0, sizeof(populated_args)); - memset(used_args, 0, sizeof(used_args)); - for (int i = 1; i < argc;) { - if (argv[i][0] == '-' && argv[i][1] == '-') { - if (argv[i][2] == '\0') { // "--" signals the rest of the arguments are literal - used_args[i] = true; - i += 1; - break; - } - - for (int s = 0; s < spec_len; s++) { - const TypeInfo_t *non_opt_type = spec[s].type; - while (non_opt_type->tag == OptionalInfo) - non_opt_type = non_opt_type->OptionalInfo.type; - - if (non_opt_type == &Bool$info && strncmp(argv[i], "--no-", strlen("--no-")) == 0 - && strcmp(argv[i] + strlen("--no-"), spec[s].name) == 0) { - *(OptionalBool_t *)spec[s].dest = false; - populated_args[s] = true; - used_args[i] = true; - goto next_arg; - } - - if (strncmp(spec[s].name, argv[i] + 2, strlen(spec[s].name)) != 0) continue; - - char after_name = argv[i][2 + strlen(spec[s].name)]; - if (after_name == '\0') { // --foo val - used_args[i] = true; - if (non_opt_type->tag == ListInfo) { - int num_args = 0; - while (i + 1 + num_args < argc) { - if (argv[i + 1 + num_args][0] == '-') break; - used_args[i + 1 + num_args] = true; - num_args += 1; - } - populated_args[s] = true; - *(OptionalList_t *)spec[s].dest = - parse_list(non_opt_type->ListInfo.item, num_args, &argv[i + 1]); - } else if (non_opt_type->tag == TableInfo) { - int num_args = 0; - while (i + 1 + num_args < argc) { - if (argv[i + 1 + num_args][0] == '-' || !strchr(argv[i + 1 + num_args], '=')) break; - used_args[i + 1 + num_args] = true; - num_args += 1; - } - populated_args[s] = true; - *(OptionalTable_t *)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i + 1]); - } else if (non_opt_type == &Bool$info) { // --flag - populated_args[s] = true; - *(OptionalBool_t *)spec[s].dest = true; - } else { - if (i + 1 >= argc) print_err("Missing argument: ", argv[i], "\n", usage); - used_args[i + 1] = true; - populated_args[s] = parse_single_arg(spec[s].type, argv[i + 1], spec[s].dest); - if (!populated_args[s]) - print_err("Couldn't parse argument: ", argv[i], " ", argv[i + 1], "\n", usage); - } - goto next_arg; - } else if (after_name == '=') { // --foo=val - used_args[i] = true; - populated_args[s] = - parse_single_arg(spec[s].type, 2 + argv[i] + strlen(spec[s].name) + 1, spec[s].dest); - if (!populated_args[s]) print_err("Couldn't parse argument: ", argv[i], "\n", usage); - goto next_arg; - } else { - continue; - } - } - - if (streq(argv[i], "--help")) { - print(help); - exit(0); - } - if (streq(argv[i], "--version")) { - print(version); - exit(0); - } - print_err("Unrecognized argument: ", argv[i], "\n", usage); - } else if (argv[i][0] == '-' && argv[i][1] && argv[i][1] != '-' && !isdigit(argv[i][1])) { // Single flag args - used_args[i] = true; - for (char *f = argv[i] + 1; *f; f++) { - char flag[] = {'-', *f, 0}; - for (int s = 0; s < spec_len; s++) { - if (spec[s].name[0] != *f || strlen(spec[s].name) > 1) continue; - - const TypeInfo_t *non_opt_type = spec[s].type; - while (non_opt_type->tag == OptionalInfo) - non_opt_type = non_opt_type->OptionalInfo.type; - - if (non_opt_type->tag == ListInfo) { - if (f[1]) print_err("No value provided for ", flag, "\n", usage); - int num_args = 0; - while (i + 1 + num_args < argc) { - if (argv[i + 1 + num_args][0] == '-') break; - used_args[i + 1 + num_args] = true; - num_args += 1; - } - populated_args[s] = true; - *(OptionalList_t *)spec[s].dest = - parse_list(non_opt_type->ListInfo.item, num_args, &argv[i + 1]); - } else if (non_opt_type->tag == TableInfo) { - int num_args = 0; - while (i + 1 + num_args < argc) { - if (argv[i + 1 + num_args][0] == '-' || !strchr(argv[i + 1 + num_args], '=')) break; - used_args[i + 1 + num_args] = true; - num_args += 1; - } - populated_args[s] = true; - *(OptionalTable_t *)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i + 1]); - } else if (non_opt_type == &Bool$info) { // -f - populated_args[s] = true; - *(OptionalBool_t *)spec[s].dest = true; - } else { - if (f[1] || i + 1 >= argc) print_err("No value provided for ", flag, "\n", usage); - used_args[i + 1] = true; - populated_args[s] = parse_single_arg(spec[s].type, argv[i + 1], spec[s].dest); - if (!populated_args[s]) - print_err("Couldn't parse argument: ", argv[i], " ", argv[i + 1], "\n", usage); - } - goto next_flag; - } - - if (*f == 'h') { - print(help); - exit(0); - } - print_err("Unrecognized flag: ", flag, "\n", usage); - next_flag:; - } - } else { - // Handle positional args later - i += 1; - continue; - } - - next_arg: - while (used_args[i] && i < argc) - i += 1; - } - - // Get remaining positional arguments - bool ignore_dashes = false; - for (int i = 1, s = 0; i < argc; i++) { - if (!ignore_dashes && streq(argv[i], "--")) { - ignore_dashes = true; - continue; - } - if (used_args[i]) continue; - - while (populated_args[s]) { - next_non_bool_flag: - ++s; - if (s >= spec_len) print_err("Extra argument: ", argv[i], "\n", usage); - } - - const TypeInfo_t *non_opt_type = spec[s].type; - while (non_opt_type->tag == OptionalInfo) - non_opt_type = non_opt_type->OptionalInfo.type; - - // You can't specify boolean flags positionally - if (non_opt_type == &Bool$info) goto next_non_bool_flag; - - if (non_opt_type->tag == ListInfo) { - int num_args = 0; - while (i + num_args < argc) { - if (!ignore_dashes && (argv[i + num_args][0] == '-' && !isdigit(argv[i + num_args][1]))) break; - used_args[i + num_args] = true; - num_args += 1; - } - populated_args[s] = true; - *(OptionalList_t *)spec[s].dest = parse_list(non_opt_type->ListInfo.item, num_args, &argv[i]); - } else if (non_opt_type->tag == TableInfo) { - int num_args = 0; - while (i + num_args < argc) { - if ((argv[i + num_args][0] == '-' && !isdigit(argv[i + num_args][1])) - || !strchr(argv[i + num_args], '=')) - break; - used_args[i + num_args] = true; - num_args += 1; - } - populated_args[s] = true; - *(OptionalTable_t *)spec[s].dest = parse_table(non_opt_type, num_args, &argv[i]); - } else { - populated_args[s] = parse_single_arg(spec[s].type, argv[i], spec[s].dest); - } - - if (!populated_args[s]) print_err("Invalid value for ", spec[s].name, ": ", argv[i], "\n", usage); - } - - for (int s = 0; s < spec_len; s++) { - if (!populated_args[s] && spec[s].required) { - if (spec[s].type->tag == ListInfo) *(OptionalList_t *)spec[s].dest = (List_t){}; - else if (spec[s].type->tag == TableInfo) *(OptionalTable_t *)spec[s].dest = (Table_t){}; - else print_err("The required argument '", spec[s].name, "' was not provided\n", usage); - } - } -} -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - public _Noreturn void fail_text(Text_t message) { fail(message); } @@ -581,30 +231,6 @@ cleanup: } public -bool pop_flag(char **argv, int *i, const char *flag, Text_t *result) { - if (argv[*i][0] != '-' || argv[*i][1] != '-') { - return false; - } else if (streq(argv[*i] + 2, flag)) { - *result = EMPTY_TEXT; - argv[*i] = NULL; - *i += 1; - return true; - } else if (strncmp(argv[*i] + 2, "no-", 3) == 0 && streq(argv[*i] + 5, flag)) { - *result = Text("no"); - argv[*i] = NULL; - *i += 1; - return true; - } else if (strncmp(argv[*i] + 2, flag, strlen(flag)) == 0 && argv[*i][2 + strlen(flag)] == '=') { - *result = Text$from_str(argv[*i] + 2 + strlen(flag) + 1); - argv[*i] = NULL; - *i += 1; - return true; - } else { - return false; - } -} - -public void sleep_num(double seconds) { struct timespec ts; ts.tv_sec = (time_t)seconds; diff --git a/src/stdlib/stdlib.h b/src/stdlib/stdlib.h index bbf8a43a..9bae5a6c 100644 --- a/src/stdlib/stdlib.h +++ b/src/stdlib/stdlib.h @@ -5,7 +5,7 @@ #include <signal.h> #include <stdbool.h> #include <stdint.h> -#include <stdio.h> +#include <stdio.h> // IWYU pragma: export #include "datatypes.h" #include "print.h" @@ -15,19 +15,7 @@ extern bool USE_COLOR; extern Text_t TOMO_VERSION_TEXT; -typedef struct { - const char *name; - bool required; - const TypeInfo_t *type; - void *dest; -} cli_arg_t; - void tomo_init(void); -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__}) #define fail(...) \ ({ \ @@ -95,7 +83,6 @@ Text_t ask(Text_t prompt, bool bold, bool force_tty); _Noreturn void tomo_exit(Text_t text, int32_t status); Closure_t spawn(Closure_t fn); -bool pop_flag(char **argv, int *i, const char *flag, Text_t *result); void sleep_num(double seconds); OptionalText_t getenv_text(Text_t name); void setenv_text(Text_t name, Text_t value); diff --git a/src/stdlib/tomo.h b/src/stdlib/tomo.h index ff16bee1..1ff065b9 100644 --- a/src/stdlib/tomo.h +++ b/src/stdlib/tomo.h @@ -11,6 +11,7 @@ #include "bools.h" // IWYU pragma: export #include "bytes.h" // IWYU pragma: export #include "c_strings.h" // IWYU pragma: export +#include "cli.h" // IWYU pragma: export #include "datatypes.h" // IWYU pragma: export #include "enums.h" // IWYU pragma: export #include "files.h" // IWYU pragma: export @@ -319,7 +319,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); @@ -445,7 +445,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) { @@ -767,7 +767,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); } @@ -806,7 +806,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); } @@ -822,7 +822,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) { @@ -862,6 +862,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 5b7a453d..9e808111 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))); @@ -1400,7 +1401,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); @@ -1618,7 +1619,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; |
