From 37633c1d2a9cc2ab5ae659ffb359f7f56f0457a2 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 4 Aug 2019 11:02:00 +0100 Subject: `intrinsics.type_*` constant evaluation procedures --- src/exact_value.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'src/exact_value.cpp') diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 428690291..7d822512b 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -53,14 +53,8 @@ HashKey hash_exact_value(ExactValue v) { return hash_integer(u64(v.value_bool)); case ExactValue_String: return hash_string(v.value_string); - case ExactValue_Integer: { - u64 *d = big_int_ptr(&v.value_integer); - u64 x = 0; - for (i32 i = 0; i < v.value_integer.len; i++) { - x |= d[i]; - } - return hash_integer(x); - } + case ExactValue_Integer: + return hashing_proc(big_int_ptr(&v.value_integer), v.value_integer.len * gb_size_of(u64)); case ExactValue_Float: return hash_f64(v.value_float); case ExactValue_Pointer: -- cgit v1.2.3 From cf23954297993b3e574c295f95f75b294fdca4ec Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 23 Aug 2019 11:51:04 +0100 Subject: Improve #assert to show the procedure and signature it was called with; Allow the ability to print ExactValue correct now. --- src/check_expr.cpp | 9 +++++- src/exact_value.cpp | 46 +++++++++++++++++++++++++++++ src/string.cpp | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++- src/types.cpp | 65 ++++++++++++++++++++++++++--------------- 4 files changed, 179 insertions(+), 25 deletions(-) (limited to 'src/exact_value.cpp') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 6c252b9b6..24a130d62 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -3360,6 +3360,11 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (!operand->value.value_bool) { gbString arg = expr_to_string(ce->args[0]); error(call, "Compile time assertion: %s", arg); + if (c->proc_name != "") { + gbString str = type_to_string(c->curr_proc_sig); + error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str); + gb_string_free(str); + } gb_string_free(arg); } @@ -3744,7 +3749,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } if (x.mode == Addressing_Constant && y.mode == Addressing_Constant) { - operand->value = exact_binary_operator_value(Token_Add, x.value, y.value); + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + operand->value = exact_value_complex(r, i); operand->mode = Addressing_Constant; } else { operand->mode = Addressing_Value; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 7d822512b..be034ecc5 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -718,3 +718,49 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { GB_PANIC("Invalid comparison"); return false; } + + +gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize string_limit=36) { + switch (v.kind) { + case ExactValue_Invalid: + return str; + case ExactValue_Bool: + return gb_string_appendc(str, v.value_bool ? "true" : "false"); + case ExactValue_String: { + String s = quote_to_ascii(heap_allocator(), v.value_string); + string_limit = gb_max(string_limit, 36); + if (s.len <= string_limit) { + str = gb_string_append_length(str, s.text, s.len); + } else { + isize n = string_limit/5; + str = gb_string_append_length(str, s.text, n); + str = gb_string_append_fmt(str, "\"..%lld chars..\"", s.len-(2*n)); + str = gb_string_append_length(str, s.text+s.len-n, n); + } + gb_free(heap_allocator(), s.text); + return str; + } + case ExactValue_Integer: { + String s = big_int_to_string(heap_allocator(), &v.value_integer); + str = gb_string_append_length(str, s.text, s.len); + gb_free(heap_allocator(), s.text); + return str; + } + case ExactValue_Float: + return gb_string_append_fmt(str, "%f", v.value_float); + case ExactValue_Complex: + return gb_string_append_fmt(str, "%f+%fi", v.value_complex.real, v.value_complex.imag); + + case ExactValue_Pointer: + return str; + case ExactValue_Compound: + return str; + case ExactValue_Procedure: + return str; + } + return str; +}; + +gbString exact_value_to_string(ExactValue const &v, isize string_limit=35) { + return write_exact_value_to_string(gb_string_make(heap_allocator(), ""), v, string_limit); +} diff --git a/src/string.cpp b/src/string.cpp index 774061edf..a29f3bd77 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -440,12 +440,94 @@ String string16_to_string(gbAllocator a, String16 s) { +bool is_printable(Rune r) { + if (r <= 0xff) { + if (0x20 <= r && r <= 0x7e) { + return true; + } + if (0xa1 <= r && r <= 0xff) { + return r != 0xad; + } + return false; + } + return false; +} +gb_global char const lower_hex[] = "0123456789abcdef"; + +String quote_to_ascii(gbAllocator a, String str, u8 quote='"') { + u8 *s = str.text; + isize n = str.len; + auto buf = array_make(a, 0, n); + array_add(&buf, quote); + for (isize width = 0; n > 0; s += width, n -= width) { + Rune r = cast(Rune)s[0]; + width = 1; + if (r >= 0x80) { + width = gb_utf8_decode(s, n, &r); + } + if (width == 1 && r == GB_RUNE_INVALID) { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[s[0]>>4]); + array_add(&buf, cast(u8)lower_hex[s[0]&0xf]); + continue; + } + if (r == quote || r == '\\') { + array_add(&buf, cast(u8)'\\'); + array_add(&buf, u8(r)); + continue; + } + if (r < 0x80 && is_printable(r)) { + array_add(&buf, u8(r)); + continue; + } + switch (r) { + case '\a': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + case '\v': + default: + if (r < ' ') { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'x'); + array_add(&buf, cast(u8)lower_hex[b>>4]); + array_add(&buf, cast(u8)lower_hex[b&0xf]); + } + if (r > GB_RUNE_MAX) { + r = 0XFFFD; + } + if (r < 0x10000) { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'u'); + for (isize i = 12; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } else { + u8 b = cast(u8)r; + array_add(&buf, cast(u8)'\\'); + array_add(&buf, cast(u8)'U'); + for (isize i = 28; i >= 0; i -= 4) { + array_add(&buf, cast(u8)lower_hex[(r>>i)&0xf]); + } + } + } + } - + array_add(&buf, quote); + String res = {}; + res.text = buf.data; + res.len = buf.count; + return res; +} diff --git a/src/types.cpp b/src/types.cpp index 4404882a9..b5c9152b6 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -2968,35 +2968,54 @@ gbString write_type_to_string(gbString str, Type *type) { isize comma_index = 0; for_array(i, type->Tuple.variables) { Entity *var = type->Tuple.variables[i]; - if (var != nullptr) { - if (var->kind == Entity_Constant) { - // Ignore - continue; + if (var == nullptr) { + continue; + } + String name = var->token.string; + if (var->kind == Entity_Constant) { + str = gb_string_appendc(str, "$"); + str = gb_string_append_length(str, name.text, name.len); + if (!is_type_untyped(var->type)) { + str = gb_string_appendc(str, ": "); + str = write_type_to_string(str, var->type); + str = gb_string_appendc(str, " = "); + str = write_exact_value_to_string(str, var->Constant.value); + } else { + str = gb_string_appendc(str, "="); + str = write_exact_value_to_string(str, var->Constant.value); } + continue; + } - if (comma_index++ > 0) { - str = gb_string_appendc(str, ", "); - } + if (comma_index++ > 0) { + str = gb_string_appendc(str, ", "); + } - if (var->kind == Entity_Variable) { - if (var->flags&EntityFlag_CVarArg) { - str = gb_string_appendc(str, "#c_vararg "); - } - if (var->flags&EntityFlag_Ellipsis) { - Type *slice = base_type(var->type); - str = gb_string_appendc(str, ".."); - GB_ASSERT(var->type->kind == Type_Slice); - str = write_type_to_string(str, slice->Slice.elem); - } else { - str = write_type_to_string(str, var->type); - } + if (var->kind == Entity_Variable) { + if (var->flags&EntityFlag_CVarArg) { + str = gb_string_appendc(str, "#c_vararg "); + } + if (var->flags&EntityFlag_Ellipsis) { + Type *slice = base_type(var->type); + str = gb_string_appendc(str, ".."); + GB_ASSERT(var->type->kind == Type_Slice); + str = write_type_to_string(str, slice->Slice.elem); + } else { + str = write_type_to_string(str, var->type); + } + } else { + GB_ASSERT(var->kind == Entity_TypeName); + if (var->type->kind == Type_Generic) { + str = gb_string_appendc(str, "typeid/"); + str = write_type_to_string(str, var->type); } else { - GB_ASSERT(var->kind == Entity_TypeName); - if (var->type->kind == Type_Generic) { - str = gb_string_appendc(str, "type/"); + if (var->kind == Entity_TypeName) { + str = gb_string_appendc(str, "$"); + str = gb_string_append_length(str, name.text, name.len); + str = gb_string_appendc(str, "="); str = write_type_to_string(str, var->type); } else { - str = gb_string_appendc(str, "type"); + str = gb_string_appendc(str, "typeid"); } } } -- cgit v1.2.3 From 59ab51acecd22674a7cc8607cd3af8eaeb55068a Mon Sep 17 00:00:00 2001 From: gingerBill Date: Fri, 23 Aug 2019 11:54:23 +0100 Subject: Fix typo --- src/exact_value.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/exact_value.cpp') diff --git a/src/exact_value.cpp b/src/exact_value.cpp index be034ecc5..520812e44 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -761,6 +761,6 @@ gbString write_exact_value_to_string(gbString str, ExactValue const &v, isize st return str; }; -gbString exact_value_to_string(ExactValue const &v, isize string_limit=35) { +gbString exact_value_to_string(ExactValue const &v, isize string_limit=36) { return write_exact_value_to_string(gb_string_make(heap_allocator(), ""), v, string_limit); } -- cgit v1.2.3 From 7bc146e6fde909298a7184fdf00cec91868ffc00 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Aug 2019 11:33:05 +0100 Subject: Built-in Quaternions (Not just an April Fool's Joke any more) --- core/fmt/fmt.odin | 29 +++++ core/reflect/reflect.odin | 2 + core/runtime/core.odin | 23 ++-- core/runtime/internal.odin | 70 +++++++++++- examples/demo/demo.odin | 60 +++++++++-- src/check_expr.cpp | 242 +++++++++++++++++++++++++++++++++++++++-- src/checker.cpp | 6 ++ src/checker_builtin_procs.hpp | 6 ++ src/exact_value.cpp | 184 +++++++++++++++++++++++++++++-- src/ir.cpp | 245 ++++++++++++++++++++++++++++++++++++++++-- src/ir_print.cpp | 53 +++++++-- src/tokenizer.cpp | 5 +- src/types.cpp | 50 +++++++-- 13 files changed, 909 insertions(+), 66 deletions(-) (limited to 'src/exact_value.cpp') diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 5dbebf972..a2f1166ac 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -1129,6 +1129,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Rune: fmt_arg(fi, v, verb); case runtime.Type_Info_Float: fmt_arg(fi, v, verb); case runtime.Type_Info_Complex: fmt_arg(fi, v, verb); + case runtime.Type_Info_Quaternion: fmt_arg(fi, v, verb); case runtime.Type_Info_String: fmt_arg(fi, v, verb); case runtime.Type_Info_Pointer: @@ -1386,6 +1387,31 @@ fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) { } } +fmt_quaternion :: proc(fi: ^Info, q: quaternion256, bits: int, verb: rune) { + switch verb { + case 'f', 'F', 'v', 'h', 'H': + r, i, j, k := real(q), imag(q), jmag(q), kmag(q); + + fmt_float(fi, r, bits/4, verb); + + if !fi.plus && i >= 0 do strings.write_rune(fi.buf, '+'); + fmt_float(fi, i, bits/4, verb); + strings.write_rune(fi.buf, 'i'); + + if !fi.plus && j >= 0 do strings.write_rune(fi.buf, '+'); + fmt_float(fi, j, bits/4, verb); + strings.write_rune(fi.buf, 'j'); + + if !fi.plus && k >= 0 do strings.write_rune(fi.buf, '+'); + fmt_float(fi, k, bits/4, verb); + strings.write_rune(fi.buf, 'k'); + + case: + fmt_bad_verb(fi, verb); + return; + } +} + fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { if arg == nil { strings.write_string(fi.buf, ""); @@ -1434,6 +1460,9 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case complex64: fmt_complex(fi, complex128(a), 64, verb); case complex128: fmt_complex(fi, a, 128, verb); + case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb); + case quaternion256: fmt_quaternion(fi, a, 256, verb); + case i8: fmt_int(fi, u64(a), true, 8, verb); case u8: fmt_int(fi, u64(a), false, 8, verb); case i16: fmt_int(fi, u64(a), true, 16, verb); diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin index a4450490f..4bc9a2225 100644 --- a/core/reflect/reflect.odin +++ b/core/reflect/reflect.odin @@ -12,6 +12,7 @@ Type_Kind :: enum { Rune, Float, Complex, + Quaternion, String, Boolean, Any, @@ -42,6 +43,7 @@ type_kind :: proc(T: typeid) -> Type_Kind { case runtime.Type_Info_Rune: return .Rune; case runtime.Type_Info_Float: return .Float; case runtime.Type_Info_Complex: return .Complex; + case runtime.Type_Info_Quaternion: return .Quaternion; case runtime.Type_Info_String: return .String; case runtime.Type_Info_Boolean: return .Boolean; case runtime.Type_Info_Any: return .Any; diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 047281b10..022a84e9e 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -47,16 +47,17 @@ Type_Info_Endianness :: enum u8 { } // Variant Types -Type_Info_Named :: struct {name: string, base: ^Type_Info}; -Type_Info_Integer :: struct {signed: bool, endianness: Type_Info_Endianness}; -Type_Info_Rune :: struct {}; -Type_Info_Float :: struct {}; -Type_Info_Complex :: struct {}; -Type_Info_String :: struct {is_cstring: bool}; -Type_Info_Boolean :: struct {}; -Type_Info_Any :: struct {}; -Type_Info_Type_Id :: struct {}; -Type_Info_Pointer :: struct { +Type_Info_Named :: struct {name: string, base: ^Type_Info}; +Type_Info_Integer :: struct {signed: bool, endianness: Type_Info_Endianness}; +Type_Info_Rune :: struct {}; +Type_Info_Float :: struct {}; +Type_Info_Complex :: struct {}; +Type_Info_Quaternion :: struct {}; +Type_Info_String :: struct {is_cstring: bool}; +Type_Info_Boolean :: struct {}; +Type_Info_Any :: struct {}; +Type_Info_Type_Id :: struct {}; +Type_Info_Pointer :: struct { elem: ^Type_Info // nil -> rawptr }; Type_Info_Procedure :: struct { @@ -135,6 +136,7 @@ Type_Info :: struct { Type_Info_Rune, Type_Info_Float, Type_Info_Complex, + Type_Info_Quaternion, Type_Info_String, Type_Info_Boolean, Type_Info_Any, @@ -163,6 +165,7 @@ Typeid_Kind :: enum u8 { Rune, Float, Complex, + Quaternion, String, Boolean, Any, diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index c33d5f62c..a5099944b 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -357,6 +357,11 @@ complex128_eq :: inline proc "contextless" (a, b: complex128) -> bool { return r complex128_ne :: inline proc "contextless" (a, b: complex128) -> bool { return real(a) != real(b) || imag(a) != imag(b); } +quaternion128_eq :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); } +quaternion128_ne :: inline proc "contextless" (a, b: quaternion128) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); } + +quaternion256_eq :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b); } +quaternion256_ne :: inline proc "contextless" (a, b: quaternion256) -> bool { return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b); } bounds_check_error :: proc "contextless" (file: string, line, column: int, index, count: int) { @@ -546,9 +551,16 @@ abs_complex128 :: inline proc "contextless" (x: complex128) -> f64 { r, i := real(x), imag(x); return _sqrt_f64(r*r + i*i); } +abs_quaternion128 :: inline proc "contextless" (x: quaternion128) -> f32 { + r, i, j, k := real(x), imag(x), jmag(x), kmag(x); + return _sqrt_f32(r*r + i*i + j*j + k*k); +} +abs_quaternion256 :: inline proc "contextless" (x: quaternion256) -> f64 { + r, i, j, k := real(x), imag(x), jmag(x), kmag(x); + return _sqrt_f64(r*r + i*i + j*j + k*k); +} - -quo_complex64 :: proc(n, m: complex64) -> complex64 { +quo_complex64 :: proc "contextless" (n, m: complex64) -> complex64 { e, f: f32; if abs(real(m)) >= abs(imag(m)) { @@ -566,7 +578,7 @@ quo_complex64 :: proc(n, m: complex64) -> complex64 { return complex(e, f); } -quo_complex128 :: proc(n, m: complex128) -> complex128 { +quo_complex128 :: proc "contextless" (n, m: complex128) -> complex128 { e, f: f64; if abs(real(m)) >= abs(imag(m)) { @@ -583,3 +595,55 @@ quo_complex128 :: proc(n, m: complex128) -> complex128 { return complex(e, f); } + +mul_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3; + t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2; + t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1; + t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0; + + return quaternion(t0, t1, t2, t3); +} + +mul_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + t0 := r0*q0 - r1*q1 - r2*q2 - r3*q3; + t1 := r0*q1 + r1*q0 - r2*q3 + r3*q2; + t2 := r0*q2 + r1*q3 + r2*q0 - r3*q1; + t3 := r0*q3 - r1*q2 + r2*q1 + r3*q0; + + return quaternion(t0, t1, t2, t3); +} + +quo_quaternion128 :: proc "contextless" (q, r: quaternion128) -> quaternion128 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3); + + t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2; + t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2; + t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2; + t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2; + + return quaternion(t0, t1, t2, t3); +} + +quo_quaternion256 :: proc "contextless" (q, r: quaternion256) -> quaternion256 { + q0, q1, q2, q3 := real(q), imag(q), jmag(q), kmag(q); + r0, r1, r2, r3 := real(r), imag(r), jmag(r), kmag(r); + + invmag2 := 1.0 / (r0*r0 + r1*r1 + r2*r2 + r3*r3); + + t0 := (r0*q0 + r1*q1 + r2*q2 + r3*q3) * invmag2; + t1 := (r0*q1 - r1*q0 - r2*q3 - r3*q2) * invmag2; + t2 := (r0*q2 - r1*q3 - r2*q0 + r3*q1) * invmag2; + t3 := (r0*q3 + r1*q2 + r2*q1 - r3*q0) * invmag2; + + return quaternion(t0, t1, t2, t3); +} diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 343c50ff7..a96056b61 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -326,7 +326,7 @@ union_type :: proc() { } parametric_polymorphism :: proc() { - fmt.println("# parametric_polymorphism"); + fmt.println("\n# parametric_polymorphism"); print_value :: proc(value: $T) { fmt.printf("print_value: %T %v\n", value, value); @@ -561,7 +561,7 @@ prefix_table := [?]string{ threading_example :: proc() { when os.OS == "windows" { - fmt.println("# threading_example"); + fmt.println("\n# threading_example"); worker_proc :: proc(t: ^thread.Thread) -> int { for iteration in 1..5 { @@ -601,7 +601,7 @@ threading_example :: proc() { } array_programming :: proc() { - fmt.println("# array_programming"); + fmt.println("\n# array_programming"); { a := [3]f32{1, 2, 3}; b := [3]f32{5, 6, 7}; @@ -646,7 +646,7 @@ array_programming :: proc() { } named_proc_return_parameters :: proc() { - fmt.println("# named proc return parameters"); + fmt.println("\n# named proc return parameters"); foo0 :: proc() -> int { return 123; @@ -668,7 +668,7 @@ named_proc_return_parameters :: proc() { using_enum :: proc() { - fmt.println("# using enum"); + fmt.println("\n# using enum"); using Foo :: enum {A, B, C}; @@ -680,7 +680,7 @@ using_enum :: proc() { } map_type :: proc() { - fmt.println("# map type"); + fmt.println("\n# map type"); // enums of type u16, u32, i16 & i32 also work Enum_u8 :: enum u8 { @@ -735,7 +735,7 @@ map_type :: proc() { } implicit_selector_expression :: proc() { - fmt.println("# implicit selector expression"); + fmt.println("\n# implicit selector expression"); Foo :: enum {A, B, C}; @@ -763,7 +763,7 @@ implicit_selector_expression :: proc() { } explicit_procedure_overloading :: proc() { - fmt.println("# explicit procedure overloading"); + fmt.println("\n# explicit procedure overloading"); add_ints :: proc(a, b: int) -> int { x := a + b; @@ -797,7 +797,7 @@ explicit_procedure_overloading :: proc() { } complete_switch :: proc() { - fmt.println("# complete_switch"); + fmt.println("\n# complete_switch"); { // enum using Foo :: enum { A, @@ -947,7 +947,7 @@ deferred_procedure_associations :: proc() { } reflection :: proc() { - fmt.println("# reflection"); + fmt.println("\n# reflection"); Foo :: struct { x: int `tag1`, @@ -981,6 +981,45 @@ reflection :: proc() { } } +quaternions :: proc() { + fmt.println("\n# quaternions"); + + { // Quaternion operations + q := 1 + 2i + 3j + 4k; + r := quaternion(5, 6, 7, 8); + t := q * r; + fmt.printf("(%v) * (%v) = %v\n", q, r, t); + v := q / r; + fmt.printf("(%v) / (%v) = %v\n", q, r, v); + u := q + r; + fmt.printf("(%v) + (%v) = %v\n", q, r, u); + s := q - r; + fmt.printf("(%v) - (%v) = %v\n", q, r, s); + } + { // The quaternion types + q128: quaternion128; // 4xf32 + q256: quaternion256; // 4xf64 + q128 = quaternion(1, 0, 0, 0); + q256 = 1; // quaternion(1, 0, 0, 0); + } + { // Built-in procedures + q := 1 + 2i + 3j + 4k; + fmt.println("q =", q); + fmt.println("real(q) =", real(q)); + fmt.println("imag(q) =", imag(q)); + fmt.println("jmag(q) =", jmag(q)); + fmt.println("kmag(q) =", kmag(q)); + fmt.println("conj(q) =", conj(q)); + fmt.println("abs(q) =", abs(q)); + } + { // Conversion of a complex type to a quaternion type + c := 1 + 2i; + q := quaternion256(c); + fmt.println(c); + fmt.println(q); + } +} + main :: proc() { when true { general_stuff(); @@ -1000,5 +1039,6 @@ main :: proc() { diverging_procedures(); deferred_procedure_associations(); reflection(); + quaternions(); } } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 24a130d62..6a9a53275 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -497,6 +497,14 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type if (is_type_complex(dst)) { return 1; } + if (is_type_quaternion(dst)) { + return 2; + } + break; + case Basic_UntypedQuaternion: + if (is_type_quaternion(dst)) { + return 1; + } break; } } @@ -1438,7 +1446,7 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ ExactValue imag = exact_value_imag(v); if (real.kind != ExactValue_Invalid && imag.kind != ExactValue_Invalid) { - if (out_value) *out_value = exact_binary_operator_value(Token_Add, real, exact_value_make_imag(imag)); + if (out_value) *out_value = exact_value_complex(exact_value_to_f64(real), exact_value_to_f64(imag)); return true; } break; @@ -1449,6 +1457,36 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ default: GB_PANIC("Compiler error: Unknown complex type!"); break; } + return false; + } else if (is_type_quaternion(type)) { + ExactValue v = exact_value_to_quaternion(in_value); + if (v.kind != ExactValue_Quaternion) { + return false; + } + + switch (type->Basic.kind) { + case Basic_quaternion128: + case Basic_quaternion256: { + ExactValue real = exact_value_real(v); + ExactValue imag = exact_value_imag(v); + ExactValue jmag = exact_value_jmag(v); + ExactValue kmag = exact_value_kmag(v); + if (real.kind != ExactValue_Invalid && + imag.kind != ExactValue_Invalid) { + if (out_value) *out_value = exact_value_quaternion(exact_value_to_f64(real), exact_value_to_f64(imag), exact_value_to_f64(jmag), exact_value_to_f64(kmag)); + return true; + } + break; + } + case Basic_UntypedComplex: + if (out_value) *out_value = exact_value_to_quaternion(*out_value); + return true; + case Basic_UntypedQuaternion: + return true; + + default: GB_PANIC("Compiler error: Unknown complex type!"); break; + } + return false; } else if (is_type_pointer(type)) { if (in_value.kind == ExactValue_Pointer) { @@ -1481,10 +1519,14 @@ void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { + #if 0 gbAllocator ha = heap_allocator(); String str = big_int_to_string(ha, &o->value.value_integer); defer (gb_free(ha, str.text)); error(o->expr, "'%s = %.*s' overflows '%s'", a, LIT(str), b); + #else + error(o->expr, "Cannot convert '%s' to '%s'", a, b); + #endif } } else { error(o->expr, "Cannot convert '%s' to '%s'", a, b); @@ -1759,6 +1801,21 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } break; } + } else if (is_type_quaternion(x->type) || is_type_quaternion(y->type)) { + switch (op) { + case Token_CmpEq: + switch (8*size) { + case 128: add_package_dependency(c, "runtime", "quaternion128_eq"); break; + case 256: add_package_dependency(c, "runtime", "quaternion256_eq"); break; + } + break; + case Token_NotEq: + switch (8*size) { + case 128: add_package_dependency(c, "runtime", "quaternion128_ne"); break; + case 256: add_package_dependency(c, "runtime", "quaternion256_ne"); break; + } + break; + } } } @@ -1978,6 +2035,14 @@ bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y) { return true; } + if (is_type_complex(src) && is_type_quaternion(dst)) { + return true; + } + + if (is_type_quaternion(src) && is_type_quaternion(dst)) { + return true; + } + if (is_type_bit_field_value(src) && is_type_integer(dst)) { return true; } @@ -2557,6 +2622,8 @@ ExactValue convert_exact_value_for_type(ExactValue v, Type *type) { v = exact_value_to_integer(v); } else if (is_type_complex(t)) { v = exact_value_to_complex(v); + } else if (is_type_quaternion(t)) { + v = exact_value_to_quaternion(v); } return v; } @@ -2652,6 +2719,7 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) { case Basic_UntypedInteger: case Basic_UntypedFloat: case Basic_UntypedComplex: + case Basic_UntypedQuaternion: case Basic_UntypedRune: if (!is_type_numeric(target_type)) { operand->mode = Addressing_Invalid; @@ -3769,10 +3837,98 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } + case BuiltinProc_quaternion: { + // quaternion :: proc(real, imag, jmag, kmag: float_type) -> complex_type + Operand x = *operand; + Operand y = {}; + Operand z = {}; + Operand w = {}; + + // NOTE(bill): Invalid will be the default till fixed + 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; + } + + 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 && + w.mode == Addressing_Constant) { + if (is_type_numeric(x.type) && exact_value_imag(x.value).value_float == 0) { + x.type = t_untyped_float; + } + if (is_type_numeric(y.type) && exact_value_imag(y.value).value_float == 0) { + y.type = t_untyped_float; + } + if (is_type_numeric(z.type) && exact_value_imag(z.value).value_float == 0) { + z.type = t_untyped_float; + } + if (is_type_numeric(w.type) && exact_value_imag(w.value).value_float == 0) { + w.type = t_untyped_float; + } + } + + if (!(are_types_identical(x.type, y.type) && are_types_identical(x.type, z.type) && are_types_identical(x.type, w.type))) { + gbString tx = type_to_string(x.type); + gbString ty = type_to_string(y.type); + gbString tz = type_to_string(z.type); + gbString tw = type_to_string(w.type); + error(call, "Mismatched types to 'quaternion', '%s' vs '%s' vs '%s' vs '%s'", tx, ty, tz, tw); + gb_string_free(tw); + gb_string_free(tz); + gb_string_free(ty); + gb_string_free(tx); + return false; + } + + if (!is_type_float(x.type)) { + gbString s = type_to_string(x.type); + error(call, "Arguments have type '%s', expected a floating point", s); + gb_string_free(s); + return false; + } + + if (x.mode == Addressing_Constant && y.mode == Addressing_Constant && z.mode == Addressing_Constant && w.mode == Addressing_Constant) { + f64 r = exact_value_to_float(x.value).value_float; + f64 i = exact_value_to_float(y.value).value_float; + f64 j = exact_value_to_float(z.value).value_float; + f64 k = exact_value_to_float(w.value).value_float; + operand->value = exact_value_quaternion(r, i, j, k); + operand->mode = Addressing_Constant; + } else { + operand->mode = Addressing_Value; + } + + BasicKind kind = core_type(x.type)->Basic.kind; + switch (kind) { + case Basic_f32: operand->type = t_quaternion128; break; + case Basic_f64: operand->type = t_quaternion256; break; + case Basic_UntypedFloat: operand->type = t_untyped_quaternion; break; + default: GB_PANIC("Invalid type"); break; + } + + break; + } + case BuiltinProc_real: case BuiltinProc_imag: { // real :: proc(x: type) -> float_type - // proc imag(x: type) -> float_type + // imag :: proc(x: type) -> float_type Operand *x = operand; if (is_type_untyped(x->type)) { @@ -3780,7 +3936,12 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (is_type_numeric(x->type)) { x->type = t_untyped_complex; } - } else { + } else if (is_type_quaternion(x->type)) { + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } else{ convert_to_typed(c, x, t_complex128); if (x->mode == Addressing_Invalid) { return false; @@ -3788,9 +3949,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } } - if (!is_type_complex(x->type)) { + if (!is_type_complex(x->type) && !is_type_quaternion(x->type)) { gbString s = type_to_string(x->type); - error(call, "Argument has type '%s', expected a complex type", s); + error(call, "Argument has type '%s', expected a complex or quaternion type", s); gb_string_free(s); return false; } @@ -3808,7 +3969,57 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 switch (kind) { case Basic_complex64: x->type = t_f32; break; case Basic_complex128: x->type = t_f64; break; + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; + default: GB_PANIC("Invalid type"); break; + } + + break; + } + + case BuiltinProc_jmag: + case BuiltinProc_kmag: { + // jmag :: proc(x: type) -> float_type + // kmag :: proc(x: type) -> float_type + + Operand *x = operand; + if (is_type_untyped(x->type)) { + if (x->mode == Addressing_Constant) { + if (is_type_numeric(x->type)) { + x->type = t_untyped_complex; + } + } else{ + convert_to_typed(c, x, t_quaternion256); + if (x->mode == Addressing_Invalid) { + return false; + } + } + } + + if (!is_type_quaternion(x->type)) { + gbString s = type_to_string(x->type); + error(call, "Argument has type '%s', expected a quaternion type", s); + gb_string_free(s); + return false; + } + + if (x->mode == Addressing_Constant) { + switch (id) { + case BuiltinProc_jmag: x->value = exact_value_jmag(x->value); break; + case BuiltinProc_kmag: x->value = exact_value_kmag(x->value); break; + } + } else { + x->mode = Addressing_Value; + } + + BasicKind kind = core_type(x->type)->Basic.kind; + switch (kind) { + case Basic_quaternion128: x->type = t_f32; break; + case Basic_quaternion256: x->type = t_f64; break; + case Basic_UntypedComplex: x->type = t_untyped_float; break; + case Basic_UntypedQuaternion: x->type = t_untyped_float; break; default: GB_PANIC("Invalid type"); break; } @@ -3822,12 +4033,24 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (x->mode == Addressing_Constant) { ExactValue v = exact_value_to_complex(x->value); f64 r = v.value_complex.real; - f64 i = v.value_complex.imag; + f64 i = -v.value_complex.imag; x->value = exact_value_complex(r, i); x->mode = Addressing_Constant; } else { x->mode = Addressing_Value; } + } else if (is_type_quaternion(x->type)) { + if (x->mode == Addressing_Constant) { + ExactValue v = exact_value_to_quaternion(x->value); + f64 r = v.value_quaternion.real; + f64 i = -v.value_quaternion.imag; + f64 j = -v.value_quaternion.jmag; + f64 k = -v.value_quaternion.kmag; + x->value = exact_value_quaternion(r, i, j, k); + x->mode = Addressing_Constant; + } else { + x->mode = Addressing_Value; + } } else { gbString s = type_to_string(x->type); error(call, "Expected a complex or quaternion, got '%s'", s); @@ -4226,6 +4449,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "abs_f64"); if (are_types_identical(bt, t_complex64)) add_package_dependency(c, "runtime", "abs_complex64"); if (are_types_identical(bt, t_complex128)) add_package_dependency(c, "runtime", "abs_complex128"); + if (are_types_identical(bt, t_quaternion128)) add_package_dependency(c, "runtime", "abs_quaternion128"); + if (are_types_identical(bt, t_quaternion256)) add_package_dependency(c, "runtime", "abs_quaternion256"); } } @@ -6232,8 +6457,11 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case Token_Imag: { String s = bl->token.string; Rune r = s[s.len-1]; + // NOTE(bill, 2019-08-25): Allow for quaternions by having j and k imaginary numbers switch (r) { - case 'i': t = t_untyped_complex; break; + case 'i': t = t_untyped_complex; break; + case 'j': t = t_untyped_quaternion; break; + case 'k': t = t_untyped_quaternion; break; } break; diff --git a/src/checker.cpp b/src/checker.cpp index 45b661a58..d6f5ad402 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -1610,6 +1610,10 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("quo_complex64"), str_lit("quo_complex128"), + str_lit("mul_quaternion128"), + str_lit("mul_quaternion256"), + str_lit("quo_quaternion128"), + str_lit("quo_quaternion256"), str_lit("umodti3"), str_lit("udivti3"), @@ -1891,6 +1895,7 @@ void init_core_type_info(Checker *c) { t_type_info_integer = find_core_type(c, str_lit("Type_Info_Integer")); t_type_info_rune = find_core_type(c, str_lit("Type_Info_Rune")); t_type_info_float = find_core_type(c, str_lit("Type_Info_Float")); + t_type_info_quaternion = find_core_type(c, str_lit("Type_Info_Quaternion")); t_type_info_complex = find_core_type(c, str_lit("Type_Info_Complex")); t_type_info_string = find_core_type(c, str_lit("Type_Info_String")); t_type_info_boolean = find_core_type(c, str_lit("Type_Info_Boolean")); @@ -1915,6 +1920,7 @@ void init_core_type_info(Checker *c) { t_type_info_integer_ptr = alloc_type_pointer(t_type_info_integer); t_type_info_rune_ptr = alloc_type_pointer(t_type_info_rune); t_type_info_float_ptr = alloc_type_pointer(t_type_info_float); + t_type_info_quaternion_ptr = alloc_type_pointer(t_type_info_quaternion); t_type_info_complex_ptr = alloc_type_pointer(t_type_info_complex); t_type_info_string_ptr = alloc_type_pointer(t_type_info_string); t_type_info_boolean_ptr = alloc_type_pointer(t_type_info_boolean); diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 536d47616..0135b3c50 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -16,8 +16,11 @@ enum BuiltinProcId { BuiltinProc_swizzle, BuiltinProc_complex, + BuiltinProc_quaternion, BuiltinProc_real, BuiltinProc_imag, + BuiltinProc_jmag, + BuiltinProc_kmag, BuiltinProc_conj, BuiltinProc_expand_to_tuple, @@ -172,8 +175,11 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("complex"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("quaternion"), 4, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("real"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("imag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("jmag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("kmag"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 520812e44..54d4ef6f2 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -12,6 +12,19 @@ bool are_types_identical(Type *x, Type *y); struct Complex128 { f64 real, imag; }; +struct Quaternion256 { + f64 imag, jmag, kmag, real; +}; + +Quaternion256 quaternion256_inverse(Quaternion256 x) { + f64 invmag2 = 1.0 / (x.real*x.real + x.imag*x.imag + x.jmag*x.jmag + x.kmag*x.kmag); + x.real = +x.real * invmag2; + x.imag = -x.imag * invmag2; + x.jmag = -x.jmag * invmag2; + x.kmag = -x.kmag * invmag2; + return x; +} + enum ExactValueKind { ExactValue_Invalid, @@ -21,6 +34,7 @@ enum ExactValueKind { ExactValue_Integer, ExactValue_Float, ExactValue_Complex, + ExactValue_Quaternion, ExactValue_Pointer, ExactValue_Compound, // TODO(bill): Is this good enough? ExactValue_Procedure, // TODO(bill): Is this good enough? @@ -37,6 +51,7 @@ struct ExactValue { f64 value_float; i64 value_pointer; Complex128 value_complex; + Quaternion256 value_quaternion; Ast * value_compound; Ast * value_procedure; }; @@ -61,7 +76,8 @@ HashKey hash_exact_value(ExactValue v) { return hash_integer(v.value_pointer); case ExactValue_Complex: return hashing_proc(&v.value_complex, gb_size_of(Complex128)); - + case ExactValue_Quaternion: + return hashing_proc(&v.value_quaternion, gb_size_of(Quaternion256)); case ExactValue_Compound: return hash_pointer(v.value_compound); case ExactValue_Procedure: @@ -116,6 +132,15 @@ ExactValue exact_value_complex(f64 real, f64 imag) { return result; } +ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) { + ExactValue result = {ExactValue_Quaternion}; + result.value_quaternion.real = real; + result.value_quaternion.imag = imag; + result.value_quaternion.jmag = jmag; + result.value_quaternion.kmag = kmag; + return result; +} + ExactValue exact_value_pointer(i64 ptr) { ExactValue result = {ExactValue_Pointer}; result.value_pointer = ptr; @@ -253,8 +278,10 @@ ExactValue exact_value_from_basic_literal(Token token) { str.len--; // Ignore the 'i|j|k' f64 imag = float_from_string(str); - if (last_rune == 'i') { - return exact_value_complex(0, imag); + switch (last_rune) { + case 'i': return exact_value_complex(0, imag); + case 'j': return exact_value_quaternion(0, 0, imag, 0); + case 'k': return exact_value_quaternion(0, 0, 0, imag); } } case Token_Rune: { @@ -318,11 +345,26 @@ ExactValue exact_value_to_complex(ExactValue v) { return exact_value_complex(v.value_float, 0); case ExactValue_Complex: return v; + // case ExactValue_Quaternion: + // return exact_value_complex(v.value_quaternion.real, v.value_quaternion.imag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} +ExactValue exact_value_to_quaternion(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(big_int_to_f64(&v.value_integer), 0, 0, 0); + case ExactValue_Float: + return exact_value_quaternion(v.value_float, 0, 0, 0); + case ExactValue_Complex: + return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0); + case ExactValue_Quaternion: + return v; } ExactValue r = {ExactValue_Invalid}; return r; } - ExactValue exact_value_real(ExactValue v) { switch (v.kind) { @@ -331,6 +373,8 @@ ExactValue exact_value_real(ExactValue v) { return v; case ExactValue_Complex: return exact_value_float(v.value_complex.real); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.real); } ExactValue r = {ExactValue_Invalid}; return r; @@ -343,6 +387,34 @@ ExactValue exact_value_imag(ExactValue v) { return exact_value_i64(0); case ExactValue_Complex: return exact_value_float(v.value_complex.imag); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.imag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_jmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + case ExactValue_Complex: + return exact_value_i64(0); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.jmag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_kmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + case ExactValue_Complex: + return exact_value_i64(0); + case ExactValue_Quaternion: + return exact_value_float(v.value_quaternion.kmag); } ExactValue r = {ExactValue_Invalid}; return r; @@ -361,6 +433,32 @@ ExactValue exact_value_make_imag(ExactValue v) { return r; } +ExactValue exact_value_make_jmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(0, 0, exact_value_to_float(v).value_float, 0); + case ExactValue_Float: + return exact_value_quaternion(0, 0, v.value_float, 0); + default: + GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'"); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_make_kmag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_quaternion(0, 0, 0, exact_value_to_float(v).value_float); + case ExactValue_Float: + return exact_value_quaternion(0, 0, 0, v.value_float); + default: + GB_PANIC("Expected an integer or float type for 'exact_value_make_imag'"); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + i64 exact_value_to_i64(ExactValue v) { v = exact_value_to_integer(v); if (v.kind == ExactValue_Integer) { @@ -389,6 +487,7 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, case ExactValue_Integer: case ExactValue_Float: case ExactValue_Complex: + case ExactValue_Quaternion: return v; } break; @@ -413,6 +512,13 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision, f64 imag = v.value_complex.imag; return exact_value_complex(-real, -imag); } + case ExactValue_Quaternion: { + f64 real = v.value_quaternion.real; + f64 imag = v.value_quaternion.imag; + f64 jmag = v.value_quaternion.jmag; + f64 kmag = v.value_quaternion.kmag; + return exact_value_quaternion(-real, -imag, -jmag, -kmag); + } } break; } @@ -463,8 +569,10 @@ i32 exact_value_order(ExactValue const &v) { return 3; case ExactValue_Complex: return 4; - case ExactValue_Pointer: + case ExactValue_Quaternion: return 5; + case ExactValue_Pointer: + return 6; default: GB_PANIC("How'd you get here? Invalid Value.kind"); @@ -485,7 +593,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: - case ExactValue_Complex: + case ExactValue_Quaternion: return; case ExactValue_Integer: @@ -499,6 +607,9 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Complex: *x = exact_value_complex(big_int_to_f64(&x->value_integer), 0); return; + case ExactValue_Quaternion: + *x = exact_value_quaternion(big_int_to_f64(&x->value_integer), 0, 0, 0); + return; } break; @@ -509,6 +620,17 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Complex: *x = exact_value_to_complex(*x); return; + case ExactValue_Quaternion: + *x = exact_value_to_quaternion(*x); + return; + } + break; + + case ExactValue_Complex: + switch (y->kind) { + case ExactValue_Quaternion: + *x = exact_value_to_quaternion(*x); + return; } break; } @@ -606,6 +728,56 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) break; } + case ExactValue_Quaternion: { + y = exact_value_to_quaternion(y); + f64 xr = x.value_quaternion.real; + f64 xi = x.value_quaternion.imag; + f64 xj = x.value_quaternion.jmag; + f64 xk = x.value_quaternion.kmag; + f64 yr = y.value_quaternion.real; + f64 yi = y.value_quaternion.imag; + f64 yj = y.value_quaternion.jmag; + f64 yk = y.value_quaternion.kmag; + + + f64 real = 0; + f64 imag = 0; + f64 jmag = 0; + f64 kmag = 0; + + switch (op) { + case Token_Add: + real = xr + yr; + imag = xi + yi; + jmag = xj + yj; + kmag = xk + yk; + break; + case Token_Sub: + real = xr - yr; + imag = xi - yi; + jmag = xj - yj; + kmag = xk - yk; + break; + case Token_Mul: + imag = xr * yi + xi * yr + xj * yk - xk * yj; + jmag = xr * yj - xi * yk + xj * yr + xk * yi; + kmag = xr * yk + xi * yj - xj * yi + xk * yr; + real = xr * yr - xi * yi - xj * yj - xk * yk; + break; + case Token_Quo: { + f64 invmag2 = 1.0 / (yr*yr + yi*yi + yj*yj + yk*yk); + imag = (xr * -yi + xi * +yr + xj * -yk - xk * -yj) * invmag2; + jmag = (xr * -yj - xi * -yk + xj * +yr + xk * -yi) * invmag2; + kmag = (xr * -yk + xi * -yj - xj * -yi + xk * +yr) * invmag2; + real = (xr * +yr - xi * -yi - xj * -yj - xk * -yk) * invmag2; + break; + } + default: goto error; + } + return exact_value_quaternion(real, imag, jmag, kmag); + break; + } + case ExactValue_String: { if (op != Token_Add) goto error; diff --git a/src/ir.cpp b/src/ir.cpp index 2499b848f..2a4bb028b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -3853,6 +3853,60 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * return ir_emit_load(proc, res); } + if (is_type_quaternion(t_left)) { + ir_emit_comment(proc, str_lit("complex.arith.begin")); + defer (ir_emit_comment(proc, str_lit("complex.arith.end"))); + + right = ir_emit_conv(proc, right, t_left); + + Type *ft = base_complex_elem_type(t_left); + + if (op == Token_Add || op == Token_Sub) { + irValue *res = ir_add_local_generated(proc, type, false); // NOTE: initialized in full later + irValue *x0 = ir_emit_struct_ev(proc, left, 0); + irValue *x1 = ir_emit_struct_ev(proc, left, 1); + irValue *x2 = ir_emit_struct_ev(proc, left, 2); + irValue *x3 = ir_emit_struct_ev(proc, left, 3); + + irValue *y0 = ir_emit_struct_ev(proc, right, 0); + irValue *y1 = ir_emit_struct_ev(proc, right, 1); + irValue *y2 = ir_emit_struct_ev(proc, right, 2); + irValue *y3 = ir_emit_struct_ev(proc, right, 3); + + irValue *z0 = ir_emit_arith(proc, op, x0, y0, ft); + irValue *z1 = ir_emit_arith(proc, op, x1, y1, ft); + irValue *z2 = ir_emit_arith(proc, op, x2, y2, ft); + irValue *z3 = ir_emit_arith(proc, op, x3, y3, ft); + + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), z0); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), z1); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), z2); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), z3); + + return ir_emit_load(proc, res); + } else if (op == Token_Mul) { + auto args = array_make(heap_allocator(), 2); + args[0] = left; + args[1] = right; + + switch (8*type_size_of(ft)) { + case 32: return ir_emit_runtime_call(proc, "mul_quaternion128", args); + case 64: return ir_emit_runtime_call(proc, "mul_quaternion256", args); + default: GB_PANIC("Unknown float type"); break; + } + } else if (op == Token_Quo) { + auto args = array_make(heap_allocator(), 2); + args[0] = left; + args[1] = right; + + switch (8*type_size_of(ft)) { + case 32: return ir_emit_runtime_call(proc, "quo_quaternion128", args); + case 64: return ir_emit_runtime_call(proc, "quo_quaternion256", args); + default: GB_PANIC("Unknown float type"); break; + } + } + } + #if 0 if (op == Token_Add) { @@ -4188,7 +4242,7 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal } if (is_type_complex(a)) { - char *runtime_proc = ""; + char const *runtime_proc = ""; i64 sz = 8*type_size_of(a); switch (sz) { case 64: @@ -4212,6 +4266,31 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal return ir_emit_runtime_call(proc, runtime_proc, args); } + if (is_type_quaternion(a)) { + char const *runtime_proc = ""; + i64 sz = 8*type_size_of(a); + switch (sz) { + case 128: + switch (op_kind) { + case Token_CmpEq: runtime_proc = "quaternion128_eq"; break; + case Token_NotEq: runtime_proc = "quaternion128_ne"; break; + } + break; + case 256: + switch (op_kind) { + case Token_CmpEq: runtime_proc = "quaternion256_eq"; break; + case Token_NotEq: runtime_proc = "quaternion256_ne"; break; + } + break; + } + GB_ASSERT(runtime_proc != nullptr); + + auto args = array_make(ir_allocator(), 2); + args[0] = left; + args[1] = right; + return ir_emit_runtime_call(proc, runtime_proc, args); + } + if (is_type_bit_set(a)) { switch (op_kind) { case Token_Lt: @@ -4295,11 +4374,18 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) { case 0: result_type = alloc_type_pointer(ft); break; case 1: result_type = alloc_type_pointer(ft); break; } + } else if (is_type_quaternion(t)) { + Type *ft = base_complex_elem_type(t); + switch (index) { + case 0: result_type = alloc_type_pointer(ft); break; + case 1: result_type = alloc_type_pointer(ft); break; + case 2: result_type = alloc_type_pointer(ft); break; + case 3: result_type = alloc_type_pointer(ft); break; + } } else if (is_type_slice(t)) { switch (index) { case 0: result_type = alloc_type_pointer(alloc_type_pointer(t->Slice.elem)); break; case 1: result_type = alloc_type_pointer(t_int); break; - case 2: result_type = alloc_type_pointer(t_int); break; } } else if (is_type_string(t)) { switch (index) { @@ -4370,6 +4456,17 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { } break; } + case Basic_quaternion128: case Basic_quaternion256: + { + Type *ft = base_complex_elem_type(t); + switch (index) { + case 0: result_type = ft; break; + case 1: result_type = ft; break; + case 2: result_type = ft; break; + case 3: result_type = ft; break; + } + break; + } } break; case Type_Struct: @@ -4752,6 +4849,8 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { ev = exact_value_to_float(ev); } else if (is_type_complex(dst)) { ev = exact_value_to_complex(ev); + } else if (is_type_quaternion(dst)) { + ev = exact_value_to_quaternion(ev); } else if (is_type_string(dst)) { // Handled elsewhere GB_ASSERT_MSG(ev.kind == ExactValue_String, "%d", ev.kind); @@ -4875,6 +4974,49 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { return ir_emit_load(proc, gen); } + if (is_type_quaternion(src) && is_type_quaternion(dst)) { + // @QuaternionLayout + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, false); + irValue *q0 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft); + irValue *q1 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft); + irValue *q2 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 2), ft); + irValue *q3 = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 3), ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), q0); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 1), q1); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 2), q2); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), q3); + return ir_emit_load(proc, gen); + } + + if (is_type_float(src) && is_type_complex(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, value, ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), real); + return ir_emit_load(proc, gen); + } + if (is_type_float(src) && is_type_quaternion(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, value, ft); + // @QuaternionLayout + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), real); + return ir_emit_load(proc, gen); + } + if (is_type_complex(src) && is_type_quaternion(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst, true); + irValue *real = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 0), ft); + irValue *imag = ir_emit_conv(proc, ir_emit_struct_ev(proc, value, 1), ft); + // @QuaternionLayout + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), imag); + return ir_emit_load(proc, gen); + } + + + // float <-> integer if (is_type_float(src) && is_type_integer(dst)) { irConvKind kind = irConv_fptosi; @@ -5315,6 +5457,7 @@ enum Typeid_Kind : u8 { Typeid_Rune, Typeid_Float, Typeid_Complex, + Typeid_Quaternion, Typeid_String, Typeid_Boolean, Typeid_Any, @@ -6067,17 +6210,77 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu return ir_emit_load(proc, dst); } + case BuiltinProc_quaternion: { + ir_emit_comment(proc, str_lit("quaternion")); + irValue *real = ir_build_expr(proc, ce->args[0]); + irValue *imag = ir_build_expr(proc, ce->args[1]); + irValue *jmag = ir_build_expr(proc, ce->args[2]); + irValue *kmag = ir_build_expr(proc, ce->args[3]); + + // @QuaternionLayout + irValue *dst = ir_add_local_generated(proc, tv.type, false); + Type *ft = base_complex_elem_type(tv.type); + real = ir_emit_conv(proc, real, ft); + imag = ir_emit_conv(proc, imag, ft); + jmag = ir_emit_conv(proc, jmag, ft); + kmag = ir_emit_conv(proc, kmag, ft); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 0), imag); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 1), jmag); + ir_emit_store(proc, ir_emit_struct_ep(proc, dst, 2), kmag); + + return ir_emit_load(proc, dst); + } + case BuiltinProc_real: { ir_emit_comment(proc, str_lit("real")); irValue *val = ir_build_expr(proc, ce->args[0]); - irValue *real = ir_emit_struct_ev(proc, val, 0); - return ir_emit_conv(proc, real, tv.type); + if (is_type_complex(ir_type(val))) { + irValue *real = ir_emit_struct_ev(proc, val, 0); + return ir_emit_conv(proc, real, tv.type); + } else if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *real = ir_emit_struct_ev(proc, val, 3); + return ir_emit_conv(proc, real, tv.type); + } + GB_PANIC("invalid type for real"); + return nullptr; } case BuiltinProc_imag: { ir_emit_comment(proc, str_lit("imag")); irValue *val = ir_build_expr(proc, ce->args[0]); - irValue *imag = ir_emit_struct_ev(proc, val, 1); - return ir_emit_conv(proc, imag, tv.type); + if (is_type_complex(ir_type(val))) { + irValue *imag = ir_emit_struct_ev(proc, val, 1); + return ir_emit_conv(proc, imag, tv.type); + } else if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 0); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for imag"); + return nullptr; + } + case BuiltinProc_jmag: { + ir_emit_comment(proc, str_lit("jmag")); + irValue *val = ir_build_expr(proc, ce->args[0]); + if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 1); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for jmag"); + return nullptr; + } + case BuiltinProc_kmag: { + ir_emit_comment(proc, str_lit("kmag")); + irValue *val = ir_build_expr(proc, ce->args[0]); + if (is_type_quaternion(ir_type(val))) { + // @QuaternionLayout + irValue *imag = ir_emit_struct_ev(proc, val, 2); + return ir_emit_conv(proc, imag, tv.type); + } + GB_PANIC("invalid type for kmag"); + return nullptr; } case BuiltinProc_conj: { @@ -6092,6 +6295,20 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag)); ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), real); ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), imag); + } else if (is_type_quaternion(t)) { + // @QuaternionLayout + res = ir_add_local_generated(proc, tv.type, false); + irValue *real = ir_emit_struct_ev(proc, val, 3); + irValue *imag = ir_emit_struct_ev(proc, val, 0); + irValue *jmag = ir_emit_struct_ev(proc, val, 1); + irValue *kmag = ir_emit_struct_ev(proc, val, 2); + imag = ir_emit_unary_arith(proc, Token_Sub, imag, ir_type(imag)); + jmag = ir_emit_unary_arith(proc, Token_Sub, jmag, ir_type(jmag)); + kmag = ir_emit_unary_arith(proc, Token_Sub, kmag, ir_type(kmag)); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 3), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), imag); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), jmag); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 2), kmag); } return ir_emit_load(proc, res); } @@ -6162,7 +6379,16 @@ irValue *ir_build_builtin_proc(irProcedure *proc, Ast *expr, TypeAndValue tv, Bu return x; } ir_emit_comment(proc, str_lit("abs")); - if (is_type_complex(t)) { + if (is_type_quaternion(t)) { + i64 sz = 8*type_size_of(t); + auto args = array_make(ir_allocator(), 1); + args[0] = x; + switch (sz) { + case 128: return ir_emit_runtime_call(proc, "abs_quaternion128", args); + case 256: return ir_emit_runtime_call(proc, "abs_quaternion256", args); + } + GB_PANIC("Unknown complex type"); + } else if (is_type_complex(t)) { i64 sz = 8*type_size_of(t); auto args = array_make(ir_allocator(), 1); args[0] = x; @@ -9876,6 +10102,11 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info tag = ir_emit_conv(proc, variant_ptr, t_type_info_complex_ptr); break; + case Basic_quaternion128: + case Basic_quaternion256: + tag = ir_emit_conv(proc, variant_ptr, t_type_info_quaternion_ptr); + break; + case Basic_rawptr: tag = ir_emit_conv(proc, variant_ptr, t_type_info_pointer_ptr); break; diff --git a/src/ir_print.cpp b/src/ir_print.cpp index c1e41c4b0..30ad914a9 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -418,20 +418,23 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) { } return; - // case Basic_f16: ir_write_str_lit(f, "half"); return; - case Basic_f32: ir_write_str_lit(f, "float"); return; - case Basic_f64: ir_write_str_lit(f, "double"); return; + // case Basic_f16: ir_write_str_lit(f, "half"); return; + case Basic_f32: ir_write_str_lit(f, "float"); return; + case Basic_f64: ir_write_str_lit(f, "double"); return; - // case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return; - case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return; - case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return; + // case Basic_complex32: ir_write_str_lit(f, "%%..complex32"); return; + case Basic_complex64: ir_write_str_lit(f, "%..complex64"); return; + case Basic_complex128: ir_write_str_lit(f, "%..complex128"); return; - case Basic_any: ir_write_str_lit(f, "%..any"); return; - case Basic_rawptr: ir_write_str_lit(f, "%..rawptr"); return; - case Basic_string: ir_write_str_lit(f, "%..string"); return; - case Basic_cstring: ir_write_str_lit(f, "i8*"); return; + case Basic_quaternion128: ir_write_str_lit(f, "%..quaternion128"); return; + case Basic_quaternion256: ir_write_str_lit(f, "%..quaternion256"); return; - case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return; + case Basic_any: ir_write_str_lit(f, "%..any"); return; + case Basic_rawptr: ir_write_str_lit(f, "%..rawptr"); return; + case Basic_string: ir_write_str_lit(f, "%..string"); return; + case Basic_cstring: ir_write_str_lit(f, "i8*"); return; + + case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return; } break; @@ -811,6 +814,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * break; } case ExactValue_Complex: { + // xy/ri format type = core_type(type); GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type)); Type *ft = base_complex_elem_type(type); @@ -823,6 +827,26 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_byte(f, '}'); break; } + + case ExactValue_Quaternion: { + // xyzw/ijkr format + type = core_type(type); + GB_ASSERT_MSG(is_type_quaternion(type), "%s", type_to_string(type)); + Type *ft = base_complex_elem_type(type); + ir_write_byte(f, ' '); + ir_write_byte(f, '{'); + ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft); + ir_write_str_lit(f, ", "); ir_print_type(f, m, ft); ir_write_byte(f, ' '); + ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft); + ir_write_byte(f, '}'); + break; + } + case ExactValue_Pointer: if (value.value_pointer == 0) { if (is_type_typeid(type)) { @@ -2246,6 +2270,13 @@ void print_llvm_ir(irGen *ir) { ir_print_encoded_local(f, str_lit("..complex128")); ir_write_str_lit(f, " = type {double, double} ; Basic_complex128\n"); + ir_print_encoded_local(f, str_lit("..quaternion64")); + ir_write_str_lit(f, " = type {half, half, half, half} ; Basic_quaternion64\n"); + ir_print_encoded_local(f, str_lit("..quaternion128")); + ir_write_str_lit(f, " = type {float, float, float, float} ; Basic_quaternion128\n"); + ir_print_encoded_local(f, str_lit("..quaternion256")); + ir_write_str_lit(f, " = type {double, double, double, double} ; Basic_quaternion256\n"); + ir_print_encoded_local(f, str_lit("..typeid")); ir_write_str_lit(f, " = type "); ir_print_type(f, m, t_uintptr); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a551f0545..5ba402858 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -745,7 +745,10 @@ exponent: scan_mantissa(t, 10); } - if (t->curr_rune == 'i') { + switch (t->curr_rune) { + case 'i': + case 'j': + case 'k': token.kind = Token_Imag; advance_to_next_rune(t); } diff --git a/src/types.cpp b/src/types.cpp index b5c9152b6..94b89d8f5 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -32,6 +32,9 @@ enum BasicKind { Basic_complex64, Basic_complex128, + Basic_quaternion128, + Basic_quaternion256, + Basic_int, Basic_uint, Basic_uintptr, @@ -66,6 +69,7 @@ enum BasicKind { Basic_UntypedInteger, Basic_UntypedFloat, Basic_UntypedComplex, + Basic_UntypedQuaternion, Basic_UntypedString, Basic_UntypedRune, Basic_UntypedNil, @@ -82,17 +86,18 @@ enum BasicFlag { BasicFlag_Unsigned = GB_BIT(2), BasicFlag_Float = GB_BIT(3), BasicFlag_Complex = GB_BIT(4), - BasicFlag_Pointer = GB_BIT(5), - BasicFlag_String = GB_BIT(6), - BasicFlag_Rune = GB_BIT(7), - BasicFlag_Untyped = GB_BIT(8), + BasicFlag_Quaternion = GB_BIT(5), + BasicFlag_Pointer = GB_BIT(6), + BasicFlag_String = GB_BIT(7), + BasicFlag_Rune = GB_BIT(8), + BasicFlag_Untyped = GB_BIT(9), - BasicFlag_LLVM = GB_BIT(10), + BasicFlag_LLVM = GB_BIT(11), BasicFlag_EndianLittle = GB_BIT(13), BasicFlag_EndianBig = GB_BIT(14), - BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex, + BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex | BasicFlag_Quaternion, BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune, BasicFlag_OrderedNumeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Rune, BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer | BasicFlag_Rune, @@ -342,6 +347,9 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_complex64, BasicFlag_Complex, 8, STR_LIT("complex64")}}, {Type_Basic, {Basic_complex128, BasicFlag_Complex, 16, STR_LIT("complex128")}}, + {Type_Basic, {Basic_quaternion128, BasicFlag_Quaternion, 16, STR_LIT("quaternion128")}}, + {Type_Basic, {Basic_quaternion256, BasicFlag_Quaternion, 32, STR_LIT("quaternion256")}}, + {Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}}, {Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}}, {Type_Basic, {Basic_uintptr, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uintptr")}}, @@ -377,6 +385,7 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}}, {Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}}, {Type_Basic, {Basic_UntypedComplex, BasicFlag_Complex | BasicFlag_Untyped, 0, STR_LIT("untyped complex")}}, + {Type_Basic, {Basic_UntypedQuaternion, BasicFlag_Quaternion | BasicFlag_Untyped, 0, STR_LIT("untyped quaternion")}}, {Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}}, {Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}}, {Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}}, @@ -412,6 +421,9 @@ gb_global Type *t_f64 = &basic_types[Basic_f64]; gb_global Type *t_complex64 = &basic_types[Basic_complex64]; gb_global Type *t_complex128 = &basic_types[Basic_complex128]; +gb_global Type *t_quaternion128 = &basic_types[Basic_quaternion128]; +gb_global Type *t_quaternion256 = &basic_types[Basic_quaternion256]; + gb_global Type *t_int = &basic_types[Basic_int]; gb_global Type *t_uint = &basic_types[Basic_uint]; gb_global Type *t_uintptr = &basic_types[Basic_uintptr]; @@ -446,6 +458,7 @@ gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool]; gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger]; gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat]; gb_global Type *t_untyped_complex = &basic_types[Basic_UntypedComplex]; +gb_global Type *t_untyped_quaternion = &basic_types[Basic_UntypedQuaternion]; gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString]; gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil]; @@ -472,6 +485,7 @@ gb_global Type *t_type_info_integer = nullptr; gb_global Type *t_type_info_rune = nullptr; gb_global Type *t_type_info_float = nullptr; gb_global Type *t_type_info_complex = nullptr; +gb_global Type *t_type_info_quaternion = nullptr; gb_global Type *t_type_info_any = nullptr; gb_global Type *t_type_info_typeid = nullptr; gb_global Type *t_type_info_string = nullptr; @@ -496,6 +510,7 @@ gb_global Type *t_type_info_integer_ptr = nullptr; gb_global Type *t_type_info_rune_ptr = nullptr; gb_global Type *t_type_info_float_ptr = nullptr; gb_global Type *t_type_info_complex_ptr = nullptr; +gb_global Type *t_type_info_quaternion_ptr = nullptr; gb_global Type *t_type_info_any_ptr = nullptr; gb_global Type *t_type_info_typeid_ptr = nullptr; gb_global Type *t_type_info_string_ptr = nullptr; @@ -924,6 +939,13 @@ bool is_type_complex(Type *t) { } return false; } +bool is_type_quaternion(Type *t) { + t = core_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Quaternion) != 0; + } + return false; +} bool is_type_f32(Type *t) { t = core_type(t); if (t->kind == Type_Basic) { @@ -1063,12 +1085,15 @@ bool is_type_simple_compare(Type *t) { Type *base_complex_elem_type(Type *t) { t = core_type(t); - if (is_type_complex(t)) { + if (t->kind == Type_Basic) { switch (t->Basic.kind) { - // case Basic_complex32: return t_f16; - case Basic_complex64: return t_f32; - case Basic_complex128: return t_f64; - case Basic_UntypedComplex: return t_untyped_float; + // case Basic_complex32: return t_f16; + case Basic_complex64: return t_f32; + case Basic_complex128: return t_f64; + case Basic_quaternion128: return t_f32; + case Basic_quaternion256: return t_f64; + case Basic_UntypedComplex: return t_untyped_float; + case Basic_UntypedQuaternion: return t_untyped_float; } } GB_PANIC("Invalid complex type"); @@ -1818,6 +1843,7 @@ Type *default_type(Type *type) { case Basic_UntypedInteger: return t_int; case Basic_UntypedFloat: return t_f64; case Basic_UntypedComplex: return t_complex128; + case Basic_UntypedQuaternion: return t_quaternion256; case Basic_UntypedString: return t_string; case Basic_UntypedRune: return t_rune; } @@ -2358,6 +2384,8 @@ i64 type_align_of_internal(Type *t, TypePath *path) { case Basic_complex64: case Basic_complex128: return type_size_of_internal(t, path) / 2; + case Basic_quaternion128: case Basic_quaternion256: + return type_size_of_internal(t, path) / 4; } } break; -- cgit v1.2.3 From 01c10aa9447d135bdda5dc25583a5e1f94cbda6d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 26 Aug 2019 13:54:35 +0100 Subject: `inline for` loops (only for 'in' based for loops) --- examples/demo/demo.odin | 39 +++++++ src/check_stmt.cpp | 272 ++++++++++++++++++++++++++++++++++++++++++++---- src/checker.hpp | 3 + src/exact_value.cpp | 4 + src/ir.cpp | 144 +++++++++++++++++++++++++ src/parser.cpp | 83 ++++++++++++++- src/parser.hpp | 9 ++ 7 files changed, 530 insertions(+), 24 deletions(-) (limited to 'src/exact_value.cpp') diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index a96056b61..cef814a56 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -1018,6 +1018,44 @@ quaternions :: proc() { fmt.println(c); fmt.println(q); } + { // Memory layout of Quaternions + q := 1 + 2i + 3j + 4k; + a := transmute([4]f64)q; + fmt.println("Quaternion memory layout: xyzw/(ijkr)"); + fmt.println(q); // 1.000+2.000i+3.000j+4.000k + fmt.println(a); // [2.000, 3.000, 4.000, 1.000] + } +} + +inline_for_statement :: proc() { + fmt.println("\n#inline for statements"); + + fmt.println("Ranges"); + inline for x, i in 1..<4 { + fmt.println(x, i); + } + + fmt.println("Strings"); + inline for r, i in "Hello, 世界" { + fmt.println(r, i); + } + + fmt.println("Arrays"); + inline for elem, idx in ([4]int{1, 4, 9, 16}) { + fmt.println(elem, idx); + } + + + Foo_Enum :: enum { + A = 1, + B, + C = 6, + D, + } + fmt.println("Enum types"); + inline for elem, idx in Foo_Enum { + fmt.println(elem, idx); + } } main :: proc() { @@ -1040,5 +1078,6 @@ main :: proc() { deferred_procedure_associations(); reflection(); quaternions(); + inline_for_statement(); } } diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2787aa436..88661558d 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -132,6 +132,10 @@ bool check_is_terminating(Ast *node) { } case_end; + case_ast_node(rs, InlineRangeStmt, node); + return false; + case_end; + case_ast_node(rs, RangeStmt, node); return false; case_end; @@ -587,6 +591,236 @@ void add_constant_switch_case(CheckerContext *ctx, Map *seen, Oper multi_map_insert(seen, key, tap); } +void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { + ast_node(irs, InlineRangeStmt, node); + check_open_scope(ctx, node); + + Type *val0 = nullptr; + Type *val1 = nullptr; + Entity *entities[2] = {}; + isize entity_count = 0; + + Ast *expr = unparen_expr(irs->expr); + + ExactValue inline_for_depth = exact_value_i64(0); + + if (is_ast_range(expr)) { + ast_node(ie, BinaryExpr, expr); + Operand x = {Addressing_Invalid}; + Operand y = {Addressing_Invalid}; + + check_expr(ctx, &x, ie->left); + if (x.mode == Addressing_Invalid) { + goto skip_expr; + } + check_expr(ctx, &y, ie->right); + if (y.mode == Addressing_Invalid) { + goto skip_expr; + } + + convert_to_typed(ctx, &x, y.type); + if (x.mode == Addressing_Invalid) { + goto skip_expr; + } + convert_to_typed(ctx, &y, x.type); + if (y.mode == Addressing_Invalid) { + goto skip_expr; + } + + convert_to_typed(ctx, &x, default_type(y.type)); + if (x.mode == Addressing_Invalid) { + goto skip_expr; + } + convert_to_typed(ctx, &y, default_type(x.type)); + if (y.mode == Addressing_Invalid) { + goto skip_expr; + } + + if (!are_types_identical(x.type, y.type)) { + if (x.type != t_invalid && + y.type != t_invalid) { + gbString xt = type_to_string(x.type); + gbString yt = type_to_string(y.type); + gbString expr_str = expr_to_string(x.expr); + error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); + } + goto skip_expr; + } + + Type *type = x.type; + if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { + error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); + goto skip_expr; + } + + if (x.mode == Addressing_Constant && + y.mode == Addressing_Constant) { + ExactValue a = x.value; + ExactValue b = y.value; + + GB_ASSERT(are_types_identical(x.type, y.type)); + + TokenKind op = Token_Lt; + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeHalf: op = Token_Lt; break; + default: error(ie->op, "Invalid range operator"); break; + } + bool ok = compare_exact_values(op, a, b); + if (!ok) { + // TODO(bill): Better error message + error(ie->op, "Invalid interval range"); + goto skip_expr; + } + + inline_for_depth = exact_value_sub(b, a); + if (ie->op.kind == Token_Ellipsis) { + inline_for_depth = exact_value_increment_one(inline_for_depth); + } + + } else { + error(ie->op, "Interval expressions must be constant"); + goto skip_expr; + } + + add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value); + add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value); + val0 = type; + val1 = t_int; + } else { + Operand operand = {Addressing_Invalid}; + check_expr_or_type(ctx, &operand, irs->expr); + + if (operand.mode == Addressing_Type) { + if (!is_type_enum(operand.type)) { + gbString t = type_to_string(operand.type); + error(operand.expr, "Cannot iterate over the type '%s'", t); + gb_string_free(t); + goto skip_expr; + } else { + val0 = operand.type; + val1 = t_int; + add_type_info_type(ctx, operand.type); + + Type *bt = base_type(operand.type); + inline_for_depth = exact_value_i64(bt->Enum.fields.count); + goto skip_expr; + } + } else if (operand.mode != Addressing_Invalid) { + Type *t = base_type(operand.type); + switch (t->kind) { + case Type_Basic: + if (is_type_string(t) && t->Basic.kind != Basic_cstring) { + val0 = t_rune; + val1 = t_int; + inline_for_depth = exact_value_i64(operand.value.value_string.len); + } + break; + case Type_Array: + val0 = t->Array.elem; + val1 = t_int; + inline_for_depth = exact_value_i64(t->Array.count); + break; + } + } + + if (val0 == nullptr) { + gbString s = expr_to_string(operand.expr); + gbString t = type_to_string(operand.type); + error(operand.expr, "Cannot iterate over '%s' of type '%s' in an 'inline for' statement", s, t); + gb_string_free(t); + gb_string_free(s); + } else if (operand.mode != Addressing_Constant) { + error(operand.expr, "An 'inline for' expression must be known at compile time"); + } + } + + skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + + Ast * lhs[2] = {irs->val0, irs->val1}; + Type *rhs[2] = {val0, val1}; + + for (isize i = 0; i < 2; i++) { + if (lhs[i] == nullptr) { + continue; + } + Ast * name = lhs[i]; + Type *type = rhs[i]; + + Entity *entity = nullptr; + if (name->kind == Ast_Ident) { + Token token = name->Ident.token; + String str = token.string; + Entity *found = nullptr; + + if (!is_blank_ident(str)) { + found = scope_lookup_current(ctx->scope, str); + } + if (found == nullptr) { + bool is_immutable = true; + entity = alloc_entity_variable(ctx->scope, token, type, is_immutable, EntityState_Resolved); + entity->flags |= EntityFlag_Value; + add_entity_definition(&ctx->checker->info, name, entity); + } else { + TokenPos pos = found->token.pos; + error(token, + "Redeclaration of '%.*s' in this scope\n" + "\tat %.*s(%td:%td)", + LIT(str), LIT(pos.file), pos.line, pos.column); + entity = found; + } + } else { + error(name, "A variable declaration must be an identifier"); + } + + if (entity == nullptr) { + entity = alloc_entity_dummy_variable(builtin_pkg->scope, ast_token(name)); + } + + entities[entity_count++] = entity; + + if (type == nullptr) { + entity->type = t_invalid; + entity->flags |= EntityFlag_Used; + } + } + + for (isize i = 0; i < entity_count; i++) { + add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]); + } + + + // NOTE(bill): Minimize the amount of nesting of an 'inline for' + i64 prev_inline_for_depth = ctx->inline_for_depth; + defer (ctx->inline_for_depth = prev_inline_for_depth); + { + i64 v = exact_value_to_i64(inline_for_depth); + if (v <= 0) { + // Do nothing + } else { + ctx->inline_for_depth = gb_max(ctx->inline_for_depth, 1) * v; + } + + if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) { + if (prev_inline_for_depth > 0) { + error(node, "Nested 'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH); + } else { + error(node, "'inline for' loop cannot be inlined as it exceeds the maximum inline for depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH); + } + error_line("\tUse a normal 'for' loop instead by removing the 'inline' prefix\n"); + ctx->inline_for_depth = MAX_INLINE_FOR_DEPTH; + } + } + + check_stmt(ctx, irs->body, mod_flags); + + + check_close_scope(ctx); +} + void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { ast_node(ss, SwitchStmt, node); @@ -1298,6 +1532,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_close_scope(ctx); case_end; + case_ast_node(rs, RangeStmt, node); u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; @@ -1320,29 +1555,29 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_expr(ctx, &x, ie->left); if (x.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } check_expr(ctx, &y, ie->right); if (y.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &x, y.type); if (x.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &y, x.type); if (y.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &x, default_type(y.type)); if (x.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } convert_to_typed(ctx, &y, default_type(x.type)); if (y.mode == Addressing_Invalid) { - goto skip_expr; + goto skip_expr_range_stmt; } if (!are_types_identical(x.type, y.type)) { @@ -1356,13 +1591,13 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { gb_string_free(yt); gb_string_free(xt); } - goto skip_expr; + goto skip_expr_range_stmt; } Type *type = x.type; if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); - goto skip_expr; + goto skip_expr_range_stmt; } if (x.mode == Addressing_Constant && @@ -1382,18 +1617,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (!ok) { // TODO(bill): Better error message error(ie->op, "Invalid interval range"); - goto skip_expr; + goto skip_expr_range_stmt; } } - if (x.mode != Addressing_Constant) { - x.value = empty_exact_value; - } - if (y.mode != Addressing_Constant) { - y.value = empty_exact_value; - } - - add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value); add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value); val0 = type; @@ -1407,12 +1634,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { gbString t = type_to_string(operand.type); error(operand.expr, "Cannot iterate over the type '%s'", t); gb_string_free(t); - goto skip_expr; + goto skip_expr_range_stmt; } else { val0 = operand.type; val1 = t_int; add_type_info_type(ctx, operand.type); - goto skip_expr; + goto skip_expr_range_stmt; } } else if (operand.mode != Addressing_Invalid) { bool is_ptr = is_type_pointer(operand.type); @@ -1457,7 +1684,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } } - skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird. + Ast * lhs[2] = {rs->val0, rs->val1}; Type *rhs[2] = {val0, val1}; @@ -1515,6 +1743,10 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { check_close_scope(ctx); case_end; + case_ast_node(irs, InlineRangeStmt, node); + check_inline_range_stmt(ctx, node, mod_flags); + case_end; + case_ast_node(ss, SwitchStmt, node); check_switch_stmt(ctx, node, mod_flags); case_end; diff --git a/src/checker.hpp b/src/checker.hpp index 4e4a8cfe1..a37a87b87 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -276,6 +276,9 @@ struct CheckerContext { CheckerPolyPath *poly_path; isize poly_level; // TODO(bill): Actually handle correctly +#define MAX_INLINE_FOR_DEPTH 1024ll + i64 inline_for_depth; + bool in_enum_type; bool collect_delayed_decls; bool allow_polymorphic_types; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 54d4ef6f2..fcc6f1973 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -813,6 +813,10 @@ gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue const &x, ExactV return exact_binary_operator_value(op, x, y); } +gb_inline ExactValue exact_value_increment_one(ExactValue const &x) { + return exact_binary_operator_value(Token_Add, x, exact_value_i64(1)); +} + i32 cmp_f64(f64 a, f64 b) { return (a > b) - (a < b); diff --git a/src/ir.cpp b/src/ir.cpp index 2a4bb028b..7dc5988e3 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9061,6 +9061,150 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { ir_start_block(proc, done); case_end; + case_ast_node(rs, InlineRangeStmt, node); + ir_emit_comment(proc, str_lit("InlineRangeStmt")); + ir_open_scope(proc); // Open scope here + + Type *val0_type = nullptr; + Type *val1_type = nullptr; + if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) { + val0_type = type_of_expr(rs->val0); + } + if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) { + val1_type = type_of_expr(rs->val1); + } + + if (val0_type != nullptr) { + ir_add_local_for_identifier(proc, rs->val0, true); + } + if (val1_type != nullptr) { + ir_add_local_for_identifier(proc, rs->val1, true); + } + + irValue *val = nullptr; + irValue *key = nullptr; + irBlock *loop = nullptr; + irBlock *done = nullptr; + Ast *expr = unparen_expr(rs->expr); + + TypeAndValue tav = type_and_value_of_expr(expr); + + if (is_ast_range(expr)) { + + irAddr val0_addr = {}; + irAddr val1_addr = {}; + if (val0_type) val0_addr = ir_build_addr(proc, rs->val0); + if (val1_type) val1_addr = ir_build_addr(proc, rs->val1); + + TokenKind op = expr->BinaryExpr.op.kind; + Ast *start_expr = expr->BinaryExpr.left; + Ast *end_expr = expr->BinaryExpr.right; + GB_ASSERT(start_expr->tav.mode == Addressing_Constant); + GB_ASSERT(end_expr->tav.mode == Addressing_Constant); + + ExactValue start = start_expr->tav.value; + ExactValue end = end_expr->tav.value; + if (op == Token_Ellipsis) { // .. [start, end] + ExactValue index = exact_value_i64(0); + for (ExactValue val = start; + compare_exact_values(Token_LtEq, val, end); + val = exact_value_increment_one(val), index = exact_value_increment_one(index)) { + + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, val)); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, index)); + + ir_build_stmt(proc, rs->body); + } + } else if (op == Token_RangeHalf) { // ..< [start, end) + ExactValue index = exact_value_i64(0); + for (ExactValue val = start; + compare_exact_values(Token_Lt, val, end); + val = exact_value_increment_one(val), index = exact_value_increment_one(index)) { + + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, val)); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, index)); + + ir_build_stmt(proc, rs->body); + } + } + + + } else if (tav.mode == Addressing_Type) { + GB_ASSERT(is_type_enum(type_deref(tav.type))); + Type *et = type_deref(tav.type); + Type *bet = base_type(et); + + irAddr val0_addr = {}; + irAddr val1_addr = {}; + if (val0_type) val0_addr = ir_build_addr(proc, rs->val0); + if (val1_type) val1_addr = ir_build_addr(proc, rs->val1); + + for_array(i, bet->Enum.fields) { + Entity *field = bet->Enum.fields[i]; + GB_ASSERT(field->kind == Entity_Constant); + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, field->Constant.value)); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(i))); + + ir_build_stmt(proc, rs->body); + } + } else { + irAddr val0_addr = {}; + irAddr val1_addr = {}; + if (val0_type) val0_addr = ir_build_addr(proc, rs->val0); + if (val1_type) val1_addr = ir_build_addr(proc, rs->val1); + + GB_ASSERT(expr->tav.mode == Addressing_Constant); + + Type *t = base_type(expr->tav.type); + + + switch (t->kind) { + case Type_Basic: + GB_ASSERT(is_type_string(t)); + { + ExactValue value = expr->tav.value; + GB_ASSERT(value.kind == ExactValue_String); + String str = value.value_string; + Rune codepoint = 0; + isize offset = 0; + do { + isize width = gb_utf8_decode(str.text+offset, str.len-offset, &codepoint); + if (val0_type) ir_addr_store(proc, val0_addr, ir_value_constant(val0_type, exact_value_i64(codepoint))); + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(offset))); + ir_build_stmt(proc, rs->body); + + offset += width; + } while (offset < str.len); + } + break; + case Type_Array: + if (t->Array.count > 0) { + irValue *val = ir_build_expr(proc, expr); + irValue *val_addr = ir_address_from_load_or_generate_local(proc, val); + + for (i64 i = 0; i < t->Array.count; i++) { + if (val0_type) { + // NOTE(bill): Due to weird legacy issues in LLVM, this needs to be an i32 + irValue *elem = ir_emit_array_epi(proc, val_addr, cast(i32)i); + ir_addr_store(proc, val0_addr, ir_emit_load(proc, elem)); + } + if (val1_type) ir_addr_store(proc, val1_addr, ir_value_constant(val1_type, exact_value_i64(i))); + + ir_build_stmt(proc, rs->body); + } + + } + break; + default: + GB_PANIC("Invalid inline for type"); + break; + } + } + + + ir_close_scope(proc, irDeferExit_Default, nullptr); + case_end; + case_ast_node(ss, SwitchStmt, node); ir_emit_comment(proc, str_lit("SwitchStmt")); if (ss->init != nullptr) { diff --git a/src/parser.cpp b/src/parser.cpp index 9aebc78d6..8c32f35ff 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -51,6 +51,7 @@ Token ast_token(Ast *node) { case Ast_ReturnStmt: return node->ReturnStmt.token; case Ast_ForStmt: return node->ForStmt.token; case Ast_RangeStmt: return node->RangeStmt.token; + case Ast_InlineRangeStmt: return node->InlineRangeStmt.inline_token; case Ast_CaseClause: return node->CaseClause.token; case Ast_SwitchStmt: return node->SwitchStmt.token; case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token; @@ -257,6 +258,12 @@ Ast *clone_ast(Ast *node) { n->RangeStmt.expr = clone_ast(n->RangeStmt.expr); n->RangeStmt.body = clone_ast(n->RangeStmt.body); break; + case Ast_InlineRangeStmt: + n->InlineRangeStmt.val0 = clone_ast(n->InlineRangeStmt.val0); + n->InlineRangeStmt.val1 = clone_ast(n->InlineRangeStmt.val1); + n->InlineRangeStmt.expr = clone_ast(n->InlineRangeStmt.expr); + n->InlineRangeStmt.body = clone_ast(n->InlineRangeStmt.body); + break; case Ast_CaseClause: n->CaseClause.list = clone_ast_array(n->CaseClause.list); n->CaseClause.stmts = clone_ast_array(n->CaseClause.stmts); @@ -748,6 +755,18 @@ Ast *ast_range_stmt(AstFile *f, Token token, Ast *val0, Ast *val1, Token in_toke return result; } +Ast *ast_inline_range_stmt(AstFile *f, Token inline_token, Token for_token, Ast *val0, Ast *val1, Token in_token, Ast *expr, Ast *body) { + Ast *result = alloc_ast_node(f, Ast_InlineRangeStmt); + result->InlineRangeStmt.inline_token = inline_token; + result->InlineRangeStmt.for_token = for_token; + result->InlineRangeStmt.val0 = val0; + result->InlineRangeStmt.val1 = val1; + result->InlineRangeStmt.in_token = in_token; + result->InlineRangeStmt.expr = expr; + result->InlineRangeStmt.body = body; + return result; +} + Ast *ast_switch_stmt(AstFile *f, Token token, Ast *init, Ast *tag, Ast *body) { Ast *result = alloc_ast_node(f, Ast_SwitchStmt); result->SwitchStmt.token = token; @@ -1119,6 +1138,17 @@ Token advance_token(AstFile *f) { return prev; } +bool peek_token_kind(AstFile *f, TokenKind kind) { + for (isize i = f->curr_token_index+1; i < f->tokens.count; i++) { + Token tok = f->tokens[i]; + if (kind != Token_Comment && tok.kind == Token_Comment) { + continue; + } + return tok.kind == kind; + } + return false; +} + Token expect_token(AstFile *f, TokenKind kind) { Token prev = f->curr_token; if (prev.kind != kind) { @@ -2092,7 +2122,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { bool prev_allow_range = f->allow_range; f->allow_range = true; - elem = parse_expr(f, false); + elem = parse_expr(f, true); f->allow_range = prev_allow_range; if (allow_token(f, Token_Semicolon)) { underlying = parse_type(f); @@ -2650,7 +2680,7 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) { allow_token(f, Token_in); bool prev_allow_range = f->allow_range; f->allow_range = true; - Ast *expr = parse_expr(f, false); + Ast *expr = parse_expr(f, true); f->allow_range = prev_allow_range; auto rhs = array_make(heap_allocator(), 0, 1); @@ -3779,10 +3809,55 @@ Ast *parse_stmt(AstFile *f) { Token token = f->curr_token; switch (token.kind) { // Operands - case Token_context: // Also allows for `context =` - case Token_proc: case Token_inline: + if (peek_token_kind(f, Token_for)) { + Token inline_token = expect_token(f, Token_inline); + Token for_token = expect_token(f, Token_for); + Ast *val0 = nullptr; + Ast *val1 = nullptr; + Token in_token = {}; + Ast *expr = nullptr; + Ast *body = nullptr; + + bool bad_stmt = false; + + if (f->curr_token.kind != Token_in) { + Array idents = parse_ident_list(f, false); + switch (idents.count) { + case 1: + val0 = idents[0]; + break; + case 2: + val0 = idents[0]; + val1 = idents[1]; + break; + default: + syntax_error(for_token, "Expected either 1 or 2 identifiers"); + bad_stmt = true; + break; + } + } + in_token = expect_token(f, Token_in); + + bool prev_allow_range = f->allow_range; + f->allow_range = true; + expr = parse_expr(f, true); + f->allow_range = prev_allow_range; + + if (allow_token(f, Token_do)) { + body = convert_stmt_to_body(f, parse_stmt(f)); + } else { + body = parse_block_stmt(f, false); + } + if (bad_stmt) { + return ast_bad_stmt(f, inline_token, f->curr_token); + } + return ast_inline_range_stmt(f, inline_token, for_token, val0, val1, in_token, expr, body); + } + /* fallthrough */ case Token_no_inline: + case Token_context: // Also allows for `context =` + case Token_proc: case Token_Ident: case Token_Integer: case Token_Float: diff --git a/src/parser.hpp b/src/parser.hpp index 3489f1a9b..32398592e 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -326,6 +326,15 @@ AST_KIND(_ComplexStmtBegin, "", bool) \ Ast *expr; \ Ast *body; \ }) \ + AST_KIND(InlineRangeStmt, "inline range statement", struct { \ + Token inline_token; \ + Token for_token; \ + Ast *val0; \ + Ast *val1; \ + Token in_token; \ + Ast *expr; \ + Ast *body; \ + }) \ AST_KIND(CaseClause, "case clause", struct { \ Token token; \ Array list; \ -- cgit v1.2.3 From 6c69e8c043e7dcf9d9965c7b28c7bdae44e0537c Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 6 Oct 2019 14:55:25 +0100 Subject: Make `typeid` semantics consistent across variables and constants --- src/check_decl.cpp | 54 ++++++++++++++++++++++++++++++++--------------- src/check_expr.cpp | 61 +++++++++++++++++++++++++++++++++++++++++------------ src/checker.cpp | 16 ++++++-------- src/exact_value.cpp | 18 ++++++++++++++++ src/ir.cpp | 5 +++++ src/parser.cpp | 4 ---- 6 files changed, 113 insertions(+), 45 deletions(-) (limited to 'src/exact_value.cpp') diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 166b6e715..5e8479a7f 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -41,11 +41,20 @@ Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *operand, Stri } if (operand->mode == Addressing_Type) { - gbString t = type_to_string(operand->type); - error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string)); - gb_string_free(t); - e->type = operand->type; - return nullptr; + if (e->type != nullptr && is_type_typeid(e->type)) { + add_type_info_type(ctx, operand->type); + add_type_and_value(ctx->info, operand->expr, Addressing_Value, e->type, exact_value_typeid(operand->type)); + return e->type; + } else { + gbString t = type_to_string(operand->type); + defer (gb_string_free(t)); + error(operand->expr, "Cannot assign a type '%s' to variable '%.*s'", t, LIT(e->token.string)); + if (e->type == nullptr) { + error_line("\tThe type of the variable '%.*s' cannot be inferred as a type does not have a type\n", LIT(e->token.string)); + } + e->type = operand->type; + return nullptr; + } } @@ -240,7 +249,7 @@ isize total_attribute_count(DeclInfo *decl) { } -void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) { +void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def) { GB_ASSERT(e->type == nullptr); DeclInfo *decl = decl_info_of_entity(e); @@ -248,9 +257,8 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) check_decl_attributes(ctx, decl->attributes, const_decl_attribute, nullptr); } - - bool is_distinct = is_type_distinct(type_expr); - Ast *te = remove_type_alias_clutter(type_expr); + bool is_distinct = is_type_distinct(init_expr); + Ast *te = remove_type_alias_clutter(init_expr); e->type = t_invalid; String name = e->token.string; Type *named = alloc_type_named(name, nullptr, e); @@ -266,7 +274,7 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) named->Named.base = base_type(bt); if (is_distinct && is_type_typeid(e->type)) { - error(type_expr, "'distinct' cannot be applied to 'typeid'"); + error(init_expr, "'distinct' cannot be applied to 'typeid'"); is_distinct = false; } if (!is_distinct) { @@ -275,6 +283,19 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Type *def) e->TypeName.is_type_alias = true; } + + if (decl->type_expr != nullptr) { + Type *t = check_type(ctx, decl->type_expr); + if (t != nullptr && !is_type_typeid(t)) { + Operand operand = {}; + operand.mode = Addressing_Type; + operand.type = e->type; + operand.expr = init_expr; + check_assignment(ctx, &operand, t, str_lit("constant declaration")); + } + } + + // using decl if (decl->is_using) { // NOTE(bill): Must be an enum declaration @@ -363,15 +384,14 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, switch (operand.mode) { case Addressing_Type: { + if (e->type != nullptr && !is_type_typeid(e->type)) { + check_assignment(ctx, &operand, e->type, str_lit("constant declaration")); + } + e->kind = Entity_TypeName; e->type = nullptr; - DeclInfo *d = ctx->decl; - if (d->type_expr != nullptr) { - error(e->token, "A type declaration cannot have an type parameter"); - } - d->type_expr = d->init_expr; - check_type_decl(ctx, e, d->type_expr, named_type); + check_type_decl(ctx, e, ctx->decl->init_expr, named_type); return; } @@ -1070,7 +1090,7 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_ check_const_decl(&c, e, d->type_expr, d->init_expr, named_type); break; case Entity_TypeName: { - check_type_decl(&c, e, d->type_expr, named_type); + check_type_decl(&c, e, d->init_expr, named_type); break; } case Entity_Procedure: diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 697abef5f..6a0d8221f 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -442,6 +442,10 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type } if (operand->mode == Addressing_Type) { + if (is_type_typeid(type)) { + add_type_info_type(c, operand->type); + return 4; + } return -1; } @@ -755,7 +759,12 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co return; } - if (!check_is_assignable_to(c, operand, type)) { + if (check_is_assignable_to(c, operand, type)) { + if (operand->mode == Addressing_Type && is_type_typeid(type)) { + add_type_info_type(c, operand->type); + add_type_and_value(c->info, operand->expr, Addressing_Value, type, exact_value_typeid(operand->type)); + } + } else { gbString expr_str = expr_to_string(operand->expr); gbString op_type_str = type_to_string(operand->type); gbString type_str = type_to_string(type); @@ -1734,6 +1743,23 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { return; } + if (x->mode == Addressing_Type && is_type_typeid(y->type)) { + add_type_info_type(c, x->type); + add_type_and_value(c->info, x->expr, Addressing_Value, y->type, exact_value_typeid(x->type)); + + x->mode = Addressing_Value; + x->type = t_untyped_bool; + return; + } else if (is_type_typeid(x->type) && y->mode == Addressing_Type) { + add_type_info_type(c, y->type); + add_type_and_value(c->info, y->expr, Addressing_Value, x->type, exact_value_typeid(y->type)); + + x->mode = Addressing_Value; + x->type = t_untyped_bool; + return; + } + + gbString err_str = nullptr; defer (if (err_str != nullptr) { @@ -2324,8 +2350,16 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint // If only one is a type, this is an error if (xt ^ yt) { GB_ASSERT(xt != yt); - if (xt) error_operand_not_expression(x); - if (yt) error_operand_not_expression(y); + if (xt) { + if (!is_type_typeid(y->type)) { + error_operand_not_expression(x); + } + } + if (yt) { + if (!is_type_typeid(x->type)) { + error_operand_not_expression(y); + } + } } break; @@ -5254,6 +5288,11 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { } } score += s; + + if (o.mode == Addressing_Type && is_type_typeid(e->type)) { + add_type_info_type(c, o.type); + add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type)); + } } if (variadic) { @@ -5496,6 +5535,11 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) { } score += s; } + + if (o->mode == Addressing_Type && is_type_typeid(e->type)) { + add_type_info_type(c, o->type); + add_type_and_value(c->info, o->expr, Addressing_Value, e->type, exact_value_typeid(o->type)); + } } if (data) { @@ -6432,17 +6476,6 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { } } - // NOTE(bill): Should this be here or on the `add_entity_use`? - // if (ce->proc != nullptr) { - // Entity *e = entity_of_node(&c->info, ce->proc); - // if (e != nullptr && e->kind == Entity_Procedure) { - // String msg = e->Procedure.deprecated_message; - // if (msg.len > 0) { - // warning(call, "%.*s is deprecated: %.*s", LIT(e->token.string), LIT(msg)); - // } - // } - // } - CallArgumentData data = check_call_arguments(c, operand, proc_type, call); Type *result_type = data.result_type; gb_zero_item(operand); diff --git a/src/checker.cpp b/src/checker.cpp index 994e0776d..f30273439 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2613,14 +2613,14 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Entity *e = nullptr; d->attributes = vd->attributes; + d->type_expr = vd->type; + d->init_expr = init; if (is_ast_type(init)) { e = alloc_entity_type_name(d->scope, token, nullptr); - if (vd->type != nullptr) { - error(name, "A type declaration cannot have an type parameter"); - } - d->type_expr = init; - d->init_expr = init; + // if (vd->type != nullptr) { + // error(name, "A type declaration cannot have an type parameter"); + // } } else if (init->kind == Ast_ProcLit) { if (c->scope->flags&ScopeFlag_Type) { error(name, "Procedure declarations are not allowed within a struct"); @@ -2647,19 +2647,15 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { pl->type->ProcType.calling_convention = cc; } d->proc_lit = init; - d->type_expr = vd->type; + d->init_expr = init; } else if (init->kind == Ast_ProcGroup) { ast_node(pg, ProcGroup, init); e = alloc_entity_proc_group(d->scope, token, nullptr); if (fl != nullptr) { error(name, "Procedure groups are not allowed within a foreign block"); } - d->init_expr = init; - d->type_expr = vd->type; } else { e = alloc_entity_constant(d->scope, token, nullptr, empty_exact_value); - d->type_expr = vd->type; - d->init_expr = init; } e->identifier = name; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index fcc6f1973..95d04d93b 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -38,6 +38,7 @@ enum ExactValueKind { ExactValue_Pointer, ExactValue_Compound, // TODO(bill): Is this good enough? ExactValue_Procedure, // TODO(bill): Is this good enough? + ExactValue_Typeid, ExactValue_Count, }; @@ -54,6 +55,7 @@ struct ExactValue { Quaternion256 value_quaternion; Ast * value_compound; Ast * value_procedure; + Type * value_typeid; }; }; @@ -82,6 +84,8 @@ HashKey hash_exact_value(ExactValue v) { return hash_pointer(v.value_compound); case ExactValue_Procedure: return hash_pointer(v.value_procedure); + case ExactValue_Typeid: + return hash_pointer(v.value_typeid); } return hashing_proc(&v, gb_size_of(ExactValue)); @@ -154,6 +158,13 @@ ExactValue exact_value_procedure(Ast *node) { } +ExactValue exact_value_typeid(Type *type) { + ExactValue result = {ExactValue_Typeid}; + result.value_typeid = type; + return result; +} + + ExactValue exact_value_integer_from_string(String const &string) { ExactValue result = {ExactValue_Integer}; big_int_from_string(&result.value_integer, string); @@ -889,6 +900,13 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { } break; } + + case ExactValue_Typeid: + switch (op) { + case Token_CmpEq: return are_types_identical(x.value_typeid, y.value_typeid); + case Token_NotEq: return !are_types_identical(x.value_typeid, y.value_typeid); + } + break; } GB_PANIC("Invalid comparison"); diff --git a/src/ir.cpp b/src/ir.cpp index 758267c24..f29d389f4 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6592,6 +6592,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { return ir_emit_conv(proc, x, tv.type); } + if (tv.value.kind == ExactValue_Typeid) { + irValue *v = ir_typeid(proc->module, tv.value.value_typeid); + return ir_emit_conv(proc, v, tv.type); + } + return ir_add_module_constant(proc->module, tv.type, tv.value); } diff --git a/src/parser.cpp b/src/parser.cpp index 204ff3984..fb093ffd9 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1923,10 +1923,6 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_typeid: { Token token = expect_token(f, Token_typeid); - // Ast *specialization = nullptr; - // if (allow_token(f, Token_Quo)) { - // specialization = parse_type(f); - // } return ast_typeid_type(f, token, nullptr); } break; -- cgit v1.2.3 From 7fae890ef9eb72f3131914f9af2469f9bfb1e59e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 Oct 2019 14:06:29 +0100 Subject: Allow ranges for array-like compound literals --- core/odin/parser/parser.odin | 3 + src/check_expr.cpp | 229 ++++++++++++++++++++++++++++++++++--------- src/check_stmt.cpp | 84 +--------------- src/common.cpp | 3 + src/exact_value.cpp | 2 +- src/ir.cpp | 124 ++++++++++++++++++----- src/ir_print.cpp | 48 +++++++-- src/parser.cpp | 11 ++- src/parser.hpp | 1 + 9 files changed, 340 insertions(+), 165 deletions(-) (limited to 'src/exact_value.cpp') diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index fee9b7bdc..fb3a87e8b 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -2391,6 +2391,9 @@ parse_value :: proc(p: ^Parser) -> ^ast.Expr { if p.curr_tok.kind == .Open_Brace { return parse_literal_value(p, nil); } + prev_allow_range = p.allow_range; + defer p.allow_range = prev_allow_range; + p.allow_range = true; return parse_expr(p, false); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index ea9248089..f07ebe09c 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -6635,6 +6635,98 @@ bool ternary_compare_types(Type *x, Type *y) { } +bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValue *inline_for_depth_) { + if (!is_ast_range(node)) { + return false; + } + + ast_node(ie, BinaryExpr, node); + + check_expr(c, x, ie->left); + if (x->mode == Addressing_Invalid) { + return false; + } + check_expr(c, y, ie->right); + if (y->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, x, default_type(y->type)); + if (x->mode == Addressing_Invalid) { + return false; + } + convert_to_typed(c, y, default_type(x->type)); + if (y->mode == Addressing_Invalid) { + return false; + } + + if (!are_types_identical(x->type, y->type)) { + if (x->type != t_invalid && + y->type != t_invalid) { + gbString xt = type_to_string(x->type); + gbString yt = type_to_string(y->type); + gbString expr_str = expr_to_string(x->expr); + error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); + gb_string_free(expr_str); + gb_string_free(yt); + gb_string_free(xt); + } + return false; + } + + Type *type = x->type; + if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { + error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); + return false; + } + + if (x->mode == Addressing_Constant && + y->mode == Addressing_Constant) { + ExactValue a = x->value; + ExactValue b = y->value; + + GB_ASSERT(are_types_identical(x->type, y->type)); + + TokenKind op = Token_Lt; + switch (ie->op.kind) { + case Token_Ellipsis: op = Token_LtEq; break; + case Token_RangeHalf: op = Token_Lt; break; + default: error(ie->op, "Invalid range operator"); break; + } + bool ok = compare_exact_values(op, a, b); + if (!ok) { + // TODO(bill): Better error message + error(ie->op, "Invalid interval range"); + return false; + } + + ExactValue inline_for_depth = exact_value_sub(b, a); + if (ie->op.kind == Token_Ellipsis) { + inline_for_depth = exact_value_increment_one(inline_for_depth); + } + + if (inline_for_depth_) *inline_for_depth_ = inline_for_depth; + } else { + error(ie->op, "Interval expressions must be constant"); + return false; + } + + add_type_and_value(&c->checker->info, ie->left, x->mode, x->type, x->value); + add_type_and_value(&c->checker->info, ie->right, y->mode, y->type, y->value); + + return true; +} + + ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type *type_hint) { ExprKind kind = Expr_Stmt; @@ -6697,35 +6789,26 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(bl, BasicLit, node); - // NOTE(bill, 2018-06-17): Placing this in the parser is slower than - // placing it here for some reason. So don't move it to the parsing - // stage if you _think_ it will be faster, only do it if you _know_ it - // will be faster. Type *t = t_invalid; - switch (bl->token.kind) { - case Token_Integer: t = t_untyped_integer; break; - case Token_Float: t = t_untyped_float; break; - case Token_String: t = t_untyped_string; break; - case Token_Rune: t = t_untyped_rune; break; - case Token_Imag: { - String s = bl->token.string; - Rune r = s[s.len-1]; - // NOTE(bill, 2019-08-25): Allow for quaternions by having j and k imaginary numbers - switch (r) { - case 'i': t = t_untyped_complex; break; - case 'j': t = t_untyped_quaternion; break; - case 'k': t = t_untyped_quaternion; break; + switch (bl->value.kind) { + case ExactValue_String: t = t_untyped_string; break; + case ExactValue_Float: t = t_untyped_float; break; + case ExactValue_Complex: t = t_untyped_complex; break; + case ExactValue_Quaternion: t = t_untyped_quaternion; break; + case ExactValue_Integer: + t = t_untyped_integer; + if (bl->token.kind == Token_Rune) { + t = t_untyped_rune; } - break; - } default: - GB_PANIC("Unknown literal"); + GB_PANIC("Unhandled value type for basic literal"); break; } + o->mode = Addressing_Constant; o->type = t; - o->value = exact_value_from_basic_literal(bl->token); + o->value = bl->value; case_end; case_ast_node(bd, BasicDirective, node); @@ -7090,9 +7173,8 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (is_type_simd_vector(t)) { error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); } else { - Map seen = {}; - map_init(&seen, heap_allocator()); - defer (map_destroy(&seen)); + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); for_array(i, cl->elems) { Ast *elem = cl->elems[i]; @@ -7102,36 +7184,89 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type } ast_node(fv, FieldValue, elem); - Operand op_index = {}; - check_expr(c, &op_index, fv->field); + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; - if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { - error(elem, "Expected a constant integer as an array field"); - continue; - } + Operand x = {}; + Operand y = {}; + bool ok = check_range(c, fv->field, &x, &y, nullptr); + if (!ok) { + continue; + } + if (x.mode != Addressing_Constant || !is_type_integer(core_type(x.type))) { + error(x.expr, "Expected a constant integer as an array field"); + continue; + } - i64 index = exact_value_to_i64(op_index.value); + if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { + error(y.expr, "Expected a constant integer as an array field"); + continue; + } - if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { - error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); - continue; - } + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + if (op.kind == Token_RangeHalf) { + hi -= 1; + } + i64 max_index = hi; - if (map_get(&seen, hash_integer(u64(index))) != nullptr) { - error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); - continue; - } - map_set(&seen, hash_integer(u64(index)), true); + bool new_range = range_cache_add_range(&rc, lo, hi); + if (!new_range) { + error(elem, "Overlapping field range index %lld %.*s %lld for %.*s", lo, LIT(op.string), hi, LIT(context_name)); + continue; + } - if (max < index) { - max = index; - } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, fv->value, elem_type); - check_assignment(c, &operand, elem_type, context_name); + if (max_type_count >= 0 && (lo < 0 || lo >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", lo, max_type_count, LIT(context_name)); + continue; + } + if (max_type_count >= 0 && (hi < 0 || hi >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", hi, max_type_count, LIT(context_name)); + continue; + } + + if (max < max_index) { + max = max_index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } else { + Operand op_index = {}; + check_expr(c, &op_index, fv->field); + + if (op_index.mode != Addressing_Constant || !is_type_integer(core_type(op_index.type))) { + error(elem, "Expected a constant integer as an array field"); + continue; + } - is_constant = is_constant && operand.mode == Addressing_Constant; + i64 index = exact_value_to_i64(op_index.value); + + if (max_type_count >= 0 && (index < 0 || index >= max_type_count)) { + error(elem, "Index %lld is out of bounds (0..<%lld) for %.*s", index, max_type_count, LIT(context_name)); + continue; + } + + bool new_index = range_cache_add_index(&rc, index); + if (!new_index) { + error(elem, "Duplicate field index %lld for %.*s", index, LIT(context_name)); + continue; + } + + if (max < index) { + max = index; + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, fv->value, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } } cl->max_index = max; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 72913903d..d4398664b 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -605,89 +605,15 @@ void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { if (is_ast_range(expr)) { ast_node(ie, BinaryExpr, expr); - Operand x = {Addressing_Invalid}; - Operand y = {Addressing_Invalid}; + Operand x = {}; + Operand y = {}; - check_expr(ctx, &x, ie->left); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - check_expr(ctx, &y, ie->right); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - convert_to_typed(ctx, &x, y.type); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - convert_to_typed(ctx, &y, x.type); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - convert_to_typed(ctx, &x, default_type(y.type)); - if (x.mode == Addressing_Invalid) { - goto skip_expr; - } - convert_to_typed(ctx, &y, default_type(x.type)); - if (y.mode == Addressing_Invalid) { - goto skip_expr; - } - - if (!are_types_identical(x.type, y.type)) { - if (x.type != t_invalid && - y.type != t_invalid) { - gbString xt = type_to_string(x.type); - gbString yt = type_to_string(y.type); - gbString expr_str = expr_to_string(x.expr); - error(ie->op, "Mismatched types in interval expression '%s' : '%s' vs '%s'", expr_str, xt, yt); - gb_string_free(expr_str); - gb_string_free(yt); - gb_string_free(xt); - } - goto skip_expr; - } - - Type *type = x.type; - if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type) && !is_type_enum(type)) { - error(ie->op, "Only numerical and pointer types are allowed within interval expressions"); - goto skip_expr; - } - - if (x.mode == Addressing_Constant && - y.mode == Addressing_Constant) { - ExactValue a = x.value; - ExactValue b = y.value; - - GB_ASSERT(are_types_identical(x.type, y.type)); - - TokenKind op = Token_Lt; - switch (ie->op.kind) { - case Token_Ellipsis: op = Token_LtEq; break; - case Token_RangeHalf: op = Token_Lt; break; - default: error(ie->op, "Invalid range operator"); break; - } - bool ok = compare_exact_values(op, a, b); - if (!ok) { - // TODO(bill): Better error message - error(ie->op, "Invalid interval range"); - goto skip_expr; - } - - inline_for_depth = exact_value_sub(b, a); - if (ie->op.kind == Token_Ellipsis) { - inline_for_depth = exact_value_increment_one(inline_for_depth); - } - - } else { - error(ie->op, "Interval expressions must be constant"); + bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth); + if (!ok) { goto skip_expr; } - add_type_and_value(&ctx->checker->info, ie->left, x.mode, x.type, x.value); - add_type_and_value(&ctx->checker->info, ie->right, y.mode, y.type, y.value); - val0 = type; + val0 = x.type; val1 = t_int; } else { Operand operand = {Addressing_Invalid}; diff --git a/src/common.cpp b/src/common.cpp index db6505a36..b034ad720 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -143,6 +143,9 @@ GB_ALLOCATOR_PROC(heap_allocator_proc) { #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++) +#include "range_cache.cpp" + + u64 fnv64a(void const *data, isize len) { u8 const *bytes = cast(u8 const *)data; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 95d04d93b..42b22c8ef 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -293,12 +293,12 @@ ExactValue exact_value_from_basic_literal(Token token) { case 'i': return exact_value_complex(0, imag); case 'j': return exact_value_quaternion(0, 0, imag, 0); case 'k': return exact_value_quaternion(0, 0, 0, imag); + default: GB_PANIC("Invalid imaginary basic literal"); } } case Token_Rune: { Rune r = GB_RUNE_INVALID; gb_utf8_decode(token.string.text, token.string.len, &r); - // gb_printf("%.*s rune: %d\n", LIT(token.string), r); return exact_value_i64(r); } default: diff --git a/src/ir.cpp b/src/ir.cpp index c654e2bf8..5eeba91a2 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7865,14 +7865,40 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (ir_is_elem_const(proc->module, fv->value, et)) { continue; } - auto tav = fv->field->tav; - GB_ASSERT(tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(tav.value); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - irCompoundLitElemTempData data = {}; - data.expr = fv->value; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); + irValue *value = ir_build_expr(proc, fv->value); + + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } else { + auto tav = fv->field->tav; + GB_ASSERT(tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(tav.value); + + irCompoundLitElemTempData data = {}; + data.value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); + data.expr = fv->value; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } } else { if (ir_is_elem_const(proc->module, elem, et)) { @@ -7897,15 +7923,15 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { defer (proc->return_ptr_hint_value = return_ptr_hint_value); defer (proc->return_ptr_hint_used = return_ptr_hint_used); + irValue *field_expr = temp_data[i].value; Ast *expr = temp_data[i].expr; - if (expr == nullptr) { - continue; - } proc->return_ptr_hint_value = temp_data[i].gep; proc->return_ptr_hint_ast = unparen_expr(expr); - irValue *field_expr = ir_build_expr(proc, expr); + if (field_expr == nullptr) { + field_expr = ir_build_expr(proc, expr); + } Type *t = ir_type(field_expr); GB_ASSERT(t->kind != Type_Tuple); irValue *ev = ir_emit_conv(proc, field_expr, et); @@ -7945,19 +7971,43 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { continue; } + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(fv->field->tav.value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); - irValue *field_expr = ir_build_expr(proc, fv->value); - GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } - irValue *ev = ir_emit_conv(proc, field_expr, et); + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(fv->field->tav.value); - irCompoundLitElemTempData data = {}; - data.value = ev; - data.elem_index = cast(i32)index; - array_add(&temp_data, data); + irValue *field_expr = ir_build_expr(proc, fv->value); + GB_ASSERT(!is_type_tuple(ir_type(field_expr))); + + irValue *ev = ir_emit_conv(proc, field_expr, et); + + irCompoundLitElemTempData data = {}; + data.value = ev; + data.elem_index = cast(i32)index; + array_add(&temp_data, data); + } } else { if (ir_is_elem_const(proc->module, elem, et)) { continue; @@ -8015,14 +8065,36 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { Ast *elem = cl->elems[i]; if (elem->kind == Ast_FieldValue) { ast_node(fv, FieldValue, elem); - GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } - i64 field_index = exact_value_to_i64(fv->field->tav.value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); - irValue *ev = ir_build_expr(proc, fv->value); - irValue *value = ir_emit_conv(proc, ev, et); - irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); - ir_emit_store(proc, ep, value); + for (i64 k = lo; k < hi; k++) { + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)k); + ir_emit_store(proc, ep, value); + } + } else { + GB_ASSERT(fv->field->tav.mode == Addressing_Constant); + + i64 field_index = exact_value_to_i64(fv->field->tav.value); + + irValue *ev = ir_build_expr(proc, fv->value); + irValue *value = ir_emit_conv(proc, ev, et); + irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); + ir_emit_store(proc, ep, value); + } } else { irValue *value = ir_emit_conv(proc, ir_build_expr(proc, elem), et); irValue *ep = ir_emit_array_epi(proc, items, cast(i32)i); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index cece0c1db..1d0c8af35 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -887,17 +887,47 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * for (isize j = 0; j < elem_count; j++) { Ast *elem = cl->elems[j]; ast_node(fv, FieldValue, elem); - TypeAndValue index_tav = fv->field->tav; - GB_ASSERT(index_tav.mode == Addressing_Constant); - i64 index = exact_value_to_i64(index_tav.value); - if (index == i) { - TypeAndValue tav = fv->value->tav; - if (tav.mode != Addressing_Constant) { + if (is_ast_range(fv->field)) { + ast_node(ie, BinaryExpr, fv->field); + TypeAndValue lo_tav = ie->left->tav; + TypeAndValue hi_tav = ie->right->tav; + GB_ASSERT(lo_tav.mode == Addressing_Constant); + GB_ASSERT(hi_tav.mode == Addressing_Constant); + + TokenKind op = ie->op.kind; + i64 lo = exact_value_to_i64(lo_tav.value); + i64 hi = exact_value_to_i64(hi_tav.value); + if (op == Token_Ellipsis) { + hi += 1; + } + if (lo == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + for (i64 k = lo; k < hi; k++) { + if (k > lo) ir_write_str_lit(f, ", "); + + ir_print_compound_element(f, m, tav.value, elem_type); + } + + found = true; + i += (hi-lo-1); + break; + } + } else { + TypeAndValue index_tav = fv->field->tav; + GB_ASSERT(index_tav.mode == Addressing_Constant); + i64 index = exact_value_to_i64(index_tav.value); + if (index == i) { + TypeAndValue tav = fv->value->tav; + if (tav.mode != Addressing_Constant) { + break; + } + ir_print_compound_element(f, m, tav.value, elem_type); + found = true; break; } - ir_print_compound_element(f, m, tav.value, elem_type); - found = true; - break; } } diff --git a/src/parser.cpp b/src/parser.cpp index 7f866922a..10aa10119 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -588,6 +588,7 @@ Ast *ast_undef(AstFile *f, Token token) { Ast *ast_basic_lit(AstFile *f, Token basic_lit) { Ast *result = alloc_ast_node(f, Ast_BasicLit); result->BasicLit.token = basic_lit; + result->BasicLit.value = exact_value_from_basic_literal(basic_lit); return result; } @@ -1509,8 +1510,11 @@ Ast *parse_value(AstFile *f) { if (f->curr_token.kind == Token_OpenBrace) { return parse_literal_value(f, nullptr); } - - Ast *value = parse_expr(f, false); + Ast *value; + bool prev_allow_range = f->allow_range; + f->allow_range = true; + value = parse_expr(f, false); + f->allow_range = prev_allow_range; return value; } @@ -1735,7 +1739,8 @@ Ast *parse_operand(AstFile *f, bool lhs) { operand = ast_bad_expr(f, token, f->curr_token); } operand->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ else if (name.string == "file") { return ast_basic_directive(f, token, name.string); + } */ else if (name.string == "file") { + return ast_basic_directive(f, token, name.string); } else if (name.string == "line") { return ast_basic_directive(f, token, name.string); } else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string); } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string); diff --git a/src/parser.hpp b/src/parser.hpp index 9c3f733e5..83df7a9d6 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -222,6 +222,7 @@ enum StmtAllowFlag { AST_KIND(Undef, "undef", Token) \ AST_KIND(BasicLit, "basic literal", struct { \ Token token; \ + ExactValue value; \ }) \ AST_KIND(BasicDirective, "basic directive", struct { \ Token token; \ -- cgit v1.2.3