diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2024-09-11 14:53:48 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2024-09-11 14:53:48 -0400 |
| commit | db0d5a1c204fb48afa5e5a53cb3c703590645f8f (patch) | |
| tree | e559f108bb3c198faa3863a32cbfe3402a3f4563 | |
| parent | 0b5bb32912cfc68c7783548006fca2dc5874eb93 (diff) | |
Change *:from_text() methods to return optional values and set up CLI
parsing to use that approach
| -rw-r--r-- | builtins/bool.c | 8 | ||||
| -rw-r--r-- | builtins/bool.h | 3 | ||||
| -rw-r--r-- | builtins/integers.c | 26 | ||||
| -rw-r--r-- | builtins/integers.h | 8 | ||||
| -rw-r--r-- | builtins/nums.c | 16 | ||||
| -rw-r--r-- | builtins/nums.h | 6 | ||||
| -rw-r--r-- | compile.c | 177 | ||||
| -rw-r--r-- | environment.c | 16 | ||||
| -rw-r--r-- | repl.c | 10 | ||||
| -rw-r--r-- | typecheck.c | 3 |
10 files changed, 150 insertions, 123 deletions
diff --git a/builtins/bool.c b/builtins/bool.c index 63eb73f9..ce27ce79 100644 --- a/builtins/bool.c +++ b/builtins/bool.c @@ -9,6 +9,7 @@ #include <sys/param.h> #include "bool.h" +#include "optionals.h" #include "text.h" #include "types.h" #include "util.h" @@ -23,23 +24,20 @@ PUREFUNC public Text_t Bool$as_text(const bool *b, bool colorize, const TypeInfo return *b ? Text("yes") : Text("no"); } -public Bool_t Bool$from_text(Text_t text, bool *success) +PUREFUNC public OptionalBool_t Bool$from_text(Text_t text) { if (Text$equal_ignoring_case(text, Text("yes")) || Text$equal_ignoring_case(text, Text("on")) || Text$equal_ignoring_case(text, Text("true")) || Text$equal_ignoring_case(text, Text("1"))) { - if (success) *success = yes; return yes; } else if (Text$equal_ignoring_case(text, Text("no")) || Text$equal_ignoring_case(text, Text("off")) || Text$equal_ignoring_case(text, Text("false")) || Text$equal_ignoring_case(text, Text("0"))) { - if (success) *success = yes; return no; } else { - if (success) *success = no; - return no; + return NULL_BOOL; } } diff --git a/builtins/bool.h b/builtins/bool.h index 5f222c49..d6e5307a 100644 --- a/builtins/bool.h +++ b/builtins/bool.h @@ -7,6 +7,7 @@ #include <stdint.h> #include "types.h" +#include "optionals.h" #include "util.h" #define Bool_t bool @@ -14,7 +15,7 @@ #define no (Bool_t)false PUREFUNC Text_t Bool$as_text(const bool *b, bool colorize, const TypeInfo *type); -bool Bool$from_text(Text_t text, bool *success); +OptionalBool_t Bool$from_text(Text_t text); Bool_t Bool$random(double p); extern const TypeInfo Bool$info; diff --git a/builtins/integers.c b/builtins/integers.c index 31e32f3d..9160bfca 100644 --- a/builtins/integers.c +++ b/builtins/integers.c @@ -10,6 +10,7 @@ #include "array.h" #include "datatypes.h" #include "integers.h" +#include "optionals.h" #include "siphash.h" #include "text.h" #include "types.h" @@ -343,7 +344,7 @@ public PUREFUNC Range_t Int$to(Int_t from, Int_t to) { return (Range_t){from, to, Int$compare_value(to, from) >= 0 ? (Int_t){.small=(1<<2)|1} : (Int_t){.small=(-1>>2)|1}}; } -public Int_t Int$from_str(const char *str, bool *success) { +public Int_t Int$from_str(const char *str) { mpz_t i; int result; if (strncmp(str, "0x", 2) == 0) { @@ -355,12 +356,13 @@ public Int_t Int$from_str(const char *str, bool *success) { } else { result = mpz_init_set_str(i, str, 10); } - if (success) *success = (result == 0); + if (result != 0) + return NULL_INT; return Int$from_mpz(i); } -public Int_t Int$from_text(Text_t text, bool *success) { - return Int$from_str(Text$as_c_string(text), success); +public OptionalInt_t Int$from_text(Text_t text) { + return Int$from_str(Text$as_c_string(text)); } public bool Int$is_prime(Int_t x, Int_t reps) @@ -458,20 +460,16 @@ public const TypeInfo Int$info = { public Range_t KindOfInt ## $to(c_type from, c_type to) { \ return (Range_t){Int64_to_Int(from), Int64_to_Int(to), to >= from ? (Int_t){.small=(1<<2)&1} : (Int_t){.small=(1<<2)&1}}; \ } \ - public PUREFUNC c_type KindOfInt ## $from_text(Text_t text, bool *success) { \ - bool parsed_int = false; \ - Int_t full_int = Int$from_text(text, &parsed_int); \ - if (!parsed_int && success) *success = false; \ + public PUREFUNC Optional ## KindOfInt ## _t KindOfInt ## $from_text(Text_t text) { \ + OptionalInt_t full_int = Int$from_text(text); \ + if (full_int.small == 0) return (Optional ## KindOfInt ## _t){.is_null=true}; \ if (Int$compare_value(full_int, I(min_val)) < 0) { \ - if (success) *success = false; \ - return min_val; \ + return (Optional ## KindOfInt ## _t){.is_null=true}; \ } \ if (Int$compare_value(full_int, I(max_val)) > 0) { \ - if (success) *success = false; \ - return max_val; \ + return (Optional ## KindOfInt ## _t){.is_null=true}; \ } \ - if (success && parsed_int) *success = true; \ - return Int_to_ ## KindOfInt(full_int, true); \ + return (Optional ## KindOfInt ## _t){.i=Int_to_ ## KindOfInt(full_int, true)}; \ } \ public const c_type KindOfInt##$min = min_val; \ public const c_type KindOfInt##$max = max_val; \ diff --git a/builtins/integers.h b/builtins/integers.h index 22dc4f7c..4f3ebe51 100644 --- a/builtins/integers.h +++ b/builtins/integers.h @@ -37,7 +37,7 @@ Array_t type_name ## $bits(c_type x); \ c_type type_name ## $random(c_type min, c_type max); \ Range_t type_name ## $to(c_type from, c_type to); \ - PUREFUNC c_type type_name ## $from_text(Text_t text, bool *success); \ + PUREFUNC Optional ## type_name ## _t type_name ## $from_text(Text_t text); \ PUREFUNC static inline c_type type_name ## $clamped(c_type x, c_type min, c_type max) { \ return x < min ? min : (x > max ? max : x); \ } \ @@ -79,6 +79,8 @@ DEFINE_INT_TYPE(int8_t, Int8, 8) #define Int16$abs(...) I16(abs(__VA_ARGS__)) #define Int8$abs(...) I8(abs(__VA_ARGS__)) +#define OptionalInt_t Int_t + Text_t Int$as_text(const Int_t *i, bool colorize, const TypeInfo *type); PUREFUNC uint64_t Int$hash(const Int_t *x, const TypeInfo *type); PUREFUNC int32_t Int$compare(const Int_t *x, const Int_t *y, const TypeInfo *type); @@ -91,8 +93,8 @@ Text_t Int$octal(Int_t i, Int_t digits, bool prefix); void Int$init_random(long seed); Int_t Int$random(Int_t min, Int_t max); PUREFUNC Range_t Int$to(Int_t from, Int_t to); -Int_t Int$from_str(const char *str, bool *success); -Int_t Int$from_text(Text_t text, bool *success); +OptionalInt_t Int$from_str(const char *str); +OptionalInt_t Int$from_text(Text_t text); Int_t Int$abs(Int_t x); Int_t Int$power(Int_t base, Int_t exponent); Int_t Int$sqrt(Int_t i); diff --git a/builtins/nums.c b/builtins/nums.c index 90eb75ce..cfa14191 100644 --- a/builtins/nums.c +++ b/builtins/nums.c @@ -66,12 +66,14 @@ public CONSTFUNC double Num$mix(double amount, double x, double y) { return (1.0-amount)*x + amount*y; } -public double Num$from_text(Text_t text, bool *success) { +public OptionalNum_t Num$from_text(Text_t text) { const char *str = Text$as_c_string(text); char *end = NULL; double d = strtod(str, &end); - if (success) *success = (end > str && end[0] == '\0'); - return d; + if (end > str && end[0] == '\0') + return d; + else + return nan("null"); } public double Num$nan(Text_t tag) { @@ -145,12 +147,14 @@ public CONSTFUNC float Num32$mix(float amount, float x, float y) { return (1.0f-amount)*x + amount*y; } -public float Num32$from_text(Text_t text, bool *success) { +public OptionalNum32_t Num32$from_text(Text_t text) { const char *str = Text$as_c_string(text); char *end = NULL; double d = strtod(str, &end); - if (success) *success = (end > str && end[0] == '\0'); - return (float)d; + if (end > str && end[0] == '\0') + return d; + else + return nan("null"); } public float Num32$nan(Text_t tag) { diff --git a/builtins/nums.h b/builtins/nums.h index 2ac942f4..51f65c57 100644 --- a/builtins/nums.h +++ b/builtins/nums.h @@ -12,6 +12,8 @@ #define Num_t double #define Num32_t float +#define OptionalNum_t double +#define OptionalNum32_t float #define N32(n) ((float)n) #define N64(n) ((double)n) @@ -28,7 +30,7 @@ CONSTFUNC bool Num$isnan(double n); double Num$nan(Text_t tag); double Num$random(void); CONSTFUNC double Num$mix(double amount, double x, double y); -double Num$from_text(Text_t text, bool *success); +OptionalNum_t Num$from_text(Text_t text); CONSTFUNC static inline double Num$clamped(double x, double low, double high) { return (x <= low) ? low : (x >= high ? high : x); } @@ -46,7 +48,7 @@ CONSTFUNC bool Num32$finite(float n); CONSTFUNC bool Num32$isnan(float n); float Num32$random(void); CONSTFUNC float Num32$mix(float amount, float x, float y); -float Num32$from_text(Text_t text, bool *success); +OptionalNum32_t Num32$from_text(Text_t text); float Num32$nan(Text_t tag); CONSTFUNC static inline float Num32$clamped(float x, float low, float high) { return (x <= low) ? low : (x >= high ? high : x); @@ -24,6 +24,7 @@ static CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg static CORD compile_maybe_incref(env_t *env, ast_t *ast); static CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target); static CORD promote_to_optional(type_t *t, CORD code); +static CORD compile_null(type_t *t); CORD promote_to_optional(type_t *t, CORD code) { @@ -787,7 +788,7 @@ CORD compile_statement(env_t *env, ast_t *ast) "}\n"); env->code->funcs = CORD_cat(env->code->funcs, wrapper); } else if (fndef->cache && fndef->cache->tag == Int) { - int64_t cache_size = Int64$from_text(Text$from_str(Match(fndef->cache, Int)->str), NULL); + OptionalInt64_t cache_size = Int64$from_text(Text$from_str(Match(fndef->cache, Int)->str)); const char *arg_type_name = heap_strf("%s$args", Match(fndef->name, Var)->name); ast_t *args_def = FakeAST(StructDef, .name=arg_type_name, .fields=fndef->args); prebind_statement(env, args_def); @@ -801,8 +802,8 @@ CORD compile_statement(env_t *env, ast_t *ast) all_args = CORD_all(all_args, "$", arg->name, arg->next ? ", " : CORD_EMPTY); CORD pop_code = CORD_EMPTY; - if (fndef->cache->tag == Int && cache_size > 0) { - pop_code = CORD_all("if (cache.entries.length > ", CORD_asprintf("%ld", cache_size), + if (fndef->cache->tag == Int && !cache_size.is_null && cache_size.i > 0) { + pop_code = CORD_all("if (cache.entries.length > ", CORD_asprintf("%ld", cache_size.i), ") Table$remove(&cache, cache.entries.data + cache.entries.stride*Int$random(I(0), I(cache.entries.length-1)), table_type);\n"); } @@ -1158,7 +1159,9 @@ CORD compile_statement(env_t *env, ast_t *ast) CORD n; if (for_->iter->tag == Int) { const char *str = Match(for_->iter, Int)->str; - Int_t int_val = Int$from_str(str, NULL); + Int_t int_val = Int$from_str(str); + if (int_val.small == 0) + code_err(for_->iter, "Failed to parse this integer"); mpz_t i; mpz_init_set_int(i, int_val); if (mpz_cmpabs_ui(i, BIGGEST_SMALL_INT) <= 0) @@ -1467,7 +1470,10 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target) } int64_t target_bits = (int64_t)Match(target, IntType)->bits; - Int_t int_val = Int$from_str(Match(ast, Int)->str, NULL); + OptionalInt_t int_val = Int$from_str(Match(ast, Int)->str); + if (int_val.small == 0) + code_err(ast, "Failed to parse this integer"); + mpz_t i; mpz_init_set_int(i, int_val); @@ -1508,7 +1514,9 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t 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) { - Int_t int_val = Int$from_str(Match(call_arg->value, Int)->str, NULL); + 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"); double n = Int_to_Num(int_val); value = CORD_asprintf(Match(spec_arg->type, NumType)->bits == TYPE_NBITS64 ? "N64(%.20g)" : "N32(%.10g)", n); @@ -1535,7 +1543,9 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t 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) { - Int_t int_val = Int$from_str(Match(call_arg->value, Int)->str, NULL); + 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"); double n = Int_to_Num(int_val); value = CORD_asprintf(Match(spec_arg->type, NumType)->bits == TYPE_NBITS64 ? "N64(%.20g)" : "N32(%.10g)", n); @@ -1687,42 +1697,47 @@ static bool string_literal_is_all_ascii(CORD literal) return true; } +CORD compile_null(type_t *t) +{ + if (t == THREAD_TYPE) return "NULL"; + + switch (t->tag) { + case BigIntType: return "NULL_INT"; + case IntType: { + switch (Match(t, IntType)->bits) { + case TYPE_IBITS8: return "NULL_INT8"; + case TYPE_IBITS16: return "NULL_INT16"; + case TYPE_IBITS32: return "NULL_INT32"; + case TYPE_IBITS64: return "NULL_INT64"; + default: errx(1, "Invalid integer bit size"); + } + break; + } + case BoolType: return "NULL_BOOL"; + case ArrayType: return "NULL_ARRAY"; + case TableType: return "NULL_TABLE"; + case SetType: return "NULL_TABLE"; + case ChannelType: return "NULL"; + case TextType: return "NULL_TEXT"; + case CStringType: return "NULL"; + case PointerType: return CORD_all("((", compile_type(t), ")NULL)"); + case ClosureType: return "NULL_CLOSURE"; + case NumType: return "nan(\"null\")"; + case StructType: return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.is_null=true})"); + case EnumType: { + env_t *enum_env = Match(t, EnumType)->env; + return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env->libname, enum_env->namespace), "null})"); + } + default: compiler_err(NULL, NULL, NULL, "Null isn't implemented for this type: %T", t); + } +} + CORD compile(env_t *env, ast_t *ast) { switch (ast->tag) { case Null: { type_t *t = parse_type_ast(env, Match(ast, Null)->type); - if (t == THREAD_TYPE) return "NULL"; - - switch (t->tag) { - case BigIntType: return "NULL_INT"; - case IntType: { - switch (Match(t, IntType)->bits) { - case TYPE_IBITS8: return "NULL_INT8"; - case TYPE_IBITS16: return "NULL_INT16"; - case TYPE_IBITS32: return "NULL_INT32"; - case TYPE_IBITS64: return "NULL_INT64"; - default: errx(1, "Invalid integer bit size"); - } - break; - } - case BoolType: return "NULL_BOOL"; - case ArrayType: return "NULL_ARRAY"; - case TableType: return "NULL_TABLE"; - case SetType: return "NULL_TABLE"; - case ChannelType: return "NULL"; - case TextType: return "NULL_TEXT"; - case CStringType: return "NULL"; - case PointerType: return CORD_all("((", compile_type(t), ")NULL)"); - case ClosureType: return "NULL_CLOSURE"; - case NumType: return "nan(\"null\")"; - case StructType: return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.is_null=true})"); - case EnumType: { - env_t *enum_env = Match(t, EnumType)->env; - return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env->libname, enum_env->namespace), "null})"); - } - default: code_err(ast, "Null isn't implemented for this type: %T", t); - } + return compile_null(t); } case Bool: return Match(ast, Bool)->b ? "yes" : "no"; case Var: { @@ -1734,7 +1749,9 @@ CORD compile(env_t *env, ast_t *ast) } case Int: { const char *str = Match(ast, Int)->str; - Int_t int_val = Int$from_str(str, NULL); + OptionalInt_t int_val = Int$from_str(str); + if (int_val.small == 0) + code_err(ast, "Failed to parse this integer"); mpz_t i; mpz_init_set_int(i, int_val); @@ -3317,10 +3334,11 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) for (arg_t *arg = fn_info->args; arg; arg = arg->next) { type_t *t = get_arg_type(main_env, arg); assert(arg->name); + type_t *optional = t->tag == OptionalType ? t : Type(OptionalType, .type=t); + type_t *non_optional = t->tag == OptionalType ? Match(t, OptionalType)->type : t; code = CORD_all( - code, compile_declaration(t, CORD_cat("$", arg->name)), ";\n", - "bool ", arg->name, "$is_set = no;\n"); - set_binding(env, arg->name, new(binding_t, .type=t, .code=CORD_cat("$", arg->name))); + code, compile_declaration(optional, CORD_cat("$", arg->name)), " = ", compile_null(non_optional), ";\n"); + set_binding(main_env, arg->name, new(binding_t, .type=optional, .code=CORD_cat("$", arg->name))); } // Provide --flags: code = CORD_all(code, "Text_t flag;\n" @@ -3341,17 +3359,17 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) for (arg_t *arg = fn_info->args; arg; arg = arg->next) { type_t *t = get_arg_type(main_env, arg); + type_t *non_optional = t->tag == OptionalType ? Match(t, OptionalType)->type : t; CORD flag = CORD_replace(arg->name, "_", "-"); - switch (t->tag) { + switch (non_optional->tag) { case BoolType: { code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &flag)) {\n" "if (flag.length != 0) {\n", - "$", arg->name, " = Bool$from_text(flag, &", arg->name, "$is_set", ");\n" - "if (!", arg->name, "$is_set) \n" + "$", arg->name, " = Bool$from_text(flag);\n" + "if (!", compile_optional_check(main_env, FakeAST(Var, arg->name)), ") \n" "USAGE_ERR(\"Invalid argument for '--", flag, "'\");\n", "} else {\n", "$", arg->name, " = yes;\n", - arg->name, "$is_set = yes;\n" "}\n" "}\n"); break; @@ -3359,7 +3377,6 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) case TextType: { code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &flag)) {\n", "$", arg->name, " = ", streq(Match(t, TextType)->lang, "Path") ? "Path$cleanup(flag)" : "flag",";\n", - arg->name, "$is_set = yes;\n" "}\n"); break; } @@ -3367,8 +3384,7 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) if (Match(t, ArrayType)->item_type->tag != TextType) compiler_err(NULL, NULL, NULL, "Main function has unsupported argument type: %T (only arrays of Text are supported)", t); code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &flag)) {\n", - "$", arg->name, " = Text$split(flag, Pattern(\",\"));\n", - arg->name, "$is_set = yes;\n"); + "$", arg->name, " = Text$split(flag, Pattern(\",\"));\n"); if (streq(Match(Match(t, ArrayType)->item_type, TextType)->lang, "Path")) { code = CORD_all(code, "for (int64_t j = 0; j < $", arg->name, ".length; j++)\n" "*(Path_t*)($", arg->name, ".data + j*$", arg->name, ".stride) " @@ -3378,12 +3394,12 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) break; } case BigIntType: case IntType: case NumType: { - CORD type_name = type_to_cord(t); + CORD type_name = type_to_cord(non_optional); code = CORD_all(code, "else if (pop_flag(argv, &i, \"", flag, "\", &flag)) {\n", "if (flag.length == 0)\n" "USAGE_ERR(\"No value provided for '--", flag, "'\");\n" - "$", arg->name, " = ", type_name, "$from_text(flag, &", arg->name, "$is_set);\n" - "if (!", arg->name, "$is_set)\n" + "$", arg->name, " = ", type_name, "$from_text(flag);\n" + "if (!", compile_optional_check(main_env, FakeAST(Var, arg->name)), ")\n" "USAGE_ERR(\"Invalid value provided for '--", flag, "'\");\n", "}\n"); break; @@ -3401,7 +3417,6 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) code = CORD_all(code, "if (Text$equal_ignoring_case(flag, Text(\"", tag->name, "\"))) {\n" "$", arg->name, " = ", b->code, ";\n", - arg->name, "$is_set = yes;\n" "} else "); } code = CORD_all(code, "USAGE_ERR(\"Invalid value provided for '--", flag, "', valid values are: ", @@ -3424,17 +3439,18 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) "++i;\n"); for (arg_t *arg = fn_info->args; arg; arg = arg->next) { - type_t *t = get_arg_type(env, arg); - code = CORD_all(code, "if (!", arg->name, "$is_set) {\n"); - if (t->tag == ArrayType) { - if (Match(t, ArrayType)->item_type->tag != TextType) - compiler_err(NULL, NULL, NULL, "Main function has unsupported argument type: %T (only arrays of Text are supported)", t); + type_t *t = get_arg_type(main_env, arg); + type_t *non_optional = t->tag == OptionalType ? Match(t, OptionalType)->type : t; + code = CORD_all(code, "if (!", compile_optional_check(main_env, FakeAST(Var, arg->name)), ") {\n"); + if (non_optional->tag == ArrayType) { + if (Match(non_optional, ArrayType)->item_type->tag != TextType) + compiler_err(NULL, NULL, NULL, "Main function has unsupported argument type: %T (only arrays of Text are supported)", non_optional); code = CORD_all( code, "$", arg->name, " = (Array_t){};\n" "for (; i < argc; i++) {\n" "if (argv[i]) {\n"); - if (streq(Match(Match(t, ArrayType)->item_type, TextType)->lang, "Path")) { + if (streq(Match(Match(non_optional, ArrayType)->item_type, TextType)->lang, "Path")) { code = CORD_all(code, "Path_t arg = Path$cleanup(Text$from_str(argv[i]));\n"); } else { code = CORD_all(code, "Text_t arg = Text$from_str(argv[i]);\n"); @@ -3442,49 +3458,50 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) code = CORD_all(code, "Array$insert(&$", arg->name, ", &arg, I(0), sizeof(Text_t));\n" "argv[i] = NULL;\n" "}\n" - "}\n", - arg->name, "$is_set = yes;\n"); + "}\n"); } else if (arg->default_val) { - code = CORD_all(code, "$", arg->name, " = ", compile(env, arg->default_val), ";\n"); + code = CORD_all(code, "$", arg->name, " = ", compile(main_env, arg->default_val), ";\n"); } else { code = CORD_all( code, "if (i < argc) {"); - if (t->tag == TextType) { + if (non_optional->tag == TextType) { code = CORD_all(code, "$", arg->name, " = Text$from_str(argv[i]);\n"); - if (streq(Match(t, TextType)->lang, "Path")) + if (streq(Match(non_optional, TextType)->lang, "Path")) code = CORD_all(code, "$", arg->name, " = Path$cleanup($", arg->name, ");\n"); - } else if (t->tag == EnumType) { - env_t *enum_env = Match(t, EnumType)->env; - for (tag_t *tag = Match(t, EnumType)->tags; tag; tag = tag->next) { + } else if (non_optional->tag == EnumType) { + env_t *enum_env = Match(non_optional, EnumType)->env; + for (tag_t *tag = Match(non_optional, EnumType)->tags; tag; tag = tag->next) { if (tag->type && Match(tag->type, StructType)->fields) compiler_err(NULL, NULL, NULL, - "The type %T has enum fields with member values, which is not yet supported for command line arguments."); + "The type %T has enum fields with member values, which is not yet supported for command line arguments.", + non_optional); binding_t *b = get_binding(enum_env, tag->name); code = CORD_all(code, "if (strcasecmp(argv[i], \"", tag->name, "\") == 0) {\n" "$", arg->name, " = ", b->code, ";\n", - arg->name, "$is_set = yes;\n" "} else "); } code = CORD_all(code, "USAGE_ERR(\"Invalid value provided for '--", arg->name, "', valid values are: ", - get_flag_options(t, ", "), "\");\n"); + get_flag_options(non_optional, ", "), "\");\n"); } else { code = CORD_all( code, - "bool success = false;\n", - "$", arg->name, " = ", type_to_cord(t), "$from_text(Text$from_str(argv[i]), &success)", ";\n" - "if (!success)\n" - "USAGE_ERR(\"Unable to parse this argument as a ", type_to_cord(t), ": %s\", argv[i]);\n"); + "$", arg->name, " = ", type_to_cord(non_optional), "$from_text(Text$from_str(argv[i]))", ";\n" + "if (!", compile_optional_check(main_env, FakeAST(Var, arg->name)), ")\n" + "USAGE_ERR(\"Unable to parse this argument as a ", type_to_cord(non_optional), ": %s\", argv[i]);\n"); } code = CORD_all( code, "argv[i++] = NULL;\n" "while (i < argc && argv[i] == NULL)\n" - "++i;\n} else {\n" - "USAGE_ERR(\"Required argument '", arg->name, "' was not provided!\");\n", - "}\n"); + "++i;\n"); + if (t->tag != OptionalType) { + code = CORD_all(code, "} else {\n" + "USAGE_ERR(\"Required argument '", arg->name, "' was not provided!\");\n"); + } + code = CORD_all(code, "}\n"); } code = CORD_all(code, "}\n"); } @@ -3495,7 +3512,11 @@ CORD compile_cli_arg_call(env_t *env, CORD fn_name, type_t *fn_type) code = CORD_all(code, fn_name, "("); for (arg_t *arg = fn_info->args; arg; arg = arg->next) { - code = CORD_all(code, "$", arg->name); + if (arg->type->tag == OptionalType) + code = CORD_all(code, "$", arg->name); + else + code = CORD_all(code, optional_var_into_nonnull(get_binding(main_env, arg->name))); + if (arg->next) code = CORD_all(code, ", "); } code = CORD_all(code, ");\n"); diff --git a/environment.c b/environment.c index 10b95f8e..d28e509d 100644 --- a/environment.c +++ b/environment.c @@ -95,7 +95,7 @@ env_t *new_compilation_unit(CORD *libname) {"Void", Type(VoidType), "Void_t", "Void$info", {}}, {"Memory", Type(MemoryType), "Memory_t", "Memory$info", {}}, {"Bool", Type(BoolType), "Bool_t", "Bool$info", TypedArray(ns_entry_t, - {"from_text", "Bool$from_text", "func(text:Text, success=!&Bool)->Bool"}, + {"from_text", "Bool$from_text", "func(text:Text)->Bool?"}, {"random", "Bool$random", "func(p=0.5)->Bool"}, )}, {"Int", Type(BigIntType), "Int_t", "Int$info", TypedArray(ns_entry_t, @@ -106,7 +106,7 @@ env_t *new_compilation_unit(CORD *libname) {"clamped", "Int$clamped", "func(x:Int,low:Int,high:Int)->Int"}, {"divided_by", "Int$divided_by", "func(x:Int,y:Int)->Int"}, {"format", "Int$format", "func(i:Int, digits=0)->Text"}, - {"from_text", "Int$from_text", "func(text:Text, success=!&Bool)->Int"}, + {"from_text", "Int$from_text", "func(text:Text)->Int?"}, {"hex", "Int$hex", "func(i:Int, digits=0, uppercase=yes, prefix=yes)->Text"}, {"is_prime", "Int$is_prime", "func(x:Int,reps=50)->Bool"}, {"left_shifted", "Int$left_shifted", "func(x:Int,y:Int)->Int"}, @@ -132,7 +132,7 @@ env_t *new_compilation_unit(CORD *libname) {"clamped", "Int64$clamped", "func(x:Int64,low:Int64,high:Int64)->Int64"}, {"divided_by", "Int64$divided_by", "func(x:Int64,y:Int64)->Int64"}, {"format", "Int64$format", "func(i:Int64, digits=0)->Text"}, - {"from_text", "Int64$from_text", "func(text:Text, success=!&Bool)->Int64"}, + {"from_text", "Int64$from_text", "func(text:Text)->Int64?"}, {"hex", "Int64$hex", "func(i:Int64, digits=0, uppercase=yes, prefix=yes)->Text"}, {"max", "Int64$max", "Int64"}, {"min", "Int64$min", "Int64"}, @@ -148,7 +148,7 @@ env_t *new_compilation_unit(CORD *libname) {"clamped", "Int32$clamped", "func(x:Int32,low:Int32,high:Int32)->Int32"}, {"divided_by", "Int32$divided_by", "func(x:Int32,y:Int32)->Int32"}, {"format", "Int32$format", "func(i:Int32, digits=0)->Text"}, - {"from_text", "Int32$from_text", "func(text:Text, success=!&Bool)->Int32"}, + {"from_text", "Int32$from_text", "func(text:Text)->Int32?"}, {"hex", "Int32$hex", "func(i:Int32, digits=0, uppercase=yes, prefix=yes)->Text"}, {"max", "Int32$max", "Int32"}, {"min", "Int32$min", "Int32"}, @@ -164,7 +164,7 @@ env_t *new_compilation_unit(CORD *libname) {"clamped", "Int16$clamped", "func(x:Int16,low:Int16,high:Int16)->Int16"}, {"divided_by", "Int16$divided_by", "func(x:Int16,y:Int16)->Int16"}, {"format", "Int16$format", "func(i:Int16, digits=0)->Text"}, - {"from_text", "Int16$from_text", "func(text:Text, success=!&Bool)->Int16"}, + {"from_text", "Int16$from_text", "func(text:Text)->Int16?"}, {"hex", "Int16$hex", "func(i:Int16, digits=0, uppercase=yes, prefix=yes)->Text"}, {"max", "Int16$max", "Int16"}, {"min", "Int16$min", "Int16"}, @@ -180,7 +180,7 @@ env_t *new_compilation_unit(CORD *libname) {"clamped", "Int8$clamped", "func(x:Int8,low:Int8,high:Int8)->Int8"}, {"divided_by", "Int8$divided_by", "func(x:Int8,y:Int8)->Int8"}, {"format", "Int8$format", "func(i:Int8, digits=0)->Text"}, - {"from_text", "Int8$from_text", "func(text:Text, success=!&Bool)->Int8"}, + {"from_text", "Int8$from_text", "func(text:Text)->Int8?"}, {"hex", "Int8$hex", "func(i:Int8, digits=0, uppercase=yes, prefix=yes)->Text"}, {"max", "Int8$max", "Int8"}, {"min", "Int8$min", "Int8"}, @@ -208,7 +208,7 @@ env_t *new_compilation_unit(CORD *libname) {"TAU", "(Num_t)(2.*M_PI)", "Num"}, {"random", "Num$random", "func()->Num"}, {"mix", "Num$mix", "func(amount:Num, x:Num, y:Num)->Num"}, - {"from_text", "Num$from_text", "func(text:Text, success=!&Bool)->Num"}, + {"from_text", "Num$from_text", "func(text:Text)->Num?"}, {"abs", "fabs", "func(n:Num)->Num"}, F(acos), F(acosh), F(asin), F(asinh), F(atan), F(atanh), F(cbrt), F(ceil), F(cos), F(cosh), F(erf), F(erfc), F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F(log), F(log10), F(log1p), F(log2), F(logb), @@ -237,7 +237,7 @@ env_t *new_compilation_unit(CORD *libname) {"TAU", "(Num32_t)(2.f*M_PI)", "Num32"}, {"random", "Num32$random", "func()->Num32"}, {"mix", "Num32$mix", "func(amount:Num32, x:Num32, y:Num32)->Num32"}, - {"from_text", "Num32$from_text", "func(text:Text, success=!&Bool)->Num32"}, + {"from_text", "Num32$from_text", "func(text:Text)->Num32?"}, {"abs", "fabsf", "func(n:Num32)->Num32"}, F(acos), F(acosh), F(asin), F(asinh), F(atan), F(atanh), F(cbrt), F(ceil), F(cos), F(cosh), F(erf), F(erfc), F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F(log), F(log10), F(log1p), F(log2), F(logb), @@ -354,11 +354,11 @@ void eval(env_t *env, ast_t *ast, void *dest) case Int: { if (!dest) return; switch (Match(ast, Int)->bits) { - case 0: *(Int_t*)dest = Int$from_text(Text$from_str(Match(ast, Int)->str), NULL); break; - case 64: *(int64_t*)dest = Int64$from_text(Text$from_str(Match(ast, Int)->str), NULL); break; - case 32: *(int32_t*)dest = Int32$from_text(Text$from_str(Match(ast, Int)->str), NULL); break; - case 16: *(int16_t*)dest = Int16$from_text(Text$from_str(Match(ast, Int)->str), NULL); break; - case 8: *(int8_t*)dest = Int8$from_text(Text$from_str(Match(ast, Int)->str), NULL); break; + case 0: *(Int_t*)dest = Int$from_text(Text$from_str(Match(ast, Int)->str)); break; + case 64: *(int64_t*)dest = Int64$from_text(Text$from_str(Match(ast, Int)->str)).i; break; + case 32: *(int32_t*)dest = Int32$from_text(Text$from_str(Match(ast, Int)->str)).i; break; + case 16: *(int16_t*)dest = Int16$from_text(Text$from_str(Match(ast, Int)->str)).i; break; + case 8: *(int8_t*)dest = Int8$from_text(Text$from_str(Match(ast, Int)->str)).i; break; default: errx(1, "Invalid int bits: %ld", Match(ast, Int)->bits); } break; diff --git a/typecheck.c b/typecheck.c index e23ece88..c459b733 100644 --- a/typecheck.c +++ b/typecheck.c @@ -1364,7 +1364,8 @@ PUREFUNC bool is_constant(env_t *env, ast_t *ast) case Int: { auto info = Match(ast, Int); if (info->bits == IBITS_UNSPECIFIED) { - Int_t int_val = Int$from_text(Text$from_str(info->str), NULL); + Int_t int_val = Int$from_text(Text$from_str(info->str)); + if (int_val.small == 0) return false; // Failed to parse return (Int$compare_value(int_val, I(BIGGEST_SMALL_INT)) <= 0); } return true; |
