From dc303cde21d23b1b57a4cb4f667b2cfbe2a39ffd Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sat, 1 Apr 2017 12:07:41 +0100 Subject: [PATCH] Complex numbers: complex64 complex128 --- build.bat | 2 +- code/demo.odin | 315 +------------------------------------------ core/_preload.odin | 1 + core/fmt.odin | 32 ++++- core/math.odin | 12 +- core/os_windows.odin | 167 ++++++++++++++++------- core/strconv.odin | 27 +--- src/check_expr.c | 153 ++++++++++++++++++++- src/checker.c | 55 +++++--- src/exact_value.c | 157 +++++++++++++++++++-- src/ir.c | 133 +++++++++++++++++- src/ir_print.c | 51 ++++--- src/main.c | 2 +- src/parser.c | 2 + src/tokenizer.c | 12 +- src/types.c | 86 +++++++----- 16 files changed, 717 insertions(+), 490 deletions(-) diff --git a/build.bat b/build.bat index c50fc93ef..51dfc84b4 100644 --- a/build.bat +++ b/build.bat @@ -4,7 +4,7 @@ set exe_name=odin.exe :: Debug = 0, Release = 1 -set release_mode=1 +set release_mode=0 set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR- if %release_mode% EQU 0 ( rem Debug diff --git a/code/demo.odin b/code/demo.odin index 3b78f7de3..e9eba17c8 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,320 +1,9 @@ -#import "atomic.odin"; -#import "hash.odin"; -#import "mem.odin"; -#import "opengl.odin"; -#import "strconv.odin"; -#import "sync.odin"; -#import win32 "sys/windows.odin"; - #import "fmt.odin"; #import "os.odin"; #import "math.odin"; main :: proc() { -when true { -/* - Added: - * Unexported entities and fields using an underscore prefix - - See `sync.odin` and explain - - Removed: - * Maybe/option types - * Remove `type` keyword and other "reserved" keywords - * ..< and ... removed and replace with .. (half-closed range) - - Changed: - * `compile_assert` and `assert`return the value of the condition for semantic reasons - * thread_local -> #thread_local - * #include -> #load - * Files only get checked if they are actually used - * match x in y {} // For type match statements - * Version numbering now starts from 0.1.0 and uses the convention: - - major.minor.patch - * Core library additions to Windows specific stuff - */ - - { - Fruit :: enum { - APPLE, - BANANA, - COCONUT, - } - fmt.println(Fruit.names); - } - - { - A :: struct {x, y: f32}; - B :: struct #align 16 {x, y: f32}; - fmt.println("align_of(A) =", align_of(A)); - fmt.println("align_of(B) =", align_of(B)); - } - - { - // Removal of ..< and ... - for i in 0..16 { - } - // Is similar to - for _i := 0; _i < 16; _i++ { immutable i := _i; - } - } - - { - #label thing - for i in 0..10 { - for j in i+1..10 { - if j == 2 { - fmt.println(i, j); - continue thing; - } - if j == 3 { - break thing; - } - } - } - - // Works with, `for`, `for in`, `match`, `match in` - // NOTE(bill): This solves most of the problems I need `goto` for - } - - { - t := type_info(int); - using Type_Info; - match i in t { - case Integer, Float: - fmt.println("It's a number"); - } - - - x: any = 123; - #label foo - match i in x { - case int, f32: - fmt.println("It's an int or f32"); - break foo; - } - } - - { - cond := true; - x: int; - if cond { - x = 3; - } else { - x = 4; - } - - - // Ternary operator - y := cond ? 3 : 4; - - FOO :: true ? 123 : 432; // Constant ternary expression - fmt.println("Ternary values:", y, FOO); - } - - { - // Slices now store a capacity - buf: [256]byte; - s: []byte; - s = buf[..0]; // == buf[0..0]; - fmt.println("count =", s.count); - fmt.println("capacity =", s.capacity); - append(s, 1, 2, 3); - fmt.println(s); - - s = buf[1..2..3]; - fmt.println("count =", s.count); - fmt.println("capacity =", s.capacity); - fmt.println(s); - - clear(s); // Sets count to zero - s.count = 0; // Equivalent - } - - { - Foo :: struct { - x, y, z: f32, - ok: bool, - flags: u32, - } - foo_array: [256]Foo; - foo_as_bytes: []byte = slice_to_bytes(foo_array[..]); - // Useful for things like - // os.write(handle, foo_as_bytes); - - foo_slice := slice_ptr(cast(^Foo)foo_as_bytes.data, foo_as_bytes.count/size_of(Foo), foo_as_bytes.capacity/size_of(Foo)); - // Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone? - // And if so what would the syntax be? - // slice_transmute([]Foo, foo_as_bytes); - } - - { - Vec3 :: [vector 3]f32; - - x := Vec3{1, 2, 3}; - y := Vec3{4, 5, 6}; - fmt.println(x < y); - fmt.println(x + y); - fmt.println(x - y); - fmt.println(x * y); - fmt.println(x / y); - - for i in x { - fmt.println(i); - } - - compile_assert(size_of([vector 7]bool) == size_of([7]bool)); - compile_assert(size_of([vector 7]i32) == size_of([7]i32)); - // align_of([vector 7]i32) != align_of([7]i32) // this may be the case - } - - { - // fmt.* changes - // bprint* returns `int` (bytes written) - // sprint* returns `string` (bytes written as a string) - - data: [256]byte; - str := fmt.sprintf(data[..0], "Hellope %d %s %c", 123, "others", '!'); - fmt.println(str); - - buf := data[..0]; - count := fmt.bprintf(^buf, "Hellope %d %s %c", 123, "others", '!'); - fmt.println(cast(string)buf[..count]); - - // NOTE(bill): We may change this but because this is a library feature, I am not that bothered yet - } - - { - x: [dynamic]f64; - reserve(x, 16); - defer free(x); // `free` is overloaded for numerous types - // Number literals can have underscores in them for readability - append(x, 2_000_000.500_000, 3, 5, 7); // variadic append - - for p, i in x { - if i > 0 { fmt.print(", "); } - fmt.print(p); - } - fmt.println(); - } - - { - // Dynamic array "literals" - x := [dynamic]f64{2_000_000.500_000, 3, 5, 7}; - defer free(x); - fmt.println(x); // fmt.print* supports printing of dynamic types - clear(x); - fmt.println(x); - } - - { - m: map[f32]int; - reserve(m, 16); - defer free(m); - - m[1.0] = 1278; - m[2.0] = 7643; - m[3.0] = 564; - _, ok := m[3.0]; - c := m[3.0]; - assert(ok && c == 564); - - fmt.print("map["); - i := 0; - for val, key in m { - if i > 0 { - fmt.print(", "); - } - fmt.printf("%v=%v", key, val); - i += 1; - } - fmt.println("]"); - } - { - m := map[string]u32{ - "a" = 56, - "b" = 13453, - "c" = 7654, - }; - defer free(m); - - c := m["c"]; - _, ok := m["c"]; - assert(ok && c == 7654); - fmt.println(m); - - delete(m, "c"); // deletes entry with key "c" - _, found := m["c"]; - assert(!found); - - fmt.println(m); - clear(m); - fmt.println(m); - - // NOTE: Fixed size maps are planned but we have not yet implemented - // them as we have had no need for them as of yet - } - - { - Vector3 :: struct{x, y, z: f32}; - Quaternion :: struct{x, y, z, w: f32}; - - Entity :: union { - // Common Fields - id: u64, - name: string, - using position: Vector3, - orientation: Quaternion, - flags: u32, - - // Variants - Frog{ - ribbit_volume: f32, - jump_height: f32, - }, - Door{ - openness: f32, - }, - Map{ - width, height: f32, - place_positions: []Vector3, - place_names: []string, - }, - } - - entity: Entity; - // implicit conversion from variant to base type - entity = Entity.Frog{ - id = 1337, - ribbit_volume = 0.5, - jump_height = 2.1, - /*other data */ - }; - - entity.name = "Frank"; - entity.position = Vector3{1, 4, 9}; - - using Entity; - match e in entity { - case Frog: - fmt.println("Ribbit"); - case Door: - fmt.println("Creak"); - case Map: - fmt.println("Rustle"); - default: - fmt.println("Just a normal entity"); - } - - if frog, ok := union_cast(Frog)entity; ok { - fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position); - } - - // Panics if not the correct type - frog: Frog; - frog = union_cast(Frog)entity; - frog, _ = union_cast(Frog)entity; // ignore error and force cast - } + x := 1+2i; + fmt.printf("%v\n", x); } -} - diff --git a/core/_preload.odin b/core/_preload.odin index 3b3e60e0f..fd61abf69 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -41,6 +41,7 @@ Type_Info :: union { Named{name: string, base: ^Type_Info}, Integer{size: int, signed: bool}, Float{size: int}, + Complex{size: int}, String{}, Boolean{}, Any{}, diff --git a/core/fmt.odin b/core/fmt.odin index 0f26b82fc..1a8766b3d 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -110,6 +110,11 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) { case 4: write_string(buf, "f32"); case 8: write_string(buf, "f64"); } + case Complex: + match info.size { + case 8: write_string(buf, "complex64"); + case 16: write_string(buf, "complex128"); + } case String: write_string(buf, "string"); case Boolean: write_string(buf, "bool"); case Pointer: @@ -733,6 +738,7 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { case Boolean: fmt_arg(fi, v, verb); case Float: fmt_arg(fi, v, verb); + case Complex: fmt_arg(fi, v, verb); case Integer: fmt_arg(fi, v, verb); case String: fmt_arg(fi, v, verb); @@ -883,6 +889,24 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) { } } +fmt_complex :: proc(fi: ^Fmt_Info, c: complex128, bits: int, verb: rune) { + match verb { + case 'f', 'F', 'v': + r := real(c); + i := imag(c); + fmt_float(fi, r, bits/2, verb); + if !fi.plus && i >= 0 { + write_rune(fi.buf, '+'); + } + fmt_float(fi, i, bits/2, verb); + write_rune(fi.buf, 'i'); + + default: + fmt_bad_verb(fi, verb); + return; + } +} + fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) { if arg.data == nil || arg.type_info == nil { write_string(fi.buf, ""); @@ -903,9 +927,11 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) { base_arg := arg; base_arg.type_info = type_info_base(base_arg.type_info); match a in base_arg { - case bool: fmt_bool(fi, a, verb); - case f32: fmt_float(fi, cast(f64)a, 32, verb); - case f64: fmt_float(fi, a, 64, verb); + case bool: fmt_bool(fi, a, verb); + case f32: fmt_float(fi, cast(f64)a, 32, verb); + case f64: fmt_float(fi, a, 64, verb); + case complex64: fmt_complex(fi, cast(complex128)a, 64, verb); + case complex128: fmt_complex(fi, a, 128, verb); case int: fmt_int(fi, cast(u64)a, true, 8*size_of(int), verb); case i8: fmt_int(fi, cast(u64)a, true, 8, verb); diff --git a/core/math.odin b/core/math.odin index 6ba540ac0..15a445831 100644 --- a/core/math.odin +++ b/core/math.odin @@ -27,14 +27,14 @@ Mat4 :: [4]Vec4; sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32"; sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64"; -sin :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32"; -sin :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64"; +sin :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.sin.f32"; +sin :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.sin.f64"; -cos :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32"; -cos :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64"; +cos :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.cos.f32"; +cos :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.cos.f64"; -tan :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); } -tan :: proc(x: f64) -> f64 #inline { return sin(x)/cos(x); } +tan :: proc(θ: f32) -> f32 #inline { return sin(θ)/cos(θ); } +tan :: proc(θ: f64) -> f64 #inline { return sin(θ)/cos(θ); } pow :: proc(x, power: f32) -> f32 #foreign __llvm_core "llvm.pow.f32"; pow :: proc(x, power: f64) -> f64 #foreign __llvm_core "llvm.pow.f64"; diff --git a/core/os_windows.odin b/core/os_windows.odin index 1adc80d18..b834a2726 100644 --- a/core/os_windows.odin +++ b/core/os_windows.odin @@ -1,6 +1,4 @@ -#import w "sys/windows.odin"; -#import "fmt.odin"; - +#import win32 "sys/windows.odin"; Handle :: int; File_Time :: u64; @@ -50,6 +48,8 @@ WSAECONNRESET: Errno : 10054; ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0; +// "Argv" arguments converted to Odin strings +args := _alloc_command_line_arguments(); open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) { @@ -59,22 +59,22 @@ open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) { access: u32; match mode & (O_RDONLY|O_WRONLY|O_RDWR) { - case O_RDONLY: access = w.FILE_GENERIC_READ; - case O_WRONLY: access = w.FILE_GENERIC_WRITE; - case O_RDWR: access = w.FILE_GENERIC_READ | w.FILE_GENERIC_WRITE; + case O_RDONLY: access = win32.FILE_GENERIC_READ; + case O_WRONLY: access = win32.FILE_GENERIC_WRITE; + case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE; } if mode&O_CREAT != 0 { - access |= w.FILE_GENERIC_WRITE; + access |= win32.FILE_GENERIC_WRITE; } if mode&O_APPEND != 0 { - access &~= w.FILE_GENERIC_WRITE; - access |= w.FILE_APPEND_DATA; + access &~= win32.FILE_GENERIC_WRITE; + access |= win32.FILE_APPEND_DATA; } - share_mode := cast(u32)(w.FILE_SHARE_READ|w.FILE_SHARE_WRITE); - sa: ^w.Security_Attributes = nil; - sa_inherit := w.Security_Attributes{length = size_of(w.Security_Attributes), inherit_handle = 1}; + share_mode := cast(u32)(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE); + sa: ^win32.Security_Attributes = nil; + sa_inherit := win32.Security_Attributes{length = size_of(win32.Security_Attributes), inherit_handle = 1}; if mode&O_CLOEXEC == 0 { sa = ^sa_inherit; } @@ -82,37 +82,37 @@ open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) { create_mode: u32; match { case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL): - create_mode = w.CREATE_NEW; + create_mode = win32.CREATE_NEW; case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC): - create_mode = w.CREATE_ALWAYS; + create_mode = win32.CREATE_ALWAYS; case mode&O_CREAT == O_CREAT: - create_mode = w.OPEN_ALWAYS; + create_mode = win32.OPEN_ALWAYS; case mode&O_TRUNC == O_TRUNC: - create_mode = w.TRUNCATE_EXISTING; + create_mode = win32.TRUNCATE_EXISTING; default: - create_mode = w.OPEN_EXISTING; + create_mode = win32.OPEN_EXISTING; } buf: [300]byte; copy(buf[..], cast([]byte)path); - handle := cast(Handle)w.CreateFileA(^buf[0], access, share_mode, sa, create_mode, w.FILE_ATTRIBUTE_NORMAL, nil); + handle := cast(Handle)win32.CreateFileA(^buf[0], access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil); if handle != INVALID_HANDLE { return handle, ERROR_NONE; } - err := w.GetLastError(); + err := win32.GetLastError(); return INVALID_HANDLE, cast(Errno)err; } close :: proc(fd: Handle) { - w.CloseHandle(cast(w.Handle)fd); + win32.CloseHandle(cast(win32.Handle)fd); } write :: proc(fd: Handle, data: []byte) -> (int, Errno) { bytes_written: i32; - e := w.WriteFile(cast(w.Handle)fd, data.data, cast(i32)data.count, ^bytes_written, nil); - if e == w.FALSE { - err := w.GetLastError(); + e := win32.WriteFile(cast(win32.Handle)fd, data.data, cast(i32)data.count, ^bytes_written, nil); + if e == win32.FALSE { + err := win32.GetLastError(); return 0, cast(Errno)err; } return cast(int)bytes_written, ERROR_NONE; @@ -120,31 +120,30 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Errno) { bytes_read: i32; - e := w.ReadFile(cast(w.Handle)fd, data.data, cast(u32)data.count, ^bytes_read, nil); - if e == w.FALSE { - err := w.GetLastError(); + e := win32.ReadFile(cast(win32.Handle)fd, data.data, cast(u32)data.count, ^bytes_read, nil); + if e == win32.FALSE { + err := win32.GetLastError(); return 0, cast(Errno)err; } return cast(int)bytes_read, ERROR_NONE; } seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { - using w; w: u32; match whence { - case 0: w = FILE_BEGIN; - case 1: w = FILE_CURRENT; - case 2: w = FILE_END; + case 0: w = win32.FILE_BEGIN; + case 1: w = win32.FILE_CURRENT; + case 2: w = win32.FILE_END; } hi := cast(i32)(offset>>32); lo := cast(i32)(offset); - ft := GetFileType(cast(Handle)fd); - if ft == FILE_TYPE_PIPE { + ft := win32.GetFileType(cast(win32.Handle)fd); + if ft == win32.FILE_TYPE_PIPE { return 0, ERROR_FILE_IS_PIPE; } - dw_ptr := SetFilePointer(cast(Handle)fd, lo, ^hi, w); - if dw_ptr == INVALID_SET_FILE_POINTER { - err := GetLastError(); + dw_ptr := win32.SetFilePointer(cast(win32.Handle)fd, lo, ^hi, w); + if dw_ptr == win32.INVALID_SET_FILE_POINTER { + err := win32.GetLastError(); return 0, cast(Errno)err; } return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE; @@ -152,14 +151,14 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { // NOTE(bill): Uses startup to initialize it -stdin := get_std_handle(w.STD_INPUT_HANDLE); -stdout := get_std_handle(w.STD_OUTPUT_HANDLE); -stderr := get_std_handle(w.STD_ERROR_HANDLE); +stdin := get_std_handle(win32.STD_INPUT_HANDLE); +stdout := get_std_handle(win32.STD_OUTPUT_HANDLE); +stderr := get_std_handle(win32.STD_ERROR_HANDLE); get_std_handle :: proc(h: int) -> Handle { - fd := w.GetStdHandle(cast(i32)h); - w.SetHandleInformation(fd, w.HANDLE_FLAG_INHERIT, 0); + fd := win32.GetStdHandle(cast(i32)h); + win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0); return cast(Handle)fd; } @@ -169,23 +168,23 @@ get_std_handle :: proc(h: int) -> Handle { last_write_time :: proc(fd: Handle) -> File_Time { - file_info: w.By_Handle_File_Information; - w.GetFileInformationByHandle(cast(w.Handle)fd, ^file_info); + file_info: win32.By_Handle_File_Information; + win32.GetFileInformationByHandle(cast(win32.Handle)fd, ^file_info); lo := cast(File_Time)file_info.last_write_time.lo; hi := cast(File_Time)file_info.last_write_time.hi; return lo | hi << 32; } last_write_time_by_name :: proc(name: string) -> File_Time { - last_write_time: w.Filetime; - data: w.File_Attribute_Data; + last_write_time: win32.Filetime; + data: win32.File_Attribute_Data; buf: [1024]byte; assert(buf.count > name.count); copy(buf[..], cast([]byte)name); - if w.GetFileAttributesExA(^buf[0], w.GetFileExInfoStandard, ^data) != 0 { + if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 { last_write_time = data.last_write_time; } @@ -209,7 +208,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) { defer close(fd); length: i64; - file_size_ok := w.GetFileSizeEx(cast(w.Handle)fd, ^length) != 0; + file_size_ok := win32.GetFileSizeEx(cast(win32.Handle)fd, ^length) != 0; if !file_size_ok { return nil, false; } @@ -232,7 +231,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) { to_read = MAX; } - w.ReadFile(cast(w.Handle)fd, ^data[total_read], to_read, ^single_read_length, nil); + win32.ReadFile(cast(win32.Handle)fd, ^data[total_read], to_read, ^single_read_length, nil); if single_read_length <= 0 { free(data); return nil, false; @@ -247,7 +246,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) { heap_alloc :: proc(size: int) -> rawptr { - return w.HeapAlloc(w.GetProcessHeap(), w.HEAP_ZERO_MEMORY, size); + return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size); } heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { if new_size == 0 { @@ -257,25 +256,89 @@ heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { if ptr == nil { return heap_alloc(new_size); } - return w.HeapReAlloc(w.GetProcessHeap(), w.HEAP_ZERO_MEMORY, ptr, new_size); + return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size); } heap_free :: proc(ptr: rawptr) { if ptr == nil { return; } - w.HeapFree(w.GetProcessHeap(), 0, ptr); + win32.HeapFree(win32.GetProcessHeap(), 0, ptr); } exit :: proc(code: int) { - w.ExitProcess(cast(u32)code); + win32.ExitProcess(cast(u32)code); } current_thread_id :: proc() -> int { - return cast(int)w.GetCurrentThreadId(); + return cast(int)win32.GetCurrentThreadId(); } + +_alloc_command_line_arguments :: proc() -> []string { + alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string { + wstr_len := 0; + for (wstr+wstr_len)^ != 0 { + wstr_len++; + } + len := 2*wstr_len-1; + buf := new_slice(byte, len+1); + str := slice_ptr(wstr, wstr_len+1); + + + i, j := 0, 0; + for str[j] != 0 { + match { + case str[j] < 0x80: + if i+1 > len { + return ""; + } + buf[i] = cast(byte)str[j]; i++; + j++; + case str[j] < 0x800: + if i+2 > len { + return ""; + } + buf[i] = cast(byte)(0xc0 + (str[j]>>6)); i++; + buf[i] = cast(byte)(0x80 + (str[j]&0x3f)); i++; + j++; + case 0xd800 <= str[j] && str[j] < 0xdc00: + if i+4 > len { + return ""; + } + c := cast(rune)((str[j] - 0xd800) << 10) + cast(rune)((str[j+1]) - 0xdc00) + 0x10000; + buf[i] = cast(byte)(0xf0 + (c >> 18)); i++; + buf[i] = cast(byte)(0x80 + ((c >> 12) & 0x3f)); i++; + buf[i] = cast(byte)(0x80 + ((c >> 6) & 0x3f)); i++; + buf[i] = cast(byte)(0x80 + ((c ) & 0x3f)); i++; + j += 2; + case 0xdc00 <= str[j] && str[j] < 0xe000: + return ""; + default: + if i+3 > len { + return ""; + } + buf[i] = 0xe0 + cast(byte) (str[j] >> 12); i++; + buf[i] = 0x80 + cast(byte)((str[j] >> 6) & 0x3f); i++; + buf[i] = 0x80 + cast(byte)((str[j] ) & 0x3f); i++; + j++; + } + } + + return cast(string)buf[..i]; + } + + arg_count: i32; + arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), ^arg_count); + arg_list := new_slice(string, arg_count); + for _, i in arg_list { + arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^); + } + return arg_list; +} + + diff --git a/core/strconv.odin b/core/strconv.odin index 5a14d6660..998129829 100644 --- a/core/strconv.odin +++ b/core/strconv.odin @@ -313,29 +313,16 @@ append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: i neg: bool; u, neg = is_integer_negative(u, is_signed, bit_size); - if is_pow2(cast(i64)base) { - b := cast(u64)base; - m := cast(uint)b - 1; - for u >= b { - i--; - a[i] = digits[cast(uint)u & m]; - u >>= b; - } + for b := cast(u64)base; u >= b; { i--; - a[i] = digits[cast(uint)u]; - } else { - b := cast(u64)base; - for u >= b { - i--; - q := u / b; - a[i] = digits[cast(uint)(u-q*b)]; - u = q; - } - - i--; - a[i] = digits[cast(uint)u]; + q := u / b; + a[i] = digits[cast(uint)(u-q*b)]; + u = q; } + i--; + a[i] = digits[cast(uint)u]; + if flags&Int_Flag.PREFIX != 0 { ok := true; match base { diff --git a/src/check_expr.c b/src/check_expr.c index db1bbd135..8c162e034 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -1754,18 +1754,37 @@ bool check_representable_as_constant(Checker *c, ExactValue in_value, Type *type if (v.kind != ExactValue_Float) { return false; } + if (out_value) *out_value = v; + switch (type->Basic.kind) { - // case Basic_f16: case Basic_f32: case Basic_f64: - // case Basic_f128: - if (out_value) *out_value = v; return true; case Basic_UntypedFloat: return true; } + } else if (is_type_complex(type)) { + ExactValue v = exact_value_to_complex(in_value); + if (v.kind != ExactValue_Complex) { + return false; + } + + switch (type->Basic.kind) { + case Basic_complex64: + case Basic_complex128: { + ExactValue real = exact_value_real(v); + 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)); + return true; + } + } break; + } + + return false; } else if (is_type_pointer(type)) { if (in_value.kind == ExactValue_Pointer) { return true; @@ -2190,6 +2209,10 @@ bool check_is_castable_to(Checker *c, Operand *operand, Type *y) { } } + if (is_type_complex(src) && is_type_complex(dst)) { + return true; + } + // Cast between pointers if (is_type_pointer(src) && is_type_pointer(dst)) { Type *s = base_type(type_deref(src)); @@ -3568,6 +3591,129 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id operand->mode = Addressing_Value; } break; + case BuiltinProc_complex: { + // complex :: proc(real, imag: float_type) -> complex_type + Operand x = *operand; + Operand y = {0}; + + // NOTE(bill): Invalid will be the default till fixed + operand->type = t_invalid; + operand->mode = Addressing_Invalid; + + check_expr(c, &y, ce->args.e[1]); + if (y.mode == Addressing_Invalid) { + return false; + } + + u32 flag = 0; + if (is_type_untyped(x.type)) { + flag |= 1; + } + if (is_type_untyped(y.type)) { + flag |= 2; + } + switch (flag) { + case 0: break; + case 1: convert_to_typed(c, &x, y.type, 0); break; + case 2: convert_to_typed(c, &y, x.type, 0); break; + case 3: { + if (x.mode == Addressing_Constant && y.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; + } + } else { + convert_to_typed(c, &x, t_f64, 0); + convert_to_typed(c, &y, t_f64, 0); + } + } break; + } + + if (x.mode == Addressing_Invalid || y.mode == Addressing_Invalid) { + return false; + } + + if (!are_types_identical(x.type, y.type)) { + gbString type_x = type_to_string(x.type); + gbString type_y = type_to_string(y.type); + error_node(call, + "Mismatched types to `complex`, `%s` vs `%s`", + type_x, type_y); + gb_string_free(type_y); + gb_string_free(type_x); + return false; + } + + if (!is_type_float(x.type)) { + gbString s = type_to_string(x.type); + error_node(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) { + operand->value = exact_binary_operator_value(Token_Add, x.value, y.value); + 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_complex64; break; + case Basic_f64: operand->type = t_complex128; break; + case Basic_UntypedFloat: operand->type = t_untyped_complex; break; + default: GB_PANIC("Invalid type"); break; + } + } break; + + case BuiltinProc_real: + case BuiltinProc_imag: { + // real :: proc(c: complex_type) -> float_type + // imag :: proc(c: complex_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_complex128, 0); + if (x->mode == Addressing_Invalid) { + return false; + } + } + } + + if (!is_type_complex(x->type)) { + gbString s = type_to_string(x->type); + error_node(call, "Argument has type `%s`, expected a complex type", s); + gb_string_free(s); + return false; + } + + if (x->mode == Addressing_Constant) { + if (id == BuiltinProc_real) { + x->value = exact_value_real(x->value); + } else { + x->value = exact_value_imag(x->value); + } + } else { + x->mode = Addressing_Value; + } + + BasicKind kind = core_type(x->type)->Basic.kind; + switch (kind) { + case Basic_complex64: x->type = t_f32; break; + case Basic_complex128: x->type = t_f64; break; + case Basic_UntypedComplex: x->type = t_untyped_float; break; + default: GB_PANIC("Invalid type"); break; + } + } break; + case BuiltinProc_slice_ptr: { // slice_ptr :: proc(a: ^T, len: int) -> []T // slice_ptr :: proc(a: ^T, len, cap: int) -> []T @@ -4405,6 +4551,7 @@ ExprKind check_expr_base_internal(Checker *c, Operand *o, AstNode *node, Type *t switch (bl->kind) { case Token_Integer: t = t_untyped_integer; break; case Token_Float: t = t_untyped_float; break; + case Token_Imag: t = t_untyped_complex; break; case Token_String: t = t_untyped_string; break; case Token_Rune: t = t_untyped_rune; break; default: GB_PANIC("Unknown literal"); break; diff --git a/src/checker.c b/src/checker.c index 93187d50c..5f1352a29 100644 --- a/src/checker.c +++ b/src/checker.c @@ -52,6 +52,10 @@ typedef enum BuiltinProcId { BuiltinProc_swizzle, + BuiltinProc_complex, + BuiltinProc_real, + BuiltinProc_imag, + // BuiltinProc_ptr_offset, // BuiltinProc_ptr_sub, BuiltinProc_slice_ptr, @@ -87,8 +91,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("type_info"), 1, false, Expr_Expr}, {STR_LIT("type_info_of_val"), 1, false, Expr_Expr}, - {STR_LIT("compile_assert"), 1, false, Expr_Stmt}, - {STR_LIT("assert"), 1, false, Expr_Stmt}, + {STR_LIT("compile_assert"), 1, false, Expr_Expr}, + {STR_LIT("assert"), 1, false, Expr_Expr}, {STR_LIT("panic"), 1, false, Expr_Stmt}, {STR_LIT("copy"), 2, false, Expr_Expr}, @@ -96,6 +100,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("swizzle"), 1, true, Expr_Expr}, + {STR_LIT("complex"), 2, false, Expr_Expr}, + {STR_LIT("real"), 1, false, Expr_Expr}, + {STR_LIT("imag"), 1, false, Expr_Expr}, + // {STR_LIT("ptr_offset"), 2, false, Expr_Expr}, // {STR_LIT("ptr_sub"), 2, false, Expr_Expr}, {STR_LIT("slice_ptr"), 2, true, Expr_Expr}, @@ -940,6 +948,15 @@ void add_type_info_type(Checker *c, Type *t) { add_type_info_type(c, t_type_info_ptr); add_type_info_type(c, t_rawptr); break; + + case Basic_complex64: + add_type_info_type(c, t_type_info_float); + add_type_info_type(c, t_f32); + break; + case Basic_complex128: + add_type_info_type(c, t_type_info_float); + add_type_info_type(c, t_f64); + break; } } break; @@ -1128,31 +1145,33 @@ void init_preload(Checker *c) { - if (record->variant_count != 19) { + if (record->variant_count != 20) { compiler_error("Invalid `Type_Info` layout"); } t_type_info_named = record->variants[ 1]->type; t_type_info_integer = record->variants[ 2]->type; t_type_info_float = record->variants[ 3]->type; - t_type_info_string = record->variants[ 4]->type; - t_type_info_boolean = record->variants[ 5]->type; - t_type_info_any = record->variants[ 6]->type; - t_type_info_pointer = record->variants[ 7]->type; - t_type_info_procedure = record->variants[ 8]->type; - t_type_info_array = record->variants[ 9]->type; - t_type_info_dynamic_array = record->variants[10]->type; - t_type_info_slice = record->variants[11]->type; - t_type_info_vector = record->variants[12]->type; - t_type_info_tuple = record->variants[13]->type; - t_type_info_struct = record->variants[14]->type; - t_type_info_raw_union = record->variants[15]->type; - t_type_info_union = record->variants[16]->type; - t_type_info_enum = record->variants[17]->type; - t_type_info_map = record->variants[18]->type; + t_type_info_complex = record->variants[ 4]->type; + t_type_info_string = record->variants[ 5]->type; + t_type_info_boolean = record->variants[ 6]->type; + t_type_info_any = record->variants[ 7]->type; + t_type_info_pointer = record->variants[ 8]->type; + t_type_info_procedure = record->variants[ 9]->type; + t_type_info_array = record->variants[10]->type; + t_type_info_dynamic_array = record->variants[11]->type; + t_type_info_slice = record->variants[12]->type; + t_type_info_vector = record->variants[13]->type; + t_type_info_tuple = record->variants[14]->type; + t_type_info_struct = record->variants[15]->type; + t_type_info_raw_union = record->variants[16]->type; + t_type_info_union = record->variants[17]->type; + t_type_info_enum = record->variants[18]->type; + t_type_info_map = record->variants[19]->type; t_type_info_named_ptr = make_type_pointer(c->allocator, t_type_info_named); t_type_info_integer_ptr = make_type_pointer(c->allocator, t_type_info_integer); t_type_info_float_ptr = make_type_pointer(c->allocator, t_type_info_float); + t_type_info_complex_ptr = make_type_pointer(c->allocator, t_type_info_complex); t_type_info_string_ptr = make_type_pointer(c->allocator, t_type_info_string); t_type_info_boolean_ptr = make_type_pointer(c->allocator, t_type_info_boolean); t_type_info_any_ptr = make_type_pointer(c->allocator, t_type_info_any); diff --git a/src/exact_value.c b/src/exact_value.c index 7e8e69f50..f1da88fc6 100644 --- a/src/exact_value.c +++ b/src/exact_value.c @@ -12,21 +12,27 @@ typedef enum ExactValueKind { ExactValue_String, ExactValue_Integer, ExactValue_Float, + ExactValue_Complex, ExactValue_Pointer, ExactValue_Compound, // TODO(bill): Is this good enough? ExactValue_Count, } ExactValueKind; +typedef struct Complex128 { + f64 real, imag; +} Complex128; + typedef struct ExactValue { ExactValueKind kind; union { - bool value_bool; - String value_string; - i64 value_integer; // NOTE(bill): This must be an integer and not a pointer - f64 value_float; - i64 value_pointer; - AstNode *value_compound; + bool value_bool; + String value_string; + i64 value_integer; // NOTE(bill): This must be an integer and not a pointer + f64 value_float; + i64 value_pointer; + Complex128 value_complex; + AstNode * value_compound; }; } ExactValue; @@ -66,6 +72,13 @@ ExactValue exact_value_float(f64 f) { return result; } +ExactValue exact_value_complex(f64 real, f64 imag) { + ExactValue result = {ExactValue_Complex}; + result.value_complex.real = real; + result.value_complex.imag = imag; + return result; +} + ExactValue exact_value_pointer(i64 ptr) { ExactValue result = {ExactValue_Pointer}; result.value_pointer = ptr; @@ -113,9 +126,7 @@ ExactValue exact_value_integer_from_string(String string) { return exact_value_integer(result); } - - -ExactValue exact_value_float_from_string(String string) { +f64 float_from_string(String string) { isize i = 0; u8 *str = string.text; isize len = string.len; @@ -190,8 +201,11 @@ ExactValue exact_value_float_from_string(String string) { while (exp > 0) { scale *= 10.0; exp -= 1; } } - f64 result = sign * (frac ? (value / scale) : (value * scale)); - return exact_value_float(result); + return sign * (frac ? (value / scale) : (value * scale)); +} + +ExactValue exact_value_float_from_string(String string) { + return exact_value_float(float_from_string(string)); } @@ -200,6 +214,12 @@ ExactValue exact_value_from_basic_literal(Token token) { case Token_String: return exact_value_string(token.string); case Token_Integer: return exact_value_integer_from_string(token.string); case Token_Float: return exact_value_float_from_string(token.string); + case Token_Imag: { + String str = token.string; + str.len--; // Ignore the `i` + f64 imag = float_from_string(str); + return exact_value_complex(0, imag); + } case Token_Rune: { Rune r = GB_RUNE_INVALID; gb_utf8_decode(token.string.text, token.string.len, &r); @@ -245,6 +265,57 @@ ExactValue exact_value_to_float(ExactValue v) { return r; } +ExactValue exact_value_to_complex(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_complex(cast(i64)v.value_integer, 0); + case ExactValue_Float: + return exact_value_complex(v.value_float, 0); + case ExactValue_Complex: + return v; + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_real(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + return v; + case ExactValue_Complex: + return exact_value_float(v.value_complex.real); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_imag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + case ExactValue_Float: + return exact_value_integer(0); + case ExactValue_Complex: + return exact_value_float(v.value_complex.imag); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + +ExactValue exact_value_make_imag(ExactValue v) { + switch (v.kind) { + case ExactValue_Integer: + return exact_value_complex(0, exact_value_to_float(v).value_float); + case ExactValue_Float: + return exact_value_complex(0, v.value_float); + default: + GB_PANIC("Expected an integer or float type for `exact_value_make_imag`"); + } + ExactValue r = {ExactValue_Invalid}; + return r; +} + + ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) { switch (op) { @@ -253,6 +324,7 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) case ExactValue_Invalid: case ExactValue_Integer: case ExactValue_Float: + case ExactValue_Complex: return v; } } break; @@ -271,6 +343,11 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) i.value_float = -i.value_float; return i; } + case ExactValue_Complex: { + f64 real = v.value_complex.real; + f64 imag = v.value_complex.imag; + return exact_value_complex(-real, -imag); + } } } break; @@ -324,8 +401,10 @@ i32 exact_value_order(ExactValue v) { return 2; case ExactValue_Float: return 3; - case ExactValue_Pointer: + case ExactValue_Complex: return 4; + case ExactValue_Pointer: + return 5; default: GB_PANIC("How'd you get here? Invalid Value.kind"); @@ -346,6 +425,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) { case ExactValue_Bool: case ExactValue_String: + case ExactValue_Complex: return; case ExactValue_Integer: @@ -356,16 +436,23 @@ void match_exact_values(ExactValue *x, ExactValue *y) { // TODO(bill): Is this good enough? *x = exact_value_float(cast(f64)x->value_integer); return; + case ExactValue_Complex: + *x = exact_value_complex(cast(f64)x->value_integer, 0); + return; } break; case ExactValue_Float: - if (y->kind == ExactValue_Float) + if (y->kind == ExactValue_Float) { return; + } else if (y->kind == ExactValue_Complex) { + *x = exact_value_to_complex(*x); + return; + } break; } - compiler_error("How'd you get here? Invalid ExactValueKind"); + compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind); } // TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough? @@ -420,6 +507,37 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) default: goto error; } } break; + + case ExactValue_Complex: { + y = exact_value_to_complex(y); + f64 a = x.value_complex.real; + f64 b = x.value_complex.imag; + f64 c = y.value_complex.real; + f64 d = y.value_complex.imag; + f64 real = 0; + f64 imag = 0; + switch (op) { + case Token_Add: + real = a + c; + imag = b + d; + break; + case Token_Sub: + real = a - c; + imag = b - d; + break; + case Token_Mul: + real = (a*c - b*d); + imag = (b*c + a*d); + break; + case Token_Quo: { + f64 s = c*c + d*d; + real = (a*c + b*d)/s; + imag = (b*c - a*d)/s; + } break; + default: goto error; + } + return exact_value_complex(real, imag); + } break; } error: @@ -480,6 +598,17 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) { } } break; + case ExactValue_Complex: { + f64 a = x.value_complex.real; + f64 b = x.value_complex.imag; + f64 c = y.value_complex.real; + f64 d = y.value_complex.imag; + switch (op) { + case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0; + case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0; + } + } break; + case ExactValue_String: { String a = x.value_string; String b = y.value_string; diff --git a/src/ir.c b/src/ir.c index fb638fb27..b5e01ee34 100644 --- a/src/ir.c +++ b/src/ir.c @@ -1653,6 +1653,7 @@ irValue *ir_addr_load(irProcedure *proc, irAddr addr) { } irValue *ir_emit_array_epi(irProcedure *proc, irValue *s, i32 index); +irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index); irValue *ir_emit_ptr_offset(irProcedure *proc, irValue *ptr, irValue *offset) { offset = ir_emit_conv(proc, offset, t_int); @@ -1716,6 +1717,64 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * return ir_emit_load(proc, res); } + if (is_type_complex(t_left)) { + ir_emit_comment(proc, str_lit("complex.arith.begin")); + Type *tl = core_type(t_left); + Type *ft = t_f32; + if (tl->Basic.kind == Basic_complex128) { + ft = t_f64; + } + + irValue *res = ir_add_local_generated(proc, type); + irValue *a = ir_emit_struct_ev(proc, left, 0); + irValue *b = ir_emit_struct_ev(proc, left, 1); + irValue *c = ir_emit_struct_ev(proc, right, 0); + irValue *d = ir_emit_struct_ev(proc, right, 1); + + irValue *real = NULL; + irValue *imag = NULL; + + switch (op) { + case Token_Add: + real = ir_emit_arith(proc, Token_Add, a, c, ft); + imag = ir_emit_arith(proc, Token_Add, b, d, ft); + break; + case Token_Sub: + real = ir_emit_arith(proc, Token_Sub, a, c, ft); + imag = ir_emit_arith(proc, Token_Sub, b, d, ft); + break; + case Token_Mul: { + irValue *x = ir_emit_arith(proc, Token_Mul, a, c, ft); + irValue *y = ir_emit_arith(proc, Token_Mul, b, d, ft); + real = ir_emit_arith(proc, Token_Sub, x, y, ft); + irValue *z = ir_emit_arith(proc, Token_Mul, b, c, ft); + irValue *w = ir_emit_arith(proc, Token_Mul, a, d, ft); + imag = ir_emit_arith(proc, Token_Add, z, w, ft); + } break; + case Token_Quo: { + irValue *s1 = ir_emit_arith(proc, Token_Mul, c, c, ft); + irValue *s2 = ir_emit_arith(proc, Token_Mul, d, d, ft); + irValue *s = ir_emit_arith(proc, Token_Add, s1, s2, ft); + + irValue *x = ir_emit_arith(proc, Token_Mul, a, c, ft); + irValue *y = ir_emit_arith(proc, Token_Mul, b, d, ft); + real = ir_emit_arith(proc, Token_Add, x, y, ft); + real = ir_emit_arith(proc, Token_Quo, real, s, ft); + + irValue *z = ir_emit_arith(proc, Token_Mul, b, c, ft); + irValue *w = ir_emit_arith(proc, Token_Mul, a, d, ft); + imag = ir_emit_arith(proc, Token_Sub, z, w, ft); + imag = ir_emit_arith(proc, Token_Quo, imag, s, ft); + } break; + } + + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 0), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, res, 1), imag); + + ir_emit_comment(proc, str_lit("complex.end.begin")); + return ir_emit_load(proc, res); + } + if (op == Token_Add) { if (is_type_pointer(t_left)) { @@ -1890,6 +1949,15 @@ irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index) { GB_ASSERT(t->Tuple.variable_count > 0); GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); result_type = make_type_pointer(a, t->Tuple.variables[index]->type); + } else if (is_type_complex(t)) { + Type *ft = t_f32; + if (core_type(t)->Basic.kind == Basic_complex128) { + ft = t_f64; + } + switch (index) { + case 0: result_type = make_type_pointer(a, ft); break; + case 1: result_type = make_type_pointer(a, ft); break; + } } else if (is_type_slice(t)) { switch (index) { case 0: result_type = make_type_pointer(a, make_type_pointer(a, t->Slice.elem)); break; @@ -1956,6 +2024,15 @@ irValue *ir_emit_struct_ev(irProcedure *proc, irValue *s, i32 index) { GB_ASSERT(t->Tuple.variable_count > 0); GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1)); result_type = t->Tuple.variables[index]->type; + } else if (is_type_complex(t)) { + Type *ft = t_f32; + if (core_type(t)->Basic.kind == Basic_complex128) { + ft = t_f64; + } + switch (index) { + case 0: result_type = ft; break; + case 1: result_type = ft; break; + } } else if (is_type_slice(t)) { switch (index) { case 0: result_type = make_type_pointer(a, t->Slice.elem); break; @@ -2285,6 +2362,8 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { ExactValue ev = value->Constant.value; if (is_type_float(dst)) { ev = exact_value_to_float(ev); + } else if (is_type_complex(dst)) { + ev = exact_value_to_complex(ev); } else if (is_type_string(dst)) { // Handled elsewhere GB_ASSERT(ev.kind == ExactValue_String); @@ -2350,6 +2429,16 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) { return ir_emit(proc, ir_instr_conv(proc, kind, value, src, dst)); } + if (is_type_complex(src) && is_type_complex(dst)) { + Type *ft = base_complex_elem_type(dst); + irValue *gen = ir_add_local_generated(proc, dst); + 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); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 0), real); + ir_emit_store(proc, ir_emit_struct_ep(proc, gen, 1), imag); + return ir_emit_load(proc, gen); + } + // float <-> integer if (is_type_float(src) && is_type_integer(dst)) { irConvKind kind = irConv_fptosi; @@ -3704,6 +3793,37 @@ irValue *ir_build_expr(irProcedure *proc, AstNode *expr) { // return ir_emit(proc, ir_instr_vector_shuffle(proc, vector, indices, index_count)); } break; + case BuiltinProc_complex: { + ir_emit_comment(proc, str_lit("complex")); + irValue *real = ir_build_expr(proc, ce->args.e[0]); + irValue *imag = ir_build_expr(proc, ce->args.e[1]); + irValue *dst = ir_add_local_generated(proc, tv->type); + + Type *ft = base_complex_elem_type(tv->type); + irValue *rp = ir_emit_struct_ep(proc, dst, 0); + irValue *ip = ir_emit_struct_ep(proc, dst, 1); + + real = ir_emit_conv(proc, real, ft); + imag = ir_emit_conv(proc, imag, ft); + ir_emit_store(proc, rp, real); + ir_emit_store(proc, ip, imag); + + return ir_emit_load(proc, dst); + } break; + + case BuiltinProc_real: { + ir_emit_comment(proc, str_lit("real")); + irValue *complex = ir_build_expr(proc, ce->args.e[0]); + irValue *real = ir_emit_struct_ev(proc, complex, 0); + return ir_emit_conv(proc, real, tv->type); + } break; + case BuiltinProc_imag: { + ir_emit_comment(proc, str_lit("imag")); + irValue *complex = ir_build_expr(proc, ce->args.e[0]); + irValue *imag = ir_emit_struct_ev(proc, complex, 1); + return ir_emit_conv(proc, imag, tv->type); + } break; + case BuiltinProc_slice_ptr: { ir_emit_comment(proc, str_lit("slice_ptr")); irValue *ptr = ir_build_expr(proc, ce->args.e[0]); @@ -6528,8 +6648,6 @@ void ir_gen_tree(irGen *s) { case Basic_u32: case Basic_i64: case Basic_u64: - // case Basic_i128: - // case Basic_u128: case Basic_int: case Basic_uint: { tag = ir_emit_conv(proc, ti_ptr, t_type_info_integer_ptr); @@ -6540,16 +6658,23 @@ void ir_gen_tree(irGen *s) { ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 1), is_signed); } break; - // case Basic_f16: case Basic_f32: case Basic_f64: - // case Basic_f128: { tag = ir_emit_conv(proc, ti_ptr, t_type_info_float_ptr); irValue *bits = ir_const_int(a, type_size_of(a, t)); ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), bits); } break; + case Basic_complex64: + case Basic_complex128: + { + tag = ir_emit_conv(proc, ti_ptr, t_type_info_complex_ptr); + irValue *bits = ir_const_int(a, type_size_of(a, t)); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 0), bits); + } break; + + case Basic_rawptr: tag = ir_emit_conv(proc, ti_ptr, t_type_info_pointer_ptr); break; diff --git a/src/ir_print.c b/src/ir_print.c index beae020db..39486c11f 100644 --- a/src/ir_print.c +++ b/src/ir_print.c @@ -154,12 +154,14 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { case Basic_u32: ir_fprintf(f, "i32"); return; case Basic_i64: ir_fprintf(f, "i64"); return; case Basic_u64: ir_fprintf(f, "i64"); return; - // case Basic_i128: ir_fprintf(f, "i128"); return; - // case Basic_u128: ir_fprintf(f, "i128"); return; - // case Basic_f16: ir_fprintf(f, "half"); return; + case Basic_f32: ir_fprintf(f, "float"); return; case Basic_f64: ir_fprintf(f, "double"); return; - // case Basic_f128: ir_fprintf(f, "fp128"); return; + + case Basic_complex64: ir_fprintf(f, "%%..complex64"); return; + case Basic_complex128: ir_fprintf(f, "%%..complex128"); return; + + case Basic_rawptr: ir_fprintf(f, "%%..rawptr"); return; case Basic_string: ir_fprintf(f, "%%..string"); return; case Basic_uint: ir_fprintf(f, "i%lld", word_bits); return; @@ -365,7 +367,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * } break; case ExactValue_Float: { GB_ASSERT_MSG(is_type_float(type), "%s", type_to_string(type)); - type = base_type(type); + type = core_type(type); u64 u = *cast(u64*)&value.value_float; switch (type->Basic.kind) { case Basic_f32: @@ -382,28 +384,27 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * switch (type->Basic.kind) { case 0: break; -#if 0 - case Basic_f16: - ir_fprintf(f, "bitcast ("); - ir_print_type(f, m, t_u16); - ir_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float)); - ir_print_type(f, m, t_f16); - ir_fprintf(f, ")"); - break; - case Basic_f128: - ir_fprintf(f, "bitcast ("); - ir_fprintf(f, "i128"); - // TODO(bill): Actually support f128 - ir_fprintf(f, " %llu to ", u); - ir_print_type(f, m, t_f128); - ir_fprintf(f, ")"); - break; -#endif default: ir_fprintf(f, "0x%016llx", u); break; } } break; + + case ExactValue_Complex: { + GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type)); + type = core_type(type); + Type *ft = base_complex_elem_type(type); + ir_fprintf(f, " {"); + ir_print_type(f, m, ft); + ir_fprintf(f, " "); + ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft); + ir_fprintf(f, ", "); + ir_print_type(f, m, ft); + ir_fprintf(f, " "); + ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft); + ir_fprintf(f, "}"); + } break; + case ExactValue_Pointer: if (value.value_pointer == 0) { ir_fprintf(f, "null"); @@ -1415,6 +1416,12 @@ void print_llvm_ir(irGen *ir) { ir_print_encoded_local(f, str_lit("..rawptr")); ir_fprintf(f, " = type i8* ; Basic_rawptr\n"); + ir_print_encoded_local(f, str_lit("..complex64")); + ir_fprintf(f, " = type {float, float} ; Basic_complex64\n"); + ir_print_encoded_local(f, str_lit("..complex128")); + ir_fprintf(f, " = type {double, double} ; Basic_complex128\n"); + + ir_print_encoded_local(f, str_lit("..any")); ir_fprintf(f, " = type {"); ir_print_type(f, m, t_type_info_ptr); diff --git a/src/main.c b/src/main.c index d44f28b75..118d0e50c 100644 --- a/src/main.c +++ b/src/main.c @@ -252,7 +252,7 @@ int main(int argc, char **argv) { i32 exit_code = 0; // For more passes arguments: http://llvm.org/docs/Passes.html exit_code = system_exec_command_line_app("llvm-opt", false, - "\"%.*sbin/opt\" \"%s\" -o \"%.*s\".bc " + "\"%.*sbin/opt\" \"%s\" -o \"%.*s.bc\" " "-mem2reg " "-memcpyopt " "-die " diff --git a/src/parser.c b/src/parser.c index fbedb3855..5169bd7bf 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1735,6 +1735,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) { case Token_Integer: case Token_Float: + case Token_Imag: case Token_Rune: operand = ast_basic_lit(f, f->curr_token); next_token(f); @@ -3238,6 +3239,7 @@ AstNode *parse_stmt(AstFile *f) { case Token_Ident: case Token_Integer: case Token_Float: + case Token_Imag: case Token_Rune: case Token_String: case Token_OpenParen: diff --git a/src/tokenizer.c b/src/tokenizer.c index 6c7f76c02..bd012519b 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -7,6 +7,7 @@ TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \ TOKEN_KIND(Token_Ident, "identifier"), \ TOKEN_KIND(Token_Integer, "integer"), \ TOKEN_KIND(Token_Float, "float"), \ + TOKEN_KIND(Token_Imag, "imaginary"), \ TOKEN_KIND(Token_Rune, "rune"), \ TOKEN_KIND(Token_String, "string"), \ TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \ @@ -547,18 +548,18 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) { } } - token.string.len = t->curr - token.string.text; - return token; + goto end; } scan_mantissa(t, 10); + fraction: if (t->curr_rune == '.') { // HACK(bill): This may be inefficient TokenizerState state = save_tokenizer_state(t); advance_to_next_rune(t); - if (digit_value(t->curr_rune) >= 10) { + if (t->curr_rune == '.') { // TODO(bill): Clean up this shit restore_tokenizer_state(t, &state); goto end; @@ -577,6 +578,11 @@ exponent: scan_mantissa(t, 10); } + if (t->curr_rune == 'i') { + token.kind = Token_Imag; + advance_to_next_rune(t); + } + end: token.string.len = t->curr - token.string.text; return token; diff --git a/src/types.c b/src/types.c index fe46dc3cd..338489234 100644 --- a/src/types.c +++ b/src/types.c @@ -12,25 +12,12 @@ typedef enum BasicKind { Basic_i64, Basic_u64, -/* Basic_i16le, - Basic_i16be, - Basic_u16le, - Basic_u16be, - Basic_i32le, - Basic_i32be, - Basic_u32le, - Basic_u32be, - Basic_i64le, - Basic_i64be, - Basic_u64le, - Basic_u64be, */ - - // Basic_i128, - // Basic_u128, - // Basic_f16, Basic_f32, Basic_f64, - // Basic_f128, + + Basic_complex64, + Basic_complex128, + Basic_int, Basic_uint, Basic_rawptr, @@ -40,6 +27,7 @@ typedef enum BasicKind { Basic_UntypedBool, Basic_UntypedInteger, Basic_UntypedFloat, + Basic_UntypedComplex, Basic_UntypedString, Basic_UntypedRune, Basic_UntypedNil, @@ -55,12 +43,13 @@ typedef enum BasicFlag { BasicFlag_Integer = GB_BIT(1), BasicFlag_Unsigned = GB_BIT(2), BasicFlag_Float = GB_BIT(3), - BasicFlag_Pointer = GB_BIT(4), - BasicFlag_String = GB_BIT(5), - BasicFlag_Rune = GB_BIT(6), - BasicFlag_Untyped = GB_BIT(7), + 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_Numeric = BasicFlag_Integer | BasicFlag_Float, + BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex, BasicFlag_Ordered = BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer, BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_String | BasicFlag_Rune, } BasicFlag; @@ -210,7 +199,9 @@ void selection_add_index(Selection *s, isize index) { gb_global Type basic_types[] = { {Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}}, + {Type_Basic, {Basic_bool, BasicFlag_Boolean, 1, STR_LIT("bool")}}, + {Type_Basic, {Basic_i8, BasicFlag_Integer, 1, STR_LIT("i8")}}, {Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("u8")}}, {Type_Basic, {Basic_i16, BasicFlag_Integer, 2, STR_LIT("i16")}}, @@ -219,20 +210,24 @@ gb_global Type basic_types[] = { {Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}}, {Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}}, {Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}}, - // {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}}, - // {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}}, - // {Type_Basic, {Basic_f16, BasicFlag_Float, 2, STR_LIT("f16")}}, + {Type_Basic, {Basic_f32, BasicFlag_Float, 4, STR_LIT("f32")}}, {Type_Basic, {Basic_f64, BasicFlag_Float, 8, STR_LIT("f64")}}, - // {Type_Basic, {Basic_f128, BasicFlag_Float, 16, STR_LIT("f128")}}, + + {Type_Basic, {Basic_complex64, BasicFlag_Complex, 8, STR_LIT("complex64")}}, + {Type_Basic, {Basic_complex128, BasicFlag_Complex, 16, STR_LIT("complex128")}}, + {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_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}}, {Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}}, {Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}}, + {Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}}, {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_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")}}, @@ -253,20 +248,24 @@ gb_global Type *t_i32 = &basic_types[Basic_i32]; gb_global Type *t_u32 = &basic_types[Basic_u32]; gb_global Type *t_i64 = &basic_types[Basic_i64]; gb_global Type *t_u64 = &basic_types[Basic_u64]; -// gb_global Type *t_i128 = &basic_types[Basic_i128]; -// gb_global Type *t_u128 = &basic_types[Basic_u128]; -// gb_global Type *t_f16 = &basic_types[Basic_f16]; + gb_global Type *t_f32 = &basic_types[Basic_f32]; gb_global Type *t_f64 = &basic_types[Basic_f64]; -// gb_global Type *t_f128 = &basic_types[Basic_f128]; + +gb_global Type *t_complex64 = &basic_types[Basic_complex64]; +gb_global Type *t_complex128 = &basic_types[Basic_complex128]; + gb_global Type *t_int = &basic_types[Basic_int]; gb_global Type *t_uint = &basic_types[Basic_uint]; + gb_global Type *t_rawptr = &basic_types[Basic_rawptr]; gb_global Type *t_string = &basic_types[Basic_string]; gb_global Type *t_any = &basic_types[Basic_any]; + 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_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]; @@ -293,6 +292,7 @@ gb_global Type *t_type_info_enum_value_ptr = NULL; gb_global Type *t_type_info_named = NULL; gb_global Type *t_type_info_integer = NULL; gb_global Type *t_type_info_float = NULL; +gb_global Type *t_type_info_complex = NULL; gb_global Type *t_type_info_any = NULL; gb_global Type *t_type_info_string = NULL; gb_global Type *t_type_info_boolean = NULL; @@ -312,6 +312,7 @@ gb_global Type *t_type_info_map = NULL; gb_global Type *t_type_info_named_ptr = NULL; gb_global Type *t_type_info_integer_ptr = NULL; gb_global Type *t_type_info_float_ptr = NULL; +gb_global Type *t_type_info_complex_ptr = NULL; gb_global Type *t_type_info_any_ptr = NULL; gb_global Type *t_type_info_string_ptr = NULL; gb_global Type *t_type_info_boolean_ptr = NULL; @@ -615,6 +616,13 @@ bool is_type_float(Type *t) { } return false; } +bool is_type_complex(Type *t) { + t = core_type(t); + if (t->kind == Type_Basic) { + return (t->Basic.flags & BasicFlag_Complex) != 0; + } + return false; +} bool is_type_f32(Type *t) { t = core_type(t); if (t->kind == Type_Basic) { @@ -695,6 +703,19 @@ Type *base_vector_type(Type *t) { return t; } +Type *base_complex_elem_type(Type *t) { + t = core_type(t); + if (is_type_complex(t)) { + switch (t->Basic.kind) { + case Basic_complex64: return t_f32; + case Basic_complex128: return t_f64; + case Basic_UntypedComplex: return t_untyped_float; + } + } + GB_PANIC("Invalid complex type"); + return t_invalid; +} + bool is_type_struct(Type *t) { t = base_type(t); @@ -953,6 +974,7 @@ Type *default_type(Type *type) { case Basic_UntypedBool: return t_bool; case Basic_UntypedInteger: return t_int; case Basic_UntypedFloat: return t_f64; + case Basic_UntypedComplex: return t_complex128; case Basic_UntypedString: return t_string; case Basic_UntypedRune: return t_rune; } @@ -1543,6 +1565,10 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) { case Basic_int: case Basic_uint: case Basic_rawptr: return build_context.word_size; + + case Basic_complex64: case Basic_complex128: + // complex{64,128} align as [2]f{32,64} + return type_size_of_internal(allocator, t, path) / 2; } } break;