Browse Source

Allow for variadic `min` `max` procs

Request #252
gingerBill 7 years ago
parent
commit
cc88dd0b71
3 changed files with 176 additions and 105 deletions
  1. 156 101
      src/check_expr.cpp
  2. 2 2
      src/checker.hpp
  3. 18 2
      src/ir.cpp

+ 156 - 101
src/check_expr.cpp

@@ -3435,152 +3435,207 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 
 
 	case BuiltinProc_min: {
 	case BuiltinProc_min: {
 		// min :: proc(a, b: ordered) -> ordered
 		// min :: proc(a, b: ordered) -> ordered
+		Type *original_type = operand->type;
 		Type *type = base_type(operand->type);
 		Type *type = base_type(operand->type);
 		if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
 		if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
-			gbString type_str = type_to_string(operand->type);
+			gbString type_str = type_to_string(original_type);
 			error(call, "Expected a ordered numeric type to 'min', got '%s'", type_str);
 			error(call, "Expected a ordered numeric type to 'min', got '%s'", type_str);
 			gb_string_free(type_str);
 			gb_string_free(type_str);
 			return false;
 			return false;
 		}
 		}
 
 
-		Ast *other_arg = ce->args[1];
-		Operand a = *operand;
-		Operand b = {};
-		check_expr(c, &b, other_arg);
-		if (b.mode == Addressing_Invalid) {
-			return false;
-		}
-		if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
-			gbString type_str = type_to_string(b.type);
-			error(call,
-			      "Expected a ordered numeric type to 'min', got '%s'",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
+		bool all_constant = operand->mode == Addressing_Constant;
 
 
-		if (a.mode == Addressing_Constant &&
-		    b.mode == Addressing_Constant) {
-			ExactValue x = a.value;
-			ExactValue y = b.value;
+		auto operands = array_make<Operand>(heap_allocator(), 0, ce->args.count);
+		defer (array_free(&operands));
 
 
-			operand->mode = Addressing_Constant;
-			if (compare_exact_values(Token_Lt, x, y)) {
-				operand->value = x;
-				operand->type = a.type;
-			} else {
-				operand->value = y;
-				operand->type = b.type;
-			}
-		} else {
-			operand->mode = Addressing_Value;
-			operand->type = type;
+		array_add(&operands, *operand);
 
 
-			convert_to_typed(c, &a, b.type);
-			if (a.mode == Addressing_Invalid) {
-				return false;
-			}
-			convert_to_typed(c, &b, a.type);
+		for (isize i = 1; i < ce->args.count; i++) {
+			Ast *other_arg = ce->args[i];
+			Operand b = {};
+			check_expr(c, &b, other_arg);
 			if (b.mode == Addressing_Invalid) {
 			if (b.mode == Addressing_Invalid) {
 				return false;
 				return false;
 			}
 			}
-
-			if (!are_types_identical(a.type, b.type)) {
-				gbString type_a = type_to_string(a.type);
-				gbString type_b = type_to_string(b.type);
+			if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
+				gbString type_str = type_to_string(b.type);
 				error(call,
 				error(call,
-				      "Mismatched types to 'min', '%s' vs '%s'",
-				      type_a, type_b);
-				gb_string_free(type_b);
-				gb_string_free(type_a);
+				      "Expected a ordered numeric type to 'min', got '%s'",
+				      type_str);
+				gb_string_free(type_str);
 				return false;
 				return false;
 			}
 			}
+			array_add(&operands, b);
+
+			if (all_constant) {
+				all_constant = b.mode == Addressing_Constant;
+			}
+		}
+
+		if (all_constant) {
+			ExactValue value = operands[0].value;
+			Type *type = operands[0].type;
+			for (isize i = 1; i < operands.count; i++) {
+				Operand y = operands[i];
+				if (compare_exact_values(Token_Lt, value, y.value)) {
+					// okay
+				} else {
+					value = y.value;
+					type = y.type;
+				}
+			}
+			operand->value = value;
+			operand->type = type;
+		} else {
+			operand->mode = Addressing_Value;
+			operand->type = original_type;
+
+			for_array(i, operands) {
+				Operand *a = &operands[i];
+				for_array(j, operands) {
+					if (i == j) {
+						continue;
+					}
+					Operand *b = &operands[j];
+
+					convert_to_typed(c, a, b->type);
+					if (a->mode == Addressing_Invalid) {
+						return false;
+					}
+					convert_to_typed(c, b, a->type);
+					if (b->mode == Addressing_Invalid) {
+						return false;
+					}
+				}
+			}
+
+			for (isize i = 0; i < operands.count-1; i++) {
+				Operand *a = &operands[i];
+				Operand *b = &operands[i+1];
+
+				if (!are_types_identical(a->type, b->type)) {
+					gbString type_a = type_to_string(a->type);
+					gbString type_b = type_to_string(b->type);
+					error(a->expr,
+					      "Mismatched types to 'min', '%s' vs '%s'",
+					      type_a, type_b);
+					gb_string_free(type_b);
+					gb_string_free(type_a);
+					return false;
+				}
+			}
+
 
 
 			{
 			{
-				Type *bt = base_type(a.type);
+				Type *bt = base_type(operands[0].type);
 				if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "__min_f32");
 				if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "__min_f32");
 				if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "__min_f64");
 				if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "__min_f64");
 			}
 			}
