Browse Source

Enforce naming the parameters with `builtin.quaternion` to reduce confusion

gingerBill 1 year ago
parent
commit
0b83e3dae5

+ 3 - 3
core/encoding/json/unmarshal.odin

@@ -137,9 +137,9 @@ assign_float :: proc(val: any, f: $T) -> bool {
 	case complex64:  dst = complex(f32(f), 0)
 	case complex64:  dst = complex(f32(f), 0)
 	case complex128: dst = complex(f64(f), 0)
 	case complex128: dst = complex(f64(f), 0)
 	
 	
-	case quaternion64:  dst = quaternion(f16(f), 0, 0, 0)
-	case quaternion128: dst = quaternion(f32(f), 0, 0, 0)
-	case quaternion256: dst = quaternion(f64(f), 0, 0, 0)
+	case quaternion64:  dst = quaternion(w=f16(f), x=0, y=0, z=0)
+	case quaternion128: dst = quaternion(w=f32(f), x=0, y=0, z=0)
+	case quaternion256: dst = quaternion(w=f64(f), x=0, y=0, z=0)
 	
 	
 	case: return false
 	case: return false
 	}
 	}

+ 1 - 1
core/math/linalg/general.odin

@@ -70,7 +70,7 @@ outer_product :: builtin.outer_product
 
 
 @(require_results)
 @(require_results)
 quaternion_inverse :: proc "contextless" (q: $Q) -> Q where IS_QUATERNION(Q) {
 quaternion_inverse :: proc "contextless" (q: $Q) -> Q where IS_QUATERNION(Q) {
-	return conj(q) * quaternion(1.0/dot(q, q), 0, 0, 0)
+	return conj(q) * quaternion(w=1.0/dot(q, q), x=0, y=0, z=0)
 }
 }
 
 
 
 

+ 6 - 6
core/runtime/internal.odin

@@ -730,7 +730,7 @@ mul_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
 	t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
 	t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
 	t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
 	t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
 
 
-	return quaternion(t0, t1, t2, t3)
+	return quaternion(w=t0, x=t1, y=t2, z=t3)
 }
 }
 
 
 mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
 mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
@@ -742,7 +742,7 @@ mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
 	t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
 	t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
 	t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
 	t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
 
 
-	return quaternion(t0, t1, t2, t3)
+	return quaternion(w=t0, x=t1, y=t2, z=t3)
 }
 }
 
 
 mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
 mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
@@ -754,7 +754,7 @@ mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
 	t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
 	t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1
 	t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
 	t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0
 
 
-	return quaternion(t0, t1, t2, t3)
+	return quaternion(w=t0, x=t1, y=t2, z=t3)
 }
 }
 
 
 quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
 quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
@@ -768,7 +768,7 @@ quo_quaternion64 :: proc "contextless" (q, r: quaternion64) -> quaternion64 {
 	t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
 	t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
 	t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
 	t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
 
 
-	return quaternion(t0, t1, t2, t3)
+	return quaternion(w=t0, x=t1, y=t2, z=t3)
 }
 }
 
 
 quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
 quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
@@ -782,7 +782,7 @@ quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 {
 	t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
 	t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
 	t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
 	t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
 
 
-	return quaternion(t0, t1, t2, t3)
+	return quaternion(w=t0, x=t1, y=t2, z=t3)
 }
 }
 
 
 quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
 quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
@@ -796,7 +796,7 @@ quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 {
 	t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
 	t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2
 	t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
 	t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2
 
 
-	return quaternion(t0, t1, t2, t3)
+	return quaternion(w=t0, x=t1, y=t2, z=t3)
 }
 }
 
 
 @(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
 @(link_name="__truncsfhf2", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)

+ 5 - 0
core/sys/windows/advapi32.odin

@@ -13,6 +13,11 @@ foreign advapi32 {
 	                         DesiredAccess: DWORD,
 	                         DesiredAccess: DWORD,
 	                         TokenHandle: ^HANDLE) -> BOOL ---
 	                         TokenHandle: ^HANDLE) -> BOOL ---
 
 
