Browse Source

Improve proc group scoring algorithm

gingerBill 7 years ago
parent
commit
cbc6c2666b
4 changed files with 111 additions and 36 deletions
  1. 10 9
      core/mem/alloc.odin
  2. 54 3
      core/runtime/core.odin
  3. 46 23
      src/check_expr.cpp
  4. 1 1
      src/types.cpp

+ 10 - 9
core/mem/alloc.odin

@@ -24,6 +24,7 @@ Allocator :: struct {
 
 
 
 
 alloc_with_allocator :: inline proc(a: Allocator, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr {
 alloc_with_allocator :: inline proc(a: Allocator, size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr {
+	if size == 0 do return nil;
 	return a.procedure(a.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
 	return a.procedure(a.data, Allocator_Mode.Alloc, size, alignment, nil, 0, 0, loc);
 }
 }
 alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr {
 alloc :: inline proc(size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr {
@@ -45,6 +46,10 @@ free_all :: inline proc(loc := #caller_location) {
 }
 }
 
 
 resize_with_allocator :: inline proc(a: Allocator, ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr {
 resize_with_allocator :: inline proc(a: Allocator, ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr {
+	if new_size == 0 {
+		free_ptr_with_allocator(a, ptr, loc);
+		return nil;
+	}
 	return a.procedure(a.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
 	return a.procedure(a.data, Allocator_Mode.Resize, new_size, alignment, ptr, old_size, 0, loc);
 }
 }
 resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr {
 resize :: inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, loc := #caller_location) -> rawptr {
@@ -82,33 +87,30 @@ delete :: proc[
 
 
 new :: inline proc(T: type, loc := #caller_location) -> ^T {
 new :: inline proc(T: type, loc := #caller_location) -> ^T {
 	ptr := (^T)(alloc(size_of(T), align_of(T), loc));
 	ptr := (^T)(alloc(size_of(T), align_of(T), loc));
-	ptr^ = T{};
+	if ptr != nil do ptr^ = T{};
 	return ptr;
 	return ptr;
 }
 }
 new_clone :: inline proc(data: $T, loc := #caller_location) -> ^T {
 new_clone :: inline proc(data: $T, loc := #caller_location) -> ^T {
 	ptr := (^T)(alloc(size_of(T), align_of(T), loc));
 	ptr := (^T)(alloc(size_of(T), align_of(T), loc));
-	ptr^ = data;
+	if ptr != nil do ptr^ = data;
 	return ptr;
 	return ptr;
 }
 }
 
 
 new_with_allocator :: inline proc(a: Allocator, T: type, loc := #caller_location) -> ^T {
 new_with_allocator :: inline proc(a: Allocator, T: type, loc := #caller_location) -> ^T {
 	ptr := (^T)(alloc_with_allocator(a, size_of(T), align_of(T), loc));
 	ptr := (^T)(alloc_with_allocator(a, size_of(T), align_of(T), loc));
-	ptr^ = T{};
+	if ptr != nil do ptr^ = T{};
 	return ptr;
 	return ptr;
 }
 }
 
 
 new_clone_with_allocator :: inline proc(a: Allocator, data: $T, loc := #caller_location) -> ^T {
 new_clone_with_allocator :: inline proc(a: Allocator, data: $T, loc := #caller_location) -> ^T {
 	ptr := (^T)(alloc_with_allocator(a, size_of(T), align_of(T), loc));
 	ptr := (^T)(alloc_with_allocator(a, size_of(T), align_of(T), loc));
-	ptr^ = data;
+	if ptr != nil do ptr^ = data;
 	return ptr;
 	return ptr;
 }
 }
 
 
 
 
 make_slice :: proc(T: type/[]$E, auto_cast len: int, loc := #caller_location) -> T {
 make_slice :: proc(T: type/[]$E, auto_cast len: int, loc := #caller_location) -> T {
 	runtime.make_slice_error_loc(loc, len);
 	runtime.make_slice_error_loc(loc, len);
-	if len == 0 {
-		return nil;
-	}
 	data := alloc(size_of(E)*len, align_of(E));
 	data := alloc(size_of(E)*len, align_of(E));
 	s := Raw_Slice{data, len};
 	s := Raw_Slice{data, len};
 	return transmute(T)s;
 	return transmute(T)s;
@@ -121,8 +123,7 @@ make_dynamic_array_len :: proc(T: type/[dynamic]$E, auto_cast len: int, loc := #
 }
 }
 make_dynamic_array_len_cap :: proc(T: type/[dynamic]$E, auto_cast len: int, auto_cast cap: int, loc := #caller_location) -> T {
 make_dynamic_array_len_cap :: proc(T: type/[dynamic]$E, auto_cast len: int, auto_cast cap: int, loc := #caller_location) -> T {
 	runtime.make_dynamic_array_error_loc(loc, len, cap);
 	runtime.make_dynamic_array_error_loc(loc, len, cap);
-	data: rawptr;
-	if cap > 0 do data = alloc(size_of(E)*cap, align_of(E));
+	data := alloc(size_of(E)*cap, align_of(E));
 	s := Raw_Dynamic_Array{data, len, cap, context.allocator};
 	s := Raw_Dynamic_Array{data, len, cap, context.allocator};
 	return transmute(T)s;
 	return transmute(T)s;
 }
 }

+ 54 - 3
core/runtime/core.odin

@@ -366,7 +366,27 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) {
 
 
 
 
 @(builtin)
 @(builtin)
-append :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
+append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> int {
+	if array == nil do return 0;
+
+	arg_len := 1;
+
+	if cap(array) <= len(array)+arg_len {
+		cap := 2 * cap(array) + max(8, arg_len);
+		_ = reserve(array, cap, loc);
+	}
+	arg_len = min(cap(array)-len(array), arg_len);
+	if arg_len > 0 {
+		a := (^mem.Raw_Dynamic_Array)(array);
+		data := (^E)(a.data);
+		assert(data != nil);
+		mem.copy(mem.ptr_offset(data, a.len), &arg, size_of(E));
+		a.len += arg_len;
+	}
+	return len(array);
+}
+@(builtin)
+append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> int {
 	if array == nil do return 0;
 	if array == nil do return 0;
 
 
 	arg_len := len(args);
 	arg_len := len(args);
@@ -387,6 +407,9 @@ append :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> in
 	}
 	}
 	return len(array);
 	return len(array);
 }
 }
+@(builtin) append :: proc[append_elem, append_elems];
+
+
 
 
 @(builtin)
 @(builtin)
 append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> int {
 append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> int {
@@ -428,14 +451,42 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
 	return true;
 	return true;
 }
 }
 
 
+
 @(builtin)
 @(builtin)
-incl :: inline proc(s: ^$B/bit_set[$T], elem: T) {
+incl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
 	s^ |= {elem};
 	s^ |= {elem};
+	return s^;
+}
+@(builtin)
+incl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
+	for elem in elems do s^ |= {elem};
+	return s^;
+}
+@(builtin)
+incl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
+	s^ |= other;
+	return s^;
 }
 }
 @(builtin)
 @(builtin)
-excl :: inline proc(s: ^$B/bit_set[$T], elem: T) {
+excl_elem :: inline proc(s: ^$S/bit_set[$E; $U], elem: E) -> S {
 	s^ &~= {elem};
 	s^ &~= {elem};
+	return s^;
 }
 }
+@(builtin)
+excl_elems :: inline proc(s: ^$S/bit_set[$E; $U], elems: ..E) -> S {
+	for elem in elems do s^ &~= {elem};
+	return s^;
+}
+@(builtin)
+excl_bit_set :: inline proc(s: ^$S/bit_set[$E; $U], other: S) -> S {
+	s^ &~= other;
+	return s^;
+}
+
+@(builtin) incl :: proc[incl_elem, incl_elems, incl_bit_set];
+@(builtin) excl :: proc[excl_elem, excl_elems, excl_bit_set];
+
+
 
 
 
 
 
 

+ 46 - 23
src/check_expr.cpp

@@ -403,6 +403,8 @@ bool check_type_specialization_to(CheckerContext *c, Type *specialization, Type
 bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, bool compound, bool modify_type);
 bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, bool compound, bool modify_type);
 bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
 bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
 
 
+#define MAXIMUM_TYPE_DISTANCE 10
+
 i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
 i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
 	if (operand->mode == Addressing_Invalid ||
 	if (operand->mode == Addressing_Invalid ||
 	    type == t_invalid) {
 	    type == t_invalid) {
@@ -443,7 +445,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 		if (is_type_any(dst)) {
 		if (is_type_any(dst)) {
 			// NOTE(bill): Anything can cast to 'Any'
 			// NOTE(bill): Anything can cast to 'Any'
 			add_type_info_type(c, s);
 			add_type_info_type(c, s);
-			return 10;
+			return MAXIMUM_TYPE_DISTANCE;
 		}
 		}
 		if (dst->kind == Type_Basic) {
 		if (dst->kind == Type_Basic) {
 			if (operand->mode == Addressing_Constant) {
 			if (operand->mode == Addressing_Constant) {
@@ -577,7 +579,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 			} else {
 			} else {
 				// NOTE(bill): Anything can cast to 'Any'
 				// NOTE(bill): Anything can cast to 'Any'
 				add_type_info_type(c, s);
 				add_type_info_type(c, s);
-				return 10;
+				return MAXIMUM_TYPE_DISTANCE;
 			}
 			}
 		}
 		}
 	}
 	}
@@ -588,7 +590,7 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 		x.expr = expr->AutoCast.expr;
 		x.expr = expr->AutoCast.expr;
 		bool ok = check_cast_internal(c, &x, type);
 		bool ok = check_cast_internal(c, &x, type);
 		if (ok) {
 		if (ok) {
-			return 10;
+			return MAXIMUM_TYPE_DISTANCE;
 		}
 		}
 	}
 	}
 
 
@@ -596,18 +598,26 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 }
 }
 
 
 
 
-i64 assign_score_function(i64 distance) {
+i64 assign_score_function(i64 distance, bool is_variadic=false) {
+	// 3*x^2 + 1 > x^2 + x + 1 (for positive x)
+	i64 const c = 3*MAXIMUM_TYPE_DISTANCE*MAXIMUM_TYPE_DISTANCE + 1;
+
 	// TODO(bill): A decent score function
 	// TODO(bill): A decent score function
-	return gb_max(1000000 - distance*distance, 0);
+	GB_ASSERT(distance <= MAXIMUM_TYPE_DISTANCE);
+	i64 d = distance*distance; // x^2
+	if (is_variadic && d >= 0) {
+		d += distance + 1; // x^2 + x + 1
+	}
+	return gb_max(c - d, 0);
 }
 }
 
 
 
 
-bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_) {
+bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) {
 	i64 score = 0;
 	i64 score = 0;
 	i64 distance = check_distance_between_types(c, operand, type);
 	i64 distance = check_distance_between_types(c, operand, type);
 	bool ok = distance >= 0;
 	bool ok = distance >= 0;
 	if (ok) {
 	if (ok) {
-		score = assign_score_function(distance);
+		score = assign_score_function(distance, is_variadic);
 	}
 	}
 	if (score_) *score_ = score;
 	if (score_) *score_ = score;
 	return ok;
 	return ok;
@@ -837,7 +847,17 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source,
 
 
 	case Type_BitSet:
 	case Type_BitSet:
 		if (source->kind == Type_BitSet) {
 		if (source->kind == Type_BitSet) {
-			return is_polymorphic_type_assignable(c, poly->BitSet.elem, source->BitSet.elem, true, modify_type);
+			if (!is_polymorphic_type_assignable(c, poly->BitSet.elem, source->BitSet.elem, true, modify_type)) {
+				return false;
+			}
+			if (poly->BitSet.underlying == nullptr) {
+				if (modify_type) {
+					poly->BitSet.underlying = source->BitSet.underlying;
+				}
+			} else if (!is_polymorphic_type_assignable(c, poly->BitSet.underlying, source->BitSet.underlying, true, modify_type)) {
+				return false;
+			}
+			return true;
 		}
 		}
 		return false;
 		return false;
 
 
@@ -3198,7 +3218,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
 	}
 	}
 
 
 	case BuiltinProc_swizzle: {
 	case BuiltinProc_swizzle: {
-		// swizzle :: proc(v: [N]T, ...int) -> [M]T
+		// swizzle :: proc(v: [N]T, ..int) -> [M]T
 		Type *type = base_type(operand->type);
 		Type *type = base_type(operand->type);
 		if (!is_type_array(type)) {
 		if (!is_type_array(type)) {
 			gbString type_str = type_to_string(operand->type);
 			gbString type_str = type_to_string(operand->type);
@@ -3926,14 +3946,14 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 	if (vari_expand && !variadic) {
 	if (vari_expand && !variadic) {
 		if (show_error) {
 		if (show_error) {
 			error(ce->ellipsis,
 			error(ce->ellipsis,
-			      "Cannot use '...' in call to a non-variadic procedure: '%.*s'",
+			      "Cannot use '..' in call to a non-variadic procedure: '%.*s'",
 			      LIT(ce->proc->Ident.token.string));
 			      LIT(ce->proc->Ident.token.string));
 		}
 		}
 		err = CallArgumentError_NonVariadicExpand;
 		err = CallArgumentError_NonVariadicExpand;
 	} else if (vari_expand && pt->c_vararg) {
 	} else if (vari_expand && pt->c_vararg) {
 		if (show_error) {
 		if (show_error) {
 			error(ce->ellipsis,
 			error(ce->ellipsis,
-			      "Cannot use '...' in call to a '#c_vararg' variadic procedure: '%.*s'",
+			      "Cannot use '..' in call to a '#c_vararg' variadic procedure: '%.*s'",
 			      LIT(ce->proc->Ident.token.string));
 			      LIT(ce->proc->Ident.token.string));
 		}
 		}
 		err = CallArgumentError_NonVariadicExpand;
 		err = CallArgumentError_NonVariadicExpand;
@@ -3998,21 +4018,22 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 					if (are_types_identical(e->type, o.type)) {
 					if (are_types_identical(e->type, o.type)) {
 						score += assign_score_function(1);
 						score += assign_score_function(1);
 					} else {
 					} else {
-						score += assign_score_function(10);
+						score += assign_score_function(MAXIMUM_TYPE_DISTANCE);
 					}
 					}
 
 
 					continue;
 					continue;
 				}
 				}
 
 
+				bool param_is_variadic = pt->variadic && pt->variadic_index == operand_index;
 
 
 				i64 s = 0;
 				i64 s = 0;
-				if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
+				if (!check_is_assignable_to_with_score(c, &o, t, &s, param_is_variadic)) {
 					bool ok = false;
 					bool ok = false;
 					if (e->flags & EntityFlag_AutoCast) {
 					if (e->flags & EntityFlag_AutoCast) {
 						ok = check_is_castable_to(c, &o, t);
 						ok = check_is_castable_to(c, &o, t);
 					}
 					}
 					if (ok) {
 					if (ok) {
-						s = assign_score_function(10);
+						s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
 					} else {
 					} else {
 						if (show_error) {
 						if (show_error) {
 							check_assignment(c, &o, t, str_lit("argument"));
 							check_assignment(c, &o, t, str_lit("argument"));
@@ -4036,7 +4057,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 						t = slice;
 						t = slice;
 						if (operand_index != param_count) {
 						if (operand_index != param_count) {
 							if (show_error) {
 							if (show_error) {
-								error(o.expr, "'...' in a variadic procedure can only have one variadic argument at the end");
+								error(o.expr, "'..' in a variadic procedure can only have one variadic argument at the end");
 							}
 							}
 							if (data) {
 							if (data) {
 								data->score = score;
 								data->score = score;
@@ -4047,7 +4068,7 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
 						}
 						}
 					}
 					}
 					i64 s = 0;
 					i64 s = 0;
-					if (!check_is_assignable_to_with_score(c, &o, t, &s)) {
+					if (!check_is_assignable_to_with_score(c, &o, t, &s, true)) {
 						if (show_error) {
 						if (show_error) {
 							check_assignment(c, &o, t, str_lit("argument"));
 							check_assignment(c, &o, t, str_lit("argument"));
 						}
 						}
@@ -4224,17 +4245,18 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
 			if (are_types_identical(e->type, o->type)) {
 			if (are_types_identical(e->type, o->type)) {
 				score += assign_score_function(1);
 				score += assign_score_function(1);
 			} else {
 			} else {
-				score += assign_score_function(10);
+				score += assign_score_function(MAXIMUM_TYPE_DISTANCE);
 			}
 			}
 		} else {
 		} else {
 			i64 s = 0;
 			i64 s = 0;
-			if (!check_is_assignable_to_with_score(c, o, e->type, &s)) {
+			bool param_is_variadic = pt->variadic && pt->variadic_index == i;
+			if (!check_is_assignable_to_with_score(c, o, e->type, &s, param_is_variadic)) {
 				bool ok = false;
 				bool ok = false;
 				if (e->flags & EntityFlag_AutoCast) {
 				if (e->flags & EntityFlag_AutoCast) {
 					ok = check_is_castable_to(c, o, e->type);
 					ok = check_is_castable_to(c, o, e->type);
 				}
 				}
 				if (ok) {
 				if (ok) {
-					s = assign_score_function(10);
+					s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
 				} else {
 				} else {
 					if (show_error) {
 					if (show_error) {
 						check_assignment(c, o, e->type, str_lit("procedure argument"));
 						check_assignment(c, o, e->type, str_lit("procedure argument"));
@@ -4276,7 +4298,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 
 
 		bool vari_expand = (ce->ellipsis.pos.line != 0);
 		bool vari_expand = (ce->ellipsis.pos.line != 0);
 		if (vari_expand) {
 		if (vari_expand) {
-			// error(ce->ellipsis, "Invalid use of '...' with 'field = value' call'");
+			// error(ce->ellipsis, "Invalid use of '..' with 'field = value' call'");
 		}
 		}
 
 
 	} else {
 	} else {
@@ -4451,6 +4473,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type
 					sep = ":=";
 					sep = ":=";
 				}
 				}
 				gb_printf_err("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column);
 				gb_printf_err("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column);
+				// gb_printf_err("\t%.*s %s %s at %.*s(%td:%td) %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, valids[i].score);
 			}
 			}
 			result_type = t_invalid;
 			result_type = t_invalid;
 		} else {
 		} else {
@@ -4538,7 +4561,7 @@ CallArgumentError check_polymorphic_struct_type(CheckerContext *c, Operand *oper
 
 
 		bool vari_expand = (ce->ellipsis.pos.line != 0);
 		bool vari_expand = (ce->ellipsis.pos.line != 0);
 		if (vari_expand) {
 		if (vari_expand) {
-			error(ce->ellipsis, "Invalid use of '...' in a polymorphic type call'");
+			error(ce->ellipsis, "Invalid use of '..' in a polymorphic type call'");
 		}
 		}
 
 
 	} else {
 	} else {
@@ -4637,7 +4660,7 @@ CallArgumentError check_polymorphic_struct_type(CheckerContext *c, Operand *oper
 			if (are_types_identical(e->type, o->type)) {
 			if (are_types_identical(e->type, o->type)) {
 				score += assign_score_function(1);
 				score += assign_score_function(1);
 			} else {
 			} else {
-				score += assign_score_function(10);
+				score += assign_score_function(MAXIMUM_TYPE_DISTANCE);
 			}
 			}
 		} else {
 		} else {
 			i64 s = 0;
 			i64 s = 0;
@@ -6256,7 +6279,7 @@ gbString write_expr_to_string(gbString str, Ast *node) {
 	case_end;
 	case_end;
 
 
 	case_ast_node(e, Ellipsis, node);
 	case_ast_node(e, Ellipsis, node);
-		str = gb_string_appendc(str, "...");
+		str = gb_string_appendc(str, "..");
 		str = write_expr_to_string(str, e->expr);
 		str = write_expr_to_string(str, e->expr);
 	case_end;
 	case_end;
 
 

+ 1 - 1
src/types.cpp

@@ -2554,7 +2554,7 @@ gbString write_type_to_string(gbString str, Type *type) {
 						}
 						}
 						if (var->flags&EntityFlag_Ellipsis) {
 						if (var->flags&EntityFlag_Ellipsis) {
 							Type *slice = base_type(var->type);
 							Type *slice = base_type(var->type);
-							str = gb_string_appendc(str, "...");
+							str = gb_string_appendc(str, "..");
 							GB_ASSERT(var->type->kind == Type_Slice);
 							GB_ASSERT(var->type->kind == Type_Slice);
 							str = write_type_to_string(str, slice->Slice.elem);
 							str = write_type_to_string(str, slice->Slice.elem);
 						} else {
 						} else {