-
 		}
 		}
-
-
 		break;
 		break;
 	}
 	}
 
 
 	case BuiltinProc_max: {
 	case BuiltinProc_max: {
-		// min :: proc(a, b: ordered) -> ordered
+		// max :: proc(a, b: ordered) -> ordered
+		Type *original_type = operand->type;
 		Type *type = base_type(operand->type);
 		Type *type = base_type(operand->type);
 		if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
 		if (!is_type_ordered(type) || !(is_type_numeric(type) || is_type_string(type))) {
-			gbString type_str = type_to_string(operand->type);
-			error(call,
-			      "Expected a ordered numeric or string type to 'max', got '%s'",
-			      type_str);
+			gbString type_str = type_to_string(original_type);
+			error(call, "Expected a ordered numeric type to 'max', got '%s'", type_str);
 			gb_string_free(type_str);
 			gb_string_free(type_str);
 			return false;
 			return false;
 		}
 		}
 
 
-		Ast *other_arg = ce->args[1];
-		Operand a = *operand;
-		Operand b = {};
-		check_expr(c, &b, other_arg);
-		if (b.mode == Addressing_Invalid) {
-			return false;
-		}
-		if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
-			gbString type_str = type_to_string(b.type);
-			error(call,
-			      "Expected a ordered numeric or string type to 'max', got '%s'",
-			      type_str);
-			gb_string_free(type_str);
-			return false;
-		}
+		bool all_constant = operand->mode == Addressing_Constant;
 
 
-		if (a.mode == Addressing_Constant &&
-		    b.mode == Addressing_Constant) {
-			ExactValue x = a.value;
-			ExactValue y = b.value;
+		auto operands = array_make<Operand>(heap_allocator(), 0, ce->args.count);
+		defer (array_free(&operands));
 
 
-			operand->mode = Addressing_Constant;
-			if (compare_exact_values(Token_Gt, x, y)) {
-				operand->value = x;
-				operand->type = a.type;
-			} else {
-				operand->value = y;
-				operand->type = b.type;
-			}
-		} else {
-			operand->mode = Addressing_Value;
-			operand->type = type;
+		array_add(&operands, *operand);
 
 
-			convert_to_typed(c, &a, b.type);
-			if (a.mode == Addressing_Invalid) {
-				return false;
-			}
-			convert_to_typed(c, &b, a.type);
+
+		for (isize i = 1; i < ce->args.count; i++) {
+			Ast *arg = ce->args[i];
+			Operand b = {};
+			check_expr(c, &b, arg);
 			if (b.mode == Addressing_Invalid) {
 			if (b.mode == Addressing_Invalid) {
 				return false;
 				return false;
 			}
 			}
-
-			if (!are_types_identical(a.type, b.type)) {
-				gbString type_a = type_to_string(a.type);
-				gbString type_b = type_to_string(b.type);
-				error(call,
-				      "Mismatched types to 'max', '%s' vs '%s'",
-				      type_a, type_b);
-				gb_string_free(type_b);
-				gb_string_free(type_a);
+			if (!is_type_ordered(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
+				gbString type_str = type_to_string(b.type);
+				error(arg,
+				      "Expected a ordered numeric type to 'max', got '%s'",
+				      type_str);
+				gb_string_free(type_str);
 				return false;
 				return false;
 			}
 			}
+			array_add(&operands, b);
 
 
-			{
-				Type *bt = base_type(a.type);
-				if (bt == t_f32) add_package_dependency(c, "runtime", "__max_f32");
-				if (bt == t_f64) add_package_dependency(c, "runtime", "__max_f64");
+			if (all_constant) {
+				all_constant = b.mode == Addressing_Constant;
 			}
 			}
 		}
 		}
 
 
+		if (all_constant) {
+			ExactValue value = operands[0].value;
+			Type *type = operands[0].type;
+			for (isize i = 1; i < operands.count; i++) {
+				Operand y = operands[i];
+				if (compare_exact_values(Token_Gt, value, y.value)) {
+					// okay
+				} else {
+					type  = y.type;
+					value = y.value;
+				}
+			}
+			operand->value = value;
+			operand->type = type;
+		} else {
+			operand->mode = Addressing_Value;
+			operand->type = original_type;
+
+			for_array(i, operands) {
+				Operand *a = &operands[i];
+				for_array(j, operands) {
+					if (i == j) {
+						continue;
+					}
+					Operand *b = &operands[j];
+
+					convert_to_typed(c, a, b->type);
+					if (a->mode == Addressing_Invalid) {
+						return false;
+					}
+					convert_to_typed(c, b, a->type);
+					if (b->mode == Addressing_Invalid) {
+						return false;
+					}
+				}
+			}
+
+			for (isize i = 0; i < operands.count-1; i++) {
+				Operand *a = &operands[i];
+				Operand *b = &operands[i+1];
+
+				if (!are_types_identical(a->type, b->type)) {
+					gbString type_a = type_to_string(a->type);
+					gbString type_b = type_to_string(b->type);
+					error(a->expr,
+					      "Mismatched types to 'max', '%s' vs '%s'",
+					      type_a, type_b);
+					gb_string_free(type_b);
+					gb_string_free(type_a);
+					return false;
+				}
+			}
 
 
+			{
+				Type *bt = base_type(operands[0].type);
+				if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "__max_f32");
+				if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "__max_f64");
+			}
+		}
 		break;
 		break;
 	}
 	}
 
 

