aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtins/bool.c8
-rw-r--r--builtins/bool.h3
-rw-r--r--builtins/integers.c26
-rw-r--r--builtins/integers.h8
-rw-r--r--builtins/nums.c16
-rw-r--r--builtins/nums.h6
-rw-r--r--compile.c177
-rw-r--r--environment.c16
-rw-r--r--repl.c10
-rw-r--r--typecheck.c3
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);
diff --git a/compile.c b/compile.c
index a8ded474..2c697364 100644
--- a/compile.c
+++ b/compile.c
@@ -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),
diff --git a/repl.c b/repl.c
index ae00f545..06806c3c 100644
--- a/repl.c
+++ b/repl.c
@@ -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;