|
@@ -1666,7 +1666,12 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
|
|
|
|
|
if (ce->args.count > 0) {
|
|
|
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");
|
|
|
return false;
|
|
|
}
|
|
@@ -2299,8 +2304,32 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
|
|
}
|
|
|
|
|
|
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
|
|
|
- Operand x = *operand;
|
|
|
+ Operand x = {};
|
|
|
Operand y = {};
|
|
|
Operand z = {};
|
|
|
Operand w = {};
|
|
@@ -2309,23 +2338,103 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
|
|
operand->type = t_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, &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, &w, x.type); if (w.mode == Addressing_Invalid) return false;
|
|
|
+
|
|
|
if (x.mode == Addressing_Constant &&
|
|
|
y.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;
|
|
|
}
|
|
|
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;
|
|
|
break;
|
|
|
}
|