+ 2 - 2
src/checker.hpp

@@ -105,8 +105,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 
 
 	{STR_LIT("expand_to_tuple"),  1, false, Expr_Expr},
 	{STR_LIT("expand_to_tuple"),  1, false, Expr_Expr},
 
 
-	{STR_LIT("min"),              2, false, Expr_Expr},
-	{STR_LIT("max"),              2, false, Expr_Expr},
+	{STR_LIT("min"),              2, true,  Expr_Expr},
+	{STR_LIT("max"),              2, true,  Expr_Expr},
 	{STR_LIT("abs"),              1, false, Expr_Expr},
 	{STR_LIT("abs"),              1, false, Expr_Expr},
 	{STR_LIT("clamp"),            3, false, Expr_Expr},
 	{STR_LIT("clamp"),            3, false, Expr_Expr},
 
 

+ 18 - 2
src/ir.cpp

@@ -4539,13 +4539,29 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu
 	case BuiltinProc_min: {
 	case BuiltinProc_min: {
 		ir_emit_comment(proc, str_lit("min"));
 		ir_emit_comment(proc, str_lit("min"));
 		Type *t = type_of_expr(expr);
 		Type *t = type_of_expr(expr);
-		return ir_emit_min(proc, t, ir_build_expr(proc, ce->args[0]), ir_build_expr(proc, ce->args[1]));
+		if (ce->args.count == 2) {
+			return ir_emit_min(proc, t, ir_build_expr(proc, ce->args[0]), ir_build_expr(proc, ce->args[1]));
+		} else {
+			irValue *x = ir_build_expr(proc, ce->args[0]);
+			for (isize i = 1; i < ce->args.count; i++) {
+				x = ir_emit_min(proc, t, x, ir_build_expr(proc, ce->args[i]));
+			}
+			return x;
+		}
 	}
 	}
 
 
 	case BuiltinProc_max: {
 	case BuiltinProc_max: {
 		ir_emit_comment(proc, str_lit("max"));
 		ir_emit_comment(proc, str_lit("max"));
 		Type *t = type_of_expr(expr);
 		Type *t = type_of_expr(expr);
-		return ir_emit_max(proc, t, ir_build_expr(proc, ce->args[0]), ir_build_expr(proc, ce->args[1]));
+		if (ce->args.count == 2) {
+			return ir_emit_max(proc, t, ir_build_expr(proc, ce->args[0]), ir_build_expr(proc, ce->args[1]));
+		} else {
+			irValue *x = ir_build_expr(proc, ce->args[0]);
+			for (isize i = 1; i < ce->args.count; i++) {
+				x = ir_emit_max(proc, t, x, ir_build_expr(proc, ce->args[i]));
+			}
+			return x;
+		}
 	}
 	}
 
 
 	case BuiltinProc_abs: {
 	case BuiltinProc_abs: {