diff options
Diffstat (limited to 'src/checker/expr.c')
| -rw-r--r-- | src/checker/expr.c | 100 |
1 files changed, 99 insertions, 1 deletions
diff --git a/src/checker/expr.c b/src/checker/expr.c index de6011a79..9823f535f 100644 --- a/src/checker/expr.c +++ b/src/checker/expr.c @@ -2572,6 +2572,10 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id } switch (id) { + default: + GB_PANIC("Implement builtin procedure: %.*s", LIT(builtin_procs[id].name)); + break; + case BuiltinProc_new: { // new :: proc(Type) -> ^Type Operand op = {0}; @@ -3248,7 +3252,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id if (b.mode == Addressing_Invalid) { return false; } - if (!is_type_comparable(b.type) || !(is_type_numeric(type) || is_type_string(type))) { + if (!is_type_comparable(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) { gbString type_str = type_to_string(b.type); error(ast_node_token(call), "Expected a comparable numeric or string type to `max`, got `%s`", @@ -3328,6 +3332,100 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->type = type; } break; + case BuiltinProc_clamp: { + // clamp :: proc(a, min, max: comparable) -> comparable + Type *type = base_type(operand->type); + if (!is_type_comparable(type) || !(is_type_numeric(type) || is_type_string(type))) { + gbString type_str = type_to_string(operand->type); + error(ast_node_token(call), + "Expected a comparable numeric or string type to `clamp`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + AstNode *min_arg = ce->args.e[1]; + AstNode *max_arg = ce->args.e[2]; + Operand x = *operand; + Operand y = {0}; + Operand z = {0}; + + check_expr(c, &y, min_arg); + if (y.mode == Addressing_Invalid) { + return false; + } + if (!is_type_comparable(y.type) || !(is_type_numeric(y.type) || is_type_string(y.type))) { + gbString type_str = type_to_string(y.type); + error(ast_node_token(call), + "Expected a comparable numeric or string type to `clamp`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + check_expr(c, &z, max_arg); + if (z.mode == Addressing_Invalid) { + return false; + } + if (!is_type_comparable(z.type) || !(is_type_numeric(z.type) || is_type_string(z.type))) { + gbString type_str = type_to_string(z.type); + error(ast_node_token(call), + "Expected a comparable numeric or string type to `clamp`, got `%s`", + type_str); + gb_string_free(type_str); + return false; + } + + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant && + z.mode == Addressing_Constant) { + ExactValue a = x.value; + ExactValue b = y.value; + ExactValue c = z.value; + + operand->mode = Addressing_Constant; + if (compare_exact_values(Token_Lt, a, b)) { + operand->value = b; + operand->type = y.type; + } else if (compare_exact_values(Token_Gt, a, c)) { + operand->value = c; + operand->type = z.type; + } else { + operand->value = a; + operand->type = x.type; + } + } else { + operand->mode = Addressing_Value; + operand->type = type; + + convert_to_typed(c, &x, y.type, 0); + if (x.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, x.type, 0); + if (y.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &x, z.type, 0); + if (x.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &z, x.type, 0); + if (z.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &y, z.type, 0); + if (y.mode == Addressing_Invalid) { return false; } + convert_to_typed(c, &z, y.type, 0); + if (z.mode == Addressing_Invalid) { return false; } + + if (!are_types_identical(x.type, y.type) || !are_types_identical(x.type, z.type)) { + gbString type_x = type_to_string(x.type); + gbString type_y = type_to_string(y.type); + gbString type_z = type_to_string(z.type); + error(ast_node_token(call), + "Mismatched types to `clamp`, `%s`, `%s`, `%s`", + type_x, type_y, type_z); + gb_string_free(type_z); + gb_string_free(type_y); + gb_string_free(type_x); + return false; + } + } + } break; + case BuiltinProc_enum_to_string: { Type *type = base_type(operand->type); if (!is_type_enum(type)) { |