Switch NaN to be identical to the null value

This commit is contained in:
Bruce Hill 2024-11-24 16:13:23 -05:00
parent 6ecf6a2724
commit 0e10313d64
13 changed files with 175 additions and 61 deletions

View File

@ -32,6 +32,8 @@ static CORD compile_unsigned_type(type_t *t);
static CORD promote_to_optional(type_t *t, CORD code);
static CORD compile_null(type_t *t);
static CORD compile_to_type(env_t *env, ast_t *ast, type_t *t);
static CORD check_null(type_t *t, CORD value);
static CORD optional_into_nonnull(type_t *t, CORD value);
CORD promote_to_optional(type_t *t, CORD code)
{
@ -54,7 +56,7 @@ CORD promote_to_optional(type_t *t, CORD code)
}
}
static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
static bool promote(env_t *env, ast_t *ast, CORD *code, type_t *actual, type_t *needed)
{
if (type_eq(actual, needed))
return true;
@ -68,6 +70,18 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
return true;
}
// Automatic optional checking for nums:
if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType) {
*code = CORD_all("({ ", compile_declaration(actual, "opt"), " = ", *code, "; ",
"if (", check_null(actual, "opt"), ")\n",
CORD_asprintf("fail_source(%r, %ld, %ld, \"This value was expected to be non-null, but it's null!\");\n",
CORD_quoted(ast->file->filename),
(long)(ast->start - ast->file->text),
(long)(ast->end - ast->file->text)),
optional_into_nonnull(actual, "opt"), "; })");
return true;
}
if (actual->tag == IntType && needed->tag == BigIntType) {
*code = CORD_all("I(", *code, ")");
return true;
@ -89,7 +103,7 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
binding_t *b = get_binding(Match(needed, EnumType)->env, tag);
assert(b && b->type->tag == FunctionType);
// Single-value enum constructor:
if (!promote(env, code, actual, Match(b->type, FunctionType)->args->type))
if (!promote(env, ast, code, actual, Match(b->type, FunctionType)->args->type))
return false;
*code = CORD_all(b->code, "(", *code, ")");
return true;
@ -105,7 +119,7 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
if (actual->tag == PointerType
&& can_promote(Match(actual, PointerType)->pointed, needed)) {
*code = CORD_all("*(", *code, ")");
return promote(env, code, Match(actual, PointerType)->pointed, needed);
return promote(env, ast, code, Match(actual, PointerType)->pointed, needed);
}
// Stack ref promotion:
@ -376,7 +390,7 @@ static CORD compile_inline_block(env_t *env, ast_t *ast)
return code;
}
static CORD optional_into_nonnull(type_t *t, CORD value)
CORD optional_into_nonnull(type_t *t, CORD value)
{
if (t->tag == OptionalType) t = Match(t, OptionalType)->type;
switch (t->tag) {
@ -391,7 +405,7 @@ static CORD optional_into_nonnull(type_t *t, CORD value)
}
}
static CORD check_null(type_t *t, CORD value)
CORD check_null(type_t *t, CORD value)
{
t = Match(t, OptionalType)->type;
if (t->tag == PointerType || t->tag == FunctionType || t->tag == CStringType
@ -539,7 +553,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
type_t *t = get_type(env, decl->value);
CORD val_code = compile_maybe_incref(env, decl->value);
if (t->tag == FunctionType) {
assert(promote(env, &val_code, t, Type(ClosureType, t)));
assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
return CORD_asprintf(
@ -627,7 +641,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
CORD val_code = compile_maybe_incref(env, decl->value);
if (t->tag == FunctionType) {
assert(promote(env, &val_code, t, Type(ClosureType, t)));
assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
@ -670,10 +684,10 @@ CORD compile_statement(env_t *env, ast_t *ast)
type_t *lhs_t = get_type(env, update->lhs);
type_t *rhs_t = get_type(env, update->rhs);
if (!promote(env, &rhs, rhs_t, lhs_t)) {
if (!promote(env, update->rhs, &rhs, rhs_t, lhs_t)) {
if (update->rhs->tag == Int && (lhs_t->tag == IntType || lhs_t->tag == ByteType))
rhs = compile_int_to_type(env, update->rhs, lhs_t);
else if (!(lhs_t->tag == ArrayType && promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)))
else if (!(lhs_t->tag == ArrayType && promote(env, update->rhs, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)))
code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t);
}
@ -755,7 +769,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
return CORD_all(lhs, " = Texts(", lhs, ", ", rhs, ");");
} else if (lhs_t->tag == ArrayType) {
CORD padded_item_size = CORD_asprintf("%ld", type_size(Match(lhs_t, ArrayType)->item_type));
if (promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)) {
if (promote(env, update->rhs, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)) {
// arr ++= item
if (update->lhs->tag == Var)
return CORD_all("Array$insert(&", lhs, ", stack(", rhs, "), I(0), ", padded_item_size, ");");
@ -1547,7 +1561,7 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t)
return compile_null(t);
CORD code = compile(env, ast);
type_t *actual = get_type(env, ast);
if (!promote(env, &code, actual, t))
if (!promote(env, ast, &code, actual, t))
code_err(ast, "I expected a %T here, but this is a %T", t, actual);
return code;
}
@ -1572,7 +1586,7 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target)
if (ast->tag != Int) {
CORD code = compile(env, ast);
type_t *actual_type = get_type(env, ast);
if (!promote(env, &code, actual_type, target))
if (!promote(env, ast, &code, actual_type, target))
code = CORD_all(type_to_cord(actual_type), "_to_", type_to_cord(target), "(", code, ", no)");
return code;
}
@ -1659,7 +1673,7 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t
env_t *arg_env = with_enum_scope(env, spec_arg->type);
type_t *actual_t = get_type(arg_env, call_arg->value);
value = compile_maybe_incref(arg_env, call_arg->value);
if (!promote(arg_env, &value, actual_t, spec_arg->type))
if (!promote(arg_env, call_arg->value, &value, actual_t, spec_arg->type))
code_err(call_arg->value, "This argument is supposed to be a %T, but this value is a %T", spec_arg->type, actual_t);
}
Table$str_set(&used_args, call_arg->name, call_arg);
@ -1688,7 +1702,7 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t
env_t *arg_env = with_enum_scope(env, spec_arg->type);
type_t *actual_t = get_type(arg_env, call_arg->value);
value = compile_maybe_incref(arg_env, call_arg->value);
if (!promote(arg_env, &value, actual_t, spec_arg->type))
if (!promote(arg_env, call_arg->value, &value, actual_t, spec_arg->type))
code_err(call_arg->value, "This argument is supposed to be a %T, but this value is a %T", spec_arg->type, actual_t);
}
@ -2050,6 +2064,13 @@ CORD compile(env_t *env, ast_t *ast)
}
}
bool lhs_is_optional_num = (lhs_t->tag == OptionalType && Match(lhs_t, OptionalType)->type->tag == NumType);
if (lhs_is_optional_num)
lhs_t = Match(lhs_t, OptionalType)->type;
bool rhs_is_optional_num = (rhs_t->tag == OptionalType && Match(rhs_t, OptionalType)->type->tag == NumType);
if (rhs_is_optional_num)
rhs_t = Match(rhs_t, OptionalType)->type;
CORD lhs, rhs;
if (lhs_t->tag == BigIntType && rhs_t->tag != BigIntType && is_numeric_type(rhs_t) && binop->lhs->tag == Int) {
lhs = compile_int_to_type(env, binop->lhs, rhs_t);
@ -2065,9 +2086,9 @@ CORD compile(env_t *env, ast_t *ast)
}
type_t *operand_t;
if (promote(env, &rhs, rhs_t, lhs_t))
if (promote(env, binop->rhs, &rhs, rhs_t, lhs_t))
operand_t = lhs_t;
else if (promote(env, &lhs, lhs_t, rhs_t))
else if (promote(env, binop->lhs, &lhs, lhs_t, rhs_t))
operand_t = rhs_t;
else
code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t);
@ -2136,6 +2157,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("Int$equal_value(", lhs, ", ", rhs, ")");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
if (lhs_is_optional_num || rhs_is_optional_num)
return CORD_asprintf("generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " == ", rhs, ")");
default:
return CORD_asprintf("generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, operand_t));
@ -2146,6 +2169,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("!Int$equal_value(", lhs, ", ", rhs, ")");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
if (lhs_is_optional_num || rhs_is_optional_num)
return CORD_asprintf("!generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " != ", rhs, ")");
default:
return CORD_asprintf("!generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, operand_t));
@ -2156,6 +2181,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") < 0)");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
if (lhs_is_optional_num || rhs_is_optional_num)
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) < 0)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " < ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) < 0)", lhs, rhs, compile_type_info(env, operand_t));
@ -2166,6 +2193,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") <= 0)");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
if (lhs_is_optional_num || rhs_is_optional_num)
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) <= 0)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " <= ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) <= 0)", lhs, rhs, compile_type_info(env, operand_t));
@ -2176,6 +2205,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") > 0)");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
if (lhs_is_optional_num || rhs_is_optional_num)
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) > 0)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " > ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) > 0)", lhs, rhs, compile_type_info(env, operand_t));
@ -2186,6 +2217,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") >= 0)");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
if (lhs_is_optional_num || rhs_is_optional_num)
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) >= 0)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " >= ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) >= 0)", lhs, rhs, compile_type_info(env, operand_t));
@ -2200,6 +2233,8 @@ CORD compile(env_t *env, ast_t *ast)
code_err(ast, "The 'and' operator isn't supported for %T and %T", lhs_t, rhs_t);
}
case BINOP_CMP: {
if (lhs_is_optional_num || rhs_is_optional_num)
operand_t = Type(OptionalType, operand_t);
return CORD_all("generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(env, operand_t), ")");
}
case BINOP_OR: {
@ -2417,7 +2452,7 @@ CORD compile(env_t *env, ast_t *ast)
code_err(ast, "This item type can't be sent over a channel because it contains reference to memory that may not be thread-safe.");
if (chan->max_size) {
CORD max_size = compile(env, chan->max_size);
if (!promote(env, &max_size, get_type(env, chan->max_size), INT_TYPE))
if (!promote(env, chan->max_size, &max_size, get_type(env, chan->max_size), INT_TYPE))
code_err(chan->max_size, "This value must be an integer, not %T", get_type(env, chan->max_size));
return CORD_all("Channel$new(", max_size, ")");
} else {
@ -3525,7 +3560,7 @@ void compile_namespace(env_t *env, const char *ns_name, ast_t *block)
} else {
CORD val_code = compile_maybe_incref(ns_env, decl->value);
if (t->tag == FunctionType) {
assert(promote(env, &val_code, t, Type(ClosureType, t)));
assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
@ -3740,7 +3775,7 @@ CORD compile_file(env_t *env, ast_t *ast)
if (!is_constant(env, decl->value)) {
CORD val_code = compile_maybe_incref(env, decl->value);
if (t->tag == FunctionType) {
assert(promote(env, &val_code, t, Type(ClosureType, t)));
assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
@ -3771,7 +3806,7 @@ CORD compile_file(env_t *env, ast_t *ast)
} else {
CORD val_code = compile_maybe_incref(env, decl->value);
if (t->tag == FunctionType) {
assert(promote(env, &val_code, t, Type(ClosureType, t)));
assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
env->code->staticdefs = CORD_all(

View File

@ -137,7 +137,7 @@ env_t *new_compilation_unit(CORD libname)
{"power", "Int$power", "func(base:Int,exponent:Int -> Int)"},
{"prev_prime", "Int$prev_prime", "func(x:Int -> Int)"},
{"right_shifted", "Int$right_shifted", "func(x,y:Int -> Int)"},
{"sqrt", "Int$sqrt", "func(x:Int -> Int)"},
{"sqrt", "Int$sqrt", "func(x:Int -> Int?)"},
{"times", "Int$times", "func(x,y:Int -> Int)"},
{"to", "Int$to", "func(from:Int,to:Int -> Range)"},
)},
@ -219,61 +219,65 @@ env_t *new_compilation_unit(CORD libname)
)},
#define C(name) {#name, "M_"#name, "Num"}
#define F(name) {#name, #name, "func(n:Num -> Num)"}
#define F_opt(name) {#name, #name, "func(n:Num -> Num?)"}
#define F2(name) {#name, #name, "func(x,y:Num -> Num)"}
{"Num", Type(NumType, .bits=TYPE_NBITS64), "Num_t", "Num$info", TypedArray(ns_entry_t,
{"near", "Num$near", "func(x,y:Num, ratio=1e-9, min_epsilon=1e-9 -> Bool)"},
{"clamped", "Num$clamped", "func(x,low,high:Num -> Num)"},
{"format", "Num$format", "func(n:Num, precision=0 -> Text)"},
{"scientific", "Num$scientific", "func(n:Num,precision=0 -> Text)"},
{"nan", "Num$nan", "func(tag=\"\" -> Num)"},
{"isinf", "Num$isinf", "func(n:Num -> Bool)"},
{"isfinite", "Num$isfinite", "func(n:Num -> Bool)"},
{"isnan", "Num$isnan", "func(n:Num -> Bool)"},
C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E),
C(PI), C(PI_4), C(SQRT2), C(SQRT1_2),
{"INF", "(Num_t)(INFINITY)", "Num"},
{"NAN", "((Num_t)NAN)", "Num?"},
{"TAU", "(Num_t)(2.*M_PI)", "Num"},
{"mix", "Num$mix", "func(amount,x,y:Num -> Num)"},
{"parse", "Num$parse", "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),
F(rint), F(round), F(significand), F(sin), F(sinh), F(sqrt),
F(tan), F(tanh), F(tgamma), F(trunc), F(y0), F(y1),
F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), F_opt(atanh),
F(cbrt), F(ceil), F_opt(cos), F(cosh), F(erf), F(erfc),
F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F_opt(log), F_opt(log10), F_opt(log1p),
F_opt(log2), F(logb), F(rint), F(round), F(significand), F_opt(sin), F(sinh), F_opt(sqrt),
F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), F_opt(y1),
F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter),
)},
#undef F2
#undef F_opt
#undef F
#undef C
#define C(name) {#name, "(Num32_t)(M_"#name")", "Num32"}
#define F(name) {#name, #name"f", "func(n:Num32 -> Num32)"}
#define F_opt(name) {#name, #name"f", "func(n:Num32 -> Num32?)"}
#define F2(name) {#name, #name"f", "func(x,y:Num32 -> Num32)"}
{"Num32", Type(NumType, .bits=TYPE_NBITS32), "Num32_t", "Num32$info", TypedArray(ns_entry_t,
{"near", "Num32$near", "func(x,y:Num32, ratio=Num32(1e-9), min_epsilon=Num32(1e-9) -> Bool)"},
{"clamped", "Num32$clamped", "func(x,low,high:Num32 -> Num32)"},
{"format", "Num32$format", "func(n:Num32, precision=0 -> Text)"},
{"scientific", "Num32$scientific", "func(n:Num32, precision=0 -> Text)"},
{"nan", "Num32$nan", "func(tag=\"\" -> Num32)"},
{"isinf", "Num32$isinf", "func(n:Num32 -> Bool)"},
{"isfinite", "Num32$isfinite", "func(n:Num32 -> Bool)"},
{"isnan", "Num32$isnan", "func(n:Num32 -> Bool)"},
C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E),
C(PI), C(PI_4), C(SQRT2), C(SQRT1_2),
{"INF", "(Num32_t)(INFINITY)", "Num32"},
{"NAN", "((Num32_t)NAN)", "Num32?"},
{"TAU", "(Num32_t)(2.f*M_PI)", "Num32"},
{"mix", "Num32$mix", "func(amount,x,y:Num32 -> Num32)"},
{"parse", "Num32$parse", "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),
F(rint), F(round), F(significand), F(sin), F(sinh), F(sqrt),
F(tan), F(tanh), F(tgamma), F(trunc), F(y0), F(y1),
F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter), F2(pow), F2(remainder),
F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), F_opt(atanh),
F(cbrt), F(ceil), F_opt(cos), F(cosh), F(erf), F(erfc),
F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F_opt(log), F_opt(log10), F_opt(log1p),
F_opt(log2), F(logb), F(rint), F(round), F(significand), F_opt(sin), F(sinh), F_opt(sqrt),
F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), F_opt(y1),
F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter),
)},
{"CString", Type(CStringType), "char*", "CString$info", TypedArray(ns_entry_t,
{"as_text", "CString$as_text_simple", "func(str:CString -> Text)"},
)},
#undef F2
#undef F_opt
#undef F
#undef C
{"Range", RANGE_TYPE, "Range_t", "Range", TypedArray(ns_entry_t,

View File

@ -17,7 +17,7 @@ struct Vec2(x,y:Num):
func divided_by(v:Vec2, divisor:Num->Vec2; inline):
return Vec2(v.x/divisor, v.y/divisor)
func length(v:Vec2->Num; inline):
return (v.x*v.x + v.y*v.y):sqrt()
return (v.x*v.x + v.y*v.y)!:sqrt()
func dist(a,b:Vec2->Num; inline):
return a:minus(b):length()
func angle(v:Vec2->Num; inline):
@ -50,7 +50,7 @@ struct Vec3(x,y,z:Num):
func divided_by(v:Vec3, divisor:Num->Vec3; inline):
return Vec3(v.x/divisor, v.y/divisor, v.z/divisor)
func length(v:Vec3->Num; inline):
return (v.x*v.x + v.y*v.y + v.z*v.z):sqrt()
return (v.x*v.x + v.y*v.y + v.z*v.z)!:sqrt()
func dist(a,b:Vec3->Num; inline):
return a:minus(b):length()
func norm(v:Vec3->Vec3; inline):

View File

@ -300,8 +300,10 @@ public Int_t Int$power(Int_t base, Int_t exponent)
return Int$from_mpz(result);
}
public Int_t Int$sqrt(Int_t i)
public OptionalInt_t Int$sqrt(Int_t i)
{
if (Int$compare_value(i, I(0)) < 0)
return NULL_INT;
mpz_t result;
mpz_init_set_int(result, i);
mpz_sqrt(result, result);

View File

@ -106,7 +106,7 @@ OptionalInt_t Int$from_str(const char *str);
OptionalInt_t Int$parse(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);
OptionalInt_t Int$sqrt(Int_t i);
#define BIGGEST_SMALL_INT ((1<<29)-1)

View File

@ -78,10 +78,6 @@ public OptionalNum_t Num$parse(Text_t text) {
return nan("null");
}
public double Num$nan(Text_t tag) {
return nan(Text$as_c_string(tag));
}
public CONSTFUNC bool Num$isinf(double n) { return (fpclassify(n) == FP_INFINITE); }
public CONSTFUNC bool Num$finite(double n) { return (fpclassify(n) != FP_INFINITE); }
public CONSTFUNC bool Num$isnan(double n) { return (fpclassify(n) == FP_NAN); }
@ -155,10 +151,6 @@ public OptionalNum32_t Num32$parse(Text_t text) {
return nan("null");
}
public float Num32$nan(Text_t tag) {
return nanf(Text$as_c_string(tag));
}
public CONSTFUNC bool Num32$isinf(float n) { return (fpclassify(n) == FP_INFINITE); }
public CONSTFUNC bool Num32$finite(float n) { return (fpclassify(n) != FP_INFINITE); }
public CONSTFUNC bool Num32$isnan(float n) { return (fpclassify(n) == FP_NAN); }

View File

@ -21,6 +21,8 @@ public PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_typ
return *((OptionalBool_t*)obj) == NULL_BOOL;
else if (non_optional_type == &Num$info)
return isnan(*((Num_t*)obj));
else if (non_optional_type == &Num32$info)
return isnan(*((Num32_t*)obj));
else if (non_optional_type == &Int64$info)
return ((OptionalInt64_t*)obj)->is_null;
else if (non_optional_type == &Int32$info)

View File

@ -27,6 +27,18 @@
public bool USE_COLOR;
static void signal_handler(int sig, siginfo_t *, void *)
{
assert(sig == SIGILL);
fflush(stdout);
if (USE_COLOR) fputs("\x1b[31;7m ===== ILLEGAL INSTRUCTION ===== \n\n\x1b[m", stderr);
else fputs("===== ILLEGAL INSTRUCTION =====\n\n", stderr);
print_stack_trace(stderr, 3, 4);
fflush(stderr);
raise(SIGABRT);
_exit(1);
}
public void tomo_init(void)
{
GC_INIT();
@ -44,6 +56,12 @@ public void tomo_init(void)
if (register_printf_specifier('k', printf_text, printf_text_size))
errx(1, "Couldn't set printf specifier");
struct sigaction sigact;
sigact.sa_sigaction = signal_handler;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigaction(SIGILL, &sigact, (struct sigaction *)NULL);
}
static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest)

View File

@ -1,7 +1,7 @@
struct Foo(x:Int, y:Int):
func len(f:Foo->Num):
return Num.sqrt(f.x*f.x + f.y*f.y)
return Num.sqrt(f.x*f.x + f.y*f.y)!
func main():
>> 3 _min_ 5

View File

@ -22,27 +22,47 @@ func main():
>> Num.INF:isinf()
= yes
>> Num.nan()
= nan
>> nan := Num.nan()
>> nan:isnan()
= yes
>> Num.NAN
= NULL : Num?
>> nan := Num.NAN
= NULL : Num?
>> nan == nan
= no
>> Num.PI:cos():near(-1)
= yes
>> Num.PI:sin():near(0)
= yes
>> Num.nan():near(Num.nan())
>> nan < nan
= no
>> nan > nan
= no
>> nan != nan
= no
>> nan <> nan
= 0
>> nan == 0.0
= no
>> nan < 0.0
= yes
>> nan > 0.0
= no
>> nan != 0.0
= yes
>> nan <> 0.0
= -1
>> nan + 1
= NULL : Num?
>> 0./0.
= NULL : Num?
>> Num.PI:cos()!:near(-1)
= yes
>> Num.PI:sin()!:near(0)
= yes
>> Num.INF:near(-Num.INF)
= no
>> Num32.sqrt(16)
= 4 : Num32
= 4 : Num32?
>> 0.25:mix(10, 20)
= 12.5

2
tomo.c
View File

@ -43,7 +43,7 @@ static OptionalBool_t verbose = false,
static OptionalText_t autofmt = Text("sed '/^\\s*$/d' | indent -kr -l100 -nbbo -nut -sob"),
cflags = Text("-Werror -fdollars-in-identifiers -std=gnu11 -Wno-trigraphs -fsanitize=signed-integer-overflow -fno-sanitize-recover"
" -fno-signed-zeros -fno-finite-math-only -fno-signaling-nans -fno-trapping-math -fno-finite-math-only"
" -fno-signed-zeros -fno-finite-math-only -fno-signaling-nans -fno-trapping-math"
" -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -fPIC -ggdb"
" -I$HOME/.local/share/tomo/installed"),
ldlibs = Text("-lgc -lgmp -lm -ltomo"),

View File

@ -126,6 +126,28 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
errx(1, "Unreachable");
}
static PUREFUNC bool risks_zero_or_inf(ast_t *ast)
{
switch (ast->tag) {
case Int: {
const char *str = Match(ast, Int)->str;
OptionalInt_t int_val = Int$from_str(str);
return (int_val.small == 0x1); // zero
}
case Num: {
return Match(ast, Num)->n == 0.0;
}
case BinaryOp: {
auto binop = Match(ast, BinaryOp);
if (binop->op == BINOP_MULT || binop->op == BINOP_DIVIDE || binop->op == BINOP_MIN || binop->op == BINOP_MAX)
return risks_zero_or_inf(binop->lhs) || risks_zero_or_inf(binop->rhs);
else
return true;
}
default: return true;
}
}
PUREFUNC type_t *get_math_type(env_t *env, ast_t *ast, type_t *lhs_t, type_t *rhs_t)
{
(void)env;
@ -1011,6 +1033,16 @@ type_t *get_type(env_t *env, ast_t *ast)
return result;
return Type(NumType, .bits=TYPE_NBITS64);
}
case BINOP_MULT: case BINOP_DIVIDE: {
type_t *math_type = get_math_type(env, ast, value_type(lhs_t), value_type(rhs_t));
if (value_type(lhs_t)->tag == NumType || value_type(rhs_t)->tag == NumType) {
if (risks_zero_or_inf(binop->lhs) && risks_zero_or_inf(binop->rhs))
return Type(OptionalType, math_type);
else
return math_type;
}
return math_type;
}
default: {
return get_math_type(env, ast, lhs_t, rhs_t);
}

View File

@ -207,6 +207,11 @@ static PUREFUNC INLINE double type_max_magnitude(type_t *t)
PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b)
{
if (a->tag == OptionalType && Match(a, OptionalType)->type->tag == NumType)
a = Match(a, OptionalType)->type;
if (b->tag == OptionalType && Match(b, OptionalType)->type->tag == NumType)
b = Match(b, OptionalType)->type;
if (is_int_type(a) && b->tag == NumType)
return NUM_PRECISION_LESS;
else if (a->tag == NumType && is_int_type(b))
@ -338,6 +343,10 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed)
if (actual->tag == PointerType && can_promote(Match(actual, PointerType)->pointed, needed))
return true;
// Optional num -> num
if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType)
return can_promote(Match(actual, OptionalType)->type, needed);
// Optional promotion:
if (needed->tag == OptionalType && can_promote(actual, Match(needed, OptionalType)->type))
return true;