+	OpenThreadToken :: proc(ThreadHandle:  HANDLE,
+	                        DesiredAccess: DWORD,
+	                        OpenAsSelf:    BOOL,
+	                        TokenHandle:   ^HANDLE) -> BOOL ---
+
 	CryptAcquireContextW :: proc(hProv: ^HCRYPTPROV, szContainer, szProvider: wstring, dwProvType, dwFlags: DWORD) -> DWORD ---
 	CryptAcquireContextW :: proc(hProv: ^HCRYPTPROV, szContainer, szProvider: wstring, dwProvType, dwFlags: DWORD) -> DWORD ---
 	CryptGenRandom       :: proc(hProv: HCRYPTPROV, dwLen: DWORD, buf: LPVOID) -> DWORD ---
 	CryptGenRandom       :: proc(hProv: HCRYPTPROV, dwLen: DWORD, buf: LPVOID) -> DWORD ---
 	CryptReleaseContext  :: proc(hProv: HCRYPTPROV, dwFlags: DWORD) -> DWORD ---
 	CryptReleaseContext  :: proc(hProv: HCRYPTPROV, dwFlags: DWORD) -> DWORD ---

+ 5 - 3
examples/demo/demo.odin

@@ -1514,7 +1514,7 @@ quaternions :: proc() {
 
 
 	{ // Quaternion operations
 	{ // Quaternion operations
 		q := 1 + 2i + 3j + 4k
 		q := 1 + 2i + 3j + 4k
-		r := quaternion(5, 6, 7, 8)
+		r := quaternion(real=5, imag=6, jmag=7, kmag=8)
 		t := q * r
 		t := q * r
 		fmt.printf("(%v) * (%v) = %v\n", q, r, t)
 		fmt.printf("(%v) * (%v) = %v\n", q, r, t)
 		v := q / r
 		v := q / r
@@ -1527,8 +1527,10 @@ quaternions :: proc() {
 	{ // The quaternion types
 	{ // The quaternion types
 		q128: quaternion128 // 4xf32
 		q128: quaternion128 // 4xf32
 		q256: quaternion256 // 4xf64
 		q256: quaternion256 // 4xf64
-		q128 = quaternion(1, 0, 0, 0)
-		q256 = 1 // quaternion(1, 0, 0, 0)
+		q128 = quaternion(w=1, x=0, y=0, z=0)
+		q256 = 1 // quaternion(x=0, y=0, z=0, w=1)
+
+		// NOTE: The internal memory layout of a quaternion is xyzw
 	}
 	}
 	{ // Built-in procedures
 	{ // Built-in procedures
 		q := 1 + 2i + 3j + 4k
 		q := 1 + 2i + 3j + 4k

+ 124 - 15
src/check_builtin.cpp

@@ -1666,7 +1666,12 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 
 
 	if (ce->args.count > 0) {
 	if (ce->args.count > 0) {
 		if (ce->args[0]->kind == Ast_FieldValue) {
 		if (ce->args[0]->kind == Ast_FieldValue) {
-			if (id != BuiltinProc_soa_zip) {
+			switch (id) {
+			case BuiltinProc_soa_zip:
+			case BuiltinProc_quaternion:
+				// okay
+				break;
+			default:
 				error(call, "'field = value' calling is not allowed on built-in procedures");
 				error(call, "'field = value' calling is not allowed on built-in procedures");
 				return false;
 				return false;
 			}
 			}
@@ -2299,8 +2304,32 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 	}
 	}
 
 
 	case BuiltinProc_quaternion: {
 	case BuiltinProc_quaternion: {
+		bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
+
+		bool fail = false;
+		for (Ast *arg : ce->args) {
+			bool mix = false;
+			if (first_is_field_value) {
+				mix = arg->kind != Ast_FieldValue;
+			} else {
+				mix = arg->kind == Ast_FieldValue;
+			}
+			if (mix) {
+				error(arg, "Mixture of 'field = value' and value elements in the procedure call '%.*s' is not allowed", LIT(builtin_name));
+				fail = true;
+				break;
+			}
+		}
+
+		if (fail) {
+			operand->type = t_untyped_quaternion;
+			operand->mode = Addressing_Constant;
+			operand->value = exact_value_quaternion(0.0, 0.0, 0.0, 0.0);
+			break;
+		}
+
 		// quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type
 		// quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type
-		Operand x = *operand;
+		Operand x = {};
 		Operand y = {};
 		Operand y = {};
 		Operand z = {};
 		Operand z = {};
 		Operand w = {};
 		Operand w = {};
@@ -2309,23 +2338,103 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		operand->type = t_invalid;
 		operand->type = t_invalid;
 		operand->mode = Addressing_Invalid;
 		operand->mode = Addressing_Invalid;
 
 
-		check_expr(c, &y, ce->args[1]);
-		if (y.mode == Addressing_Invalid) {
-			return false;
-		}
-		check_expr(c, &z, ce->args[2]);
-		if (y.mode == Addressing_Invalid) {
-			return false;
-		}
-		check_expr(c, &w, ce->args[3]);
-		if (y.mode == Addressing_Invalid) {
-			return false;
-		}
+		if (first_is_field_value) {
+			u32 fields_set[4] = {}; // 0 unset, 1 xyzw, 2 real/etc
+
+			auto const check_field = [&fields_set, &builtin_name](CheckerContext *c, Operand *o, Ast *arg, i32 *index) -> bool {
+				*index = -1;
+
+				ast_node(field, FieldValue, arg);
+				String name = {};
+				if (field->field->kind == Ast_Ident) {
+					name = field->field->Ident.token.string;
+				} else {
+					error(field->field, "Expected an identifier for field argument");
+					return false;
+				}
+
+				u32 style = 0;
+
+				if (name == "x") {
+					*index = 1; style = 1;
+				} else if (name == "y") {
+					*index = 2; style = 1;
+				} else if (name == "z") {
+					*index = 3; style = 1;
+				}  else if (name == "w") {
+					*index = 0; style = 1;
+				} else if (name == "imag") {
+					*index = 1; style = 2;
+				} else if (name == "jmag") {
+					*index = 2; style = 2;
+				} else if (name == "kmag") {
+					*index = 3; style = 2;
+				}  else if (name == "real") {
+					*index = 0; style = 2;
+				} else {
+					error(field->field, "Unknown name for '%.*s', expected (w, x, y, z; or real, imag, jmag, kmag), got '%.*s'", LIT(builtin_name), LIT(name));
+					return false;
+				}
+
+				if (fields_set[*index]) {
+					error(field->field, "Previously assigned field: '%.*s'", LIT(name));
+				}
+				fields_set[*index] = style;
+
+				check_expr(c, o, field->value);
+				return o->mode != Addressing_Invalid;
+			};
+
+			// TODO(bill): disallow style mixing
 
 
+			Operand *refs[4] = {&x, &y, &z, &w};
+
+			for (i32 i = 0; i < 4; i++) {
+				i32 index = -1;
+				Operand o = {};
+				bool ok = check_field(c, &o, ce->args[i], &index);
+				if (!ok) {
+					return false;
+				}
+
+				*refs[index] = o;
+			}
+
+			for (i32 i = 0; i < 4; i++) {
+				GB_ASSERT(fields_set[i]);
+			}
+			for (i32 i = 1; i < 4; i++) {
+				if (fields_set[i] != fields_set[i-1]) {
+					error(call, "Mixture of xyzw and real/etc is not allowed with '%.*s'", LIT(builtin_name));
+					break;
+				}
+			}
+		} else {
+			error(call, "'%.*s' requires that all arguments are named (w, x, y, z; or real, imag, jmag, kmag)", LIT(builtin_name));
+
+			check_expr(c, &x, ce->args[0]);
+			if (x.mode == Addressing_Invalid) {
+				return false;
+			}
+
+			check_expr(c, &y, ce->args[1]);
+			if (y.mode == Addressing_Invalid) {
+				return false;
+			}
+			check_expr(c, &z, ce->args[2]);
+			if (z.mode == Addressing_Invalid) {
+				return false;
+			}
+			check_expr(c, &w, ce->args[3]);
+			if (w.mode == Addressing_Invalid) {
+				return false;
+			}
+		}
 		convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false;
 		convert_to_typed(c, &x, y.type); if (x.mode == Addressing_Invalid) return false;
 		convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
 		convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
 		convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false;
 		convert_to_typed(c, &z, x.type); if (z.mode == Addressing_Invalid) return false;
 		convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false;
 		convert_to_typed(c, &w, x.type); if (w.mode == Addressing_Invalid) return false;
+
 		if (x.mode == Addressing_Constant &&
 		if (x.mode == Addressing_Constant &&
 		    y.mode == Addressing_Constant &&
 		    y.mode == Addressing_Constant &&
 		    z.mode == Addressing_Constant &&
 		    z.mode == Addressing_Constant &&
@@ -3092,7 +3201,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 				mix = arg->kind == Ast_FieldValue;
 				mix = arg->kind == Ast_FieldValue;
 			}
 			}
 			if (mix) {
 			if (mix) {
-				error(arg, "Mixture of 'field = value' and value elements in the procedure call 'soa_zip' is not allowed");
+				error(arg, "Mixture of 'field = value' and value elements in the procedure call '%.*s' is not allowed", LIT(builtin_name));
 				fail = true;
 				fail = true;
 				break;
 				break;
 			}
 			}

+ 30 - 13
src/llvm_backend_proc.cpp

@@ -1826,24 +1826,41 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
 	}
 	}
 
 
 	case BuiltinProc_quaternion: {
 	case BuiltinProc_quaternion: {
-		lbValue real = lb_build_expr(p, ce->args[0]);
-		lbValue imag = lb_build_expr(p, ce->args[1]);
-		lbValue jmag = lb_build_expr(p, ce->args[2]);
-		lbValue kmag = lb_build_expr(p, ce->args[3]);
+		lbValue xyzw[4] = {};
+		for (i32 i = 0; i < 4; i++) {
+			ast_node(f, FieldValue, ce->args[i]);
+			GB_ASSERT(f->field->kind == Ast_Ident);
+			String name = f->field->Ident.token.string;
+			i32 index = -1;
+
+			// @QuaternionLayout
+			if (name == "x" || name == "imag") {
+				index = 0;
+			} else if (name == "y" || name == "jmag") {
+				index = 1;
+			} else if (name == "z" || name == "kmag") {
+				index = 2;
+			} else if (name == "w" || name == "real") {
+				index = 3;
+			}
+			GB_ASSERT(index >= 0);
+
+			xyzw[index] = lb_build_expr(p, ce->args[i]);
+		}
+
 
 
-		// @QuaternionLayout
 		lbAddr dst_addr = lb_add_local_generated(p, tv.type, false);
 		lbAddr dst_addr = lb_add_local_generated(p, tv.type, false);
 		lbValue dst = lb_addr_get_ptr(p, dst_addr);
 		lbValue dst = lb_addr_get_ptr(p, dst_addr);
 
 
 		Type *ft = base_complex_elem_type(tv.type);
 		Type *ft = base_complex_elem_type(tv.type);
-		real = lb_emit_conv(p, real, ft);
-		imag = lb_emit_conv(p, imag, ft);
-		jmag = lb_emit_conv(p, jmag, ft);
-		kmag = lb_emit_conv(p, kmag, ft);
-		lb_emit_store(p, lb_emit_struct_ep(p, dst, 3), real);
-		lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), imag);
-		lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), jmag);
-		lb_emit_store(p, lb_emit_struct_ep(p, dst, 2), kmag);
+		xyzw[0] = lb_emit_conv(p, xyzw[0], ft);
+		xyzw[1] = lb_emit_conv(p, xyzw[1], ft);
+		xyzw[2] = lb_emit_conv(p, xyzw[2], ft);
+		xyzw[3] = lb_emit_conv(p, xyzw[3], ft);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 0), xyzw[0]);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 1), xyzw[1]);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 2), xyzw[2]);
+		lb_emit_store(p, lb_emit_struct_ep(p, dst, 3), xyzw[3]);
 
 
 		return lb_emit_load(p, dst);
 		return lb_emit_load(p, dst);
 	}
 	}