diff options
| author | Chris Heyes <rumcode@icloud.com> | 2019-11-01 19:18:33 +0000 |
|---|---|---|
| committer | Chris Heyes <rumcode@icloud.com> | 2019-11-01 19:18:33 +0000 |
| commit | 153e7525b5d64f12deb318d50aee1d9dbeb1fc8e (patch) | |
| tree | b8ac88c0788af31bcb3c5041d78306c2120714f6 | |
| parent | d85893954dccc7833e3d954db641d9fd2a3c7451 (diff) | |
| parent | 44a303e5778fb8564964d53523634f34f8589489 (diff) | |
Merge remote-tracking branch 'upstream/master'
84 files changed, 10286 insertions, 2816 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..7c43b01e1 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,57 @@ +name: CI +on: [push, pull_request] + +jobs: + build_unix: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macOS-latest] + + steps: + - uses: actions/checkout@v1 + - name: (macOS) Download LLVM and setup PATH + if: startsWith(matrix.os, 'macOS') + run: | + brew install llvm + echo ::add-path::/usr/local/opt/llvm/bin + echo ::set-env name=CPATH::`xcrun --show-sdk-path`/usr/include + - name: (Linux) Download LLVM + if: startsWith(matrix.os, 'ubuntu') + run: | + sudo apt-get install llvm + - name: build odin + run: make release + - name: Odin run + run: ./odin run examples/demo/demo.odin + - name: Odin check + run: ./odin check examples/demo/demo.odin -vet + build_windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v1 + - name: Install cURL + run: choco install curl + - name: Download and unpack LLVM bins + shell: cmd + run: | + cd bin + curl -sL https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip --output llvm-binaries.zip + 7z x llvm-binaries.zip > nul + - name: build Odin + shell: cmd + run: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + ./build_ci.bat + - name: Odin run + shell: cmd + run: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + odin run examples/demo/demo.odin + - name: Odin check + shell: cmd + run: | + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat + odin check examples/demo/demo.odin -vet + + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a0169259d..000000000 --- a/.travis.yml +++ /dev/null @@ -1,24 +0,0 @@ -language: cpp -git: - depth: false - -os: - - linux - - osx - -compiler: - - clang - -addons: - homebrew: - packages: - - llvm - -script: - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export PATH="/usr/local/opt/llvm/bin:$PATH" ; fi - - make release - - ./odin run examples/demo/demo.odin - - ./odin check examples/demo/demo.odin -vet - -notifications: - email: false @@ -10,15 +10,12 @@ <a href="https://github.com/odin-lang/odin/releases/latest"> <img src="https://img.shields.io/badge/platforms-Windows%20|%20Linux%20|%20macOS-green.svg"> </a> - <a href="https://github.com/odin-lang/odin/blob/master/LICENSE"> - <img src="https://img.shields.io/github/license/odin-lang/odin.svg"> - </a> <br> - <a href="https://ci.appveyor.com/project/ThisDrunkDane/odin-vf0ap"> - <img src="https://ci.appveyor.com/api/projects/status/qss6l921c0eu85u6/branch/master?svg=true"> + <a href="https://discord.gg/hnwN2Rj"> + <img src="https://img.shields.io/discord/568138951836172421?logo=discord"> </a> - <a href="https://travis-ci.org/odin-lang/Odin"> - <img src="https://travis-ci.org/odin-lang/Odin.svg?branch=master"> + <a href="https://github.com/odin-lang/odin/actions"> + <img src="https://github.com/odin-lang/odin/workflows/CI/badge.svg"> </a> </p> diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 5e5abe979..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,19 +0,0 @@ -image: - - Visual Studio 2017 -shallow_clone: true - -platform: x64 - -install: - - cd bin - - appveyor DownloadFile https://github.com/odin-lang/Odin/releases/download/llvm-windows/llvm-binaries.zip - - 7z x llvm-binaries.zip > nul - - cd .. - -build_script: - - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" - - ./build_ci.bat - -test_script: - - odin run examples/demo/demo.odin - - odin check examples/demo/demo.odin -vet
\ No newline at end of file @@ -39,7 +39,6 @@ set linker_settings=%libs% %linker_flags% del *.pdb > NUL 2> NUL del *.ilk > NUL 2> NUL - cl %compiler_settings% "src\main.cpp" ^ /link %linker_settings% -OUT:%exe_name% ^ && odin run examples/demo/demo.odin diff --git a/core/builtin/builtin.odin b/core/builtin/builtin.odin new file mode 100644 index 000000000..74ade0260 --- /dev/null +++ b/core/builtin/builtin.odin @@ -0,0 +1,105 @@ +// This is purely for documentation +package builtin + +nil :: nil; +false :: 0!==0; +true :: 0==0; + +ODIN_OS :: ODIN_OS; +ODIN_ARCH :: ODIN_ARCH; +ODIN_ENDIAN :: ODIN_ENDIAN; +ODIN_VENDOR :: ODIN_VENDOR; +ODIN_VERSION :: ODIN_VERSION; +ODIN_ROOT :: ODIN_ROOT; +ODIN_DEBUG :: ODIN_DEBUG; + +byte :: u8; // alias + +bool :: bool; +b8 :: b8; +b16 :: b16; +b32 :: b32; +b64 :: b64; + +i8 :: i8; +u8 :: u8; +i16 :: i16; +u16 :: u16; +i32 :: i32; +u32 :: u32; +i64 :: i64; +u64 :: u64; + +i128 :: i128; +u128 :: u128; + +rune :: rune; + +f16 :: f16; +f32 :: f32; +f64 :: f64; + +complex32 :: complex32; +complex64 :: complex64; +complex128 :: complex128; + +quaternion128 :: quaternion128; +quaternion256 :: quaternion256; + +int :: int; +uint :: uint; +uintptr :: uintptr; + +rawptr :: rawptr; +string :: string; +cstring :: cstring; +any :: any; + +typeid :: typeid; + +// Endian Specific Types +i16le :: i16le; +u16le :: u16le; +i32le :: i32le; +u32le :: u32le; +i64le :: i64le; +u64le :: u64le; +i128le :: i128le; +u128le :: u128le; + +i16be :: i16be; +u16be :: u16be; +i32be :: i32be; +u32be :: u32be; +i64be :: i64be; +u64be :: u64be; +i128be :: i128be; +u128be :: u128be; + +// Procedures +len :: proc(array: Array_Type) -> int --- +cap :: proc(array: Array_Type) -> int --- + +size_of :: proc($T: typeid) -> int --- +align_of :: proc($T: typeid) -> int --- +offset_of :: proc($T: typeid) -> uintptr --- +type_of :: proc(x: expr) -> type --- +type_info_of :: proc($T: typeid) -> ^runtime.Type_Info --- +typeid_of :: proc($T: typeid) -> typeid --- + +swizzle :: proc(x: [N]T, indices: ..int) -> [len(indices)]T --- + +complex :: proc(real, imag: Float) -> Complex_Type --- +quaternion :: proc(real, imag, jmag, kmag: Float) -> Quaternion_Type --- +real :: proc(value: Complex_Or_Quaternion) -> Float --- +imag :: proc(value: Complex_Or_Quaternion) -> Float --- +jmag :: proc(value: Quaternion) -> Float --- +kmag :: proc(value: Quaternion) -> Float --- +conj :: proc(value: Complex_Or_Quaternion) -> Complex_Or_Quaternion --- + +expand_to_tuple :: proc(value: Struct_Or_Array) -> (A, B, C, ...) --- + +min :: proc(values: ..T) -> T --- +max :: proc(values: ..T) -> T --- +abs :: proc(value: T) -> T --- +clamp :: proc(value, minimum, maximum: T) -> T --- diff --git a/core/c/c.odin b/core/c/c.odin index 47af84165..3ed49c0c2 100644 --- a/core/c/c.odin +++ b/core/c/c.odin @@ -31,3 +31,5 @@ ssize_t :: b.int; ptrdiff_t :: b.int; uintptr_t :: b.uintptr; intptr_t :: b.int; + +wchar_t :: (ODIN_OS == "windows") ? b.u16 : b.u32; diff --git a/core/encoding/base64/base64.odin b/core/encoding/base64/base64.odin new file mode 100644 index 000000000..0224e93e4 --- /dev/null +++ b/core/encoding/base64/base64.odin @@ -0,0 +1,93 @@ +package base64 + +// @note(zh): Encoding utility for Base64 +// A secondary param can be used to supply a custom alphabet to +// @link(encode) and a matching decoding table to @link(decode). +// If none is supplied it just uses the standard Base64 alphabet. +// Incase your specific version does not use padding, you may +// truncate it from the encoded output. + +ENC_TABLE := [64]byte { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/' +}; + +PADDING :: '='; + +DEC_TABLE := [128]int { + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, -1, -1, -1, -1, -1, -1, + -1, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, -1, -1, -1, -1, -1, + -1, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, + 49, 50, 51, -1, -1, -1, -1, -1 +}; + +encode :: proc(data: []byte, ENC_TBL := ENC_TABLE, allocator := context.allocator) -> string #no_bounds_check { + length := len(data); + if length == 0 do return ""; + + out_length := ((4 * length / 3) + 3) &~ 3; + out := make([]byte, out_length, allocator); + + c0, c1, c2, block: int; + + for i, d := 0, 0; i < length; i, d = i + 3, d + 4 { + c0, c1, c2 = int(data[i]), 0, 0; + + if i + 1 < length do c1 = int(data[i + 1]); + if i + 2 < length do c2 = int(data[i + 2]); + + block = (c0 << 16) | (max(c1, 0) << 8) | max(c2, 0); + + out[d] = ENC_TBL[block >> 18 & 63]; + out[d + 1] = ENC_TBL[block >> 12 & 63]; + out[d + 2] = c1 == 0 ? PADDING : ENC_TBL[block >> 6 & 63]; + out[d + 3] = c2 == 0 ? PADDING : ENC_TBL[block & 63]; + } + return string(out); +} + +decode :: proc(data: string, DEC_TBL := DEC_TABLE, allocator := context.allocator) -> []byte #no_bounds_check{ + length := len(data); + if length == 0 do return []byte{}; + + pad_count := data[length - 1] == PADDING ? (data[length - 2] == PADDING ? 2 : 1) : 0; + out_length := ((length * 6) >> 3) - pad_count; + out := make([]byte, out_length, allocator); + + c0, c1, c2, c3: int; + b0, b1, b2: int; + + for i, j := 0, 0; i < length; i, j = i + 4, j + 3 { + c0 = DEC_TBL[data[i]]; + c1 = DEC_TBL[data[i + 1]]; + c2 = DEC_TBL[data[i + 2]]; + c3 = DEC_TBL[data[i + 3]]; + + b0 = (c0 << 2) | (c1 >> 4); + b1 = (c1 << 4) | (c2 >> 2); + b2 = (c2 << 6) | c3; + + out[j] = byte(b0); + out[j + 1] = byte(b1); + out[j + 2] = byte(b2); + } + return out; +}
\ No newline at end of file diff --git a/core/encoding/cel/cel.odin b/core/encoding/cel/cel.odin index 793dba231..754d8cbfa 100644 --- a/core/encoding/cel/cel.odin +++ b/core/encoding/cel/cel.odin @@ -168,9 +168,9 @@ destroy :: proc(p: ^Parser) { } error :: proc(p: ^Parser, pos: Pos, msg: string, args: ..any) { - fmt.printf_err("%s(%d:%d) Error: ", pos.file, pos.line, pos.column); - fmt.printf_err(msg, ..args); - fmt.println_err(); + fmt.eprintf("%s(%d:%d) Error: ", pos.file, pos.line, pos.column); + fmt.eprintf(msg, ..args); + fmt.eprintln(); p.error_count += 1; } diff --git a/core/encoding/cel/token.odin b/core/encoding/cel/token.odin index 1ad86b299..a2bcb963a 100644 --- a/core/encoding/cel/token.odin +++ b/core/encoding/cel/token.odin @@ -183,9 +183,9 @@ tokenizer_init :: proc(t: ^Tokenizer, src: []byte, file := "") { } token_error :: proc(t: ^Tokenizer, msg: string, args: ..any) { - fmt.printf_err("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1); - fmt.printf_err(msg, ..args); - fmt.println_err(); + fmt.eprintf("%s(%d:%d) Error: ", t.file, t.line_count, t.read_offset-t.line_offset+1); + fmt.eprintf(msg, ..args); + fmt.eprintln(); t.error_count += 1; } diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin index cd9aae169..60839b21b 100644 --- a/core/encoding/json/marshal.odin +++ b/core/encoding/json/marshal.odin @@ -5,7 +5,7 @@ import "core:math/bits" import "core:runtime" import "core:strconv" import "core:strings" -import "core:types" +import "core:reflect" Marshal_Error :: enum { None, @@ -194,7 +194,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { data := uintptr(entries.data) + uintptr(i*entry_size); header := cast(^Map_Entry_Header)data; - if types.is_string(info.key) { + if reflect.is_string(info.key) { marshal_arg(b, header.key.str); } else { marshal_arg(b, any{rawptr(&header.key.hash), info.key.id}); @@ -284,11 +284,10 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error { t := runtime.type_info_base(ti); switch info in t.variant { case runtime.Type_Info_Integer: - using runtime.Type_Info_Endianness; switch info.endianness { - case Platform: return false; - case Little: return ODIN_ENDIAN != "little"; - case Big: return ODIN_ENDIAN != "big"; + case .Platform: return false; + case .Little: return ODIN_ENDIAN != "little"; + case .Big: return ODIN_ENDIAN != "big"; } } return false; diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 60b1fdcc1..e6830e734 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -5,9 +5,9 @@ import "core:os" import "core:mem" import "core:math/bits" import "core:unicode/utf8" -import "core:types" import "core:strconv" import "core:strings" +import "core:reflect" @private @@ -59,12 +59,18 @@ fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int { // print* procedures return the number of bytes written -print :: proc(args: ..any) -> int { return fprint(context.stdout, ..args); } -print_err :: proc(args: ..any) -> int { return fprint(context.stderr, ..args); } -println :: proc(args: ..any) -> int { return fprintln(context.stdout, ..args); } -println_err :: proc(args: ..any) -> int { return fprintln(context.stderr, ..args); } -printf :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stdout, fmt, ..args); } -printf_err :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stderr, fmt, ..args); } +print :: proc(args: ..any) -> int { return fprint(context.stdout, ..args); } +println :: proc(args: ..any) -> int { return fprintln(context.stdout, ..args); } +printf :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stdout, fmt, ..args); } + +eprint :: proc(args: ..any) -> int { return fprint(context.stderr, ..args); } +eprintln :: proc(args: ..any) -> int { return fprintln(context.stderr, ..args); } +eprintf :: proc(fmt: string, args: ..any) -> int { return fprintf(context.stderr, fmt, ..args); } + + +@(deprecated="prefer eprint") print_err :: proc(args: ..any) -> int { return eprint(..args); } +@(deprecated="prefer eprintf") printf_err :: proc(fmt: string, args: ..any) -> int { return eprintf(fmt, ..args); } +@(deprecated="prefer eprintln") println_err :: proc(args: ..any) -> int { return eprintln(..args); } // aprint* procedures return a string that was allocated with the current context @@ -143,7 +149,7 @@ panicf :: proc "contextless" (fmt: string, args: ..any, loc := #caller_location) fprint_type :: proc(fd: os.Handle, info: ^runtime.Type_Info) { data: [DEFAULT_BUFFER_SIZE]byte; buf := strings.builder_from_slice(data[:]); - write_type(&buf, info); + reflect.write_type(&buf, info); os.write_string(fd, strings.to_string(buf)); } @@ -156,7 +162,7 @@ sbprint :: proc(buf: ^strings.Builder, args: ..any) -> string { fi.buf = buf; for arg, i in args { - is_string := arg != nil && types.is_string(type_info_of(arg.id)); + is_string := arg != nil && reflect.is_string(type_info_of(arg.id)); if i > 0 && !is_string && !prev_string { strings.write_byte(buf, ' '); } @@ -399,7 +405,7 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { strings.write_rune(buf, verb); strings.write_byte(buf, '('); if arg.id != nil { - write_typeid(buf, arg.id); + reflect.write_typeid(buf, arg.id); strings.write_byte(buf, '='); fmt_value(fi, arg, 'v'); } else { @@ -792,7 +798,7 @@ enum_value_to_string :: proc(val: any) -> (string, bool) { case: return "", false; case runtime.Type_Info_Enum: get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) { - if types.is_string(e.base) { + if reflect.is_string(e.base) { for val, idx in e.values { if v, ok := val.(T); ok && v == i { return e.names[idx], true; @@ -947,7 +953,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { if name != "" { strings.write_string(fi.buf, name); } else { - write_type(fi.buf, type_info); + reflect.write_type(fi.buf, type_info); } strings.write_byte(fi.buf, '{'); defer strings.write_byte(fi.buf, '}'); @@ -1042,7 +1048,7 @@ fmt_opaque :: proc(fi: ^Info, v: any) { if ot, ok := rt.type_info_base(type_info).variant.(rt.Type_Info_Opaque); ok { elem := rt.type_info_base(ot.elem); if elem == nil do return; - write_type(fi.buf, type_info); + reflect.write_type(fi.buf, type_info); strings.write_byte(fi.buf, '{'); defer strings.write_byte(fi.buf, '}'); @@ -1053,7 +1059,7 @@ fmt_opaque :: proc(fi: ^Info, v: any) { // Okay } } else { - write_type(fi.buf, type_info); + reflect.write_type(fi.buf, type_info); strings.write_byte(fi.buf, '{'); strings.write_byte(fi.buf, '}'); } @@ -1101,7 +1107,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { strings.write_string(fi.buf, name); strings.write_string(fi.buf, " = "); - if t := b.types[i]; types.is_any(t) { + if t := b.types[i]; reflect.is_any(t) { strings.write_string(fi.buf, "any{}"); } else { data := rawptr(uintptr(v.data) + b.offsets[i]); @@ -1129,11 +1135,12 @@ 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: if v.id == typeid_of(^runtime.Type_Info) { - write_type(fi.buf, (^^runtime.Type_Info)(v.data)^); + reflect.write_type(fi.buf, (^^runtime.Type_Info)(v.data)^); } else { ptr := (^rawptr)(v.data)^; if verb != 'p' && info.elem != nil { @@ -1256,7 +1263,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { data := uintptr(entries.data) + uintptr(i*entry_size); header := cast(^runtime.Map_Entry_Header)data; - if types.is_string(info.key) { + if reflect.is_string(info.key) { strings.write_string(fi.buf, header.key.str); } else { fi := Info{buf = fi.buf}; @@ -1297,7 +1304,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { strings.write_string(fi.buf, info.names[i]); strings.write_string(fi.buf, " = "); - if t := info.types[i]; types.is_any(t) { + if t := info.types[i]; reflect.is_any(t) { strings.write_string(fi.buf, "any{}"); } else { data := uintptr(v.data) + info.offsets[i]; @@ -1349,14 +1356,14 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { if ptr == nil { strings.write_string(fi.buf, "nil"); } else { - write_typeid(fi.buf, v.id); + reflect.write_typeid(fi.buf, v.id); strings.write_string(fi.buf, " @ "); fmt_pointer(fi, ptr, 'p'); } case runtime.Type_Info_Type_Id: id := (^typeid)(v.data)^; - write_typeid(fi.buf, id); + reflect.write_typeid(fi.buf, id); case runtime.Type_Info_Bit_Field: fmt_bit_field(fi, v); @@ -1386,6 +1393,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, "<nil>"); @@ -1398,7 +1430,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { switch a in arg { case ^runtime.Type_Info: ti = a; } - write_type(fi.buf, ti); + reflect.write_type(fi.buf, ti); return; } @@ -1434,6 +1466,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); @@ -1449,7 +1484,7 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { case string: fmt_string(fi, a, verb); case cstring: fmt_cstring(fi, a, verb); - case typeid: write_typeid(fi.buf, a); + case typeid: reflect.write_typeid(fi.buf, a); case i16le: fmt_int(fi, u64(a), true, 16, verb); case u16le: fmt_int(fi, u64(a), false, 16, verb); @@ -1482,212 +1517,3 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) { - -write_typeid :: proc(buf: ^strings.Builder, id: typeid) { - write_type(buf, type_info_of(id)); -} - -write_type :: proc(buf: ^strings.Builder, ti: ^runtime.Type_Info) { - using strings; - if ti == nil { - write_string(buf, "nil"); - return; - } - - switch info in ti.variant { - case runtime.Type_Info_Named: - write_string(buf, info.name); - case runtime.Type_Info_Integer: - switch ti.id { - case int: write_string(buf, "int"); - case uint: write_string(buf, "uint"); - case uintptr: write_string(buf, "uintptr"); - case: - write_byte(buf, info.signed ? 'i' : 'u'); - write_i64(buf, i64(8*ti.size), 10); - switch info.endianness { - case runtime.Type_Info_Endianness.Little: - write_string(buf, "le"); - case runtime.Type_Info_Endianness.Big: - write_string(buf, "be"); - } - } - case runtime.Type_Info_Rune: - write_string(buf, "rune"); - case runtime.Type_Info_Float: - write_byte(buf, 'f'); - write_i64(buf, i64(8*ti.size), 10); - case runtime.Type_Info_Complex: - write_string(buf, "complex"); - write_i64(buf, i64(8*ti.size), 10); - case runtime.Type_Info_String: - if info.is_cstring { - write_string(buf, "cstring"); - } else { - write_string(buf, "string"); - } - case runtime.Type_Info_Boolean: - switch ti.id { - case bool: write_string(buf, "bool"); - case: - write_byte(buf, 'b'); - write_i64(buf, i64(8*ti.size), 10); - } - case runtime.Type_Info_Any: - write_string(buf, "any"); - - case runtime.Type_Info_Type_Id: - write_string(buf, "typeid"); - - case runtime.Type_Info_Pointer: - if info.elem == nil { - write_string(buf, "rawptr"); - } else { - write_string(buf, "^"); - write_type(buf, info.elem); - } - case runtime.Type_Info_Procedure: - write_string(buf, "proc"); - if info.params == nil { - write_string(buf, "()"); - } else { - t := info.params.variant.(runtime.Type_Info_Tuple); - write_string(buf, "("); - for t, i in t.types { - if i > 0 do write_string(buf, ", "); - write_type(buf, t); - } - write_string(buf, ")"); - } - if info.results != nil { - write_string(buf, " -> "); - write_type(buf, info.results); - } - case runtime.Type_Info_Tuple: - count := len(info.names); - if count != 1 do write_string(buf, "("); - for name, i in info.names { - if i > 0 do write_string(buf, ", "); - - t := info.types[i]; - - if len(name) > 0 { - write_string(buf, name); - write_string(buf, ": "); - } - write_type(buf, t); - } - if count != 1 do write_string(buf, ")"); - - case runtime.Type_Info_Array: - write_string(buf, "["); - write_i64(buf, i64(info.count), 10); - write_string(buf, "]"); - write_type(buf, info.elem); - case runtime.Type_Info_Dynamic_Array: - write_string(buf, "[dynamic]"); - write_type(buf, info.elem); - case runtime.Type_Info_Slice: - write_string(buf, "[]"); - write_type(buf, info.elem); - - case runtime.Type_Info_Map: - write_string(buf, "map["); - write_type(buf, info.key); - write_byte(buf, ']'); - write_type(buf, info.value); - - case runtime.Type_Info_Struct: - write_string(buf, "struct "); - if info.is_packed do write_string(buf, "#packed "); - if info.is_raw_union do write_string(buf, "#raw_union "); - if info.custom_align { - write_string(buf, "#align "); - write_i64(buf, i64(ti.align), 10); - write_byte(buf, ' '); - } - write_byte(buf, '{'); - for name, i in info.names { - if i > 0 do write_string(buf, ", "); - write_string(buf, name); - write_string(buf, ": "); - write_type(buf, info.types[i]); - } - write_byte(buf, '}'); - - case runtime.Type_Info_Union: - write_string(buf, "union "); - if info.custom_align { - write_string(buf, "#align "); - write_i64(buf, i64(ti.align), 10); - write_byte(buf, ' '); - } - write_byte(buf, '{'); - for variant, i in info.variants { - if i > 0 do write_string(buf, ", "); - write_type(buf, variant); - } - write_byte(buf, '}'); - - case runtime.Type_Info_Enum: - write_string(buf, "enum "); - write_type(buf, info.base); - write_string(buf, " {"); - for name, i in info.names { - if i > 0 do write_string(buf, ", "); - write_string(buf, name); - } - write_byte(buf, '}'); - - case runtime.Type_Info_Bit_Field: - write_string(buf, "bit_field "); - if ti.align != 1 { - write_string(buf, "#align "); - write_i64(buf, i64(ti.align), 10); - write_byte(buf, ' '); - } - write_string(buf, " {"); - for name, i in info.names { - if i > 0 do write_string(buf, ", "); - write_string(buf, name); - write_string(buf, ": "); - write_i64(buf, i64(info.bits[i]), 10); - } - write_byte(buf, '}'); - - case runtime.Type_Info_Bit_Set: - write_string(buf, "bit_set["); - switch { - case types.is_enum(info.elem): - write_type(buf, info.elem); - case types.is_rune(info.elem): - write_encoded_rune(buf, rune(info.lower)); - write_string(buf, ".."); - write_encoded_rune(buf, rune(info.upper)); - case: - write_i64(buf, info.lower, 10); - write_string(buf, ".."); - write_i64(buf, info.upper, 10); - } - if info.underlying != nil { - write_string(buf, "; "); - write_type(buf, info.underlying); - } - write_byte(buf, ']'); - - case runtime.Type_Info_Opaque: - write_string(buf, "opaque "); - write_type(buf, info.elem); - - case runtime.Type_Info_Simd_Vector: - if info.is_x86_mmx { - write_string(buf, "intrinsics.x86_mmx"); - } else { - write_string(buf, "intrinsics.vector("); - write_i64(buf, i64(info.count)); - write_string(buf, ", "); - write_type(buf, info.elem); - write_byte(buf, ')'); - } - } -} diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin new file mode 100644 index 000000000..8ee12554c --- /dev/null +++ b/core/intrinsics/intrinsics.odin @@ -0,0 +1,126 @@ +// This is purely for documentation +package intrinsics + + +vector :: proc() --- + +atomic_fence :: proc() --- +atomic_fence_acq :: proc() --- +atomic_fence_rel :: proc() --- +atomic_fence_acqrel :: proc() --- + +atomic_store :: proc(dst: ^$T, val: $T) --- +atomic_store_rel :: proc(dst: ^$T, val: $T) --- +atomic_store_relaxed :: proc(dst: ^$T, val: $T) --- +atomic_store_unordered :: proc(dst: ^$T, val: $T) --- + +atomic_load :: proc(dst: ^$T) -> T --- +atomic_load_acq :: proc(dst: ^$T) -> T --- +atomic_load_relaxed :: proc(dst: ^$T) -> T --- +atomic_load_unordered :: proc(dst: ^$T) -> T --- + +atomic_add :: proc(dst; ^$T, val: $T) -> T --- +atomic_add_acq :: proc(dst; ^$T, val: $T) -> T --- +atomic_add_rel :: proc(dst; ^$T, val: $T) -> T --- +atomic_add_acqrel :: proc(dst; ^$T, val: $T) -> T --- +atomic_add_relaxed :: proc(dst; ^$T, val: $T) -> T --- +atomic_sub :: proc(dst; ^$T, val: $T) -> T --- +atomic_sub_acq :: proc(dst; ^$T, val: $T) -> T --- +atomic_sub_rel :: proc(dst; ^$T, val: $T) -> T --- +atomic_sub_acqrel :: proc(dst; ^$T, val: $T) -> T --- +atomic_sub_relaxed :: proc(dst; ^$T, val: $T) -> T --- +atomic_and :: proc(dst; ^$T, val: $T) -> T --- +atomic_and_acq :: proc(dst; ^$T, val: $T) -> T --- +atomic_and_rel :: proc(dst; ^$T, val: $T) -> T --- +atomic_and_acqrel :: proc(dst; ^$T, val: $T) -> T --- +atomic_and_relaxed :: proc(dst; ^$T, val: $T) -> T --- +atomic_nand :: proc(dst; ^$T, val: $T) -> T --- +atomic_nand_acq :: proc(dst; ^$T, val: $T) -> T --- +atomic_nand_rel :: proc(dst; ^$T, val: $T) -> T --- +atomic_nand_acqrel :: proc(dst; ^$T, val: $T) -> T --- +atomic_nand_relaxed :: proc(dst; ^$T, val: $T) -> T --- +atomic_or :: proc(dst; ^$T, val: $T) -> T --- +atomic_or_acq :: proc(dst; ^$T, val: $T) -> T --- +atomic_or_rel :: proc(dst; ^$T, val: $T) -> T --- +atomic_or_acqrel :: proc(dst; ^$T, val: $T) -> T --- +atomic_or_relaxed :: proc(dst; ^$T, val: $T) -> T --- +atomic_xor :: proc(dst; ^$T, val: $T) -> T --- +atomic_xor_acq :: proc(dst; ^$T, val: $T) -> T --- +atomic_xor_rel :: proc(dst; ^$T, val: $T) -> T --- +atomic_xor_acqrel :: proc(dst; ^$T, val: $T) -> T --- +atomic_xor_relaxed :: proc(dst; ^$T, val: $T) -> T --- + +atomic_xchg :: proc(dst; ^$T, val: $T) -> T --- +atomic_xchg_acq :: proc(dst; ^$T, val: $T) -> T --- +atomic_xchg_rel :: proc(dst; ^$T, val: $T) -> T --- +atomic_xchg_acqrel :: proc(dst; ^$T, val: $T) -> T --- +atomic_xchg_relaxed :: proc(dst; ^$T, val: $T) -> T --- + +atomic_cxchg :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchg_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchg_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchg_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchg_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchg_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchg_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchg_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchg_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- + +atomic_cxchgweak :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchgweak_acq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchgweak_rel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchgweak_acqrel :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchgweak_relaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchgweak_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchgweak_failacq :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchgweak_acq_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- +atomic_cxchgweak_acqrel_failrelaxed :: proc(dst: ^$T, old, new: T) -> (T, /*option*/bool) --- + + +// Constant type tests + +type_base_type :: proc($T: typeid) -> type --- +type_core_type :: proc($T: typeid) -> type --- +type_elem_type :: proc($T: typeid) -> type --- + +type_is_boolean :: proc($T: typeid) -> bool --- +type_is_integer :: proc($T: typeid) -> bool --- +type_is_rune :: proc($T: typeid) -> bool --- +type_is_float :: proc($T: typeid) -> bool --- +type_is_complex :: proc($T: typeid) -> bool --- +type_is_quaternion :: proc($T: typeid) -> bool --- +type_is_string :: proc($T: typeid) -> bool --- +type_is_typeid :: proc($T: typeid) -> bool --- +type_is_any :: proc($T: typeid) -> bool --- + +type_is_endian_little :: proc($T: typeid) -> bool --- +type_is_endian_big :: proc($T: typeid) -> bool --- +type_is_numeric :: proc($T: typeid) -> bool --- +type_is_ordered :: proc($T: typeid) -> bool --- +type_is_ordered_numeric :: proc($T: typeid) -> bool --- +type_is_indexable :: proc($T: typeid) -> bool --- +type_is_sliceable :: proc($T: typeid) -> bool --- +type_is_simple_compare :: proc($T: typeid) -> bool --- // easily compared using memcmp +type_is_dereferenceable :: proc($T: typeid) -> bool --- +type_is_valid_map_key :: proc($T: typeid) -> bool --- + +type_is_named :: proc($T: typeid) -> bool --- +type_is_pointer :: proc($T: typeid) -> bool --- +type_is_opaque :: proc($T: typeid) -> bool --- +type_is_array :: proc($T: typeid) -> bool --- +type_is_slice :: proc($T: typeid) -> bool --- +type_is_dynamic_array :: proc($T: typeid) -> bool --- +type_is_map :: proc($T: typeid) -> bool --- +type_is_struct :: proc($T: typeid) -> bool --- +type_is_union :: proc($T: typeid) -> bool --- +type_is_enum :: proc($T: typeid) -> bool --- +type_is_proc :: proc($T: typeid) -> bool --- +type_is_bit_field :: proc($T: typeid) -> bool --- +type_is_bit_field_value :: proc($T: typeid) -> bool --- +type_is_bit_set :: proc($T: typeid) -> bool --- +type_is_simd_vector :: proc($T: typeid) -> bool --- + +type_has_nil :: proc($T: typeid) -> bool --- + +type_proc_parameter_count :: proc($T: typeid) -> int where type_is_proc(T) --- +type_proc_return_count :: proc($T: typeid) -> int where type_is_proc(T) --- diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin index 3778d3e53..6bddf7ffb 100644 --- a/core/log/file_console_logger.odin +++ b/core/log/file_console_logger.odin @@ -14,18 +14,18 @@ Level_Headers := []string{ }; Default_Console_Logger_Opts :: Options{ - Option.Level, - Option.Terminal_Color, - Option.Short_File_Path, - Option.Line, - Option.Procedure, + .Level, + .Terminal_Color, + .Short_File_Path, + .Line, + .Procedure, } | Full_Timestamp_Opts; Default_File_Logger_Opts :: Options{ - Option.Level, - Option.Short_File_Path, - Option.Line, - Option.Procedure, + .Level, + .Short_File_Path, + .Line, + .Procedure, } | Full_Timestamp_Opts; @@ -109,10 +109,10 @@ do_level_header :: proc(opts : Options, level : Level, str : ^strings.Builder) { case Level.Error, Level.Fatal : col = RED; } - if Option.Level in opts { - if Option.Terminal_Color in opts do fmt.sbprint(str, col); + if .Level in opts { + if .Terminal_Color in opts do fmt.sbprint(str, col); fmt.sbprint(str, Level_Headers[level]); - if Option.Terminal_Color in opts do fmt.sbprint(str, RESET); + if .Terminal_Color in opts do fmt.sbprint(str, RESET); } } @@ -120,7 +120,7 @@ do_location_header :: proc(opts : Options, buf : ^strings.Builder, location := # if Location_Header_Opts & opts != nil do fmt.sbprint(buf, "["); else do return; file := location.file_path; - if Option.Short_File_Path in opts { + if .Short_File_Path in opts { when os.OS == "windows" do delimiter := '\\'; else do delimiter := '/'; last := 0; for r, i in location.file_path do if r == delimiter do last = i+1; @@ -129,13 +129,13 @@ do_location_header :: proc(opts : Options, buf : ^strings.Builder, location := # if Location_File_Opts & opts != nil do fmt.sbprint(buf, file); - if Option.Procedure in opts { + if .Procedure in opts { if Location_File_Opts & opts != nil do fmt.sbprint(buf, "."); fmt.sbprintf(buf, "%s()", location.procedure); } - if Option.Line in opts { - if Location_File_Opts & opts != nil || Option.Procedure in opts do fmt.sbprint(buf, ":"); + if .Line in opts { + if Location_File_Opts & opts != nil || .Procedure in opts do fmt.sbprint(buf, ":"); fmt.sbprint(buf, location.line); } diff --git a/core/log/log.odin b/core/log/log.odin index 13e370b91..76b8550e8 100644 --- a/core/log/log.odin +++ b/core/log/log.odin @@ -11,30 +11,30 @@ Level :: enum { } Option :: enum { - Level, - Date, - Time, - Short_File_Path, - Long_File_Path, - Line, - Procedure, - Terminal_Color + Level, + Date, + Time, + Short_File_Path, + Long_File_Path, + Line, + Procedure, + Terminal_Color } Options :: bit_set[Option]; Full_Timestamp_Opts :: Options{ - Option.Date, - Option.Time + .Date, + .Time }; Location_Header_Opts :: Options{ - Option.Short_File_Path, - Option.Long_File_Path, - Option.Line, - Option.Procedure, + .Short_File_Path, + .Long_File_Path, + .Line, + .Procedure, }; Location_File_Opts :: Options{ - Option.Short_File_Path, - Option.Long_File_Path + .Short_File_Path, + .Long_File_Path }; Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location); @@ -42,30 +42,34 @@ Logger_Proc :: #type proc(data: rawptr, level: Level, text: string, options: Opt Logger :: struct { procedure: Logger_Proc, data: rawptr, - options: Options, + options: Options, } Multi_Logger_Data :: struct { - loggers : []Logger, + loggers : []Logger, } create_multi_logger :: proc(logs: ..Logger) -> Logger { - data := new(Multi_Logger_Data); - data.loggers = make([]Logger, len(logs)); - copy(data.loggers, logs); - return Logger{multi_logger_proc, data, nil}; + data := new(Multi_Logger_Data); + data.loggers = make([]Logger, len(logs)); + copy(data.loggers, logs); + return Logger{multi_logger_proc, data, nil}; } -destroy_multi_logger ::proc(log : ^Logger) { - free(log.data); - log^ = nil_logger(); +destroy_multi_logger :: proc(log : ^Logger) { + free(log.data); + log^ = nil_logger(); } multi_logger_proc :: proc(logger_data: rawptr, level: Level, text: string, options: Options, location := #caller_location) { - data := cast(^Multi_Logger_Data)logger_data; - if data.loggers == nil || len(data.loggers) == 0 do return; - for log in data.loggers do log.procedure(log.data, level, text, log.options, location); + data := cast(^Multi_Logger_Data)logger_data; + if data.loggers == nil || len(data.loggers) == 0 { + return; + } + for log in data.loggers { + log.procedure(log.data, level, text, log.options, location); + } } nil_logger_proc :: proc(data: rawptr, level: Level, text: string, options: Options, location := #caller_location) { @@ -76,6 +80,7 @@ nil_logger :: proc() -> Logger { return Logger{nil_logger_proc, nil, nil}; } +// TODO(bill): Should these be redesigned so that they are do not rely upon `package fmt`? debug :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Debug, fmt_str=fmt_str, args=args, location=location); info :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Info, fmt_str=fmt_str, args=args, location=location); warn :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Warning, fmt_str=fmt_str, args=args, location=location); @@ -83,7 +88,7 @@ error :: proc(fmt_str : string, args : ..any, location := #caller_location) do l fatal :: proc(fmt_str : string, args : ..any, location := #caller_location) do logf(level=Level.Fatal, fmt_str=fmt_str, args=args, location=location); logf :: proc(level : Level, fmt_str : string, args : ..any, location := #caller_location) { - logger := context.logger; - str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is. - logger.procedure(logger.data, level, str, logger.options, location); + logger := context.logger; + str := len(args) > 0 ? fmt.tprintf(fmt_str, ..args) : fmt.tprint(fmt_str); //NOTE(Hoej): While tprint isn't thread-safe, no logging is. + logger.procedure(logger.data, level, str, logger.options, location); } diff --git a/core/math/linalg/linalg.odin b/core/math/linalg/linalg.odin new file mode 100644 index 000000000..ceef645cb --- /dev/null +++ b/core/math/linalg/linalg.odin @@ -0,0 +1,283 @@ +package linalg + +import "core:math" +import "intrinsics" + +// Generic + +dot_vector :: proc(a, b: $T/[$N]$E) -> (c: E) { + for i in 0..<N { + c += a[i] * b[i]; + } + return; +} +dot_quaternion128 :: proc(a, b: $T/quaternion128) -> (c: f32) { + return real(a)*real(a) + imag(a)*imag(b) + jmag(a)*jmag(b) + kmag(a)*kmag(b); +} +dot_quaternion256 :: proc(a, b: $T/quaternion256) -> (c: f64) { + return real(a)*real(a) + imag(a)*imag(b) + jmag(a)*jmag(b) + kmag(a)*kmag(b); +} + +dot :: proc{dot_vector, dot_quaternion128, dot_quaternion256}; + +cross2 :: proc(a, b: $T/[2]$E) -> E { + return a[0]*b[1] - b[0]*a[1]; +} + +cross3 :: proc(a, b: $T/[3]$E) -> (c: T) { + c[0] = +(a[1]*b[2] - b[1]*a[2]); + c[1] = -(a[2]*b[3] - b[2]*a[3]); + c[2] = +(a[3]*b[1] - b[3]*a[1]); + return; +} + +cross :: proc{cross2, cross3}; + + +normalize_vector :: proc(v: $T/[$N]$E) -> T { + return v / length(v); +} +normalize_quaternion128 :: proc(q: $Q/quaternion128) -> Q { + return q/abs(q); +} +normalize_quaternion256 :: proc(q: $Q/quaternion256) -> Q { + return q/abs(q); +} +normalize :: proc{normalize_vector, normalize_quaternion128, normalize_quaternion256}; + +normalize0_vector :: proc(v: $T/[$N]$E) -> T { + m := length(v); + return m == 0 ? 0 : v/m; +} +normalize0_quaternion128 :: proc(q: $Q/quaternion128) -> Q { + m := abs(q); + return m == 0 ? 0 : q/m; +} +normalize0_quaternion256 :: proc(q: $Q/quaternion256) -> Q { + m := abs(q); + return m == 0 ? 0 : q/m; +} +normalize0 :: proc{normalize0_vector, normalize0_quaternion128, normalize0_quaternion256}; + + +length :: proc(v: $T/[$N]$E) -> E { + return math.sqrt(dot(v, v)); +} + + +identity :: proc($T: typeid/[$N][N]$E) -> (m: T) { + for i in 0..<N do m[i][i] = E(1); + return m; +} + +transpose :: proc(a: $T/[$N][$M]$E) -> (m: [M][N]E) { + for j in 0..<M { + for i in 0..<N { + m[j][i] = a[i][j]; + } + } + return; +} + +mul_matrix :: proc(a, b: $M/[$N][N]$E) -> (c: M) + where !intrinsics.type_is_array(E), + intrinsics.type_is_numeric(E) { + for i in 0..<N { + for k in 0..<N { + for j in 0..<N { + c[i][k] += a[i][j] * b[j][k]; + } + } + } + return; +} + +mul_matrix_differ :: proc(a: $A/[$I][$J]$E, b: $B/[J][$K]E) -> (c: [I][K]E) + where !intrinsics.type_is_array(E), + intrinsics.type_is_numeric(E), + I != J { + for i in 0..<I { + for k in 0..<K { + for j in 0..<J { + c[i][k] += a[i][j] * b[j][k]; + } + } + } + return; +} + + +mul_matrix_vector :: proc(a: $A/[$I][$J]$E, b: $B/[I]E) -> (c: B) + where !intrinsics.type_is_array(E), + intrinsics.type_is_numeric(E) { + for i in 0..<I { + for j in 0..<J { + c[i] += a[i][j] * b[i]; + } + } + return; +} + +mul_quaternion128_vector3 :: proc(q: $Q/quaternion128, v: $V/[3]$F/f32) -> V { + Raw_Quaternion :: struct {xyz: [3]f32, r: f32}; + + q := transmute(Raw_Quaternion)q; + v := transmute([3]f32)v; + + t := cross(2*q.xyz, v); + return V(v + q.r*t + cross(q.xyz, t)); +} + +mul_quaternion256_vector3 :: proc(q: $Q/quaternion256, v: $V/[3]$F/f64) -> V { + Raw_Quaternion :: struct {xyz: [3]f64, r: f64}; + + q := transmute(Raw_Quaternion)q; + v := transmute([3]f64)v; + + t := cross(2*q.xyz, v); + return V(v + q.r*t + cross(q.xyz, t)); +} +mul_quaternion_vector3 :: proc{mul_quaternion128_vector3, mul_quaternion256_vector3}; + +mul :: proc{ + mul_matrix, + mul_matrix_differ, + mul_matrix_vector, + mul_quaternion128_vector3, + mul_quaternion256_vector3, +}; + + +// Specific + +Float :: f32; + +Vector2 :: distinct [2]Float; +Vector3 :: distinct [3]Float; +Vector4 :: distinct [4]Float; + +Matrix2x1 :: distinct [2][1]Float; +Matrix2x2 :: distinct [2][2]Float; +Matrix2x3 :: distinct [2][3]Float; +Matrix2x4 :: distinct [2][4]Float; + +Matrix3x1 :: distinct [3][1]Float; +Matrix3x2 :: distinct [3][2]Float; +Matrix3x3 :: distinct [3][3]Float; +Matrix3x4 :: distinct [3][4]Float; + +Matrix4x1 :: distinct [4][1]Float; +Matrix4x2 :: distinct [4][2]Float; +Matrix4x3 :: distinct [4][3]Float; +Matrix4x4 :: distinct [4][4]Float; + + +Matrix2 :: Matrix2x2; +Matrix3 :: Matrix3x3; +Matrix4 :: Matrix4x4; + + +Quaternion :: distinct (size_of(Float) == size_of(f32) ? quaternion128 : quaternion256); + + +translate_matrix4 :: proc(v: Vector3) -> Matrix4 { + m := identity(Matrix4); + m[3][0] = v[0]; + m[3][1] = v[1]; + m[3][2] = v[2]; + return m; +} + + +rotate_matrix4 :: proc(v: Vector3, angle_radians: Float) -> Matrix4 { + c := math.cos(angle_radians); + s := math.sin(angle_radians); + + a := normalize(v); + t := a * (1-c); + + rot := identity(Matrix4); + + rot[0][0] = c + t[0]*a[0]; + rot[0][1] = 0 + t[0]*a[1] + s*a[2]; + rot[0][2] = 0 + t[0]*a[2] - s*a[1]; + rot[0][3] = 0; + + rot[1][0] = 0 + t[1]*a[0] - s*a[2]; + rot[1][1] = c + t[1]*a[1]; + rot[1][2] = 0 + t[1]*a[2] + s*a[0]; + rot[1][3] = 0; + + rot[2][0] = 0 + t[2]*a[0] + s*a[1]; + rot[2][1] = 0 + t[2]*a[1] - s*a[0]; + rot[2][2] = c + t[2]*a[2]; + rot[2][3] = 0; + + return rot; +} + +scale_matrix4 :: proc(m: Matrix4, v: Vector3) -> Matrix4 { + mm := m; + mm[0][0] *= v[0]; + mm[1][1] *= v[1]; + mm[2][2] *= v[2]; + return mm; +} + + +look_at :: proc(eye, centre, up: Vector3) -> Matrix4 { + f := normalize(centre - eye); + s := normalize(cross(f, up)); + u := cross(s, f); + return Matrix4{ + {+s.x, +u.x, -f.x, 0}, + {+s.y, +u.y, -f.y, 0}, + {+s.z, +u.z, -f.z, 0}, + {-dot(s, eye), -dot(u, eye), +dot(f, eye), 1}, + }; +} + + +perspective :: proc(fovy, aspect, near, far: Float) -> (m: Matrix4) { + tan_half_fovy := math.tan(0.5 * fovy); + m[0][0] = 1 / (aspect*tan_half_fovy); + m[1][1] = 1 / (tan_half_fovy); + m[2][2] = -(far + near) / (far - near); + m[2][3] = -1; + m[3][2] = -2*far*near / (far - near); + return; +} + + +ortho3d :: proc(left, right, bottom, top, near, far: Float) -> (m: Matrix4) { + m[0][0] = +2 / (right - left); + m[1][1] = +2 / (top - bottom); + m[2][2] = -2 / (far - near); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -(far + near) / (far- near); + m[3][3] = 1; + return; +} + + +axis_angle :: proc(axis: Vector3, angle_radians: Float) -> Quaternion { + t := angle_radians*0.5; + w := math.cos(t); + v := normalize(axis) * math.sin(t); + return quaternion(w, v.x, v.y, v.z); +} + +angle_axis :: proc(angle_radians: Float, axis: Vector3) -> Quaternion { + t := angle_radians*0.5; + w := math.cos(t); + v := normalize(axis) * math.sin(t); + return quaternion(w, v.x, v.y, v.z); +} + +euler_angles :: proc(pitch, yaw, roll: Float) -> Quaternion { + p := axis_angle({1, 0, 0}, pitch); + y := axis_angle({0, 1, 0}, yaw); + r := axis_angle({0, 0, 1}, roll); + return (y * p) * r; +} diff --git a/core/math/math.odin b/core/math/math.odin index 016e8143a..583da00d0 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -1,36 +1,41 @@ package math +import "intrinsics" + +Float_Class :: enum { + Normal, // an ordinary nonzero floating point value + Subnormal, // a subnormal floating point value + Zero, // zero + Neg_Zero, // the negative zero + NaN, // Not-A-Number (NaN) + Inf, // positive infinity + Neg_Inf // negative infinity +}; + TAU :: 6.28318530717958647692528676655900576; PI :: 3.14159265358979323846264338327950288; E :: 2.71828182845904523536; -SQRT_TWO :: 1.41421356237309504880168872420969808; -SQRT_THREE :: 1.73205080756887729352744634150587236; -SQRT_FIVE :: 2.23606797749978969640917366873127623; - -LOG_TWO :: 0.693147180559945309417232121458176568; -LOG_TEN :: 2.30258509299404568401799145468436421; - -EPSILON :: 1.19209290e-7; τ :: TAU; π :: PI; +e :: E; -Vec2 :: distinct [2]f32; -Vec3 :: distinct [3]f32; -Vec4 :: distinct [4]f32; +SQRT_TWO :: 1.41421356237309504880168872420969808; +SQRT_THREE :: 1.73205080756887729352744634150587236; +SQRT_FIVE :: 2.23606797749978969640917366873127623; -// Column major -Mat2 :: distinct [2][2]f32; -Mat3 :: distinct [3][3]f32; -Mat4 :: distinct [4][4]f32; +LN2 :: 0.693147180559945309417232121458176568; +LN10 :: 2.30258509299404568401799145468436421; -Quat :: struct {x, y, z, w: f32}; +MAX_F64_PRECISION :: 16; // Maximum number of meaningful digits after the decimal point for 'f64' +MAX_F32_PRECISION :: 8; // Maximum number of meaningful digits after the decimal point for 'f32' -QUAT_IDENTITY := Quat{x = 0, y = 0, z = 0, w = 1}; +RAD_PER_DEG :: TAU/360.0; +DEG_PER_RAD :: 360.0/TAU; -@(default_calling_convention="c") +@(default_calling_convention="none") foreign _ { @(link_name="llvm.sqrt.f32") sqrt_f32 :: proc(x: f32) -> f32 ---; @@ -58,9 +63,9 @@ foreign _ { fmuladd_f64 :: proc(a, b, c: f64) -> f64 ---; @(link_name="llvm.log.f32") - log_f32 :: proc(x: f32) -> f32 ---; + ln_f32 :: proc(x: f32) -> f32 ---; @(link_name="llvm.log.f64") - log_f64 :: proc(x: f64) -> f64 ---; + ln_f64 :: proc(x: f64) -> f64 ---; @(link_name="llvm.exp.f32") exp_f32 :: proc(x: f32) -> f32 ---; @@ -68,20 +73,40 @@ foreign _ { exp_f64 :: proc(x: f64) -> f64 ---; } -log :: proc{log_f32, log_f64}; -exp :: proc{exp_f32, exp_f64}; +sqrt :: proc{sqrt_f32, sqrt_f64}; +sin :: proc{sin_f32, sin_f64}; +cos :: proc{cos_f32, cos_f64}; +pow :: proc{pow_f32, pow_f64}; +fmuladd :: proc{fmuladd_f32, fmuladd_f64}; +ln :: proc{ln_f32, ln_f64}; +exp :: proc{exp_f32, exp_f64}; + +log_f32 :: proc(x, base: f32) -> f32 { return ln(x) / ln(base); } +log_f64 :: proc(x, base: f64) -> f64 { return ln(x) / ln(base); } +log :: proc{log_f32, log_f64}; + +log2_f32 :: proc(x: f32) -> f32 { return ln(x)/LN2; } +log2_f64 :: proc(x: f64) -> f64 { return ln(x)/LN2; } +log2 :: proc{log2_f32, log2_f64}; + +log10_f32 :: proc(x: f32) -> f32 { return ln(x)/LN10; } +log10_f64 :: proc(x: f64) -> f64 { return ln(x)/LN10; } +log10 :: proc{log10_f32, log10_f64}; + tan_f32 :: proc "c" (θ: f32) -> f32 { return sin(θ)/cos(θ); } tan_f64 :: proc "c" (θ: f64) -> f64 { return sin(θ)/cos(θ); } +tan :: proc{tan_f32, tan_f64}; lerp :: proc(a, b: $T, t: $E) -> (x: T) { return a*(1-t) + b*t; } unlerp_f32 :: proc(a, b, x: f32) -> (t: f32) { return (x-a)/(b-a); } unlerp_f64 :: proc(a, b, x: f64) -> (t: f64) { return (x-a)/(b-a); } +unlerp :: proc{unlerp_f32, unlerp_f64}; - -sign_f32 :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; } -sign_f64 :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; } +sign_f32 :: proc(x: f32) -> f32 { return f32(int(0 < x) - int(x < 0)); } +sign_f64 :: proc(x: f64) -> f64 { return f64(int(0 < x) - int(x < 0)); } +sign :: proc{sign_f32, sign_f64}; copy_sign_f32 :: proc(x, y: f32) -> f32 { ix := transmute(u32)x; @@ -90,7 +115,6 @@ copy_sign_f32 :: proc(x, y: f32) -> f32 { ix |= iy & 0x8000_0000; return transmute(f32)ix; } - copy_sign_f64 :: proc(x, y: f64) -> f64 { ix := transmute(u64)x; iy := transmute(u64)y; @@ -98,22 +122,89 @@ copy_sign_f64 :: proc(x, y: f64) -> f64 { ix |= iy & 0x8000_0000_0000_0000; return transmute(f64)ix; } +copy_sign :: proc{copy_sign_f32, copy_sign_f64}; -sqrt :: proc{sqrt_f32, sqrt_f64}; -sin :: proc{sin_f32, sin_f64}; -cos :: proc{cos_f32, cos_f64}; -tan :: proc{tan_f32, tan_f64}; -pow :: proc{pow_f32, pow_f64}; -fmuladd :: proc{fmuladd_f32, fmuladd_f64}; -sign :: proc{sign_f32, sign_f64}; -copy_sign :: proc{copy_sign_f32, copy_sign_f64}; +to_radians_f32 :: proc(degrees: f32) -> f32 { return degrees * RAD_PER_DEG; } +to_radians_f64 :: proc(degrees: f64) -> f64 { return degrees * RAD_PER_DEG; } +to_degrees_f32 :: proc(radians: f32) -> f32 { return radians * DEG_PER_RAD; } +to_degrees_f64 :: proc(radians: f64) -> f64 { return radians * DEG_PER_RAD; } +to_radians :: proc{to_radians_f32, to_radians_f64}; +to_degrees :: proc{to_degrees_f32, to_degrees_f64}; + +trunc_f32 :: proc(x: f32) -> f32 { + trunc_internal :: proc(f: f32) -> f32 { + mask :: 0xff; + shift :: 32 - 9; + bias :: 0x7f; + + if f < 1 { + switch { + case f < 0: return -trunc_internal(-f); + case f == 0: return f; + case: return 0; + } + } + x := transmute(u32)f; + e := (x >> shift) & mask - bias; -round_f32 :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); } -round_f64 :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); } + if e < shift { + x &= ~(1 << (shift-e)) - 1; + } + return transmute(f32)x; + } + switch classify(x) { + case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf: + return x; + } + return trunc_internal(x); +} + +trunc_f64 :: proc(x: f64) -> f64 { + trunc_internal :: proc(f: f64) -> f64 { + mask :: 0x7ff; + shift :: 64 - 12; + bias :: 0x3ff; + + if f < 1 { + switch { + case f < 0: return -trunc_internal(-f); + case f == 0: return f; + case: return 0; + } + } + + x := transmute(u64)f; + e := (x >> shift) & mask - bias; + + if e < shift { + x &= ~(1 << (shift-e)) - 1; + } + return transmute(f64)x; + } + switch classify(x) { + case .Zero, .Neg_Zero, .NaN, .Inf, .Neg_Inf: + return x; + } + return trunc_internal(x); +} + +trunc :: proc{trunc_f32, trunc_f64}; + +round_f32 :: proc(x: f32) -> f32 { + return x < 0 ? ceil(x - 0.5) : floor(x + 0.5); +} +round_f64 :: proc(x: f64) -> f64 { + return x < 0 ? ceil(x - 0.5) : floor(x + 0.5); +} round :: proc{round_f32, round_f64}; + +ceil_f32 :: proc(x: f32) -> f32 { return -floor(-x); } +ceil_f64 :: proc(x: f64) -> f64 { return -floor(-x); } +ceil :: proc{ceil_f32, ceil_f64}; + floor_f32 :: proc(x: f32) -> f32 { if x == 0 || is_nan(x) || is_inf(x) { return x; @@ -144,33 +235,27 @@ floor_f64 :: proc(x: f64) -> f64 { } floor :: proc{floor_f32, floor_f64}; -ceil_f32 :: proc(x: f32) -> f32 { return -floor_f32(-x); } -ceil_f64 :: proc(x: f64) -> f64 { return -floor_f64(-x); } -ceil :: proc{ceil_f32, ceil_f64}; -remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; } -remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; } -remainder :: proc{remainder_f32, remainder_f64}; - -mod_f32 :: proc(x, y: f32) -> (n: f32) { - z := abs(y); - n = remainder(abs(x), z); - if sign(n) < 0 { - n += z; +floor_div :: proc(x, y: $T) -> T + where intrinsics.type_is_integer(T) { + a := x / y; + r := x % y; + if (r > 0 && y < 0) || (r < 0 && y > 0) { + a -= 1; } - return copy_sign(n, x); + return a; } -mod_f64 :: proc(x, y: f64) -> (n: f64) { - z := abs(y); - n = remainder(abs(x), z); - if sign(n) < 0 { - n += z; + +floor_mod :: proc(x, y: $T) -> T + where intrinsics.type_is_integer(T) { + r := x % y; + if (r > 0 && y < 0) || (r < 0 && y > 0) { + r += y; } - return copy_sign(n, x); + return r; } -mod :: proc{mod_f32, mod_f64}; -// TODO(bill): These need to implemented with the actual instructions + modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) { shift :: 32 - 8 - 1; mask :: 0xff; @@ -190,8 +275,8 @@ modf_f32 :: proc(x: f32) -> (int: f32, frac: f32) { i := transmute(u32)x; e := uint(i>>shift)&mask - bias; - if e < 32-9 { - i &~= 1<<(32-9-e) - 1; + if e < shift { + i &~= 1<<(shift-e) - 1; } int = transmute(f32)i; frac = x - int; @@ -216,361 +301,275 @@ modf_f64 :: proc(x: f64) -> (int: f64, frac: f64) { i := transmute(u64)x; e := uint(i>>shift)&mask - bias; - if e < 64-12 { - i &~= 1<<(64-12-e) - 1; + if e < shift { + i &~= 1<<(shift-e) - 1; } int = transmute(f64)i; frac = x - int; return; } modf :: proc{modf_f32, modf_f64}; +split_decimal :: modf; -is_nan_f32 :: inline proc(x: f32) -> bool { return x != x; } -is_nan_f64 :: inline proc(x: f64) -> bool { return x != x; } -is_nan :: proc{is_nan_f32, is_nan_f64}; - -is_finite_f32 :: inline proc(x: f32) -> bool { return !is_nan(x-x); } -is_finite_f64 :: inline proc(x: f64) -> bool { return !is_nan(x-x); } -is_finite :: proc{is_finite_f32, is_finite_f64}; - -is_inf_f32 :: proc(x: f32, sign := 0) -> bool { - return sign >= 0 && x > F32_MAX || sign <= 0 && x < -F32_MAX; +mod_f32 :: proc(x, y: f32) -> (n: f32) { + z := abs(y); + n = remainder(abs(x), z); + if sign(n) < 0 { + n += z; + } + return copy_sign(n, x); } -is_inf_f64 :: proc(x: f64, sign := 0) -> bool { - return sign >= 0 && x > F64_MAX || sign <= 0 && x < -F64_MAX; +mod_f64 :: proc(x, y: f64) -> (n: f64) { + z := abs(y); + n = remainder(abs(x), z); + if sign(n) < 0 { + n += z; + } + return copy_sign(n, x); } -// If sign > 0, is_inf reports whether f is positive infinity -// If sign < 0, is_inf reports whether f is negative infinity -// If sign == 0, is_inf reports whether f is either infinity -is_inf :: proc{is_inf_f32, is_inf_f64}; - - - -to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; } -to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; } - - - +mod :: proc{mod_f32, mod_f64}; -mul :: proc{ - mat3_mul, - mat4_mul, mat4_mul_vec4, - quat_mul, quat_mulf, -}; +remainder_f32 :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; } +remainder_f64 :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; } +remainder :: proc{remainder_f32, remainder_f64}; -div :: proc{quat_div, quat_divf}; -inverse :: proc{mat4_inverse, quat_inverse}; -dot :: proc{vec_dot, quat_dot}; -cross :: proc{cross2, cross3}; -vec_dot :: proc(a, b: $T/[$N]$E) -> E { - res: E; - for i in 0..<N { - res += a[i] * b[i]; +gcd :: proc(x, y: $T) -> T + where intrinsics.type_is_ordered_numeric(T) { + x, y := x, y; + for y != 0 { + x %= y; + x, y = y, x; } - return res; + return abs(x); } -cross2 :: proc(a, b: $T/[2]$E) -> E { - return a[0]*b[1] - a[1]*b[0]; +lcm :: proc(x, y: $T) -> T + where intrinsics.type_is_ordered_numeric(T) { + return x / gcd(x, y) * y; } -cross3 :: proc(a, b: $T/[3]$E) -> T { - i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1); - j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0); - return T(i - j); +frexp_f32 :: proc(x: f32) -> (significand: f32, exponent: int) { + switch { + case x == 0: + return 0, 0; + case x < 0: + significand, exponent = frexp(-x); + return -significand, exponent; + } + ex := trunc(log2(x)); + exponent = int(ex); + significand = x / pow(2.0, ex); + if abs(significand) >= 1 { + exponent += 1; + significand /= 2; + } + if exponent == 1024 && significand == 0 { + significand = 0.99999999999999988898; + } + return; } +frexp_f64 :: proc(x: f64) -> (significand: f64, exponent: int) { + switch { + case x == 0: + return 0, 0; + case x < 0: + significand, exponent = frexp(-x); + return -significand, exponent; + } + ex := trunc(log2(x)); + exponent = int(ex); + significand = x / pow(2.0, ex); + if abs(significand) >= 1 { + exponent += 1; + significand /= 2; + } + if exponent == 1024 && significand == 0 { + significand = 0.99999999999999988898; + } + return; +} +frexp :: proc{frexp_f32, frexp_f64}; -length :: proc(v: $T/[$N]$E) -> E { return sqrt(dot(v, v)); } - -norm :: proc(v: $T/[$N]$E) -> T { return v / length(v); } -norm0 :: proc(v: $T/[$N]$E) -> T { - m := length(v); - return m == 0 ? 0 : v/m; -} +binomial :: proc(n, k: int) -> int { + switch { + case k <= 0: return 1; + case 2*k > n: return binomial(n, n-k); + } + b := n; + for i in 2..<k { + b = (b * (n+1-i))/i; + } + return b; +} + +factorial :: proc(n: int) -> int { + when size_of(int) == size_of(i64) { + @static table := [21]int{ + 1, + 1, + 2, + 6, + 24, + 120, + 720, + 5_040, + 40_320, + 362_880, + 3_628_800, + 39_916_800, + 479_001_600, + 6_227_020_800, + 87_178_291_200, + 1_307_674_368_000, + 20_922_789_888_000, + 355_687_428_096_000, + 6_402_373_705_728_000, + 121_645_100_408_832_000, + 2_432_902_008_176_640_000, + }; + } else { + @static table := [13]int{ + 1, + 1, + 2, + 6, + 24, + 120, + 720, + 5_040, + 40_320, + 362_880, + 3_628_800, + 39_916_800, + 479_001_600, + }; + } -identity :: proc($T: typeid/[$N][N]$E) -> T { - m: T; - for i in 0..<N do m[i][i] = E(1); - return m; + assert(n >= 0, "parameter must not be negative"); + assert(n < len(table), "parameter is too large to lookup in the table"); + return 0; } -transpose :: proc(m: $M/[$N][N]f32) -> M { - for j in 0..<N { - for i in 0..<N { - m[i][j], m[j][i] = m[j][i], m[i][j]; +classify_f32 :: proc(x: f32) -> Float_Class { + switch { + case x == 0: + i := transmute(i32)x; + if i < 0 { + return .Neg_Zero; } - } - return m; -} - -mat3_mul :: proc(a, b: Mat3) -> Mat3 { - c: Mat3; - for j in 0..<3 { - for i in 0..<3 { - c[j][i] = a[0][i]*b[j][0] + - a[1][i]*b[j][1] + - a[2][i]*b[j][2]; + return .Zero; + case x*0.5 == x: + if x < 0 { + return .Neg_Inf; } + return .Inf; + case x != x: + return .NaN; } - return c; -} -mat4_mul :: proc(a, b: Mat4) -> Mat4 { - c: Mat4; - for j in 0..<4 { - for i in 0..<4 { - c[j][i] = a[0][i]*b[j][0] + - a[1][i]*b[j][1] + - a[2][i]*b[j][2] + - a[3][i]*b[j][3]; + u := transmute(u32)x; + exp := int(u>>23) & (1<<8 - 1); + if exp == 0 { + return .Subnormal; + } + return .Normal; +} +classify_f64 :: proc(x: f64) -> Float_Class { + switch { + case x == 0: + i := transmute(i64)x; + if i < 0 { + return .Neg_Zero; + } + return .Zero; + case x*0.5 == x: + if x < 0 { + return .Neg_Inf; } + return .Inf; + case x != x: + return .NaN; } - return c; -} - -mat4_mul_vec4 :: proc(m: Mat4, v: Vec4) -> Vec4 { - return Vec4{ - m[0][0]*v[0] + m[1][0]*v[1] + m[2][0]*v[2] + m[3][0]*v[3], - m[0][1]*v[0] + m[1][1]*v[1] + m[2][1]*v[2] + m[3][1]*v[3], - m[0][2]*v[0] + m[1][2]*v[1] + m[2][2]*v[2] + m[3][2]*v[3], - m[0][3]*v[0] + m[1][3]*v[1] + m[2][3]*v[2] + m[3][3]*v[3], - }; -} - -mat4_inverse :: proc(m: Mat4) -> Mat4 { - o: Mat4; - - sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3]; - sf01 := m[2][1] * m[3][3] - m[3][1] * m[2][3]; - sf02 := m[2][1] * m[3][2] - m[3][1] * m[2][2]; - sf03 := m[2][0] * m[3][3] - m[3][0] * m[2][3]; - sf04 := m[2][0] * m[3][2] - m[3][0] * m[2][2]; - sf05 := m[2][0] * m[3][1] - m[3][0] * m[2][1]; - sf06 := m[1][2] * m[3][3] - m[3][2] * m[1][3]; - sf07 := m[1][1] * m[3][3] - m[3][1] * m[1][3]; - sf08 := m[1][1] * m[3][2] - m[3][1] * m[1][2]; - sf09 := m[1][0] * m[3][3] - m[3][0] * m[1][3]; - sf10 := m[1][0] * m[3][2] - m[3][0] * m[1][2]; - sf11 := m[1][1] * m[3][3] - m[3][1] * m[1][3]; - sf12 := m[1][0] * m[3][1] - m[3][0] * m[1][1]; - sf13 := m[1][2] * m[2][3] - m[2][2] * m[1][3]; - sf14 := m[1][1] * m[2][3] - m[2][1] * m[1][3]; - sf15 := m[1][1] * m[2][2] - m[2][1] * m[1][2]; - sf16 := m[1][0] * m[2][3] - m[2][0] * m[1][3]; - sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2]; - sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1]; - - - o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02); - o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04); - o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05); - o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05); - - o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02); - o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04); - o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05); - o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05); - - o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08); - o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10); - o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12); - o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12); - - o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15); - o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17); - o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18); - o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18); - - ood := 1.0 / (m[0][0] * o[0][0] + - m[0][1] * o[0][1] + - m[0][2] * o[0][2] + - m[0][3] * o[0][3]); - - o[0][0] *= ood; - o[0][1] *= ood; - o[0][2] *= ood; - o[0][3] *= ood; - o[1][0] *= ood; - o[1][1] *= ood; - o[1][2] *= ood; - o[1][3] *= ood; - o[2][0] *= ood; - o[2][1] *= ood; - o[2][2] *= ood; - o[2][3] *= ood; - o[3][0] *= ood; - o[3][1] *= ood; - o[3][2] *= ood; - o[3][3] *= ood; - - return o; -} - - -mat4_translate :: proc(v: Vec3) -> Mat4 { - m := identity(Mat4); - m[3][0] = v[0]; - m[3][1] = v[1]; - m[3][2] = v[2]; - m[3][3] = 1; - return m; -} - -mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 { - c := cos(angle_radians); - s := sin(angle_radians); - - a := norm(v); - t := a * (1-c); - - rot := identity(Mat4); - - rot[0][0] = c + t[0]*a[0]; - rot[0][1] = 0 + t[0]*a[1] + s*a[2]; - rot[0][2] = 0 + t[0]*a[2] - s*a[1]; - rot[0][3] = 0; - - rot[1][0] = 0 + t[1]*a[0] - s*a[2]; - rot[1][1] = c + t[1]*a[1]; - rot[1][2] = 0 + t[1]*a[2] + s*a[0]; - rot[1][3] = 0; - - rot[2][0] = 0 + t[2]*a[0] + s*a[1]; - rot[2][1] = 0 + t[2]*a[1] - s*a[0]; - rot[2][2] = c + t[2]*a[2]; - rot[2][3] = 0; - - return rot; -} - -scale_vec3 :: proc(m: Mat4, v: Vec3) -> Mat4 { - mm := m; - mm[0][0] *= v[0]; - mm[1][1] *= v[1]; - mm[2][2] *= v[2]; - return m; -} - -scale_f32 :: proc(m: Mat4, s: f32) -> Mat4 { - mm := m; - mm[0][0] *= s; - mm[1][1] *= s; - mm[2][2] *= s; - return m; -} - -scale :: proc{scale_vec3, scale_f32}; - - -look_at :: proc(eye, centre, up: Vec3) -> Mat4 { - f := norm(centre - eye); - s := norm(cross(f, up)); - u := cross(s, f); - - return Mat4{ - {+s.x, +u.x, -f.x, 0}, - {+s.y, +u.y, -f.y, 0}, - {+s.z, +u.z, -f.z, 0}, - {-dot(s, eye), -dot(u, eye), dot(f, eye), 1}, - }; -} - -perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 { - m: Mat4; - tan_half_fovy := tan(0.5 * fovy); - - m[0][0] = 1.0 / (aspect*tan_half_fovy); - m[1][1] = 1.0 / (tan_half_fovy); - m[2][2] = -(far + near) / (far - near); - m[2][3] = -1.0; - m[3][2] = -2.0*far*near / (far - near); - return m; + u := transmute(u64)x; + exp := int(u>>52) & (1<<11 - 1); + if exp == 0 { + return .Subnormal; + } + return .Normal; } +classify :: proc{classify_f32, classify_f64}; +is_nan_f32 :: proc(x: f32) -> bool { return classify(x) == .NaN; } +is_nan_f64 :: proc(x: f64) -> bool { return classify(x) == .NaN; } +is_nan :: proc{is_nan_f32, is_nan_f64}; -ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 { - m := identity(Mat4); - m[0][0] = +2.0 / (right - left); - m[1][1] = +2.0 / (top - bottom); - m[2][2] = -2.0 / (far - near); - m[3][0] = -(right + left) / (right - left); - m[3][1] = -(top + bottom) / (top - bottom); - m[3][2] = -(far + near) / (far - near); - return m; -} - +is_inf_f32 :: proc(x: f32) -> bool { return classify(abs(x)) == .Inf; } +is_inf_f64 :: proc(x: f64) -> bool { return classify(abs(x)) == .Inf; } +is_inf :: proc{is_inf_f32, is_inf_f64}; -// Quaternion operations -conj :: proc(q: Quat) -> Quat { - return Quat{-q.x, -q.y, -q.z, q.w}; -} -quat_mul :: proc(q0, q1: Quat) -> Quat { - d: Quat; - d.x = q0.w * q1.x + q0.x * q1.w + q0.y * q1.z - q0.z * q1.y; - d.y = q0.w * q1.y - q0.x * q1.z + q0.y * q1.w + q0.z * q1.x; - d.z = q0.w * q1.z + q0.x * q1.y - q0.y * q1.x + q0.z * q1.w; - d.w = q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z; - return d; +is_power_of_two :: proc(x: int) -> bool { + return x > 0 && (x & (x-1)) == 0; } -quat_mulf :: proc(q: Quat, f: f32) -> Quat { return Quat{q.x*f, q.y*f, q.z*f, q.w*f}; } -quat_divf :: proc(q: Quat, f: f32) -> Quat { return Quat{q.x/f, q.y/f, q.z/f, q.w/f}; } - -quat_div :: proc(q0, q1: Quat) -> Quat { return mul(q0, quat_inverse(q1)); } -quat_inverse :: proc(q: Quat) -> Quat { return div(conj(q), dot(q, q)); } -quat_dot :: proc(q0, q1: Quat) -> f32 { return q0.x*q1.x + q0.y*q1.y + q0.z*q1.z + q0.w*q1.w; } - -quat_norm :: proc(q: Quat) -> Quat { - m := sqrt(dot(q, q)); - return div(q, m); +next_power_of_two :: proc(x: int) -> int { + k := x -1; + when size_of(int) == 8 { + k = k | (k >> 32); + } + k = k | (k >> 16); + k = k | (k >> 8); + k = k | (k >> 4); + k = k | (k >> 2); + k = k | (k >> 1); + k += 1 + int(x <= 0); + return k; +} + +sum :: proc(x: $T/[]$E) -> (res: E) + where intrinsics.BuiltinProc_type_is_numeric(E) { + for i in x { + res += i; + } + return; } -axis_angle :: proc(axis: Vec3, angle_radians: f32) -> Quat { - v := norm(axis) * sin(0.5*angle_radians); - w := cos(0.5*angle_radians); - return Quat{v.x, v.y, v.z, w}; +prod :: proc(x: $T/[]$E) -> (res: E) + where intrinsics.BuiltinProc_type_is_numeric(E) { + for i in x { + res *= i; + } + return; } -euler_angles :: proc(pitch, yaw, roll: f32) -> Quat { - p := axis_angle(Vec3{1, 0, 0}, pitch); - y := axis_angle(Vec3{0, 1, 0}, yaw); - r := axis_angle(Vec3{0, 0, 1}, roll); - return mul(mul(y, p), r); +cumsum_inplace :: proc(x: $T/[]$E) -> T + where intrinsics.BuiltinProc_type_is_numeric(E) { + for i in 1..<len(x) { + x[i] = x[i-1] + x[i]; + } } -quat_to_mat4 :: proc(q: Quat) -> Mat4 { - a := quat_norm(q); - xx := a.x*a.x; yy := a.y*a.y; zz := a.z*a.z; - xy := a.x*a.y; xz := a.x*a.z; yz := a.y*a.z; - wx := a.w*a.x; wy := a.w*a.y; wz := a.w*a.z; - - m := identity(Mat4); - - m[0][0] = 1 - 2*(yy + zz); - m[0][1] = 2*(xy + wz); - m[0][2] = 2*(xz - wy); - m[1][0] = 2*(xy - wz); - m[1][1] = 1 - 2*(xx + zz); - m[1][2] = 2*(yz + wx); - - m[2][0] = 2*(xz + wy); - m[2][1] = 2*(yz - wx); - m[2][2] = 1 - 2*(xx + yy); - return m; +cumsum :: proc(dst, src: $T/[]$E) -> T + where intrinsics.BuiltinProc_type_is_numeric(E) { + N := min(len(dst), len(src)); + if N > 0 { + dst[0] = src[0]; + for i in 1..<N { + dst[i] = dst[i-1] + src[i]; + } + } + return dst[:N]; } - - F32_DIG :: 6; F32_EPSILON :: 1.192092896e-07; F32_GUARD :: 0; diff --git a/core/math/rand/normal.odin b/core/math/rand/normal.odin index 82c2dda6d..4e4810660 100644 --- a/core/math/rand/normal.odin +++ b/core/math/rand/normal.odin @@ -128,8 +128,8 @@ norm_float64 :: proc(r: ^Rand = global_rand_ptr) -> f64 { if i == 0 { for { - x = -math.log(float64(r)) * (1.0/ rn); - y := -math.log(float64(r)); + x = -math.ln(float64(r)) * (1.0/ rn); + y := -math.ln(float64(r)); if y+y >= x*x { break; } diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin index 04b0fd0b1..7ef094bc1 100644 --- a/core/mem/allocators.odin +++ b/core/mem/allocators.odin @@ -1,7 +1,5 @@ package mem - - nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64 = 0, loc := #caller_location) -> rawptr { diff --git a/core/mem/raw.odin b/core/mem/raw.odin index c7f4ee9db..97cd71e7a 100644 --- a/core/mem/raw.odin +++ b/core/mem/raw.odin @@ -31,6 +31,12 @@ Raw_Map :: struct { entries: Raw_Dynamic_Array, } +Raw_Complex64 :: struct {real, imag: f32}; +Raw_Complex128 :: struct {real, imag: f64}; +Raw_Quaternion128 :: struct {imag, jmag, kmag: f32, real: f32}; +Raw_Quaternion256 :: struct {imag, jmag, kmag: f64, real: f64}; +Raw_Quaternion128_Vector_Scalar :: struct {vector: [3]f32, scalar: f32}; +Raw_Quaternion256_Vector_Scalar :: struct {vector: [3]f64, scalar: f64}; make_any :: inline proc(data: rawptr, id: typeid) -> any { return transmute(any)Raw_Any{data, id}; diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index c0d2948ee..309d2c147 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -1,11 +1,10 @@ package odin_ast -import "core:odin/token" +import "core:odin/tokenizer" Proc_Tag :: enum { Bounds_Check, No_Bounds_Check, - Require_Results, } Proc_Tags :: distinct bit_set[Proc_Tag; u32]; @@ -34,12 +33,12 @@ Node_State_Flags :: distinct bit_set[Node_State_Flag]; Comment_Group :: struct { - list: []token.Token, + list: []tokenizer.Token, } Node :: struct { - pos: token.Pos, - end: token.Pos, + pos: tokenizer.Pos, + end: tokenizer.Pos, derived: any, state_flags: Node_State_Flags, } @@ -68,29 +67,29 @@ Ident :: struct { Implicit :: struct { using node: Expr, - tok: token.Token, + tok: tokenizer.Token, } Undef :: struct { using node: Expr, - tok: token.Kind, + tok: tokenizer.Token_Kind, } Basic_Lit :: struct { using node: Expr, - tok: token.Token, + tok: tokenizer.Token, } Basic_Directive :: struct { using node: Expr, - tok: token.Token, + tok: tokenizer.Token, name: string, } Ellipsis :: struct { using node: Expr, - tok: token.Kind, + tok: tokenizer.Token_Kind, expr: ^Expr, } @@ -100,42 +99,44 @@ Proc_Lit :: struct { body: ^Stmt, tags: Proc_Tags, inlining: Proc_Inlining, + where_token: tokenizer.Token, + where_clauses: []^Expr, } Comp_Lit :: struct { using node: Expr, type: ^Expr, - open: token.Pos, + open: tokenizer.Pos, elems: []^Expr, - close: token.Pos, + close: tokenizer.Pos, } Tag_Expr :: struct { using node: Expr, - op: token.Token, + op: tokenizer.Token, name: string, expr: ^Expr, } Unary_Expr :: struct { using node: Expr, - op: token.Token, + op: tokenizer.Token, expr: ^Expr, } Binary_Expr :: struct { using node: Expr, left: ^Expr, - op: token.Token, + op: tokenizer.Token, right: ^Expr, } Paren_Expr :: struct { using node: Expr, - open: token.Pos, + open: tokenizer.Pos, expr: ^Expr, - close: token.Pos, + close: tokenizer.Pos, } Selector_Expr :: struct { @@ -152,74 +153,74 @@ Implicit_Selector_Expr :: struct { Index_Expr :: struct { using node: Expr, expr: ^Expr, - open: token.Pos, + open: tokenizer.Pos, index: ^Expr, - close: token.Pos, + close: tokenizer.Pos, } Deref_Expr :: struct { using node: Expr, expr: ^Expr, - op: token.Token, + op: tokenizer.Token, } Slice_Expr :: struct { using node: Expr, expr: ^Expr, - open: token.Pos, + open: tokenizer.Pos, low: ^Expr, - interval: token.Token, + interval: tokenizer.Token, high: ^Expr, - close: token.Pos, + close: tokenizer.Pos, } Call_Expr :: struct { using node: Expr, inlining: Proc_Inlining, expr: ^Expr, - open: token.Pos, + open: tokenizer.Pos, args: []^Expr, - ellipsis: token.Token, - close: token.Pos, + ellipsis: tokenizer.Token, + close: tokenizer.Pos, } Field_Value :: struct { using node: Expr, field: ^Expr, - sep: token.Pos, + sep: tokenizer.Pos, value: ^Expr, } Ternary_Expr :: struct { using node: Expr, cond: ^Expr, - op1: token.Token, + op1: tokenizer.Token, x: ^Expr, - op2: token.Token, + op2: tokenizer.Token, y: ^Expr, } Type_Assertion :: struct { using node: Expr, expr: ^Expr, - dot: token.Pos, - open: token.Pos, + dot: tokenizer.Pos, + open: tokenizer.Pos, type: ^Expr, - close: token.Pos, + close: tokenizer.Pos, } Type_Cast :: struct { using node: Expr, - tok: token.Token, - open: token.Pos, + tok: tokenizer.Token, + open: tokenizer.Pos, type: ^Expr, - close: token.Pos, + close: tokenizer.Pos, expr: ^Expr, } Auto_Cast :: struct { using node: Expr, - op: token.Token, + op: tokenizer.Token, expr: ^Expr, } @@ -234,7 +235,7 @@ Bad_Stmt :: struct { Empty_Stmt :: struct { using node: Stmt, - semicolon: token.Pos, // Position of the following ';' + semicolon: tokenizer.Pos, // Position of the following ';' } Expr_Stmt :: struct { @@ -244,7 +245,7 @@ Expr_Stmt :: struct { Tag_Stmt :: struct { using node: Stmt, - op: token.Token, + op: tokenizer.Token, name: string, stmt: ^Stmt, } @@ -252,7 +253,7 @@ Tag_Stmt :: struct { Assign_Stmt :: struct { using node: Stmt, lhs: []^Expr, - op: token.Token, + op: tokenizer.Token, rhs: []^Expr, } @@ -260,15 +261,15 @@ Assign_Stmt :: struct { Block_Stmt :: struct { using node: Stmt, label: ^Expr, - open: token.Pos, + open: tokenizer.Pos, stmts: []^Stmt, - close: token.Pos, + close: tokenizer.Pos, } If_Stmt :: struct { using node: Stmt, label: ^Expr, - if_pos: token.Pos, + if_pos: tokenizer.Pos, init: ^Stmt, cond: ^Expr, body: ^Stmt, @@ -277,7 +278,7 @@ If_Stmt :: struct { When_Stmt :: struct { using node: Stmt, - when_pos: token.Pos, + when_pos: tokenizer.Pos, cond: ^Expr, body: ^Stmt, else_stmt: ^Stmt, @@ -296,7 +297,7 @@ Defer_Stmt :: struct { For_Stmt :: struct { using node: Stmt, label: ^Expr, - for_pos: token.Pos, + for_pos: tokenizer.Pos, init: ^Stmt, cond: ^Expr, post: ^Stmt, @@ -306,10 +307,10 @@ For_Stmt :: struct { Range_Stmt :: struct { using node: Stmt, label: ^Expr, - for_pos: token.Pos, + for_pos: tokenizer.Pos, val0: ^Expr, val1: ^Expr, - in_pos: token.Pos, + in_pos: tokenizer.Pos, expr: ^Expr, body: ^Stmt, } @@ -317,16 +318,16 @@ Range_Stmt :: struct { Case_Clause :: struct { using node: Stmt, - case_pos: token.Pos, + case_pos: tokenizer.Pos, list: []^Expr, - terminator: token.Token, + terminator: tokenizer.Token, body: []^Stmt, } Switch_Stmt :: struct { using node: Stmt, label: ^Expr, - switch_pos: token.Pos, + switch_pos: tokenizer.Pos, init: ^Stmt, cond: ^Expr, body: ^Stmt, @@ -336,7 +337,7 @@ Switch_Stmt :: struct { Type_Switch_Stmt :: struct { using node: Stmt, label: ^Expr, - switch_pos: token.Pos, + switch_pos: tokenizer.Pos, tag: ^Stmt, expr: ^Expr, body: ^Stmt, @@ -345,7 +346,7 @@ Type_Switch_Stmt :: struct { Branch_Stmt :: struct { using node: Stmt, - tok: token.Token, + tok: tokenizer.Token, label: ^Ident, } @@ -376,7 +377,7 @@ Value_Decl :: struct { Package_Decl :: struct { using node: Decl, docs: ^Comment_Group, - token: token.Token, + token: tokenizer.Token, name: string, comment: ^Comment_Group, } @@ -385,9 +386,9 @@ Import_Decl :: struct { using node: Decl, docs: ^Comment_Group, is_using: bool, - import_tok: token.Token, - name: token.Token, - relpath: token.Token, + import_tok: tokenizer.Token, + name: tokenizer.Token, + relpath: tokenizer.Token, fullpath: string, comment: ^Comment_Group, } @@ -396,7 +397,7 @@ Foreign_Block_Decl :: struct { using node: Decl, docs: ^Comment_Group, attributes: [dynamic]^Attribute, // dynamic as parsing will add to them lazily - tok: token.Token, + tok: tokenizer.Token, foreign_library: ^Expr, body: ^Stmt, } @@ -404,8 +405,8 @@ Foreign_Block_Decl :: struct { Foreign_Import_Decl :: struct { using node: Decl, docs: ^Comment_Group, - foreign_tok: token.Token, - import_tok: token.Token, + foreign_tok: tokenizer.Token, + import_tok: tokenizer.Token, name: ^Ident, collection_name: string, fullpaths: []string, @@ -435,7 +436,9 @@ Field_Flag :: enum { C_Vararg, Auto_Cast, In, + Results, + Tags, Default_Parameters, Typeid_Token, } @@ -443,18 +446,19 @@ Field_Flag :: enum { Field_Flags :: distinct bit_set[Field_Flag]; Field_Flags_Struct :: Field_Flags{ - Field_Flag.Using, + .Using, + .Tags, }; Field_Flags_Record_Poly_Params :: Field_Flags{ - Field_Flag.Typeid_Token, + .Typeid_Token, }; Field_Flags_Signature :: Field_Flags{ - Field_Flag.Ellipsis, - Field_Flag.Using, - Field_Flag.No_Alias, - Field_Flag.C_Vararg, - Field_Flag.Auto_Cast, - Field_Flag.Default_Parameters, + .Ellipsis, + .Using, + .No_Alias, + .C_Vararg, + .Auto_Cast, + .Default_Parameters, }; Field_Flags_Signature_Params :: Field_Flags_Signature | {Field_Flag.Typeid_Token}; @@ -463,18 +467,18 @@ Field_Flags_Signature_Results :: Field_Flags_Signature; Proc_Group :: struct { using node: Expr, - tok: token.Token, - open: token.Pos, + tok: tokenizer.Token, + open: tokenizer.Pos, args: []^Expr, - close: token.Pos, + close: tokenizer.Pos, } Attribute :: struct { using node: Node, - tok: token.Kind, - open: token.Pos, + tok: tokenizer.Token_Kind, + open: tokenizer.Pos, elems: []^Expr, - close: token.Pos, + close: tokenizer.Pos, } Field :: struct { @@ -483,56 +487,57 @@ Field :: struct { names: []^Expr, // Could be polymorphic type: ^Expr, default_value: ^Expr, + tag: tokenizer.Token, flags: Field_Flags, comment: ^Comment_Group, } Field_List :: struct { using node: Node, - open: token.Pos, + open: tokenizer.Pos, list: []^Field, - close: token.Pos, + close: tokenizer.Pos, } // Types Typeid_Type :: struct { using node: Expr, - tok: token.Kind, + tok: tokenizer.Token_Kind, specialization: ^Expr, } Helper_Type :: struct { using node: Expr, - tok: token.Kind, + tok: tokenizer.Token_Kind, type: ^Expr, } Distinct_Type :: struct { using node: Expr, - tok: token.Kind, + tok: tokenizer.Token_Kind, type: ^Expr, } Opaque_Type :: struct { using node: Expr, - tok: token.Kind, + tok: tokenizer.Token_Kind, type: ^Expr, } Poly_Type :: struct { using node: Expr, - dollar: token.Pos, + dollar: tokenizer.Pos, type: ^Ident, specialization: ^Expr, } Proc_Type :: struct { using node: Expr, - tok: token.Token, + tok: tokenizer.Token, calling_convention: Proc_Calling_Convention, params: ^Field_List, - arrow: token.Pos, + arrow: tokenizer.Pos, results: ^Field_List, tags: Proc_Tags, generic: bool, @@ -541,77 +546,81 @@ Proc_Type :: struct { Pointer_Type :: struct { using node: Expr, - pointer: token.Pos, + pointer: tokenizer.Pos, elem: ^Expr, } Array_Type :: struct { using node: Expr, - open: token.Pos, + open: tokenizer.Pos, len: ^Expr, // Ellipsis node for [?]T arrray types, nil for slice types - close: token.Pos, + close: tokenizer.Pos, elem: ^Expr, } Dynamic_Array_Type :: struct { using node: Expr, - open: token.Pos, - dynamic_pos: token.Pos, - close: token.Pos, + open: tokenizer.Pos, + dynamic_pos: tokenizer.Pos, + close: tokenizer.Pos, elem: ^Expr, } Struct_Type :: struct { using node: Expr, - tok_pos: token.Pos, - poly_params: ^Field_List, - align: ^Expr, - is_packed: bool, - is_raw_union: bool, - fields: ^Field_List, - name_count: int, + tok_pos: tokenizer.Pos, + poly_params: ^Field_List, + align: ^Expr, + fields: ^Field_List, + name_count: int, + where_token: tokenizer.Token, + where_clauses: []^Expr, + is_packed: bool, + is_raw_union: bool, } Union_Type :: struct { using node: Expr, - tok_pos: token.Pos, + tok_pos: tokenizer.Pos, poly_params: ^Field_List, align: ^Expr, variants: []^Expr, + where_token: tokenizer.Token, + where_clauses: []^Expr, } Enum_Type :: struct { using node: Expr, - tok_pos: token.Pos, + tok_pos: tokenizer.Pos, base_type: ^Expr, - open: token.Pos, + open: tokenizer.Pos, fields: []^Expr, - close: token.Pos, + close: tokenizer.Pos, is_using: bool, } Bit_Field_Type :: struct { using node: Expr, - tok_pos: token.Pos, + tok_pos: tokenizer.Pos, align: ^Expr, - open: token.Pos, + open: tokenizer.Pos, fields: []^Field_Value, // Field_Value with ':' rather than '=' - close: token.Pos, + close: tokenizer.Pos, } Bit_Set_Type :: struct { using node: Expr, - tok_pos: token.Pos, - open: token.Pos, + tok_pos: tokenizer.Pos, + open: tokenizer.Pos, elem: ^Expr, underlying: ^Expr, - close: token.Pos, + close: tokenizer.Pos, } Map_Type :: struct { using node: Expr, - tok_pos: token.Pos, + tok_pos: tokenizer.Pos, key: ^Expr, value: ^Expr, } diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index ded47a931..ddb3068f1 100644 --- a/core/odin/ast/clone.odin +++ b/core/odin/ast/clone.odin @@ -2,9 +2,9 @@ package odin_ast import "core:mem" import "core:fmt" -import "core:odin/token" +import "core:odin/tokenizer" -new :: proc($T: typeid, pos, end: token.Pos) -> ^T { +new :: proc($T: typeid, pos, end: tokenizer.Pos) -> ^T { n := mem.new(T); n.pos = pos; n.end = end; diff --git a/core/odin/ast/file.odin b/core/odin/ast/file.odin index 9d7550f45..dafb10e14 100644 --- a/core/odin/ast/file.odin +++ b/core/odin/ast/file.odin @@ -1,6 +1,6 @@ package odin_ast -import "core:odin/token" +import "core:odin/tokenizer" Package_Kind :: enum { Normal, @@ -26,7 +26,7 @@ File :: struct { src: []byte, pkg_decl: ^Package_Decl, - pkg_token: token.Token, + pkg_token: tokenizer.Token, pkg_name: string, decls: [dynamic]^Stmt, diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 077e04cc9..fb3a87e8b 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1,13 +1,12 @@ package odin_parser import "core:odin/ast" -import "core:odin/token" import "core:odin/tokenizer" import "core:fmt" -Warning_Handler :: #type proc(pos: token.Pos, fmt: string, args: ..any); -Error_Handler :: #type proc(pos: token.Pos, fmt: string, args: ..any); +Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any); +Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any); Parser :: struct { file: ^ast.File, @@ -16,8 +15,8 @@ Parser :: struct { warn: Warning_Handler, err: Error_Handler, - prev_tok: token.Token, - curr_tok: token.Token, + prev_tok: tokenizer.Token, + curr_tok: tokenizer.Token, // >= 0: In Expression // < 0: In Control Clause @@ -50,25 +49,25 @@ Import_Decl_Kind :: enum { -default_warning_handler :: proc(pos: token.Pos, msg: string, args: ..any) { - fmt.printf_err("%s(%d:%d): Warning: ", pos.file, pos.line, pos.column); - fmt.printf_err(msg, ..args); - fmt.printf_err("\n"); +default_warning_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { + fmt.eprintf("%s(%d:%d): Warning: ", pos.file, pos.line, pos.column); + fmt.eprintf(msg, ..args); + fmt.eprintf("\n"); } -default_error_handler :: proc(pos: token.Pos, msg: string, args: ..any) { - fmt.printf_err("%s(%d:%d): ", pos.file, pos.line, pos.column); - fmt.printf_err(msg, ..args); - fmt.printf_err("\n"); +default_error_handler :: proc(pos: tokenizer.Pos, msg: string, args: ..any) { + fmt.eprintf("%s(%d:%d): ", pos.file, pos.line, pos.column); + fmt.eprintf(msg, ..args); + fmt.eprintf("\n"); } -warn :: proc(p: ^Parser, pos: token.Pos, msg: string, args: ..any) { +warn :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, args: ..any) { if p.warn != nil { p.warn(pos, msg, ..args); } p.file.syntax_warning_count += 1; } -error :: proc(p: ^Parser, pos: token.Pos, msg: string, args: ..any) { +error :: proc(p: ^Parser, pos: tokenizer.Pos, msg: string, args: ..any) { if p.err != nil { p.err(pos, msg, ..args); } @@ -77,11 +76,11 @@ error :: proc(p: ^Parser, pos: token.Pos, msg: string, args: ..any) { } -end_pos :: proc(tok: token.Token) -> token.Pos { +end_pos :: proc(tok: tokenizer.Token) -> tokenizer.Pos { pos := tok.pos; pos.offset += len(tok.text); - if tok.kind == token.Comment { + if tok.kind == .Comment { if tok.text[:2] != "/*" { pos.column += len(tok.text); } else { @@ -133,13 +132,13 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { docs := p.lead_comment; - p.file.pkg_token = expect_token(p, token.Package); - if p.file.pkg_token.kind != token.Package { + p.file.pkg_token = expect_token(p, .Package); + if p.file.pkg_token.kind != .Package { return false; } - pkg_name := expect_token_after(p, token.Ident, "package"); - if pkg_name.kind == token.Ident { + pkg_name := expect_token_after(p, .Ident, "package"); + if pkg_name.kind == .Ident { if is_blank_ident(pkg_name) { error(p, pkg_name.pos, "invalid package name '_'"); } @@ -161,7 +160,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { p.file.decls = make([dynamic]^ast.Stmt); - for p.curr_tok.kind != token.EOF { + for p.curr_tok.kind != .EOF { stmt := parse_stmt(p); if stmt != nil { if _, ok := stmt.derived.(ast.Empty_Stmt); !ok { @@ -181,16 +180,16 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { next_token0 :: proc(p: ^Parser) -> bool { p.curr_tok = tokenizer.scan(&p.tok); - if p.curr_tok.kind == token.EOF { + if p.curr_tok.kind == .EOF { // error(p, p.curr_tok.pos, "token is EOF"); return false; } return true; } -consume_comment :: proc(p: ^Parser) -> (tok: token.Token, end_line: int) { +consume_comment :: proc(p: ^Parser) -> (tok: tokenizer.Token, end_line: int) { tok = p.curr_tok; - assert(tok.kind == token.Comment); + assert(tok.kind == .Comment); end_line = tok.pos.line; if tok.text[1] == '*' { @@ -210,11 +209,11 @@ consume_comment :: proc(p: ^Parser) -> (tok: token.Token, end_line: int) { } consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Group, end_line: int) { - list: [dynamic]token.Token; + list: [dynamic]tokenizer.Token; end_line = p.curr_tok.pos.line; - for p.curr_tok.kind == token.Comment && + for p.curr_tok.kind == .Comment && p.curr_tok.pos.line <= end_line+n { - comment: token.Token; + comment: tokenizer.Token; comment, end_line = consume_comment(p); append(&list, comment); } @@ -228,31 +227,31 @@ consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Gro return; } -consume_comment_groups :: proc(p: ^Parser, prev: token.Token) { - if p.curr_tok.kind == token.Comment { +consume_comment_groups :: proc(p: ^Parser, prev: tokenizer.Token) { + if p.curr_tok.kind == .Comment { comment: ^ast.Comment_Group; end_line := 0; if p.curr_tok.pos.line == prev.pos.line { comment, end_line = consume_comment_group(p, 0); - if p.curr_tok.pos.line != end_line || p.curr_tok.kind == token.EOF { + if p.curr_tok.pos.line != end_line || p.curr_tok.kind == .EOF { p.line_comment = comment; } } end_line = -1; - for p.curr_tok.kind == token.Comment { + for p.curr_tok.kind == .Comment { comment, end_line = consume_comment_group(p, 1); } if end_line+1 >= p.curr_tok.pos.line || end_line < 0 { p.lead_comment = comment; } - assert(p.curr_tok.kind != token.Comment); + assert(p.curr_tok.kind != .Comment); } } -advance_token :: proc(p: ^Parser) -> token.Token { +advance_token :: proc(p: ^Parser) -> tokenizer.Token { p.lead_comment = nil; p.line_comment = nil; p.prev_tok = p.curr_tok; @@ -264,39 +263,39 @@ advance_token :: proc(p: ^Parser) -> token.Token { return prev; } -expect_token :: proc(p: ^Parser, kind: token.Kind) -> token.Token { +expect_token :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> tokenizer.Token { prev := p.curr_tok; if prev.kind != kind { - e := token.to_string(kind); - g := token.to_string(prev.kind); + e := tokenizer.to_string(kind); + g := tokenizer.to_string(prev.kind); error(p, prev.pos, "expected '%s', got '%s'", e, g); } advance_token(p); return prev; } -expect_token_after :: proc(p: ^Parser, kind: token.Kind, msg: string) -> token.Token { +expect_token_after :: proc(p: ^Parser, kind: tokenizer.Token_Kind, msg: string) -> tokenizer.Token { prev := p.curr_tok; if prev.kind != kind { - e := token.to_string(kind); - g := token.to_string(prev.kind); + e := tokenizer.to_string(kind); + g := tokenizer.to_string(prev.kind); error(p, prev.pos, "expected '%s' after %s, got '%s'", e, msg, g); } advance_token(p); return prev; } -expect_operator :: proc(p: ^Parser) -> token.Token { +expect_operator :: proc(p: ^Parser) -> tokenizer.Token { prev := p.curr_tok; - if !token.is_operator(prev.kind) { - g := token.to_string(prev.kind); + if !tokenizer.is_operator(prev.kind) { + g := tokenizer.to_string(prev.kind); error(p, prev.pos, "expected an operator, got '%s'", g); } advance_token(p); return prev; } -allow_token :: proc(p: ^Parser, kind: token.Kind) -> bool { +allow_token :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> bool { if p.curr_tok.kind == kind { advance_token(p); return true; @@ -313,8 +312,8 @@ is_blank_ident :: proc{ is_blank_ident_string :: inline proc(str: string) -> bool { return str == "_"; } -is_blank_ident_token :: inline proc(tok: token.Token) -> bool { - if tok.kind == token.Ident { +is_blank_ident_token :: inline proc(tok: tokenizer.Token) -> bool { + if tok.kind == .Ident { return is_blank_ident_string(tok.text); } return false; @@ -347,7 +346,8 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { case ast.Pointer_Type: return is_semicolon_optional_for_node(p, n.elem); case ast.Struct_Type, ast.Union_Type, ast.Enum_Type, ast.Bit_Field_Type: - return true; + // Require semicolon within a procedure body + return p.curr_proc == nil; case ast.Proc_Lit: return true; @@ -371,16 +371,16 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool { expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool { - if allow_token(p, token.Semicolon) { + if allow_token(p, .Semicolon) { return true; } prev := p.prev_tok; - if prev.kind == token.Semicolon { + if prev.kind == .Semicolon { return true; } - if p.curr_tok.kind == token.EOF { + if p.curr_tok.kind == .EOF { return true; } @@ -391,20 +391,20 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool { } } else { switch p.curr_tok.kind { - case token.Close_Brace: - case token.Close_Paren: - case token.Else: + case .Close_Brace: + case .Close_Paren: + case .Else: return true; } } } - error(p, prev.pos, "expected ';', got %s", token.to_string(prev.kind)); + error(p, prev.pos, "expected ';', got %s", tokenizer.to_string(prev.kind)); return false; } -new_blank_ident :: proc(p: ^Parser, pos: token.Pos) -> ^ast.Ident { - tok: token.Token; +new_blank_ident :: proc(p: ^Parser, pos: tokenizer.Pos) -> ^ast.Ident { + tok: tokenizer.Token; tok.pos = pos; i := ast.new(ast.Ident, pos, end_pos(tok)); i.name = "_"; @@ -415,11 +415,11 @@ parse_ident :: proc(p: ^Parser) -> ^ast.Ident { tok := p.curr_tok; pos := tok.pos; name := "_"; - if tok.kind == token.Ident { + if tok.kind == .Ident { name = tok.text; advance_token(p); } else { - expect_token(p, token.Ident); + expect_token(p, .Ident); } i := ast.new(ast.Ident, pos, end_pos(tok)); i.name = name; @@ -428,9 +428,9 @@ parse_ident :: proc(p: ^Parser) -> ^ast.Ident { parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt { list: [dynamic]^ast.Stmt; - for p.curr_tok.kind != token.Case && - p.curr_tok.kind != token.Close_Brace && - p.curr_tok.kind != token.EOF { + for p.curr_tok.kind != .Case && + p.curr_tok.kind != .Close_Brace && + p.curr_tok.kind != .EOF { stmt := parse_stmt(p); if stmt != nil { if _, ok := stmt.derived.(ast.Empty_Stmt); !ok { @@ -454,7 +454,7 @@ parse_block_stmt :: proc(p: ^Parser, is_when: bool) -> ^ast.Stmt { } parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt { - tok := expect_token(p, token.When); + tok := expect_token(p, .When); cond: ^ast.Expr; body: ^ast.Stmt; @@ -468,20 +468,20 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt { if cond == nil { error(p, p.curr_tok.pos, "expected a condition for when statement"); } - if allow_token(p, token.Do) { + if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_block_stmt(p, true); } - if allow_token(p, token.Else) { + if allow_token(p, .Else) { switch p.curr_tok.kind { - case token.When: + case .When: else_stmt = parse_when_stmt(p); - case token.Open_Brace: + case .Open_Brace: else_stmt = parse_block_stmt(p, true); - case token.Do: - expect_token(p, token.Do); + case .Do: + expect_token(p, .Do); else_stmt = convert_stmt_to_body(p, parse_stmt(p)); case: error(p, p.curr_tok.pos, "expected when statement block statement"); @@ -513,7 +513,7 @@ convert_stmt_to_expr :: proc(p: ^Parser, stmt: ^ast.Stmt, kind: string) -> ^ast. } parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { - tok := expect_token(p, token.If); + tok := expect_token(p, .If); init: ^ast.Stmt; cond: ^ast.Expr; @@ -524,11 +524,11 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { p.expr_level = -1; prev_allow_in_expr := p.allow_in_expr; p.allow_in_expr = true; - if allow_token(p, token.Semicolon) { + if allow_token(p, .Semicolon) { cond = parse_expr(p, false); } else { init = parse_simple_stmt(p, nil); - if allow_token(p, token.Semicolon) { + if allow_token(p, .Semicolon) { cond = parse_expr(p, false); } else { cond = convert_stmt_to_expr(p, init, "boolean expression"); @@ -543,20 +543,20 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { error(p, p.curr_tok.pos, "expected a condition for if statement"); } - if allow_token(p, token.Do) { + if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_block_stmt(p, false); } - if allow_token(p, token.Else) { + if allow_token(p, .Else) { switch p.curr_tok.kind { - case token.If: + case .If: else_stmt = parse_if_stmt(p); - case token.Open_Brace: + case .Open_Brace: else_stmt = parse_block_stmt(p, false); - case token.Do: - expect_token(p, token.Do); + case .Do: + expect_token(p, .Do); else_stmt = convert_stmt_to_body(p, parse_stmt(p)); case: error(p, p.curr_tok.pos, "expected if statement block statement"); @@ -582,7 +582,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope"); } - tok := expect_token(p, token.For); + tok := expect_token(p, .For); init: ^ast.Stmt; cond: ^ast.Stmt; @@ -590,13 +590,13 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { body: ^ast.Stmt; is_range := false; - if p.curr_tok.kind != token.Open_Brace && p.curr_tok.kind != token.Do { + if p.curr_tok.kind != .Open_Brace && p.curr_tok.kind != .Do { prev_level := p.expr_level; defer p.expr_level = prev_level; p.expr_level = -1; - if p.curr_tok.kind == token.In { - in_tok := expect_token(p, token.In); + if p.curr_tok.kind == .In { + in_tok := expect_token(p, .In); rhs: ^ast.Expr; prev_allow_range := p.allow_range; @@ -604,7 +604,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { rhs = parse_expr(p, false); p.allow_range = prev_allow_range; - if allow_token(p, token.Do) { + if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_body(p); @@ -618,27 +618,27 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { return range_stmt; } - if p.curr_tok.kind != token.Semicolon { + if p.curr_tok.kind != .Semicolon { cond = parse_simple_stmt(p, {Stmt_Allow_Flag.In}); - if as, ok := cond.derived.(ast.Assign_Stmt); ok && as.op.kind == token.In { + if as, ok := cond.derived.(ast.Assign_Stmt); ok && as.op.kind == .In { is_range = true; } } - if !is_range && allow_token(p, token.Semicolon) { + if !is_range && allow_token(p, .Semicolon) { init = cond; cond = nil; - if p.curr_tok.kind != token.Semicolon { + if p.curr_tok.kind != .Semicolon { cond = parse_simple_stmt(p, nil); } expect_semicolon(p, cond); - if p.curr_tok.kind != token.Open_Brace && p.curr_tok.kind != token.Do { + if p.curr_tok.kind != .Open_Brace && p.curr_tok.kind != .Do { post = parse_simple_stmt(p, nil); } } } - if allow_token(p, token.Do) { + if allow_token(p, .Do) { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_body(p); @@ -686,11 +686,11 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } parse_case_clause :: proc(p: ^Parser, is_type_switch: bool) -> ^ast.Case_Clause { - tok := expect_token(p, token.Case); + tok := expect_token(p, .Case); list: []^ast.Expr; - if p.curr_tok.kind != token.Colon { + if p.curr_tok.kind != .Colon { prev_allow_range, prev_allow_in_expr := p.allow_range, p.allow_in_expr; defer p.allow_range, p.allow_in_expr = prev_allow_range, prev_allow_in_expr; p.allow_range, p.allow_in_expr = !is_type_switch, !is_type_switch; @@ -698,7 +698,7 @@ parse_case_clause :: proc(p: ^Parser, is_type_switch: bool) -> ^ast.Case_Clause list = parse_rhs_expr_list(p); } - terminator := expect_token(p, token.Colon); + terminator := expect_token(p, .Colon); stmts := parse_stmt_list(p); @@ -710,20 +710,20 @@ parse_case_clause :: proc(p: ^Parser, is_type_switch: bool) -> ^ast.Case_Clause } parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { - tok := expect_token(p, token.Switch); + tok := expect_token(p, .Switch); init: ^ast.Stmt; tag: ^ast.Stmt; is_type_switch := false; clauses: [dynamic]^ast.Stmt; - if p.curr_tok.kind != token.Open_Brace { + if p.curr_tok.kind != .Open_Brace { prev_level := p.expr_level; defer p.expr_level = prev_level; p.expr_level = -1; - if p.curr_tok.kind == token.In { - in_tok := expect_token(p, token.In); + if p.curr_tok.kind == .In { + in_tok := expect_token(p, .In); is_type_switch = true; lhs := make([]^ast.Expr, 1); @@ -738,12 +738,12 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { tag = as; } else { tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In}); - if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == token.In { + if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In { is_type_switch = true; - } else if allow_token(p, token.Semicolon) { + } else if allow_token(p, .Semicolon) { init = tag; tag = nil; - if p.curr_tok.kind != token.Open_Brace { + if p.curr_tok.kind != .Open_Brace { tag = parse_simple_stmt(p, nil); } } @@ -751,14 +751,14 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } - open := expect_token(p, token.Open_Brace); + open := expect_token(p, .Open_Brace); - for p.curr_tok.kind == token.Case { + for p.curr_tok.kind == .Case { clause := parse_case_clause(p, is_type_switch); append(&clauses, clause); } - close := expect_token(p, token.Close_Brace); + close := expect_token(p, .Close_Brace); body := ast.new(ast.Block_Stmt, open.pos, end_pos(close)); body.stmts = clauses[:]; @@ -778,23 +778,23 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } } -parse_attribute :: proc(p: ^Parser, tok: token.Token, open_kind, close_kind: token.Kind, docs: ^ast.Comment_Group) -> ^ast.Stmt { +parse_attribute :: proc(p: ^Parser, tok: tokenizer.Token, open_kind, close_kind: tokenizer.Token_Kind, docs: ^ast.Comment_Group) -> ^ast.Stmt { elems: [dynamic]^ast.Expr; - open, close: token.Token; + open, close: tokenizer.Token; - if p.curr_tok.kind == token.Ident { + if p.curr_tok.kind == .Ident { elem := parse_ident(p); append(&elems, elem); } else { open = expect_token(p, open_kind); p.expr_level += 1; for p.curr_tok.kind != close_kind && - p.curr_tok.kind != token.EOF { + p.curr_tok.kind != .EOF { elem: ^ast.Expr; elem = parse_ident(p); - if p.curr_tok.kind == token.Eq { - eq := expect_token(p, token.Eq); + if p.curr_tok.kind == .Eq { + eq := expect_token(p, .Eq); value := parse_value(p); fv := ast.new(ast.Field_Value, elem.pos, value.end); fv.field = elem; @@ -805,7 +805,7 @@ parse_attribute :: proc(p: ^Parser, tok: token.Token, open_kind, close_kind: tok } append(&elems, elem); - if !allow_token(p, token.Comma) { + if !allow_token(p, .Comma) { break; } } @@ -852,12 +852,12 @@ parse_foreign_block_decl :: proc(p: ^Parser) -> ^ast.Stmt { } -parse_foreign_block :: proc(p: ^Parser, tok: token.Token) -> ^ast.Foreign_Block_Decl { +parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Block_Decl { docs := p.lead_comment; foreign_library: ^ast.Expr; switch p.curr_tok.kind { - case token.Open_Brace: + case .Open_Brace: i := ast.new(ast.Ident, tok.pos, end_pos(tok)); i.name = "_"; foreign_library = i; @@ -871,14 +871,14 @@ parse_foreign_block :: proc(p: ^Parser, tok: token.Token) -> ^ast.Foreign_Block_ defer p.in_foreign_block = prev_in_foreign_block; p.in_foreign_block = true; - open := expect_token(p, token.Open_Brace); - for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF { + open := expect_token(p, .Open_Brace); + for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { decl := parse_foreign_block_decl(p); if decl != nil { append(&decls, decl); } } - close := expect_token(p, token.Close_Brace); + close := expect_token(p, .Close_Brace); body := ast.new(ast.Block_Stmt, open.pos, end_pos(close)); body.open = open.pos; @@ -896,16 +896,16 @@ parse_foreign_block :: proc(p: ^Parser, tok: token.Token) -> ^ast.Foreign_Block_ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl { docs := p.lead_comment; - tok := expect_token(p, token.Foreign); + tok := expect_token(p, .Foreign); switch p.curr_tok.kind { - case token.Ident, token.Open_Brace: + case .Ident, .Open_Brace: return parse_foreign_block(p, tok); - case token.Import: - import_tok := expect_token(p, token.Import); + case .Import: + import_tok := expect_token(p, .Import); name: ^ast.Ident; - if p.curr_tok.kind == token.Ident { + if p.curr_tok.kind == .Ident { name = parse_ident(p); } @@ -914,19 +914,19 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl { } fullpaths: [dynamic]string; - if allow_token(p, token.Open_Brace) { - for p.curr_tok.kind != token.Close_Brace && - p.curr_tok.kind != token.EOF { - path := expect_token(p, token.String); + if allow_token(p, .Open_Brace) { + for p.curr_tok.kind != .Close_Brace && + p.curr_tok.kind != .EOF { + path := expect_token(p, .String); append(&fullpaths, path.text); - if !allow_token(p, token.Comma) { + if !allow_token(p, .Comma) { break; } } - expect_token(p, token.Close_Brace); + expect_token(p, .Close_Brace); } else { - path := expect_token(p, token.String); + path := expect_token(p, .String); reserve(&fullpaths, 1); append(&fullpaths, path.text); } @@ -954,28 +954,29 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl { parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { switch p.curr_tok.kind { // Operands - case token.Context, // Also allows for 'context = ' - token.Inline, token.No_Inline, - token.Ident, - token.Integer, token.Float, token.Imag, - token.Rune, token.String, - token.Open_Paren, - token.Pointer, + case .Context, // Also allows for 'context = ' + .Proc, + .Inline, .No_Inline, + .Ident, + .Integer, .Float, .Imag, + .Rune, .String, + .Open_Paren, + .Pointer, // Unary Expressions - token.Add, token.Sub, token.Xor, token.Not, token.And: + .Add, .Sub, .Xor, .Not, .And: s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label}); expect_semicolon(p, s); return s; - case token.Import: return parse_import_decl(p); - case token.Foreign: return parse_foreign_decl(p); - case token.If: return parse_if_stmt(p); - case token.When: return parse_when_stmt(p); - case token.For: return parse_for_stmt(p); - case token.Switch: return parse_switch_stmt(p); + case .Import: return parse_import_decl(p); + case .Foreign: return parse_foreign_decl(p); + case .If: return parse_if_stmt(p); + case .When: return parse_when_stmt(p); + case .For: return parse_for_stmt(p); + case .Switch: return parse_switch_stmt(p); - case token.Defer: + case .Defer: tok := advance_token(p); stmt := parse_stmt(p); switch s in stmt.derived { @@ -991,7 +992,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { ds.stmt = stmt; return ds; - case token.Return: + case .Return: tok := advance_token(p); if p.expr_level > 0 { @@ -999,11 +1000,11 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { } results: [dynamic]^ast.Expr; - for p.curr_tok.kind != token.Semicolon { + for p.curr_tok.kind != .Semicolon { result := parse_expr(p, false); append(&results, result); - if p.curr_tok.kind != token.Comma || - p.curr_tok.kind == token.EOF { + if p.curr_tok.kind != .Comma || + p.curr_tok.kind == .EOF { break; } advance_token(p); @@ -1018,10 +1019,10 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { rs.results = results[:]; return rs; - case token.Break, token.Continue, token.Fallthrough: + case .Break, .Continue, .Fallthrough: tok := advance_token(p); label: ^ast.Expr; - if tok.kind != token.Fallthrough && p.curr_tok.kind == token.Ident { + if tok.kind != .Fallthrough && p.curr_tok.kind == .Ident { label = parse_ident(p); } end := label != nil ? label.end : end_pos(tok); @@ -1029,11 +1030,11 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { expect_semicolon(p, s); return s; - case token.Using: + case .Using: docs := p.lead_comment; - tok := expect_token(p, token.Using); + tok := expect_token(p, .Using); - if p.curr_tok.kind == token.Import { + if p.curr_tok.kind == .Import { return parse_import_decl(p, Import_Decl_Kind.Using); } @@ -1044,14 +1045,14 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok)); } - if p.curr_tok.kind != token.Colon { + if p.curr_tok.kind != .Colon { end := list[len(list)-1]; expect_semicolon(p, end); us := ast.new(ast.Using_Stmt, tok.pos, end.end); us.list = list; return us; } - expect_token_after(p, token.Colon, "identifier list"); + expect_token_after(p, .Colon, "identifier list"); decl := parse_value_decl(p, list, docs); if decl != nil do switch d in &decl.derived { case ast.Value_Decl: @@ -1062,14 +1063,14 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { error(p, tok.pos, "illegal use of 'using' statement"); return ast.new(ast.Bad_Stmt, tok.pos, end_pos(p.prev_tok)); - case token.At: + case .At: docs := p.lead_comment; tok := advance_token(p); - return parse_attribute(p, tok, token.Open_Paren, token.Close_Paren, docs); + return parse_attribute(p, tok, .Open_Paren, .Close_Paren, docs); - case token.Hash: - tok := expect_token(p, token.Hash); - tag := expect_token(p, token.Ident); + case .Hash: + tok := expect_token(p, .Hash); + tag := expect_token(p, .Ident); name := tag.text; switch name { @@ -1090,7 +1091,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { case: error(p, stmt.pos, "#complete can only be applied to a switch statement"); } return stmt; - case "assert": + case "assert", "panic": bd := ast.new(ast.Basic_Directive, tok.pos, end_pos(tag)); bd.tok = tok; bd.name = name; @@ -1109,51 +1110,51 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { te.stmt = stmt; return te; } - case token.Open_Brace: + case .Open_Brace: return parse_block_stmt(p, false); - case token.Semicolon: + case .Semicolon: tok := advance_token(p); s := ast.new(ast.Empty_Stmt, tok.pos, end_pos(tok)); return s; } tok := advance_token(p); - error(p, tok.pos, "expected a statement, got %s", token.to_string(tok.kind)); + error(p, tok.pos, "expected a statement, got %s", tokenizer.to_string(tok.kind)); s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok)); return s; } -token_precedence :: proc(p: ^Parser, kind: token.Kind) -> int { +token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int { switch kind { - case token.Question: + case .Question: return 1; - case token.Ellipsis, token.Range_Half: + case .Ellipsis, .Range_Half: if !p.allow_range { return 0; } return 2; - case token.Cmp_Or: + case .Cmp_Or: return 3; - case token.Cmp_And: + case .Cmp_And: return 4; - case token.Cmp_Eq, token.Not_Eq, - token.Lt, token.Gt, - token.Lt_Eq, token.Gt_Eq: + case .Cmp_Eq, .Not_Eq, + .Lt, .Gt, + .Lt_Eq, .Gt_Eq: return 5; - case token.In, token.Notin: - if p.expr_level >= 0 || p.allow_in_expr { - return 6; + case .In, .Notin: + if p.expr_level < 0 && !p.allow_in_expr { + return 0; } - return 0; - case token.Add, token.Sub, token.Or, token.Xor: + fallthrough; + case .Add, .Sub, .Or, .Xor: + return 6; + case .Mul, .Quo, + .Mod, .Mod_Mod, + .And, .And_Not, + .Shl, .Shr: return 7; - case token.Mul, token.Quo, - token.Mod, token.Mod_Mod, - token.And, token.And_Not, - token.Shl, token.Shr: - return 8; } return 0; } @@ -1186,9 +1187,9 @@ parse_body :: proc(p: ^Parser) -> ^ast.Block_Stmt { defer p.expr_level = prev_expr_level; p.expr_level = 0; - open := expect_token(p, token.Open_Brace); + open := expect_token(p, .Open_Brace); stmts := parse_stmt_list(p); - close := expect_token(p, token.Close_Brace); + close := expect_token(p, .Close_Brace); bs := ast.new(ast.Block_Stmt, open.pos, end_pos(close)); bs.open = open.pos; @@ -1215,7 +1216,7 @@ convert_stmt_to_body :: proc(p: ^Parser, stmt: ^ast.Stmt) -> ^ast.Stmt { } new_ast_field :: proc(names: []^ast.Expr, type: ^ast.Expr, default_value: ^ast.Expr) -> ^ast.Field { - pos, end: token.Pos; + pos, end: tokenizer.Pos; if len(names) > 0 { pos = names[0].pos; @@ -1305,22 +1306,22 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags, is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix { using Field_Prefix; switch p.curr_tok.kind { - case token.EOF: + case .EOF: return Invalid; - case token.Using: + case .Using: advance_token(p); return Using; - case token.In: + case .In: advance_token(p); return In; - case token.Auto_Cast: + case .Auto_Cast: advance_token(p); return Auto_Cast; - case token.Hash: + case .Hash: advance_token(p); defer advance_token(p); switch p.curr_tok.kind { - case token.Ident: + case .Ident: switch p.curr_tok.text { case "no_alias": return No_Alias; @@ -1387,19 +1388,18 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se for flag in ast.Field_Flag { if flag notin allowed_flags && flag in flags { - using ast.Field_Flag; #complete switch flag { - case Using: + case .Using: error(p, p.curr_tok.pos, "'using' is not allowed within this field list"); - case No_Alias: + case .No_Alias: error(p, p.curr_tok.pos, "'#no_alias' is not allowed within this field list"); - case C_Vararg: + case .C_Vararg: error(p, p.curr_tok.pos, "'#c_vararg' is not allowed within this field list"); - case Auto_Cast: + case .Auto_Cast: error(p, p.curr_tok.pos, "'auto_cast' is not allowed within this field list"); - case In: + case .In: error(p, p.curr_tok.pos, "'in' is not allowed within this field list"); - case Ellipsis, Results, Default_Parameters, Typeid_Token: + case .Tags, .Ellipsis, .Results, .Default_Parameters, .Typeid_Token: panic("Impossible prefixes"); } flags &~= {flag}; @@ -1414,7 +1414,7 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se } parse_var_type :: proc(p: ^Parser, flags: ast.Field_Flags) -> ^ast.Expr { - if ast.Field_Flag.Ellipsis in flags && p.curr_tok.kind == token.Ellipsis { + if ast.Field_Flag.Ellipsis in flags && p.curr_tok.kind == .Ellipsis { tok := advance_token(p); type := parse_type_or_ident(p); if type == nil { @@ -1426,11 +1426,11 @@ parse_var_type :: proc(p: ^Parser, flags: ast.Field_Flags) -> ^ast.Expr { return e; } type: ^ast.Expr; - if ast.Field_Flag.Typeid_Token in flags && p.curr_tok.kind == token.Typeid { - tok := expect_token(p, token.Typeid); + if ast.Field_Flag.Typeid_Token in flags && p.curr_tok.kind == .Typeid { + tok := expect_token(p, .Typeid); specialization: ^ast.Expr; end := tok.pos; - if allow_token(p, token.Quo) { + if allow_token(p, .Quo) { specialization = parse_type(p); end = specialization.end; } @@ -1482,8 +1482,8 @@ parse_ident_list :: proc(p: ^Parser, allow_poly_names: bool) -> []^ast.Expr { list: [dynamic]^ast.Expr; for { - if allow_poly_names && p.curr_tok.kind == token.Dollar { - tok := expect_token(p, token.Dollar); + if allow_poly_names && p.curr_tok.kind == .Dollar { + tok := expect_token(p, .Dollar); ident := parse_ident(p); if is_blank_ident(ident) { error(p, ident.pos, "invalid polymorphic type definition with a blank identifier"); @@ -1495,8 +1495,8 @@ parse_ident_list :: proc(p: ^Parser, allow_poly_names: bool) -> []^ast.Expr { ident := parse_ident(p); append(&list, ident); } - if p.curr_tok.kind != token.Comma || - p.curr_tok.kind == token.EOF { + if p.curr_tok.kind != .Comma || + p.curr_tok.kind == .EOF { break; } advance_token(p); @@ -1507,7 +1507,7 @@ parse_ident_list :: proc(p: ^Parser, allow_poly_names: bool) -> []^ast.Expr { -parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Field_Flags) -> (field_list: ^ast.Field_List, total_name_count: int) { +parse_field_list :: proc(p: ^Parser, follow: tokenizer.Token_Kind, allowed_flags: ast.Field_Flags) -> (field_list: ^ast.Field_List, total_name_count: int) { handle_field :: proc(p: ^Parser, seen_ellipsis: ^bool, fields: ^[dynamic]^ast.Field, docs: ^ast.Comment_Group, @@ -1517,10 +1517,10 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel expect_field_separator :: proc(p: ^Parser, param: ^ast.Expr) -> bool { tok := p.curr_tok; - if allow_token(p, token.Comma) { + if allow_token(p, .Comma) { return true; } - if allow_token(p, token.Semicolon) { + if allow_token(p, .Semicolon) { error(p, tok.pos, "expected a comma, got a semicolon"); return true; } @@ -1539,9 +1539,10 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel type: ^ast.Expr; default_value: ^ast.Expr; + tag: tokenizer.Token; - expect_token_after(p, token.Colon, "field list"); - if p.curr_tok.kind != token.Eq { + expect_token_after(p, .Colon, "field list"); + if p.curr_tok.kind != .Eq { type = parse_var_type(p, allowed_flags); tt := ast.unparen_expr(type); if is_signature && !any_polymorphic_names { @@ -1551,7 +1552,7 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel } } - if allow_token(p, token.Eq) { + if allow_token(p, .Eq) { default_value = parse_expr(p, false); if ast.Field_Flag.Default_Parameters notin allowed_flags { error(p, p.curr_tok.pos, "default parameters are only allowed for procedures"); @@ -1578,9 +1579,19 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel error(p, p.curr_tok.pos, "extra parameter after ellipsis without a default value"); } + if type != nil && default_value == nil { + if p.curr_tok.kind == .String { + tag = expect_token(p, .String); + if .Tags notin allowed_flags { + error(p, tag.pos, "Field tags are only allowed within structures"); + } + } + } + ok := expect_field_separator(p, type); field := new_ast_field(names, type, default_value); + field.tag = tag; field.docs = docs; field.flags = flags; field.comment = p.line_comment; @@ -1605,8 +1616,8 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel allow_poly_names := allow_typeid_token; for p.curr_tok.kind != follow && - p.curr_tok.kind != token.Colon && - p.curr_tok.kind != token.EOF { + p.curr_tok.kind != .Colon && + p.curr_tok.kind != .EOF { prefix_flags := parse_field_prefixes(p); param := parse_var_type(p, allowed_flags & {ast.Field_Flag.Typeid_Token, ast.Field_Flag.Ellipsis}); if _, ok := param.derived.(ast.Ellipsis); ok { @@ -1620,15 +1631,15 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel eaf := Expr_And_Flags{param, prefix_flags}; append(&list, eaf); - if !allow_token(p, token.Comma) { + if !allow_token(p, .Comma) { break; } } - if p.curr_tok.kind != token.Colon { + if p.curr_tok.kind != .Colon { for eaf in list { type := eaf.expr; - tok: token.Token; + tok: tokenizer.Token; tok.pos = type.pos; if ast.Field_Flag.Results notin allowed_flags { tok.text = "_"; @@ -1659,7 +1670,7 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel total_name_count += len(names); handle_field(p, &seen_ellipsis, &fields, docs, names, allowed_flags, set_flags); - for p.curr_tok.kind != follow && p.curr_tok.kind != token.EOF { + for p.curr_tok.kind != follow && p.curr_tok.kind != .EOF { docs = p.lead_comment; set_flags = parse_field_prefixes(p); names = parse_ident_list(p, allow_poly_names); @@ -1679,11 +1690,11 @@ parse_field_list :: proc(p: ^Parser, follow: token.Kind, allowed_flags: ast.Fiel parse_results :: proc(p: ^Parser) -> (list: ^ast.Field_List, diverging: bool) { - if !allow_token(p, token.Arrow_Right) { + if !allow_token(p, .Arrow_Right) { return; } - if allow_token(p, token.Not) { + if allow_token(p, .Not) { diverging = true; return; } @@ -1691,7 +1702,7 @@ parse_results :: proc(p: ^Parser) -> (list: ^ast.Field_List, diverging: bool) { prev_level := p.expr_level; defer p.expr_level = prev_level; - if p.curr_tok.kind != token.Open_Paren { + if p.curr_tok.kind != .Open_Paren { type := parse_type(p); field := new_ast_field(nil, type, nil); @@ -1701,9 +1712,9 @@ parse_results :: proc(p: ^Parser) -> (list: ^ast.Field_List, diverging: bool) { return; } - expect_token(p, token.Open_Paren); - list, _ = parse_field_list(p, token.Close_Paren, ast.Field_Flags_Signature_Results); - expect_token_after(p, token.Close_Paren, "parameter list"); + expect_token(p, .Open_Paren); + list, _ = parse_field_list(p, .Close_Paren, ast.Field_Flags_Signature_Results); + expect_token_after(p, .Close_Paren, "parameter list"); return; } @@ -1729,13 +1740,11 @@ string_to_calling_convention :: proc(s: string) -> ast.Proc_Calling_Convention { } parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) { - for p.curr_tok.kind == token.Hash { - tok := expect_token(p, token.Hash); - ident := expect_token(p, token.Ident); + for p.curr_tok.kind == .Hash { + _ = expect_token(p, .Hash); + ident := expect_token(p, .Ident); switch ident.text { - case "require_results": - tags |= {.Require_Results}; case "bounds_check": tags |= {.Bounds_Check}; case "no_bounds_check": @@ -1751,10 +1760,10 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) { return; } -parse_proc_type :: proc(p: ^Parser, tok: token.Token) -> ^ast.Proc_Type { +parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type { cc := ast.Proc_Calling_Convention.Invalid; - if p.curr_tok.kind == token.String { - str := expect_token(p, token.String); + if p.curr_tok.kind == .String { + str := expect_token(p, .String); cc = string_to_calling_convention(str.text); if cc == ast.Proc_Calling_Convention.Invalid { error(p, str.pos, "unknown calling convention '%s'", str.text); @@ -1769,9 +1778,9 @@ parse_proc_type :: proc(p: ^Parser, tok: token.Token) -> ^ast.Proc_Type { } } - expect_token(p, token.Open_Paren); - params, _ := parse_field_list(p, token.Close_Paren, ast.Field_Flags_Signature_Params); - expect_token(p, token.Close_Paren); + expect_token(p, .Open_Paren); + params, _ := parse_field_list(p, .Close_Paren, ast.Field_Flags_Signature_Params); + expect_token(p, .Close_Paren); results, diverging := parse_results(p); is_generic := false; @@ -1802,7 +1811,7 @@ parse_proc_type :: proc(p: ^Parser, tok: token.Token) -> ^ast.Proc_Type { return pt; } -check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok: token.Token) { +check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok: tokenizer.Token) { if poly_params == nil { return; } @@ -1821,46 +1830,46 @@ check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { switch p.curr_tok.kind { - case token.Ident: + case .Ident: return parse_ident(p); - case token.Undef: - tok := expect_token(p, token.Undef); + case .Undef: + tok := expect_token(p, .Undef); undef := ast.new(ast.Undef, tok.pos, end_pos(tok)); undef.tok = tok.kind; return undef; - case token.Context: - tok := expect_token(p, token.Context); + case .Context: + tok := expect_token(p, .Context); ctx := ast.new(ast.Implicit, tok.pos, end_pos(tok)); ctx.tok = tok; return ctx; - case token.Integer, token.Float, token.Imag, - token.Rune, token.String: + case .Integer, .Float, .Imag, + .Rune, .String: tok := advance_token(p); bl := ast.new(ast.Basic_Lit, tok.pos, end_pos(tok)); bl.tok = tok; return bl; - case token.Size_Of, token.Align_Of, token.Offset_Of: + case .Size_Of, .Align_Of, .Offset_Of: tok := advance_token(p); expr := ast.new(ast.Implicit, tok.pos, end_pos(tok)); expr.tok = tok; return parse_call_expr(p, expr); - case token.Open_Brace: + case .Open_Brace: if !lhs { return parse_literal_value(p, nil); } - case token.Open_Paren: - open := expect_token(p, token.Open_Paren); + case .Open_Paren: + open := expect_token(p, .Open_Paren); p.expr_level += 1; expr := parse_expr(p, false); p.expr_level -= 1; - close := expect_token(p, token.Close_Paren); + close := expect_token(p, .Close_Paren); pe := ast.new(ast.Paren_Expr, open.pos, end_pos(close)); pe.open = open.pos; @@ -1868,7 +1877,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { pe.close = close.pos; return pe; - case token.Distinct: + case .Distinct: tok := advance_token(p); type := parse_type(p); dt := ast.new(ast.Distinct_Type, tok.pos, type.end); @@ -1876,16 +1885,16 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { dt.type = type; return dt; - case token.Opaque: + case .Opaque: tok := advance_token(p); type := parse_type(p); ot := ast.new(ast.Opaque_Type, tok.pos, type.end); ot.tok = tok.kind; ot.type = type; return ot; - case token.Hash: - tok := expect_token(p, token.Hash); - name := expect_token(p, token.Ident); + case .Hash: + tok := expect_token(p, .Hash); + name := expect_token(p, .Ident); switch name.text { case "type": type := parse_type(p); @@ -1913,15 +1922,15 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { return te; } - case token.Inline, token.No_Inline: + case .Inline, .No_Inline: tok := advance_token(p); expr := parse_unary_expr(p, lhs); pi := ast.Proc_Inlining.None; switch tok.kind { - case token.Inline: + case .Inline: pi = ast.Proc_Inlining.Inline; - case token.No_Inline: + case .No_Inline: pi = ast.Proc_Inlining.No_Inline; } @@ -1942,25 +1951,25 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } return expr; - case token.Proc: - tok := expect_token(p, token.Proc); + case .Proc: + tok := expect_token(p, .Proc); - if p.curr_tok.kind == token.Open_Brace { - open := expect_token(p, token.Open_Brace); + if p.curr_tok.kind == .Open_Brace { + open := expect_token(p, .Open_Brace); args: [dynamic]^ast.Expr; - for p.curr_tok.kind != token.Close_Brace && - p.curr_tok.kind != token.EOF { + for p.curr_tok.kind != .Close_Brace && + p.curr_tok.kind != .EOF { elem := parse_expr(p, false); append(&args, elem); - if !allow_token(p, token.Comma) { + if !allow_token(p, .Comma) { break; } } - close := expect_token(p, token.Close_Brace); + close := expect_token(p, .Close_Brace); if len(args) == 0 { error(p, tok.pos, "expected at least 1 argument in procedure group"); @@ -1976,19 +1985,35 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { type := parse_proc_type(p, tok); + where_token: tokenizer.Token; + where_clauses: []^ast.Expr; + if (p.curr_tok.kind == .Where) { + where_token = expect_token(p, .Where); + prev_level := p.expr_level; + p.expr_level = -1; + where_clauses = parse_rhs_expr_list(p); + p.expr_level = prev_level; + } + if p.allow_type && p.expr_level < 0 { + if where_token.kind != .Invalid { + error(p, where_token.pos, "'where' clauses are not allowed on procedure types"); + } return type; } body: ^ast.Stmt; - if allow_token(p, token.Undef) { + if allow_token(p, .Undef) { // Okay - } else if p.curr_tok.kind == token.Open_Brace { + if where_token.kind != .Invalid { + error(p, where_token.pos, "'where' clauses are not allowed on procedure literals without a defined body (replaced with ---"); + } + } else if p.curr_tok.kind == .Open_Brace { prev_proc := p.curr_proc; p.curr_proc = type; body = parse_body(p); p.curr_proc = prev_proc; - } else if allow_token(p, token.Do) { + } else if allow_token(p, .Do) { prev_proc := p.curr_proc; p.curr_proc = type; body = convert_stmt_to_body(p, parse_stmt(p)); @@ -2000,15 +2025,17 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { pl := ast.new(ast.Proc_Lit, tok.pos, end_pos(p.prev_tok)); pl.type = type; pl.body = body; + pl.where_token = where_token; + pl.where_clauses = where_clauses; return pl; - case token.Dollar: + case .Dollar: tok := advance_token(p); type := parse_ident(p); end := type.end; specialization: ^ast.Expr; - if allow_token(p, token.Quo) { + if allow_token(p, .Quo) { specialization = parse_type(p); end = specialization.pos; } @@ -2022,19 +2049,19 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { pt.specialization = specialization; return pt; - case token.Typeid: + case .Typeid: tok := advance_token(p); ti := ast.new(ast.Typeid_Type, tok.pos, end_pos(tok)); ti.tok = tok.kind; ti.specialization = nil; return ti; - case token.Type_Of: + case .Type_Of: tok := advance_token(p); i := ast.new(ast.Implicit, tok.pos, end_pos(tok)); i.tok = tok; type: ^ast.Expr = parse_call_expr(p, i); - for p.curr_tok.kind == token.Period { + for p.curr_tok.kind == .Period { period := advance_token(p); field := parse_ident(p); @@ -2048,24 +2075,24 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { return type; - case token.Pointer: - tok := expect_token(p, token.Pointer); + case .Pointer: + tok := expect_token(p, .Pointer); elem := parse_type(p); ptr := ast.new(ast.Pointer_Type, tok.pos, elem.end); ptr.elem = elem; return ptr; - case token.Open_Bracket: - open := expect_token(p, token.Open_Bracket); + case .Open_Bracket: + open := expect_token(p, .Open_Bracket); count: ^ast.Expr; - if p.curr_tok.kind == token.Question { - tok := expect_token(p, token.Question); + if p.curr_tok.kind == .Question { + tok := expect_token(p, .Question); q := ast.new(ast.Unary_Expr, tok.pos, end_pos(tok)); q.op = tok; count = q; - } else if p.curr_tok.kind == token.Dynamic { - tok := expect_token(p, token.Dynamic); - close := expect_token(p, token.Close_Bracket); + } else if p.curr_tok.kind == .Dynamic { + tok := expect_token(p, .Dynamic); + close := expect_token(p, .Close_Bracket); elem := parse_type(p); da := ast.new(ast.Dynamic_Array_Type, open.pos, elem.end); da.open = open.pos; @@ -2074,12 +2101,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { da.elem = elem; return da; - } else if p.curr_tok.kind != token.Close_Bracket { + } else if p.curr_tok.kind != .Close_Bracket { p.expr_level += 1; count = parse_expr(p, false); p.expr_level -= 1; } - close := expect_token(p, token.Close_Bracket); + close := expect_token(p, .Close_Bracket); elem := parse_type(p); at := ast.new(ast.Array_Type, open.pos, elem.end); at.open = open.pos; @@ -2088,11 +2115,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { at.elem = elem; return at; - case token.Map: - tok := expect_token(p, token.Map); - expect_token(p, token.Open_Bracket); + case .Map: + tok := expect_token(p, .Map); + expect_token(p, .Open_Bracket); key := parse_type(p); - expect_token(p, token.Close_Bracket); + expect_token(p, .Close_Bracket); value := parse_type(p); mt := ast.new(ast.Map_Type, tok.pos, value.end); @@ -2101,8 +2128,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { mt.value = value; return mt; - case token.Struct: - tok := expect_token(p, token.Struct); + case .Struct: + tok := expect_token(p, .Struct); poly_params: ^ast.Field_List; align: ^ast.Expr; @@ -2111,21 +2138,21 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { fields: ^ast.Field_List; name_count: int; - if allow_token(p, token.Open_Paren) { + if allow_token(p, .Open_Paren) { param_count: int; - poly_params, param_count = parse_field_list(p, token.Close_Paren, ast.Field_Flags_Record_Poly_Params); + poly_params, param_count = parse_field_list(p, .Close_Paren, ast.Field_Flags_Record_Poly_Params); if param_count == 0 { error(p, poly_params.pos, "expected at least 1 polymorphic parameter"); poly_params = nil; } - expect_token_after(p, token.Close_Paren, "parameter list"); + expect_token_after(p, .Close_Paren, "parameter list"); check_poly_params_for_type(p, poly_params, tok); } prev_level := p.expr_level; p.expr_level = -1; - for allow_token(p, token.Hash) { - tag := expect_token_after(p, token.Ident, "#"); + for allow_token(p, .Hash) { + tag := expect_token_after(p, .Ident, "#"); switch tag.text { case "packed": if is_packed do error(p, tag.pos, "duplicate struct tag '#%s'", tag.text); @@ -2147,39 +2174,51 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { error(p, tok.pos, "'#raw_union' cannot also be '#packed"); } - expect_token(p, token.Open_Brace); - fields, name_count = parse_field_list(p, token.Close_Brace, ast.Field_Flags_Struct); - close := expect_token(p, token.Close_Brace); + where_token: tokenizer.Token; + where_clauses: []^ast.Expr; + if (p.curr_tok.kind == .Where) { + where_token = expect_token(p, .Where); + prev_level := p.expr_level; + p.expr_level = -1; + where_clauses = parse_rhs_expr_list(p); + p.expr_level = prev_level; + } + + expect_token(p, .Open_Brace); + fields, name_count = parse_field_list(p, .Close_Brace, ast.Field_Flags_Struct); + close := expect_token(p, .Close_Brace); st := ast.new(ast.Struct_Type, tok.pos, end_pos(close)); - st.poly_params = poly_params; - st.align = align; - st.is_packed = is_packed; - st.is_raw_union = is_raw_union; - st.fields = fields; - st.name_count = name_count; + st.poly_params = poly_params; + st.align = align; + st.is_packed = is_packed; + st.is_raw_union = is_raw_union; + st.fields = fields; + st.name_count = name_count; + st.where_token = where_token; + st.where_clauses = where_clauses; return st; - case token.Union: - tok := expect_token(p, token.Union); + case .Union: + tok := expect_token(p, .Union); poly_params: ^ast.Field_List; align: ^ast.Expr; - if allow_token(p, token.Open_Paren) { + if allow_token(p, .Open_Paren) { param_count: int; - poly_params, param_count = parse_field_list(p, token.Close_Paren, ast.Field_Flags_Record_Poly_Params); + poly_params, param_count = parse_field_list(p, .Close_Paren, ast.Field_Flags_Record_Poly_Params); if param_count == 0 { error(p, poly_params.pos, "expected at least 1 polymorphic parameter"); poly_params = nil; } - expect_token_after(p, token.Close_Paren, "parameter list"); + expect_token_after(p, .Close_Paren, "parameter list"); check_poly_params_for_type(p, poly_params, tok); } prev_level := p.expr_level; p.expr_level = -1; - for allow_token(p, token.Hash) { - tag := expect_token_after(p, token.Ident, "#"); + for allow_token(p, .Hash) { + tag := expect_token_after(p, .Ident, "#"); switch tag.text { case "align": if align != nil do error(p, tag.pos, "duplicate union tag '#%s'", tag.text); @@ -2190,38 +2229,50 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } p.expr_level = prev_level; + where_token: tokenizer.Token; + where_clauses: []^ast.Expr; + if (p.curr_tok.kind == .Where) { + where_token = expect_token(p, .Where); + prev_level := p.expr_level; + p.expr_level = -1; + where_clauses = parse_rhs_expr_list(p); + p.expr_level = prev_level; + } + variants: [dynamic]^ast.Expr; - expect_token_after(p, token.Open_Brace, "union"); + expect_token_after(p, .Open_Brace, "union"); - for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF { + for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { type := parse_type(p); if _, ok := type.derived.(ast.Bad_Expr); !ok { append(&variants, type); } - if !allow_token(p, token.Comma) { + if !allow_token(p, .Comma) { break; } } - close := expect_token(p, token.Close_Brace); + close := expect_token(p, .Close_Brace); ut := ast.new(ast.Union_Type, tok.pos, end_pos(close)); - ut.poly_params = poly_params; - ut.variants = variants[:]; - ut.align = align; + ut.poly_params = poly_params; + ut.variants = variants[:]; + ut.align = align; + ut.where_token = where_token; + ut.where_clauses = where_clauses; return ut; - case token.Enum: - tok := expect_token(p, token.Enum); + case .Enum: + tok := expect_token(p, .Enum); base_type: ^ast.Expr; - if p.curr_tok.kind != token.Open_Brace { + if p.curr_tok.kind != .Open_Brace { base_type = parse_type(p); } - open := expect_token(p, token.Open_Brace); + open := expect_token(p, .Open_Brace); fields := parse_elem_list(p); - close := expect_token(p, token.Close_Brace); + close := expect_token(p, .Close_Brace); et := ast.new(ast.Enum_Type, tok.pos, end_pos(close)); et.base_type = base_type; @@ -2230,8 +2281,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { et.close = close.pos; return et; - case token.Bit_Field: - tok := expect_token(p, token.Bit_Field); + case .Bit_Field: + tok := expect_token(p, .Bit_Field); fields: [dynamic]^ast.Field_Value; align: ^ast.Expr; @@ -2239,8 +2290,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { prev_level := p.expr_level; p.expr_level = -1; - for allow_token(p, token.Hash) { - tag := expect_token_after(p, token.Ident, "#"); + for allow_token(p, .Hash) { + tag := expect_token_after(p, .Ident, "#"); switch tag.text { case "align": if align != nil { @@ -2254,11 +2305,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { p.expr_level = prev_level; - open := expect_token_after(p, token.Open_Brace, "bit_field"); + open := expect_token_after(p, .Open_Brace, "bit_field"); - for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF { + for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { name := parse_ident(p); - colon := expect_token(p, token.Colon); + colon := expect_token(p, .Colon); value := parse_expr(p, true); fv := ast.new(ast.Field_Value, name.pos, value.end); @@ -2267,12 +2318,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { fv.value = value; append(&fields, fv); - if !allow_token(p, token.Comma) { + if !allow_token(p, .Comma) { break; } } - close := expect_token(p, token.Close_Brace); + close := expect_token(p, .Close_Brace); bft := ast.new(ast.Bit_Field_Type, tok.pos, end_pos(close)); bft.tok_pos = tok.pos; @@ -2283,9 +2334,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { return bft; - case token.Bit_Set: - tok := expect_token(p, token.Bit_Set); - open := expect_token(p, token.Open_Bracket); + case .Bit_Set: + tok := expect_token(p, .Bit_Set); + open := expect_token(p, .Open_Bracket); elem, underlying: ^ast.Expr; prev_allow_range := p.allow_range; @@ -2293,12 +2344,12 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { elem = parse_expr(p, false); p.allow_range = prev_allow_range; - if allow_token(p, token.Semicolon) { + if allow_token(p, .Semicolon) { underlying = parse_type(p); } - close := expect_token(p, token.Close_Bracket); + close := expect_token(p, .Close_Bracket); bst := ast.new(ast.Bit_Set_Type, tok.pos, end_pos(close)); bst.tok_pos = tok.pos; @@ -2337,19 +2388,22 @@ is_literal_type :: proc(expr: ^ast.Expr) -> bool { } parse_value :: proc(p: ^Parser) -> ^ast.Expr { - if p.curr_tok.kind == token.Open_Brace { + 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); } parse_elem_list :: proc(p: ^Parser) -> []^ast.Expr { elems: [dynamic]^ast.Expr; - for p.curr_tok.kind != token.Close_Brace && p.curr_tok.kind != token.EOF { + for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { elem := parse_value(p); - if p.curr_tok.kind == token.Eq { - eq := expect_token(p, token.Eq); + if p.curr_tok.kind == .Eq { + eq := expect_token(p, .Eq); value := parse_value(p); fv := ast.new(ast.Field_Value, elem.pos, value.end); @@ -2362,7 +2416,7 @@ parse_elem_list :: proc(p: ^Parser) -> []^ast.Expr { append(&elems, elem); - if !allow_token(p, token.Comma) { + if !allow_token(p, .Comma) { break; } } @@ -2372,14 +2426,14 @@ parse_elem_list :: proc(p: ^Parser) -> []^ast.Expr { parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit { elems: []^ast.Expr; - open := expect_token(p, token.Open_Brace); + open := expect_token(p, .Open_Brace); p.expr_level += 1; - if p.curr_tok.kind != token.Close_Brace { + if p.curr_tok.kind != .Close_Brace { elems = parse_elem_list(p); } p.expr_level -= 1; - close := expect_token_after(p, token.Close_Brace, "compound literal"); + close := expect_token_after(p, .Close_Brace, "compound literal"); pos := type != nil ? type.pos : open.pos; lit := ast.new(ast.Comp_Lit, pos, end_pos(close)); @@ -2393,30 +2447,30 @@ parse_literal_value :: proc(p: ^Parser, type: ^ast.Expr) -> ^ast.Comp_Lit { parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr { args: [dynamic]^ast.Expr; - ellipsis: token.Token; + ellipsis: tokenizer.Token; p.expr_level += 1; - open := expect_token(p, token.Open_Paren); + open := expect_token(p, .Open_Paren); - for p.curr_tok.kind != token.Close_Paren && - p.curr_tok.kind != token.EOF && + for p.curr_tok.kind != .Close_Paren && + p.curr_tok.kind != .EOF && ellipsis.pos.line == 0 { - if p.curr_tok.kind == token.Comma { + if p.curr_tok.kind == .Comma { error(p, p.curr_tok.pos, "expected an expression not ,"); - } else if p.curr_tok.kind == token.Eq { + } else if p.curr_tok.kind == .Eq { error(p, p.curr_tok.pos, "expected an expression not ="); } prefix_ellipsis := false; - if p.curr_tok.kind == token.Ellipsis { + if p.curr_tok.kind == .Ellipsis { prefix_ellipsis = true; - ellipsis = expect_token(p, token.Ellipsis); + ellipsis = expect_token(p, .Ellipsis); } arg := parse_expr(p, false); - if p.curr_tok.kind == token.Eq { - eq := expect_token(p, token.Eq); + if p.curr_tok.kind == .Eq { + eq := expect_token(p, .Eq); if prefix_ellipsis { error(p, ellipsis.pos, "'..' must be applied to value rather than a field name"); @@ -2433,12 +2487,12 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Call_Expr { append(&args, arg); - if !allow_token(p, token.Comma) { + if !allow_token(p, .Comma) { break; } } - close := expect_token_after(p, token.Close_Paren, "argument list"); + close := expect_token_after(p, .Close_Paren, "argument list"); p.expr_level -= 1; ce := ast.new(ast.Call_Expr, operand.pos, end_pos(close)); @@ -2469,23 +2523,23 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a case: loop = false; - case token.Open_Paren: + case .Open_Paren: operand = parse_call_expr(p, operand); - case token.Open_Bracket: + case .Open_Bracket: prev_allow_range := p.allow_range; defer p.allow_range = prev_allow_range; p.allow_range = false; indicies: [2]^ast.Expr; - interval: token.Token; + interval: tokenizer.Token; is_slice_op := false; p.expr_level += 1; - open := expect_token(p, token.Open_Bracket); + open := expect_token(p, .Open_Bracket); switch p.curr_tok.kind { - case token.Colon, token.Ellipsis, token.Range_Half: + case .Colon, .Ellipsis, .Range_Half: // NOTE(bill): Do not err yet break; case: @@ -2493,18 +2547,18 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a } switch p.curr_tok.kind { - case token.Ellipsis, token.Range_Half: + case .Ellipsis, .Range_Half: error(p, p.curr_tok.pos, "expected a colon, not a range"); fallthrough; - case token.Colon: + case .Colon: interval = advance_token(p); is_slice_op = true; - if (p.curr_tok.kind != token.Close_Bracket && p.curr_tok.kind != token.EOF) { + if (p.curr_tok.kind != .Close_Bracket && p.curr_tok.kind != .EOF) { indicies[1] = parse_expr(p, false); } } - close := expect_token(p, token.Close_Bracket); + close := expect_token(p, .Close_Bracket); p.expr_level -= 1; if is_slice_op { @@ -2528,10 +2582,10 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a } - case token.Period: - tok := expect_token(p, token.Period); + case .Period: + tok := expect_token(p, .Period); switch p.curr_tok.kind { - case token.Ident: + case .Ident: field := parse_ident(p); sel := ast.new(ast.Selector_Expr, operand.pos, field.end); @@ -2540,10 +2594,10 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a operand = sel; - case token.Open_Paren: - open := expect_token(p, token.Open_Paren); + case .Open_Paren: + open := expect_token(p, .Open_Paren); type := parse_type(p); - close := expect_token(p, token.Close_Paren); + close := expect_token(p, .Close_Paren); ta := ast.new(ast.Type_Assertion, operand.pos, end_pos(close)); ta.expr = operand; @@ -2559,15 +2613,15 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a operand = ast.new(ast.Bad_Expr, operand.pos, end_pos(tok)); } - case token.Pointer: - op := expect_token(p, token.Pointer); + case .Pointer: + op := expect_token(p, .Pointer); deref := ast.new(ast.Deref_Expr, operand.pos, end_pos(op)); deref.expr = operand; deref.op = op; operand = deref; - case token.Open_Brace: + case .Open_Brace: if !is_lhs && is_literal_type(operand) && p.expr_level >= 0 { operand = parse_literal_value(p, operand); } else { @@ -2588,11 +2642,11 @@ parse_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { } parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { switch p.curr_tok.kind { - case token.Transmute, token.Cast: + case .Transmute, .Cast: tok := advance_token(p); - open := expect_token(p, token.Open_Paren); + open := expect_token(p, .Open_Paren); type := parse_type(p); - close := expect_token(p, token.Close_Paren); + close := expect_token(p, .Close_Paren); expr := parse_unary_expr(p, lhs); tc := ast.new(ast.Type_Cast, tok.pos, expr.end); @@ -2603,7 +2657,7 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { tc.expr = expr; return tc; - case token.Auto_Cast: + case .Auto_Cast: op := advance_token(p); expr := parse_unary_expr(p, lhs); @@ -2612,9 +2666,9 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { ac.expr = expr; return ac; - case token.Add, token.Sub, - token.Not, token.Xor, - token.And: + case .Add, .Sub, + .Not, .Xor, + .And: op := advance_token(p); expr := parse_unary_expr(p, lhs); @@ -2623,7 +2677,7 @@ parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { ue.expr = expr; return ue; - case token.Period: + case .Period: op := advance_token(p); field := parse_ident(p); ise := ast.new(ast.Implicit_Selector_Expr, op.pos, field.end); @@ -2644,10 +2698,10 @@ parse_binary_expr :: proc(p: ^Parser, lhs: bool, prec_in: int) -> ^ast.Expr { } expect_operator(p); - if op.kind == token.Question { + if op.kind == .Question { cond := expr; x := parse_expr(p, lhs); - colon := expect_token(p, token.Colon); + colon := expect_token(p, .Colon); y := parse_expr(p, lhs); te := ast.new(ast.Ternary_Expr, expr.pos, end_pos(p.prev_tok)); te.cond = cond; @@ -2681,7 +2735,7 @@ parse_expr_list :: proc(p: ^Parser, lhs: bool) -> ([]^ast.Expr) { for { expr := parse_expr(p, lhs); append(&list, expr); - if p.curr_tok.kind != token.Comma || p.curr_tok.kind == token.EOF { + if p.curr_tok.kind != .Comma || p.curr_tok.kind == .EOF { break; } advance_token(p); @@ -2703,7 +2757,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { lhs := parse_lhs_expr_list(p); op := p.curr_tok; switch { - case token.is_assignment_operator(op.kind): + case tokenizer.is_assignment_operator(op.kind): // if p.curr_proc == nil { // error(p, p.curr_tok.pos, "simple statements are not allowed at the file scope"); // return ast.new(ast.Bad_Stmt, start_tok.pos, end_pos(p.curr_tok)); @@ -2720,9 +2774,9 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { stmt.rhs = rhs; return stmt; - case op.kind == token.In: + case op.kind == .In: if .In in flags { - allow_token(p, token.In); + allow_token(p, .In); prev_allow_range := p.allow_range; p.allow_range = true; expr := parse_expr(p, false); @@ -2737,11 +2791,11 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt { stmt.rhs = rhs; return stmt; } - case op.kind == token.Colon: - expect_token_after(p, token.Colon, "identifier list"); + case op.kind == .Colon: + expect_token_after(p, .Colon, "identifier list"); if .Label in flags && len(lhs) == 1 { switch p.curr_tok.kind { - case token.Open_Brace, token.If, token.For, token.Switch: + case .Open_Brace, .If, .For, .Switch: label := lhs[0]; stmt := parse_stmt(p); @@ -2776,9 +2830,9 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou type := parse_type_or_ident(p); switch p.curr_tok.kind { - case token.Eq, token.Colon: + case .Eq, .Colon: sep := advance_token(p); - is_mutable = sep.kind != token.Colon; + is_mutable = sep.kind != .Colon; values = parse_rhs_expr_list(p); if len(values) > len(names) { @@ -2807,7 +2861,7 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou if !is_mutable && len(values) > 0 { end = values[len(values)-1]; } - if p.curr_tok.kind == token.Close_Brace && + if p.curr_tok.kind == .Close_Brace && p.curr_tok.pos.line == p.prev_tok.pos.line { } else { @@ -2833,13 +2887,13 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou parse_import_decl :: proc(p: ^Parser, kind := Import_Decl_Kind.Standard) -> ^ast.Import_Decl { docs := p.lead_comment; - tok := expect_token(p, token.Import); + tok := expect_token(p, .Import); - import_name: token.Token; + import_name: tokenizer.Token; is_using := kind != Import_Decl_Kind.Standard; switch p.curr_tok.kind { - case token.Ident: + case .Ident: import_name = advance_token(p); case: import_name.pos = p.curr_tok.pos; @@ -2849,7 +2903,7 @@ parse_import_decl :: proc(p: ^Parser, kind := Import_Decl_Kind.Standard) -> ^ast error(p, import_name.pos, "illegal import name: '_'"); } - path := expect_token_after(p, token.String, "import"); + path := expect_token_after(p, .String, "import"); decl := ast.new(ast.Import_Decl, tok.pos, end_pos(path)); decl.docs = docs; diff --git a/core/odin/token/token.odin b/core/odin/tokenizer/token.odin index 8ce98d1d9..d692da5d9 100644 --- a/core/odin/token/token.odin +++ b/core/odin/tokenizer/token.odin @@ -1,9 +1,9 @@ -package odin_token +package odin_tokenizer import "core:strings" Token :: struct { - kind: Kind, + kind: Token_Kind, text: string, pos: Pos, } @@ -28,7 +28,7 @@ pos_compare :: proc(lhs, rhs: Pos) -> int { return strings.compare(lhs.file, rhs.file); } -using Kind :: enum u32 { +Token_Kind :: enum u32 { Invalid, EOF, Comment, @@ -118,6 +118,7 @@ using Kind :: enum u32 { Package, Typeid, When, + Where, If, Else, For, @@ -154,9 +155,6 @@ using Kind :: enum u32 { Offset_Of, Type_Of, Const, - Asm, - Yield, - Await, B_Keyword_End, COUNT, @@ -165,7 +163,7 @@ using Kind :: enum u32 { // ... Custom keywords }; -tokens := [Kind.COUNT]string { +tokens := [Token_Kind.COUNT]string { "Invalid", "EOF", "Comment", @@ -255,6 +253,7 @@ tokens := [Kind.COUNT]string { "package", "typeid", "when", + "where", "if", "else", "for", @@ -291,20 +290,17 @@ tokens := [Kind.COUNT]string { "offset_of", "type_of", "const", - "asm", - "yield", - "await", "", }; custom_keyword_tokens: []string; -to_string :: proc(kind: Kind) -> string { - if Invalid <= kind && kind < COUNT { +to_string :: proc(kind: Token_Kind) -> string { + if Token_Kind.Invalid <= kind && kind < Token_Kind.COUNT { return tokens[kind]; } - if B_Custom_Keyword_Begin < kind { - n := int(u16(kind)-u16(B_Custom_Keyword_Begin)); + if Token_Kind.B_Custom_Keyword_Begin < kind { + n := int(u16(kind)-u16(Token_Kind.B_Custom_Keyword_Begin)); if n < len(custom_keyword_tokens) { return custom_keyword_tokens[n]; } @@ -313,24 +309,26 @@ to_string :: proc(kind: Kind) -> string { return "Invalid"; } -is_literal :: proc(kind: Kind) -> bool { return B_Literal_Begin < kind && kind < B_Literal_End; } -is_operator :: proc(kind: Kind) -> bool { +is_literal :: proc(kind: Token_Kind) -> bool { + return Token_Kind.B_Literal_Begin < kind && kind < Token_Kind.B_Literal_End; +} +is_operator :: proc(kind: Token_Kind) -> bool { switch kind { - case B_Operator_Begin..B_Operator_End: + case .B_Operator_Begin .. .B_Operator_End: return true; - case In, Notin: + case .In, .Notin: return true; } return false; } -is_assignment_operator :: proc(kind: Kind) -> bool { - return B_Assign_Op_Begin < kind && kind < B_Assign_Op_End || kind == Eq; +is_assignment_operator :: proc(kind: Token_Kind) -> bool { + return Token_Kind.B_Assign_Op_Begin < kind && kind < Token_Kind.B_Assign_Op_End || kind == Token_Kind.Eq; } -is_keyword :: proc(kind: Kind) -> bool { +is_keyword :: proc(kind: Token_Kind) -> bool { switch { - case B_Keyword_Begin < kind && kind < B_Keyword_End: + case Token_Kind.B_Keyword_Begin < kind && kind < Token_Kind.B_Keyword_End: return true; - case B_Custom_Keyword_Begin < kind: + case Token_Kind.B_Custom_Keyword_Begin < kind: return true; } return false; diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin index 764fe6e34..26aab7c1c 100644 --- a/core/odin/tokenizer/tokenizer.odin +++ b/core/odin/tokenizer/tokenizer.odin @@ -1,10 +1,9 @@ package odin_tokenizer import "core:fmt" -import "core:odin/token" import "core:unicode/utf8" -Error_Handler :: #type proc(pos: token.Pos, fmt: string, args: ..any); +Error_Handler :: #type proc(pos: Pos, fmt: string, args: ..any); Tokenizer :: struct { // Immutable data @@ -41,11 +40,11 @@ init :: proc(t: ^Tokenizer, src: []byte, path: string, err: Error_Handler = defa } @(private) -offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> token.Pos { +offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> Pos { line := t.line_count; column := offset - t.line_offset + 1; - return token.Pos { + return Pos { file = t.path, offset = offset, line = line, @@ -53,10 +52,10 @@ offset_to_pos :: proc(t: ^Tokenizer, offset: int) -> token.Pos { }; } -default_error_handler :: proc(pos: token.Pos, msg: string, args: ..any) { - fmt.printf_err("%s(%d:%d) ", pos.file, pos.line, pos.column); - fmt.printf_err(msg, ..args); - fmt.printf_err("\n"); +default_error_handler :: proc(pos: Pos, msg: string, args: ..any) { + fmt.eprintf("%s(%d:%d) ", pos.file, pos.line, pos.column); + fmt.eprintf(msg, ..args); + fmt.eprintf("\n"); } error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) { @@ -322,15 +321,15 @@ scan_rune :: proc(t: ^Tokenizer) -> string { return string(t.src[offset : t.offset]); } -scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, string) { +scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Token_Kind, string) { scan_mantissa :: proc(t: ^Tokenizer, base: int) { for digit_val(t.ch) < base || t.ch == '_' { advance_rune(t); } } - scan_exponent :: proc(t: ^Tokenizer, kind: ^token.Kind) { + scan_exponent :: proc(t: ^Tokenizer, kind: ^Token_Kind) { if t.ch == 'e' || t.ch == 'E' { - kind^ = token.Float; + kind^ = .Float; advance_rune(t); if t.ch == '-' || t.ch == '+' { advance_rune(t); @@ -343,17 +342,18 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str } // NOTE(bill): This needs to be here for sanity's sake - if t.ch == 'i' { - kind^ = token.Imag; + switch t.ch { + case 'i', 'j', 'k': + kind^ = .Imag; advance_rune(t); } } - scan_fraction :: proc(t: ^Tokenizer, kind: ^token.Kind) -> (early_exit: bool) { + scan_fraction :: proc(t: ^Tokenizer, kind: ^Token_Kind) -> (early_exit: bool) { if t.ch == '.' && peek_byte(t) == '.' { return true; } if t.ch == '.' { - kind^ = token.Float; + kind^ = .Float; advance_rune(t); scan_mantissa(t, 10); } @@ -362,22 +362,22 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str offset := t.offset; - kind := token.Integer; + kind := Token_Kind.Integer; seen_point := seen_decimal_point; if seen_point { offset -= 1; - kind = token.Float; + kind = .Float; scan_mantissa(t, 10); scan_exponent(t, &kind); } else { if t.ch == '0' { - int_base :: inline proc(t: ^Tokenizer, kind: ^token.Kind, base: int, msg: string) { + int_base :: inline proc(t: ^Tokenizer, kind: ^Token_Kind, base: int, msg: string) { prev := t.offset; advance_rune(t); scan_mantissa(t, base); if t.offset - prev <= 1 { - kind^ = token.Invalid; + kind^ = .Invalid; error(t, t.offset, msg); } } @@ -394,7 +394,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str advance_rune(t); scan_mantissa(t, 16); if t.offset - prev <= 1 { - kind = token.Invalid; + kind = .Invalid; error(t, t.offset, "illegal hexadecimal floating-point number"); } else { sub := t.src[prev+1 : t.offset]; @@ -439,15 +439,15 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (token.Kind, str } -scan :: proc(t: ^Tokenizer) -> token.Token { - switch2 :: proc(t: ^Tokenizer, tok0, tok1: token.Kind) -> token.Kind { +scan :: proc(t: ^Tokenizer) -> Token { + switch2 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind) -> Token_Kind { if t.ch == '=' { advance_rune(t); return tok1; } return tok0; } - switch3 :: proc(t: ^Tokenizer, tok0, tok1: token.Kind, ch2: rune, tok2: token.Kind) -> token.Kind { + switch3 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2: Token_Kind) -> Token_Kind { if t.ch == '=' { advance_rune(t); return tok1; @@ -458,7 +458,7 @@ scan :: proc(t: ^Tokenizer) -> token.Token { } return tok0; } - switch4 :: proc(t: ^Tokenizer, tok0, tok1: token.Kind, ch2: rune, tok2, tok3: token.Kind) -> token.Kind { + switch4 :: proc(t: ^Tokenizer, tok0, tok1: Token_Kind, ch2: rune, tok2, tok3: Token_Kind) -> Token_Kind { if t.ch == '=' { advance_rune(t); return tok1; @@ -479,25 +479,25 @@ scan :: proc(t: ^Tokenizer) -> token.Token { offset := t.offset; - kind: token.Kind; + kind: Token_Kind; lit: string; pos := offset_to_pos(t, offset); switch ch := t.ch; true { case is_letter(ch): lit = scan_identifier(t); - kind = token.Ident; + kind = .Ident; check_keyword: if len(lit) > 1 { // TODO(bill): Maybe have a hash table lookup rather than this linear search - for i in token.B_Keyword_Begin .. token.B_Keyword_End { - if lit == token.tokens[i] { - kind = token.Kind(i); + for i in Token_Kind.B_Keyword_Begin .. Token_Kind.B_Keyword_End { + if lit == tokens[i] { + kind = Token_Kind(i); break check_keyword; } } - for keyword, i in token.custom_keyword_tokens { + for keyword, i in custom_keyword_tokens { if lit == keyword { - kind = token.Kind(i+1)+token.B_Custom_Keyword_Begin; + kind = Token_Kind(i+1) + .B_Custom_Keyword_Begin; break check_keyword; } } @@ -508,115 +508,115 @@ scan :: proc(t: ^Tokenizer) -> token.Token { advance_rune(t); switch ch { case -1: - kind = token.EOF; + kind = .EOF; case '"': - kind = token.String; + kind = .String; lit = scan_string(t); case '\'': - kind = token.Rune; + kind = .Rune; lit = scan_rune(t); case '`': - kind = token.String; + kind = .String; lit = scan_raw_string(t); case '=': if t.ch == '>' { advance_rune(t); - kind = token.Double_Arrow_Right; + kind = .Double_Arrow_Right; } else { - kind = switch2(t, token.Eq, token.Cmp_Eq); + kind = switch2(t, .Eq, .Cmp_Eq); } - case '!': kind = switch2(t, token.Not, token.Not_Eq); + case '!': kind = switch2(t, .Not, .Not_Eq); case '#': - kind = token.Hash; + kind = .Hash; if t.ch == '!' { - kind = token.Comment; + kind = .Comment; lit = scan_comment(t); } - case '?': kind = token.Question; - case '@': kind = token.At; - case '$': kind = token.Dollar; - case '^': kind = token.Pointer; - case '+': kind = switch2(t, token.Add, token.Add_Eq); + case '?': kind = .Question; + case '@': kind = .At; + case '$': kind = .Dollar; + case '^': kind = .Pointer; + case '+': kind = switch2(t, .Add, .Add_Eq); case '-': if t.ch == '>' { advance_rune(t); - kind = token.Arrow_Right; + kind = .Arrow_Right; } else if t.ch == '-' && peek_byte(t) == '-' { advance_rune(t); advance_rune(t); - kind = token.Undef; + kind = .Undef; } else { - kind = switch2(t, token.Sub, token.Sub_Eq); + kind = switch2(t, .Sub, .Sub_Eq); } - case '*': kind = switch2(t, token.Mul, token.Mul_Eq); + case '*': kind = switch2(t, .Mul, .Mul_Eq); case '/': if t.ch == '/' || t.ch == '*' { - kind = token.Comment; + kind = .Comment; lit = scan_comment(t); } else { - kind = switch2(t, token.Quo, token.Quo_Eq); + kind = switch2(t, .Quo, .Quo_Eq); } - case '%': kind = switch4(t, token.Mod, token.Mod_Eq, '%', token.Mod_Mod, token.Mod_Mod_Eq); + case '%': kind = switch4(t, .Mod, .Mod_Eq, '%', .Mod_Mod, .Mod_Mod_Eq); case '&': if t.ch == '~' { advance_rune(t); - kind = switch2(t, token.And_Not, token.And_Not_Eq); + kind = switch2(t, .And_Not, .And_Not_Eq); } else { - kind = switch3(t, token.And, token.And_Eq, '&', token.Cmp_And); + kind = switch3(t, .And, .And_Eq, '&', .Cmp_And); } - case '|': kind = switch3(t, token.Or, token.Or_Eq, '|', token.Cmp_Or); - case '~': kind = token.Xor; + case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or); + case '~': kind = .Xor; case '<': if t.ch == '-' { advance_rune(t); - kind = token.Arrow_Left; + kind = .Arrow_Left; } else { - kind = switch4(t, token.Lt, token.Lt_Eq, '<', token.Shl, token.Shl_Eq); + kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq); } - case '>': kind = switch4(t, token.Gt, token.Gt_Eq, '>', token.Shr,token.Shr_Eq); + case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq); - case '≠': kind = token.Not_Eq; - case '≤': kind = token.Lt_Eq; - case '≥': kind = token.Gt_Eq; - case '∈': kind = token.In; - case '∉': kind = token.Notin; + case '≠': kind = .Not_Eq; + case '≤': kind = .Lt_Eq; + case '≥': kind = .Gt_Eq; + case '∈': kind = .In; + case '∉': kind = .Notin; case '.': if '0' <= t.ch && t.ch <= '9' { kind, lit = scan_number(t, true); } else { - kind = token.Period; + kind = .Period; if t.ch == '.' { advance_rune(t); - kind = token.Ellipsis; + kind = .Ellipsis; if t.ch == '<' { advance_rune(t); - kind = token.Range_Half; + kind = .Range_Half; } } } - case ':': kind = token.Colon; - case ',': kind = token.Comma; - case ';': kind = token.Semicolon; - case '(': kind = token.Open_Paren; - case ')': kind = token.Close_Paren; - case '[': kind = token.Open_Bracket; - case ']': kind = token.Close_Bracket; - case '{': kind = token.Open_Brace; - case '}': kind = token.Close_Brace; - - case '\\': kind = token.Back_Slash; + case ':': kind = .Colon; + case ',': kind = .Comma; + case ';': kind = .Semicolon; + case '(': kind = .Open_Paren; + case ')': kind = .Close_Paren; + case '[': kind = .Open_Bracket; + case ']': kind = .Close_Bracket; + case '{': kind = .Open_Brace; + case '}': kind = .Close_Brace; + + case '\\': kind = .Back_Slash; case: if ch != utf8.RUNE_BOM { error(t, t.offset, "illegal character '%r': %d", ch, ch); } - kind = token.Invalid; + kind = .Invalid; } } if lit == "" { lit = string(t.src[offset : t.offset]); } - return token.Token{kind, lit, pos}; + return Token{kind, lit, pos}; } diff --git a/core/os/os_osx.odin b/core/os/os_darwin.odin index e0950dee1..f48a547b6 100644 --- a/core/os/os_osx.odin +++ b/core/os/os_darwin.odin @@ -6,7 +6,7 @@ foreign import libc "system:c" import "core:runtime" import "core:strings" -OS :: "osx"; +OS :: "darwin"; Handle :: distinct i32; File_Time :: distinct u64; @@ -319,9 +319,9 @@ dlerror :: proc() -> string { _alloc_command_line_arguments :: proc() -> []string { - args := make([]string, len(runtime.args__)); + res := make([]string, len(runtime.args__)); for arg, i in runtime.args__ { - args[i] = string(arg); + res[i] = string(arg); } - return args; + return res; } diff --git a/core/os/os_essence.odin b/core/os/os_essence.odin index 2fe62b873..d11c0503b 100644 --- a/core/os/os_essence.odin +++ b/core/os/os_essence.odin @@ -1,180 +1,2440 @@ -package os +package os; +Data :: struct { _private : [4]rawptr, } +Generic :: rawptr; +Element :: struct { _private : u8, }; +Object :: rawptr; +LongDouble :: struct { value : [10]u8, }; +NodeType :: u64; +Error :: int; +Handle :: uint; +Response :: i32; +FileOffset :: u64; +ListViewIndex :: i32; +ThreadEntryFunction :: distinct #type proc (Generic); +ComparisonCallbackFunction :: distinct #type proc (rawptr, rawptr, Generic) -> i32; +SwapCallbackFunction :: distinct #type proc (rawptr, rawptr, Generic); +CRTComparisonCallback :: distinct #type proc (rawptr, rawptr) -> i32; +MessageCallbackFunction :: distinct #type proc (Object, ^Message, ^Response); +UICallbackFunction :: distinct #type proc (^Element, ^Message, ^Response); +Window :: struct { using element : Element, }; +Panel :: struct { using element : Element, }; +Scrollbar :: struct { using element : Element, }; +Button :: struct { using element : Element, }; +Textbox :: struct { using element : Element, }; +ListView :: struct { using element : Element, }; +NumericEntry :: struct { using element : Element, }; +Menu :: struct { using element : Element, }; +MenuCallbackFunction :: distinct #type proc (^Element, Generic); +INSTANCE_TYPE :: Instance; +SCANCODE_A :: (0x1C); +SCANCODE_B :: (0x32); +SCANCODE_C :: (0x21); +SCANCODE_D :: (0x23); +SCANCODE_E :: (0x24); +SCANCODE_F :: (0x2B); +SCANCODE_G :: (0x34); +SCANCODE_H :: (0x33); +SCANCODE_I :: (0x43); +SCANCODE_J :: (0x3B); +SCANCODE_K :: (0x42); +SCANCODE_L :: (0x4B); +SCANCODE_M :: (0x3A); +SCANCODE_N :: (0x31); +SCANCODE_O :: (0x44); +SCANCODE_P :: (0x4D); +SCANCODE_Q :: (0x15); +SCANCODE_R :: (0x2D); +SCANCODE_S :: (0x1B); +SCANCODE_T :: (0x2C); +SCANCODE_U :: (0x3C); +SCANCODE_V :: (0x2A); +SCANCODE_W :: (0x1D); +SCANCODE_X :: (0x22); +SCANCODE_Y :: (0x35); +SCANCODE_Z :: (0x1A); +SCANCODE_0 :: (0x45); +SCANCODE_1 :: (0x16); +SCANCODE_2 :: (0x1E); +SCANCODE_3 :: (0x26); +SCANCODE_4 :: (0x25); +SCANCODE_5 :: (0x2E); +SCANCODE_6 :: (0x36); +SCANCODE_7 :: (0x3D); +SCANCODE_8 :: (0x3E); +SCANCODE_9 :: (0x46); +SCANCODE_CAPS_LOCK :: (0x58); +SCANCODE_SCROLL_LOCK :: (0x7E); +SCANCODE_NUM_LOCK :: (0x77) ; +SCANCODE_LEFT_SHIFT :: (0x12); +SCANCODE_LEFT_CTRL :: (0x14); +SCANCODE_LEFT_ALT :: (0x11); +SCANCODE_LEFT_FLAG :: (0x11F); +SCANCODE_RIGHT_SHIFT :: (0x59); +SCANCODE_RIGHT_CTRL :: (0x114); +SCANCODE_RIGHT_ALT :: (0x111); +SCANCODE_PAUSE :: (0xE1); +SCANCODE_CONTEXT_MENU :: (0x127); +SCANCODE_BACKSPACE :: (0x66); +SCANCODE_ESCAPE :: (0x76); +SCANCODE_INSERT :: (0x170); +SCANCODE_HOME :: (0x16C); +SCANCODE_PAGE_UP :: (0x17D); +SCANCODE_DELETE :: (0x171); +SCANCODE_END :: (0x169); +SCANCODE_PAGE_DOWN :: (0x17A); +SCANCODE_UP_ARROW :: (0x175); +SCANCODE_LEFT_ARROW :: (0x16B); +SCANCODE_DOWN_ARROW :: (0x172); +SCANCODE_RIGHT_ARROW :: (0x174); +SCANCODE_SPACE :: (0x29); +SCANCODE_TAB :: (0x0D); +SCANCODE_ENTER :: (0x5A); +SCANCODE_SLASH :: (0x4A); +SCANCODE_BACKSLASH :: (0x5D); +SCANCODE_LEFT_BRACE :: (0x54); +SCANCODE_RIGHT_BRACE :: (0x5B); +SCANCODE_EQUALS :: (0x55); +SCANCODE_BACKTICK :: (0x0E); +SCANCODE_HYPHEN :: (0x4E); +SCANCODE_SEMICOLON :: (0x4C); +SCANCODE_QUOTE :: (0x52); +SCANCODE_COMMA :: (0x41); +SCANCODE_PERIOD :: (0x49); +SCANCODE_NUM_DIVIDE :: (0x14A); +SCANCODE_NUM_MULTIPLY :: (0x7C); +SCANCODE_NUM_SUBTRACT :: (0x7B); +SCANCODE_NUM_ADD :: (0x79); +SCANCODE_NUM_ENTER :: (0x15A); +SCANCODE_NUM_POINT :: (0x71); +SCANCODE_NUM_0 :: (0x70); +SCANCODE_NUM_1 :: (0x69); +SCANCODE_NUM_2 :: (0x72); +SCANCODE_NUM_3 :: (0x7A); +SCANCODE_NUM_4 :: (0x6B); +SCANCODE_NUM_5 :: (0x73); +SCANCODE_NUM_6 :: (0x74); +SCANCODE_NUM_7 :: (0x6C); +SCANCODE_NUM_8 :: (0x75); +SCANCODE_NUM_9 :: (0x7D); +SCANCODE_PRINT_SCREEN_1 :: (0x112) ; +SCANCODE_PRINT_SCREEN_2 :: (0x17C); +SCANCODE_F1 :: (0x05); +SCANCODE_F2 :: (0x06); +SCANCODE_F3 :: (0x04); +SCANCODE_F4 :: (0x0C); +SCANCODE_F5 :: (0x03); +SCANCODE_F6 :: (0x0B); +SCANCODE_F7 :: (0x83); +SCANCODE_F8 :: (0x0A); +SCANCODE_F9 :: (0x01); +SCANCODE_F10 :: (0x09); +SCANCODE_F11 :: (0x78); +SCANCODE_F12 :: (0x07); +SCANCODE_ACPI_POWER :: (0x137); +SCANCODE_ACPI_SLEEP :: (0x13F); +SCANCODE_ACPI_WAKE :: (0x15E); +SCANCODE_MM_NEXT :: (0x14D); +SCANCODE_MM_PREVIOUS :: (0x115); +SCANCODE_MM_STOP :: (0x13B); +SCANCODE_MM_PAUSE :: (0x134); +SCANCODE_MM_MUTE :: (0x123); +SCANCODE_MM_QUIETER :: (0x121); +SCANCODE_MM_LOUDER :: (0x132); +SCANCODE_MM_SELECT :: (0x150); +SCANCODE_MM_EMAIL :: (0x148); +SCANCODE_MM_CALC :: (0x12B); +SCANCODE_MM_FILES :: (0x140); +SCANCODE_WWW_SEARCH :: (0x110); +SCANCODE_WWW_HOME :: (0x13A); +SCANCODE_WWW_BACK :: (0x138); +SCANCODE_WWW_FORWARD :: (0x130); +SCANCODE_WWW_STOP :: (0x128); +SCANCODE_WWW_REFRESH :: (0x120); +SCANCODE_WWW_STARRED :: (0x118); +PROCESS_STATE_ALL_THREADS_TERMINATED :: (1); +PROCESS_STATE_TERMINATING :: (2); +PROCESS_STATE_CRASHED :: (4); +FLAGS_DEFAULT :: (0); +SUCCESS :: (-1); +ERROR_BUFFER_TOO_SMALL :: (-2); +ERROR_UNKNOWN_OPERATION_FAILURE :: (-7); +ERROR_NO_MESSAGES_AVAILABLE :: (-9); +ERROR_MESSAGE_QUEUE_FULL :: (-10); +ERROR_MESSAGE_NOT_HANDLED_BY_GUI :: (-13); +ERROR_PATH_NOT_WITHIN_MOUNTED_VOLUME :: (-14); +ERROR_PATH_NOT_TRAVERSABLE :: (-15); +ERROR_FILE_ALREADY_EXISTS :: (-19); +ERROR_FILE_DOES_NOT_EXIST :: (-20); +ERROR_DRIVE_ERROR_FILE_DAMAGED :: (-21) ; +ERROR_ACCESS_NOT_WITHIN_FILE_BOUNDS :: (-22) ; +ERROR_FILE_PERMISSION_NOT_GRANTED :: (-23); +ERROR_FILE_IN_EXCLUSIVE_USE :: (-24); +ERROR_FILE_CANNOT_GET_EXCLUSIVE_USE :: (-25); +ERROR_INCORRECT_NODE_TYPE :: (-26); +ERROR_EVENT_NOT_SET :: (-27); +ERROR_TIMEOUT_REACHED :: (-29); +ERROR_REQUEST_CLOSED_BEFORE_COMPLETE :: (-30); +ERROR_NO_CHARACTER_AT_COORDINATE :: (-31); +ERROR_FILE_ON_READ_ONLY_VOLUME :: (-32); +ERROR_USER_CANCELED_IO :: (-33); +ERROR_INVALID_DIMENSIONS :: (-34); +ERROR_DRIVE_CONTROLLER_REPORTED :: (-35); +ERROR_COULD_NOT_ISSUE_PACKET :: (-36); +ERROR_HANDLE_TABLE_FULL :: (-37); +ERROR_COULD_NOT_RESIZE_FILE :: (-38); +ERROR_DIRECTORY_NOT_EMPTY :: (-39); +ERROR_UNSUPPORTED_FILESYSTEM :: (-40); +ERROR_NODE_ALREADY_DELETED :: (-41); +ERROR_NODE_IS_ROOT :: (-42); +ERROR_VOLUME_MISMATCH :: (-43); +ERROR_TARGET_WITHIN_SOURCE :: (-44); +ERROR_TARGET_INVALID_TYPE :: (-45); +ERROR_NOTHING_TO_DRAW :: (-46); +ERROR_MALFORMED_NODE_PATH :: (-47); +ERROR_OUT_OF_CACHE_RESOURCES :: (-48); +ERROR_TARGET_IS_SOURCE :: (-49); +ERROR_INVALID_NAME :: (-50); +ERROR_CORRUPT_DATA :: (-51); +ERROR_INSUFFICIENT_RESOURCES :: (-52); +ERROR_UNSUPPORTED_FEATURE :: (-53); +ERROR_FILE_TOO_FRAGMENTED :: (-54); +ERROR_DRIVE_FULL :: (-55); +ERROR_COULD_NOT_RESOLVE_SYMBOL :: (-56); +ERROR_ALREADY_EMBEDDED :: (-57); +SYSTEM_CONSTANT_TIME_STAMP_UNITS_PER_MICROSECOND :: (0); +SYSTEM_CONSTANT_NO_FANCY_GRAPHICS :: (2); +SYSTEM_CONSTANT_REPORTED_PROBLEMS :: (3); +SYSTEM_CONSTANT_RIGHT_TO_LEFT :: (4); +INVALID_HANDLE :: ((Handle) (0)); +CURRENT_THREAD :: ((Handle) (0x10)); +CURRENT_PROCESS :: ((Handle) (0x11)); +SURFACE_UI_SHEET :: ((Handle) (0x20)); +SURFACE_WALLPAPER :: ((Handle) (0x21)); +DRAW_ALPHA_OVERWRITE :: (0x100); +DRAW_ALPHA_FULL :: (0x200) ; +WAIT_NO_TIMEOUT :: (-1); +MAX_WAIT_COUNT :: (16); +MAX_DIRECTORY_CHILD_NAME_LENGTH :: (256); +PROCESS_EXECUTABLE_NOT_LOADED :: 0; +PROCESS_EXECUTABLE_FAILED_TO_LOAD :: 1; +PROCESS_EXECUTABLE_LOADED :: 2; +SNAPSHOT_MAX_PROCESS_NAME_LENGTH :: (80); +SYSTEM_SNAPSHOT_PROCESSES :: (1); +SYSTEM_SNAPSHOT_DRIVES :: (2); +NOT_HANDLED :: (-1); +HANDLED :: (0); +REJECTED :: (-2); +SHARED_MEMORY_MAXIMUM_SIZE :: ( (1024) * 1024 * 1024 * 1024); +SHARED_MEMORY_NAME_MAX_LENGTH :: (32); +MAP_OBJECT_ALL :: (0); +DRAW_STRING_HALIGN_LEFT :: (1); +DRAW_STRING_HALIGN_RIGHT :: (2); +DRAW_STRING_HALIGN_CENTER :: (3); +DRAW_STRING_VALIGN_TOP :: (4); +DRAW_STRING_VALIGN_BOTTOM :: (8); +DRAW_STRING_VALIGN_CENTER :: (12); +DRAW_STRING_CLIP :: (0); +DRAW_STRING_WORD_WRAP :: (16); +DRAW_STRING_ELLIPSIS :: (32); +NODE_READ_NONE :: (0x0); +NODE_READ_BLOCK :: (0x1); +NODE_READ_ACCESS :: (0x2); +NODE_READ_EXCLUSIVE :: (0x3); +NODE_WRITE_NONE :: (0x00); +NODE_WRITE_BLOCK :: (0x10); +NODE_WRITE_ACCESS :: (0x20); +NODE_WRITE_EXCLUSIVE :: (0x30); +NODE_RESIZE_NONE :: (0x000); +NODE_RESIZE_BLOCK :: (0x100); +NODE_RESIZE_ACCESS :: (0x200); +NODE_RESIZE_EXCLUSIVE :: (0x300); +NODE_FAIL_IF_FOUND :: (0x1000); +NODE_FAIL_IF_NOT_FOUND :: (0x2000); +NODE_CREATE_DIRECTORIES :: (0x8000) ; +NODE_POSIX_NAMESPACE :: (0x10000) ; +DIRECTORY_CHILDREN_UNKNOWN :: ( (-1)); +MEMORY_OPEN_FAIL_IF_FOUND :: (0x1000); +MEMORY_OPEN_FAIL_IF_NOT_FOUND :: (0x2000); +MAP_OBJECT_READ_WRITE :: (0); +MAP_OBJECT_READ_ONLY :: (1); +MAP_OBJECT_COPY_ON_WRITE :: (2); +BOX_STYLE_OUTWARDS :: (0x01) ; +BOX_STYLE_INWARDS :: (0x02) ; +BOX_STYLE_NEUTRAL :: (0x03) ; +BOX_STYLE_FLAT :: (0x04) ; +BOX_STYLE_NONE :: (0x05) ; +BOX_STYLE_SELECTED :: (0x06) ; +BOX_STYLE_PUSHED :: (0x07) ; +BOX_STYLE_DOTTED :: (0x80); +BOX_COLOR_GRAY :: (0xC0C0C0); +BOX_COLOR_DARK_GRAY :: (0x808080); +BOX_COLOR_WHITE :: (0xFFFFFF); +BOX_COLOR_BLUE :: (0x000080); +BOX_COLOR_TRANSPARENT :: (0xFF00FF); +BOX_COLOR_BLACK :: (0x000000); +STRING_FORMAT_ENOUGH_SPACE :: ( (-1)); +POSIX_SYSCALL_GET_POSIX_FD_PATH :: (0x10000); +PERMISSION_ACCESS_SYSTEM_FILES :: (1 << 0); +PERMISSION_ACCESS_USER_FILES :: (1 << 1); +PERMISSION_PROCESS_CREATE :: (1 << 2); +PERMISSION_PROCESS_OPEN :: (1 << 3); +PERMISSION_SCREEN_MODIFY :: (1 << 4) ; +PERMISSION_SHUTDOWN :: (1 << 5); +PERMISSION_TAKE_SYSTEM_SNAPSHOT :: (1 << 6); +PERMISSION_WINDOW_OPEN :: (1 << 7); +PERMISSION_ALL :: ( (-1)); +PERMISSION_INHERIT :: ( (1 << 63)); +PANEL_STYLE_DEFAULT :: "Panel.Default"; +PANEL_STYLE_MENU_COLUMN :: "Panel.Menu.Column"; +PANEL_WRAP :: ( (0x0001) << 32); +PANEL_H_LEFT :: ( (0x0010) << 32); +PANEL_H_RIGHT :: ( (0x0020) << 32); +PANEL_H_CENTER :: ( (0x0040) << 32); +PANEL_H_JUSTIFY :: ( (0x0080) << 32); +PANEL_V_TOP :: ( (0x0100) << 32); +PANEL_V_BOTTOM :: ( (0x0200) << 32); +PANEL_V_CENTER :: ( (0x0400) << 32); +PANEL_V_JUSTIFY :: ( (0x0800) << 32); +PANEL_H_SCROLL :: ( (0x1000) << 32); +PANEL_V_SCROLL :: ( (0x2000) << 32); +CELL_H_PUSH :: ( (0x0001) << 16); +CELL_H_EXPAND :: ( (0x0002) << 16); +CELL_H_LEFT :: ( (0x0004) << 16); +CELL_H_RIGHT :: ( (0x0008) << 16); +CELL_H_SHRINK :: ( (0x0010) << 16); +CELL_V_PUSH :: ( (0x0100) << 16); +CELL_V_EXPAND :: ( (0x0200) << 16); +CELL_V_TOP :: ( (0x0400) << 16); +CELL_V_BOTTOM :: ( (0x0800) << 16); +CELL_V_SHRINK :: ( (0x1000) << 16); +CELL_NEW_BAND :: ( (0x8000) << 16); +CELL_HIDDEN :: ( (0xFFFF) << 16); +ELEMENT_DO_NOT_FREE_STYLE_OVERRIDE :: (1 << 0); +ELEMENT_RICH_TEXT :: (1 << 1); +ELEMENT_FOCUSABLE :: (1 << 2); +ELEMENT_Z_STACK :: (1 << 3) ; +ELEMENT_HIDDEN :: (1 << 4); +ELEMENT_USE_CHILD_AS_PARENT :: (1 << 5) ; +ELEMENT_DISABLED :: (1 << 6); +ELEMENT_WINDOW_COORDS_FOR_MOUSE :: (1 << 7) ; +TEXTBOX_MULTILINE :: (1 << 0); +TEXTBOX_BORDERED :: (1 << 1); +BUTTON_DEFAULT :: ( (1) << 32); +BUTTON_DANGEROUS :: ( (1) << 33); +BUTTON_MENU_ITEM :: ( (1) << 34); +BUTTON_NOT_FOCUSABLE :: ( (1) << 35); +BUTTON_TOOLBAR :: ( (1) << 36); +SCROLLBAR_VERTICAL :: ( (0) << 32); +SCROLLBAR_HORIZONTAL :: ( (1) << 32); +LIST_VIEW_INDEX_GROUP_HEADER :: (-1); +LIST_VIEW_ITEM_CONTENT_TEXT :: (1 << 0); +LIST_VIEW_ITEM_CONTENT_ICON :: (1 << 1); +LIST_VIEW_ITEM_CONTENT_INDENTATION :: (1 << 2); +LIST_VIEW_ITEM_STATE_SELECTED :: (1 << 0); +LIST_VIEW_ITEM_STATE_CHECKED :: (1 << 1); +LIST_VIEW_ITEM_STATE_HIDDEN :: (1 << 2); +LIST_VIEW_ITEM_STATE_EXPANDABLE :: (1 << 3); +LIST_VIEW_ITEM_STATE_CHECKABLE :: (1 << 4); +LIST_VIEW_ITEM_STATE_DROP_TARGET :: (1 << 5); +LIST_VIEW_ITEM_STATE_COLLAPSABLE :: (1 << 6); +LIST_VIEW_ITEM_STATE_PARTIAL_CHECK :: (1 << 7); +LIST_VIEW_ITEM_STATE_DRAG_SOURCE :: (1 << 8); +LIST_VIEW_ITEM_STATE_CUT :: (1 << 9); +LIST_VIEW_FIND_ITEM_FROM_Y_POSITION :: (0); +LIST_VIEW_FIND_ITEM_FROM_TEXT_PREFIX :: (1); +LIST_VIEW_FIND_ITEM_NON_HIDDEN :: (2); +LIST_VIEW_FIND_ITEM_PARENT :: (3); +LIST_VIEW_COLUMN_DEFAULT_WIDTH_PRIMARY :: (270); +LIST_VIEW_COLUMN_DEFAULT_WIDTH_SECONDARY :: (130); +LIST_VIEW_COLUMN_PRIMARY :: (1); +LIST_VIEW_COLUMN_RIGHT_ALIGNED :: (2); +LIST_VIEW_COLUMN_SORT_ASCENDING :: (8); +LIST_VIEW_COLUMN_SORT_DESCENDING :: (16); +LIST_VIEW_COLUMN_SORTABLE :: (32); +LIST_VIEW_SINGLE_SELECT :: ( (1) << 32) ; +LIST_VIEW_MULTI_SELECT :: ( (1) << 33) ; +LIST_VIEW_HAS_COLUMNS :: ( (1) << 34) ; +LIST_VIEW_HAS_GROUPS :: ( (1) << 35) ; +LIST_VIEW_FIXED_HEIGHT :: ( (1) << 36) ; +LIST_VIEW_VARIABLE_HEIGHT :: ( (1) << 37) ; +LIST_VIEW_TREE :: ( (1) << 38) ; +LIST_VIEW_TILED :: ( (1) << 39) ; +LIST_VIEW_BORDERED :: ( (1) << 41) ; +LIST_VIEW_DROP_TARGET_ORDERED :: ( (1) << 43) ; +LIST_VIEW_DROP_TARGET_UNORDERED :: ( (1) << 44) ; +LIST_VIEW_ROW_DIVIDERS :: ( (1) << 45) ; +LIST_VIEW_STATIC_GROUP_HEADERS :: ( (1) << 46) ; +LIST_VIEW_COLLAPSABLE_GROUPS :: ( (1) << 47) ; +LIST_VIEW_INTERNAL_SELECTION_STORAGE :: ( (1) << 48) ; +LIST_VIEW_HAND_CURSOR :: ( (1) << 49) ; +LIST_VIEW_NO_ITEM_BACKGROUNDS :: ( (1) << 50) ; +LIST_VIEW_RICH_TEXT :: ( (1) << 52) ; +LIST_VIEW_LABELS_BELOW :: ( (1) << 53) ; +LIST_VIEW_MAXIMUM_ITEMS :: (10 * 1000 * 1000); +LIST_VIEW_MAXIMUM_GROUPS :: (10 * 1000); +LIST_VIEW_TRANSITION_BACKWARDS :: (1); +LIST_VIEW_TRANSITION_DRAW_NEW_CONTENTS_ONCE :: (2) ; +MENU_AT_CURSOR :: (1 << 0); +StandardIcon :: enum { + ICON_ACTION_UNAVAILABLE_SYMBOLIC, + ICON_ADDRESS_BOOK_NEW, + ICON_ADDRESS_BOOK_NEW_SYMBOLIC, + ICON_ALIGN_HORIZONTAL_CENTER, + ICON_ALIGN_HORIZONTAL_CENTER_SYMBOLIC, + ICON_ALIGN_HORIZONTAL_LEFT, + ICON_ALIGN_HORIZONTAL_LEFT_SYMBOLIC, + ICON_ALIGN_HORIZONTAL_LEFT_TO_ANCHOR, + ICON_ALIGN_HORIZONTAL_LEFT_TO_ANCHOR_SYMBOLIC, + ICON_ALIGN_HORIZONTAL_RIGHT, + ICON_ALIGN_HORIZONTAL_RIGHT_SYMBOLIC, + ICON_ALIGN_HORIZONTAL_RIGHT_TO_ANCHOR, + ICON_ALIGN_HORIZONTAL_RIGHT_TO_ANCHOR_SYMBOLIC, + ICON_ALIGN_VERTICAL_BOTTOM, + ICON_ALIGN_VERTICAL_BOTTOM_SYMBOLIC, + ICON_ALIGN_VERTICAL_BOTTOM_TO_ANCHOR, + ICON_ALIGN_VERTICAL_BOTTOM_TO_ANCHOR_SYMBOLIC, + ICON_ALIGN_VERTICAL_CENTER, + ICON_ALIGN_VERTICAL_CENTER_SYMBOLIC, + ICON_ALIGN_VERTICAL_TOP, + ICON_ALIGN_VERTICAL_TOP_SYMBOLIC, + ICON_ALIGN_VERTICAL_TOP_TO_ANCHOR, + ICON_ALIGN_VERTICAL_TOP_TO_ANCHOR_SYMBOLIC, + ICON_APPLICATION_ADD_SYMBOLIC, + ICON_APPOINTMENT_NEW, + ICON_APPOINTMENT_NEW_SYMBOLIC, + ICON_APPOINTMENT_SYMBOLIC, + ICON_BOOKMARK_NEW, + ICON_BOOKMARK_NEW_SYMBOLIC, + ICON_CALL_START, + ICON_CALL_START_SYMBOLIC, + ICON_CALL_STOP, + ICON_CALL_STOP_SYMBOLIC, + ICON_COLOR_FILL, + ICON_COLOR_GRADIENT, + ICON_COLOR_GRADIENT_MESH, + ICON_COLOR_SELECT_SYMBOLIC, + ICON_CONTACT_NEW, + ICON_CONTACT_NEW_SYMBOLIC, + ICON_DISTRIBUTE_HORIZONTAL_CENTER, + ICON_DISTRIBUTE_HORIZONTAL_GAPS, + ICON_DISTRIBUTE_HORIZONTAL_LEFT, + ICON_DISTRIBUTE_HORIZONTAL_RIGHT, + ICON_DISTRIBUTE_VERTICAL_BOTTOM, + ICON_DISTRIBUTE_VERTICAL_CENTER, + ICON_DISTRIBUTE_VERTICAL_GAPS, + ICON_DISTRIBUTE_VERTICAL_TOP, + ICON_DOCUMENT_EDIT, + ICON_DOCUMENT_EDIT_SYMBOLIC, + ICON_DOCUMENT_EXPORT, + ICON_DOCUMENT_EXPORT_SYMBOLIC, + ICON_DOCUMENT_IMPORT, + ICON_DOCUMENT_IMPORT_SYMBOLIC, + ICON_DOCUMENT_NEW, + ICON_DOCUMENT_NEW_SYMBOLIC, + ICON_DOCUMENT_OPEN_RECENT, + ICON_DOCUMENT_OPEN_RECENT_SYMBOLIC, + ICON_DOCUMENT_OPEN_SYMBOLIC, + ICON_DOCUMENT_PAGE_SETUP, + ICON_DOCUMENT_PAGE_SETUP_SYMBOLIC, + ICON_DOCUMENT_PRINT_PREVIEW, + ICON_DOCUMENT_PRINT_PREVIEW_SYMBOLIC, + ICON_DOCUMENT_PRINT_SYMBOLIC, + ICON_DOCUMENT_PROPERTIES, + ICON_DOCUMENT_PROPERTIES_SYMBOLIC, + ICON_DOCUMENT_REVERT, + ICON_DOCUMENT_REVERT_SYMBOLIC, + ICON_DOCUMENT_SAVE_AS, + ICON_DOCUMENT_SAVE_AS_SYMBOLIC, + ICON_DOCUMENT_SAVE_SYMBOLIC, + ICON_DOCUMENT_SEND, + ICON_DOCUMENT_SEND_SYMBOLIC, + ICON_DRAW_CUBOID, + ICON_DRAW_ELLIPSE, + ICON_DRAW_ERASER, + ICON_DRAW_FREEHAND, + ICON_DRAW_PATH, + ICON_DRAW_POLYGON_STAR, + ICON_DRAW_RECTANGLE, + ICON_DRAW_SPIRAL, + ICON_DRAW_TEXT, + ICON_EDIT_CLEAR, + ICON_EDIT_CLEAR_ALL_SYMBOLIC, + ICON_EDIT_CLEAR_SYMBOLIC, + ICON_EDIT_COPY, + ICON_EDIT_COPY_SYMBOLIC, + ICON_EDIT_CUT, + ICON_EDIT_CUT_SYMBOLIC, + ICON_EDIT_DELETE_SYMBOLIC, + ICON_EDIT_FIND, + ICON_EDIT_FIND_REPLACE, + ICON_EDIT_FIND_REPLACE_SYMBOLIC, + ICON_EDIT_FIND_SYMBOLIC, + ICON_EDIT_FLAG, + ICON_EDIT_FLAG_SYMBOLIC, + ICON_EDIT_MARK, + ICON_EDIT_PASTE, + ICON_EDIT_PASTE_SYMBOLIC, + ICON_EDIT_REDO, + ICON_EDIT_REDO_SYMBOLIC, + ICON_EDIT_SELECT_ALL, + ICON_EDIT_SELECT_ALL_SYMBOLIC, + ICON_EDIT_SELECT_SYMBOLIC, + ICON_EDIT_UNDO, + ICON_EDIT_UNDO_ARCHIVE, + ICON_EDIT_UNDO_SYMBOLIC, + ICON_ERROR_CORRECT_SYMBOLIC, + ICON_EVENT_NEW, + ICON_FIND_LOCATION, + ICON_FIND_LOCATION_SYMBOLIC, + ICON_FOLDER_COPY, + ICON_FOLDER_MOVE, + ICON_FOLDER_NEW, + ICON_FOLDER_NEW_SYMBOLIC, + ICON_FONT_SELECT_SYMBOLIC, + ICON_FORMAT_INDENT_LESS, + ICON_FORMAT_INDENT_LESS_SYMBOLIC, + ICON_FORMAT_INDENT_MORE, + ICON_FORMAT_INDENT_MORE_SYMBOLIC, + ICON_FORMAT_JUSTIFY_CENTER, + ICON_FORMAT_JUSTIFY_CENTER_SYMBOLIC, + ICON_FORMAT_JUSTIFY_FILL, + ICON_FORMAT_JUSTIFY_FILL_SYMBOLIC, + ICON_FORMAT_JUSTIFY_LEFT, + ICON_FORMAT_JUSTIFY_LEFT_SYMBOLIC, + ICON_FORMAT_JUSTIFY_RIGHT, + ICON_FORMAT_JUSTIFY_RIGHT_SYMBOLIC, + ICON_FORMAT_TEXT_BOLD, + ICON_FORMAT_TEXT_BOLD_ES_SYMBOLIC, + ICON_FORMAT_TEXT_BOLD_FR_SYMBOLIC, + ICON_FORMAT_TEXT_BOLD_SYMBOLIC, + ICON_FORMAT_TEXT_CLEAR_FORMATTING_SYMBOLIC, + ICON_FORMAT_TEXT_DIRECTION_LTR_SYMBOLIC, + ICON_FORMAT_TEXT_HIGHLIGHT, + ICON_FORMAT_TEXT_ITALIC, + ICON_FORMAT_TEXT_ITALIC_ES_SYMBOLIC, + ICON_FORMAT_TEXT_ITALIC_SYMBOLIC, + ICON_FORMAT_TEXT_LARGER_SYMBOLIC, + ICON_FORMAT_TEXT_NONE, + ICON_FORMAT_TEXT_SMALLER_SYMBOLIC, + ICON_FORMAT_TEXT_STRIKETHROUGH, + ICON_FORMAT_TEXT_STRIKETHROUGH_FR_SYMBOLIC, + ICON_FORMAT_TEXT_STRIKETHROUGH_SYMBOLIC, + ICON_FORMAT_TEXT_UNDERLINE, + ICON_FORMAT_TEXT_UNDERLINE_FR_SYMBOLIC, + ICON_FORMAT_TEXT_UNDERLINE_SYMBOLIC, + ICON_GO_BOTTOM, + ICON_GO_BOTTOM_SYMBOLIC, + ICON_GO_DOWN, + ICON_GO_DOWN_SYMBOLIC, + ICON_GO_FIRST, + ICON_GO_FIRST_SYMBOLIC, + ICON_GO_HOME_SYMBOLIC, + ICON_GO_JUMP, + ICON_GO_JUMP_SYMBOLIC, + ICON_GO_LAST, + ICON_GO_LAST_SYMBOLIC, + ICON_GO_NEXT, + ICON_GO_NEXT_SYMBOLIC, + ICON_GO_PREVIOUS, + ICON_GO_PREVIOUS_SYMBOLIC, + ICON_GO_TOP, + ICON_GO_TOP_SYMBOLIC, + ICON_GO_UP, + ICON_GO_UP_SYMBOLIC, + ICON_HELP_ABOUT, + ICON_HELP_ABOUT_SYMBOLIC, + ICON_HELP_CONTENTS, + ICON_HELP_CONTENTS_SYMBOLIC, + ICON_HELP_INFO_SYMBOLIC, + ICON_IMAGE_ADJUST, + ICON_IMAGE_AUTO_ADJUST, + ICON_IMAGE_CROP, + ICON_IMAGE_CROP_SYMBOLIC, + ICON_IMAGE_RED_EYE, + ICON_IMAGE_RED_EYE_SYMBOLIC, + ICON_INSERT_IMAGE, + ICON_INSERT_IMAGE_SYMBOLIC, + ICON_INSERT_LINK, + ICON_INSERT_LINK_SYMBOLIC, + ICON_INSERT_OBJECT, + ICON_INSERT_OBJECT_SYMBOLIC, + ICON_INSERT_TEXT_SYMBOLIC, + ICON_LIST_ADD, + ICON_LIST_ADD_SYMBOLIC, + ICON_LIST_REMOVE, + ICON_LIST_REMOVE_SYMBOLIC, + ICON_MAIL_ARCHIVE, + ICON_MAIL_FORWARD, + ICON_MAIL_FORWARD_SYMBOLIC, + ICON_MAIL_MARK_IMPORTANT, + ICON_MAIL_MARK_IMPORTANT_SYMBOLIC, + ICON_MAIL_MARK_JUNK, + ICON_MAIL_MARK_JUNK_SYMBOLIC, + ICON_MAIL_MARK_NOTJUNK, + ICON_MAIL_MARK_NOTJUNK_SYMBOLIC, + ICON_MAIL_MESSAGE_NEW, + ICON_MAIL_MESSAGE_NEW_SYMBOLIC, + ICON_MAIL_MOVE, + ICON_MAIL_MOVE_SYMBOLIC, + ICON_MAIL_REPLY_ALL, + ICON_MAIL_REPLY_ALL_SYMBOLIC, + ICON_MAIL_REPLY_SENDER, + ICON_MAIL_REPLY_SENDER_SYMBOLIC, + ICON_MAIL_SEND, + ICON_MAIL_SEND_RECEIVE_SYMBOLIC, + ICON_MAIL_SEND_SYMBOLIC, + ICON_MARK_LOCATION_SYMBOLIC, + ICON_MEDIA_EJECT, + ICON_MEDIA_EJECT_SYMBOLIC, + ICON_MEDIA_EQ_SYMBOLIC, + ICON_MEDIA_PLAYBACK_PAUSE, + ICON_MEDIA_PLAYBACK_PAUSE_SYMBOLIC, + ICON_MEDIA_PLAYBACK_START, + ICON_MEDIA_PLAYBACK_START_SYMBOLIC, + ICON_MEDIA_PLAYBACK_STOP, + ICON_MEDIA_PLAYBACK_STOP_SYMBOLIC, + ICON_MEDIA_RECORD, + ICON_MEDIA_RECORD_SYMBOLIC, + ICON_MEDIA_SEEK_BACKWARD, + ICON_MEDIA_SEEK_BACKWARD_SYMBOLIC, + ICON_MEDIA_SEEK_FORWARD, + ICON_MEDIA_SEEK_FORWARD_SYMBOLIC, + ICON_MEDIA_SKIP_BACKWARD, + ICON_MEDIA_SKIP_FORWARD, + ICON_MEDIA_VIEW_SUBTITLES_SYMBOLIC, + ICON_NODE_ADD, + ICON_NODE_ALIGN_HORIZONTAL, + ICON_NODE_ALIGN_VERTICAL, + ICON_NODE_BREAK, + ICON_NODE_CUSP, + ICON_NODE_DELETE, + ICON_NODE_DELETE_SEGMENT, + ICON_NODE_DISTRIBUTE_HORIZONTAL, + ICON_NODE_DISTRIBUTE_VERTICAL, + ICON_NODE_INSERT, + ICON_NODE_JOIN, + ICON_NODE_JOIN_SEGMENT, + ICON_NODE_SMOOTH, + ICON_NODE_SYMMETRIC, + ICON_OBJECT_FLIP_HORIZONTAL, + ICON_OBJECT_FLIP_HORIZONTAL_SYMBOLIC, + ICON_OBJECT_FLIP_VERTICAL, + ICON_OBJECT_FLIP_VERTICAL_SYMBOLIC, + ICON_OBJECT_GROUP, + ICON_OBJECT_GROUP_SYMBOLIC, + ICON_OBJECT_INVERSE, + ICON_OBJECT_INVERSE_SYMBOLIC, + ICON_OBJECT_MERGE, + ICON_OBJECT_ROTATE_LEFT, + ICON_OBJECT_ROTATE_LEFT_SYMBOLIC, + ICON_OBJECT_ROTATE_RIGHT, + ICON_OBJECT_ROTATE_RIGHT_SYMBOLIC, + ICON_OBJECT_SELECT_SYMBOLIC, + ICON_OBJECT_STRAIGHTEN, + ICON_OBJECT_TO_PATH, + ICON_OBJECT_UNGROUP, + ICON_OBJECT_UNGROUP_SYMBOLIC, + ICON_OPEN_MENU, + ICON_OPEN_MENU_SYMBOLIC, + ICON_PAN_DOWN_SYMBOLIC, + ICON_PAN_END_SYMBOLIC, + ICON_PAN_START_SYMBOLIC, + ICON_PAN_UP_SYMBOLIC, + ICON_PANE_HIDE_SYMBOLIC, + ICON_PANE_SHOW_SYMBOLIC, + ICON_PATH_BREAK_APART, + ICON_PATH_BREAK_APART_SYMBOLIC, + ICON_PATH_COMBINE, + ICON_PATH_COMBINE_SYMBOLIC, + ICON_PATH_DIFFERENCE, + ICON_PATH_DIFFERENCE_SYMBOLIC, + ICON_PATH_DIVISION, + ICON_PATH_DIVISION_SYMBOLIC, + ICON_PATH_EXCLUSION, + ICON_PATH_EXCLUSION_SYMBOLIC, + ICON_PATH_INTERSECTION, + ICON_PATH_INTERSECTION_SYMBOLIC, + ICON_PATH_UNION, + ICON_PATH_UNION_SYMBOLIC, + ICON_PROCESS_STOP, + ICON_PROCESS_STOP_SYMBOLIC, + ICON_SEGMENT_CURVE, + ICON_SEGMENT_LINE, + ICON_SELECTION_ADD, + ICON_SELECTION_BOTTOM, + ICON_SELECTION_BOTTOM_SYMBOLIC, + ICON_SELECTION_CHECKED, + ICON_SELECTION_END_SYMBOLIC, + ICON_SELECTION_LOWER, + ICON_SELECTION_LOWER_SYMBOLIC, + ICON_SELECTION_RAISE, + ICON_SELECTION_RAISE_SYMBOLIC, + ICON_SELECTION_REMOVE, + ICON_SELECTION_START_SYMBOLIC, + ICON_SELECTION_TOP, + ICON_SELECTION_TOP_SYMBOLIC, + ICON_SEND_TO, + ICON_SEND_TO_SYMBOLIC, + ICON_STAR_NEW_SYMBOLIC, + ICON_STROKE_TO_PATH, + ICON_SYSTEM_LOCK_SCREEN, + ICON_SYSTEM_LOCK_SCREEN_SYMBOLIC, + ICON_SYSTEM_LOG_OUT, + ICON_SYSTEM_REBOOT, + ICON_SYSTEM_RUN, + ICON_SYSTEM_RUN_SYMBOLIC, + ICON_SYSTEM_SHUTDOWN, + ICON_SYSTEM_SHUTDOWN_SYMBOLIC, + ICON_SYSTEM_SUSPEND, + ICON_TAB_NEW_SYMBOLIC, + ICON_TAG_NEW, + ICON_TAG_NEW_SYMBOLIC, + ICON_TOOL_MEASURE, + ICON_TOOL_NODE_EDITOR, + ICON_TOOLS_CHECK_SPELLING_SYMBOLIC, + ICON_TOOLS_TIMER_SYMBOLIC, + ICON_VIEW_COLUMN_SYMBOLIC, + ICON_VIEW_CONTINUOUS_SYMBOLIC, + ICON_VIEW_DUAL_SYMBOLIC, + ICON_VIEW_FILTER_SYMBOLIC, + ICON_VIEW_FULLSCREEN_SYMBOLIC, + ICON_VIEW_GRID_SYMBOLIC, + ICON_VIEW_LIST_COMPACT_SYMBOLIC, + ICON_VIEW_LIST_IMAGES_SYMBOLIC, + ICON_VIEW_LIST_SYMBOLIC, + ICON_VIEW_LIST_VIDEO_SYMBOLIC, + ICON_VIEW_MORE_HORIZONTAL_SYMBOLIC, + ICON_VIEW_MORE_SYMBOLIC, + ICON_VIEW_PAGED_SYMBOLIC, + ICON_VIEW_PIN_SYMBOLIC, + ICON_VIEW_REFRESH, + ICON_VIEW_REFRESH_SYMBOLIC, + ICON_VIEW_RESTORE_SYMBOLIC, + ICON_VIEW_SORT_ASCENDING_SYMBOLIC, + ICON_VIEW_SORT_DESCENDING_SYMBOLIC, + ICON_WINDOW_CLOSE, + ICON_WINDOW_CLOSE_SYMBOLIC, + ICON_WINDOW_MAXIMIZE_SYMBOLIC, + ICON_WINDOW_MINIMIZE_SYMBOLIC, + ICON_WINDOW_NEW, + ICON_WINDOW_NEW_SYMBOLIC, + ICON_WINDOW_POP_OUT_SYMBOLIC, + ICON_WINDOW_RESTORE_SYMBOLIC, + ICON_ZOOM_FIT_BEST, + ICON_ZOOM_FIT_BEST_SYMBOLIC, + ICON_ZOOM_IN, + ICON_ZOOM_IN_SYMBOLIC, + ICON_ZOOM_ORIGINAL, + ICON_ZOOM_ORIGINAL_SYMBOLIC, + ICON_ZOOM_OUT, + ICON_ZOOM_OUT_SYMBOLIC, + ICON_ACCESSORIES_CALCULATOR, + ICON_ACCESSORIES_CALCULATOR_SYMBOLIC, + ICON_ACCESSORIES_SCREENSHOT, + ICON_ACCESSORIES_TEXT_EDITOR, + ICON_ACCESSORIES_TEXT_EDITOR_SYMBOLIC, + ICON_APPLICATION_DEFAULT_ICON, + ICON_ARCHIVE_MANAGER, + ICON_INTERNET_CHAT, + ICON_INTERNET_CHAT_SYMBOLIC, + ICON_INTERNET_MAIL, + ICON_INTERNET_MAIL_SYMBOLIC, + ICON_INTERNET_NEWS_READER, + ICON_INTERNET_NEWS_READER_SYMBOLIC, + ICON_INTERNET_WEB_BROWSER, + ICON_INTERNET_WEB_BROWSER_SYMBOLIC, + ICON_MULTIMEDIA_AUDIO_PLAYER, + ICON_MULTIMEDIA_PHOTO_MANAGER, + ICON_MULTIMEDIA_VIDEO_PLAYER, + ICON_OFFICE_ADDRESS_BOOK, + ICON_OFFICE_CALENDAR, + ICON_OFFICE_CALENDAR_SYMBOLIC, + ICON_ONBOARD, + ICON_POSTSCRIPT_VIEWER, + ICON_PREFERENCES_DESKTOP, + ICON_PREFERENCES_DESKTOP_FONT, + ICON_SYSTEM_FILE_MANAGER, + ICON_SYSTEM_OS_INSTALLER, + ICON_SYSTEM_SOFTWARE_INSTALL, + ICON_SYSTEM_SOFTWARE_INSTALL_SYMBOLIC, + ICON_SYSTEM_SOFTWARE_UPDATE, + ICON_SYSTEM_USERS, + ICON_SYSTEM_USERS_SYMBOLIC, + ICON_UTILITIES_SYSTEM_MONITOR, + ICON_UTILITIES_TERMINAL, + ICON_UTILITIES_TERMINAL_SYMBOLIC, + ICON_APPLICATIONS_ACCESSORIES, + ICON_APPLICATIONS_AUDIO_SYMBOLIC, + ICON_APPLICATIONS_DEVELOPMENT, + ICON_APPLICATIONS_DEVELOPMENT_SYMBOLIC, + ICON_APPLICATIONS_EDUCATION, + ICON_APPLICATIONS_EDUCATION_SYMBOLIC, + ICON_APPLICATIONS_ENGINEERING_SYMBOLIC, + ICON_APPLICATIONS_FONTS, + ICON_APPLICATIONS_GAMES, + ICON_APPLICATIONS_GAMES_SYMBOLIC, + ICON_APPLICATIONS_GRAPHICS, + ICON_APPLICATIONS_GRAPHICS_SYMBOLIC, + ICON_APPLICATIONS_INTERFACEDESIGN, + ICON_APPLICATIONS_INTERNET_SYMBOLIC, + ICON_APPLICATIONS_MULTIMEDIA, + ICON_APPLICATIONS_MULTIMEDIA_SYMBOLIC, + ICON_APPLICATIONS_OFFICE, + ICON_APPLICATIONS_OFFICE_SYMBOLIC, + ICON_APPLICATIONS_OTHER, + ICON_APPLICATIONS_OTHER_SYMBOLIC, + ICON_APPLICATIONS_PHOTOGRAPHY, + ICON_APPLICATIONS_SCIENCE, + ICON_APPLICATIONS_SCIENCE_SYMBOLIC, + ICON_APPLICATIONS_UTILITIES, + ICON_APPLICATIONS_UTILITIES_SYMBOLIC, + ICON_APPLICATIONS_VIDEO_SYMBOLIC, + ICON_BUG, + ICON_BUG_SYMBOLIC, + ICON_EMOJI_ACTIVITY_SYMBOLIC, + ICON_EMOJI_BODY_SYMBOLIC, + ICON_EMOJI_FOOD_SYMBOLIC, + ICON_EMOJI_NATURE_SYMBOLIC, + ICON_EMOJI_OBJECTS_SYMBOLIC, + ICON_EMOJI_TRAVEL_SYMBOLIC, + ICON_EVENT_BIRTHDAY_SYMBOLIC, + ICON_PREFERENCES_BLUETOOTH_SYMBOLIC, + ICON_PREFERENCES_COLOR, + ICON_PREFERENCES_COLOR_SYMBOLIC, + ICON_PREFERENCES_DESKTOP_ACCESSIBILITY, + ICON_PREFERENCES_DESKTOP_ACCESSIBILITY_POINTING, + ICON_PREFERENCES_DESKTOP_ACCESSIBILITY_SYMBOLIC, + ICON_PREFERENCES_DESKTOP_ACCESSIBILITY_ZOOM, + ICON_PREFERENCES_DESKTOP_APPLICATIONS, + ICON_PREFERENCES_DESKTOP_DISPLAY, + ICON_PREFERENCES_DESKTOP_DISPLAY_SYMBOLIC, + ICON_PREFERENCES_DESKTOP_KEYBOARD, + ICON_PREFERENCES_DESKTOP_KEYBOARD_SYMBOLIC, + ICON_PREFERENCES_DESKTOP_LOCALE, + ICON_PREFERENCES_DESKTOP_LOCALE_SYMBOLIC, + ICON_PREFERENCES_DESKTOP_ONLINE_ACCOUNTS, + ICON_PREFERENCES_DESKTOP_ONLINE_ACCOUNTS_SYMBOLIC, + ICON_PREFERENCES_DESKTOP_PERIPHERALS, + ICON_PREFERENCES_DESKTOP_SOUND, + ICON_PREFERENCES_DESKTOP_WALLPAPER, + ICON_PREFERENCES_OTHER_SYMBOLIC, + ICON_PREFERENCES_SYSTEM, + ICON_PREFERENCES_SYSTEM_NETWORK, + ICON_PREFERENCES_SYSTEM_NETWORK_SYMBOLIC, + ICON_PREFERENCES_SYSTEM_NOTIFICATIONS, + ICON_PREFERENCES_SYSTEM_PARENTAL_CONTROL_SYMBOLIC, + ICON_PREFERENCES_SYSTEM_PARENTAL_CONTROLS, + ICON_PREFERENCES_SYSTEM_POWER, + ICON_PREFERENCES_SYSTEM_POWER_SYMBOLIC, + ICON_PREFERENCES_SYSTEM_PRIVACY_HOUSEKEEPING, + ICON_PREFERENCES_SYSTEM_SHARING, + ICON_PREFERENCES_SYSTEM_SHARING_SYMBOLIC, + ICON_PREFERENCES_SYSTEM_TIME, + ICON_PREFERENCES_SYSTEM_TIME_SYMBOLIC, + ICON_PREFERENCES_SYSTEM_WINDOWS, + ICON_AC_ADAPTER_SYMBOLIC, + ICON_AUDIO_CARD_SYMBOLIC, + ICON_AUDIO_HEADPHONES, + ICON_AUDIO_HEADPHONES_SYMBOLIC, + ICON_AUDIO_HEADSET_SYMBOLIC, + ICON_AUDIO_HEADSETS, + ICON_AUDIO_INPUT_MICROPHONE, + ICON_AUDIO_INPUT_MICROPHONE_SYMBOLIC, + ICON_AUDIO_SPEAKER_CENTER, + ICON_AUDIO_SPEAKER_CENTER_BACK, + ICON_AUDIO_SPEAKER_CENTER_BACK_TESTING, + ICON_AUDIO_SPEAKER_CENTER_TESTING, + ICON_AUDIO_SPEAKER_LEFT, + ICON_AUDIO_SPEAKER_LEFT_BACK, + ICON_AUDIO_SPEAKER_LEFT_BACK_TESTING, + ICON_AUDIO_SPEAKER_LEFT_SIDE, + ICON_AUDIO_SPEAKER_LEFT_SIDE_TESTING, + ICON_AUDIO_SPEAKER_LEFT_TESTING, + ICON_AUDIO_SPEAKER_RIGHT, + ICON_AUDIO_SPEAKER_RIGHT_BACK, + ICON_AUDIO_SPEAKER_RIGHT_BACK_TESTING, + ICON_AUDIO_SPEAKER_RIGHT_SIDE, + ICON_AUDIO_SPEAKER_RIGHT_SIDE_TESTING, + ICON_AUDIO_SPEAKER_RIGHT_TESTING, + ICON_AUDIO_SPEAKERS, + ICON_AUDIO_SPEAKERS_SYMBOLIC, + ICON_AUDIO_SUBWOOFER, + ICON_AUDIO_SUBWOOFER_TESTING, + ICON_BATTERY, + ICON_BATTERY_SYMBOLIC, + ICON_BLUETOOTH, + ICON_BLUETOOTH_SYMBOLIC, + ICON_CAMERA_PHOTO, + ICON_CAMERA_PHOTO_SYMBOLIC, + ICON_CAMERA_VIDEO, + ICON_CAMERA_VIDEO_SYMBOLIC, + ICON_CAMERA_WEB, + ICON_CAMERA_WEB_SYMBOLIC, + ICON_COLORIMETER_COLORHUG_SYMBOLIC, + ICON_COMPUTER_LAPTOP, + ICON_COMPUTER_LAPTOP_SYMBOLIC, + ICON_DISPLAY_PROJECTOR_SYMBOLIC, + ICON_DRIVE_HARDDISK, + ICON_DRIVE_HARDDISK_IEEE1394_SYMBOLIC, + ICON_DRIVE_HARDDISK_SOLIDSTATE, + ICON_DRIVE_HARDDISK_SOLIDSTATE_SYMBOLIC, + ICON_DRIVE_HARDDISK_SYMBOLIC, + ICON_DRIVE_MULTIDISK_SYMBOLIC, + ICON_DRIVE_OPTICAL_SYMBOLIC, + ICON_DRIVE_REMOVABLE_MEDIA, + ICON_DRIVE_REMOVABLE_MEDIA_SYMBOLIC, + ICON_DRIVE_REMOVABLE_MEDIA_USB, + ICON_FINGERPRINT, + ICON_FINGERPRINT_SYMBOLIC, + ICON_GNOME_DEV_PRINTER_NEW, + ICON_INPUT_DIALPAD_SYMBOLIC, + ICON_INPUT_GAMING, + ICON_INPUT_GAMING_SYMBOLIC, + ICON_INPUT_KEYBOARD, + ICON_INPUT_KEYBOARD_SYMBOLIC, + ICON_INPUT_MOUSE, + ICON_INPUT_MOUSE_SYMBOLIC, + ICON_INPUT_TABLET, + ICON_INPUT_TABLET_SYMBOLIC, + ICON_INPUT_TOUCHPAD, + ICON_INPUT_TOUCHPAD_SYMBOLIC, + ICON_MEDIA_FLASH_CF, + ICON_MEDIA_FLASH_MS, + ICON_MEDIA_FLASH_SYMBOLIC, + ICON_MEDIA_FLOPPY_SYMBOLIC, + ICON_MEDIA_MEMORY, + ICON_MEDIA_MEMORY_SD, + ICON_MEDIA_MEMORY_SEMBOLIC, + ICON_MEDIA_MEMORY_SM, + ICON_MEDIA_OPTICAL, + ICON_MEDIA_OPTICAL_SYMBOLIC, + ICON_MEDIA_REMOVABLE_SYMBOLIC, + ICON_MEDIA_TAPE_SYMBOLIC, + ICON_MEDIA_ZIP_SYMBOLIC, + ICON_MODEM, + ICON_MODEM_SYMBOLIC, + ICON_MULTIMEDIA_PLAYER, + ICON_MULTIMEDIA_PLAYER_SYMBOLIC, + ICON_NETWORK_CELLULAR, + ICON_NETWORK_FIREWALL, + ICON_NETWORK_VPN, + ICON_NETWORK_WIRED, + ICON_NETWORK_WIRELESS, + ICON_NETWORK_WIRELESS_HOTSPOT, + ICON_NM_DEVICE_WWAN, + ICON_PDA_SYMBOLIC, + ICON_PHONE, + ICON_PHONE_SYMBOLIC, + ICON_PRINTER, + ICON_PRINTER_NETWORK, + ICON_PRINTER_SYMBOLIC, + ICON_SCANNER, + ICON_SCANNER_SYMBOLIC, + ICON_TABLET, + ICON_TABLET_SYMBOLIC, + ICON_TV_SYMBOLIC, + ICON_UNINTERRUPTIBLE_POWER_SUPPLY, + ICON_UNINTERRUPTIBLE_POWER_SUPPLY_SYMBOLIC, + ICON_VIDEO_DISPLAY, + ICON_VIDEO_DISPLAY_SYMBOLIC, + ICON_EMBLEM_DEFAULT_SYMBOLIC, + ICON_EMBLEM_DOCUMENTS_SYMBOLIC, + ICON_EMBLEM_FAVORITE_SYMBOLIC, + ICON_EMBLEM_IMPORTANT_SYMBOLIC, + ICON_EMBLEM_MUSIC_SYMBOLIC, + ICON_EMBLEM_OK_SYMBOLIC, + ICON_EMBLEM_PHOTOS_SYMBOLIC, + ICON_EMBLEM_READONLY, + ICON_EMBLEM_SHARED_SYMBOLIC, + ICON_EMBLEM_SYMBOLIC_LINK, + ICON_EMBLEM_SYNCHRONIZED, + ICON_EMBLEM_SYNCHRONIZING_SYMBOLIC, + ICON_EMBLEM_UNREADABLE, + ICON_EMBLEM_VIDEOS_SYMBOLIC, + ICON_FACE_ANGEL, + ICON_FACE_ANGEL_SYMBOLIC, + ICON_FACE_ANGRY, + ICON_FACE_ANGRY_SYMBOLIC, + ICON_FACE_COOL, + ICON_FACE_COOL_SYMBOLIC, + ICON_FACE_CRYING, + ICON_FACE_CRYING_SYMBOLIC, + ICON_FACE_DEVILISH, + ICON_FACE_DEVILISH_SYMBOLIC, + ICON_FACE_EMBARRASSED, + ICON_FACE_EMBARRASSED_SYMBOLIC, + ICON_FACE_HEART, + ICON_FACE_HEART_BROKEN, + ICON_FACE_HEART_BROKEN_SYMBOLIC, + ICON_FACE_HEART_SYMBOLIC, + ICON_FACE_KISS, + ICON_FACE_KISS_SYMBOLIC, + ICON_FACE_LAUGH, + ICON_FACE_LAUGH_SYMBOLIC, + ICON_FACE_MONKEY_SYMBOLIC, + ICON_FACE_PLAIN, + ICON_FACE_PLAIN_SYMBOLIC, + ICON_FACE_RASPBERRY, + ICON_FACE_RASPBERRY_SYMBOLIC, + ICON_FACE_SAD, + ICON_FACE_SAD_SYMBOLIC, + ICON_FACE_SICK, + ICON_FACE_SICK_SYMBOLIC, + ICON_FACE_SMILE, + ICON_FACE_SMILE_BIG, + ICON_FACE_SMILE_BIG_SYMBOLIC, + ICON_FACE_SMILE_SYMBOLIC, + ICON_FACE_SMIRK, + ICON_FACE_SMIRK_SYMBOLIC, + ICON_FACE_SURPRISE, + ICON_FACE_SURPRISE_SYMBOLIC, + ICON_FACE_TIRED, + ICON_FACE_TIRED_SYMBOLIC, + ICON_FACE_UNCERTAIN, + ICON_FACE_UNCERTAIN_SYMBOLIC, + ICON_FACE_WINK, + ICON_FACE_WINK_SYMBOLIC, + ICON_FACE_WORRIED, + ICON_FACE_WORRIED_SYMBOLIC, + ICON_APPLICATION_CERTIFICATE_SYMBOLIC, + ICON_APPLICATION_EPUB_ZIP, + ICON_APPLICATION_ILLUSTRATOR, + ICON_APPLICATION_JAVASCRIPT, + ICON_APPLICATION_MSWORD, + ICON_APPLICATION_OCTET_STREAM, + ICON_APPLICATION_PDF, + ICON_APPLICATION_PGP, + ICON_APPLICATION_RSS_XML_SYMBOLIC, + ICON_APPLICATION_VND, + ICON_APPLICATION_X_APPLIANCE_SYMBOLIC, + ICON_APPLICATION_X_BITTORRENT, + ICON_APPLICATION_X_CD_IMAGE, + ICON_APPLICATION_X_DESKTOP, + ICON_APPLICATION_X_EXECUTABLE_SYMBOLIC, + ICON_APPLICATION_X_FICTIONBOOK_XML, + ICON_APPLICATION_X_FIRMWARE, + ICON_APPLICATION_X_FIRMWARE_SYMBOLIC, + ICON_APPLICATION_X_FLASH_VIDEO, + ICON_APPLICATION_X_MS_DOS_EXECUTABLE, + ICON_APPLICATION_X_PARTIAL_DOWNLOAD, + ICON_APPLICATION_X_PHP, + ICON_APPLICATION_X_RUBY, + ICON_AUDIO_X_GENERIC, + ICON_AUDIO_X_GENERIC_SYMBOLIC, + ICON_AUDIO_X_PLAYLIST, + ICON_EXTENSION, + ICON_FONT_X_GENERIC, + ICON_FONT_X_GENERIC_SYMBOLIC, + ICON_IMAGE_VND, + ICON_IMAGE_X_GENERIC, + ICON_IMAGE_X_GENERIC_SYMBOLIC, + ICON_IMAGE_X_XCF, + ICON_INTERNET_FEED, + ICON_MODEL, + ICON_OFFICE_CONTACT, + ICON_OFFICE_DATABASE, + ICON_PACKAGE_X_GENERIC, + ICON_PACKAGE_X_GENERIC_SYMBOLIC, + ICON_PAYMENT_CARD, + ICON_PAYMENT_CARD_AMEX, + ICON_PAYMENT_CARD_DINERS_CLUB, + ICON_PAYMENT_CARD_DISCOVER, + ICON_PAYMENT_CARD_JCB, + ICON_PAYMENT_CARD_MASTERCARD, + ICON_PAYMENT_CARD_SYMBOLIC, + ICON_PAYMENT_CARD_UNIONPAY, + ICON_PAYMENT_CARD_VISA, + ICON_TEXT, + ICON_TEXT_CSS, + ICON_TEXT_HTML, + ICON_TEXT_HTML_SYMBOLIC, + ICON_TEXT_MARKDOWN, + ICON_TEXT_X_BIBTEX, + ICON_TEXT_X_CHANGELOG, + ICON_TEXT_X_CHDR, + ICON_TEXT_X_COPYING, + ICON_TEXT_X_COPYING_SYMBOLIC, + ICON_TEXT_X_CSRC, + ICON_TEXT_X_GENERIC_SYMBOLIC, + ICON_TEXT_X_GENERIC_TEMPLATE, + ICON_TEXT_X_GETTEXT_TRANSLATION, + ICON_TEXT_X_GETTEXT_TRANSLATION_TEMPLATE, + ICON_TEXT_X_GO, + ICON_TEXT_X_INSTALL, + ICON_TEXT_X_MAKEFILE, + ICON_TEXT_X_PREVIEW, + ICON_TEXT_X_PYTHON, + ICON_TEXT_X_README, + ICON_TEXT_X_SASS, + ICON_TEXT_X_SCRIPT, + ICON_TEXT_X_SSA, + ICON_TEXT_X_TEX, + ICON_TEXT_X_VALA, + ICON_UNKNOWN, + ICON_VIDEO_X_GENERIC, + ICON_VIDEO_X_GENERIC_SYMBOLIC, + ICON_X_OFFICE_ADDRESS_BOOK_SYMBOLIC, + ICON_X_OFFICE_DOCUMENT, + ICON_X_OFFICE_DOCUMENT_SYMBOLIC, + ICON_X_OFFICE_DOCUMENT_TEMPLATE, + ICON_X_OFFICE_DRAWING, + ICON_X_OFFICE_DRAWING_SYMBOLIC, + ICON_X_OFFICE_DRAWING_TEMPLATE, + ICON_X_OFFICE_PRESENTATION, + ICON_X_OFFICE_PRESENTATION_SYMBOLIC, + ICON_X_OFFICE_PRESENTATION_TEMPLATE, + ICON_X_OFFICE_SPREADSHEET, + ICON_X_OFFICE_SPREADSHEET_SYMBOLIC, + ICON_X_OFFICE_SPREADSHEET_TEMPLATE, + ICON_BOOKMARK_MISSING, + ICON_DISTRIBUTOR_LOGO, + ICON_DISTRIBUTOR_LOGO_SYMBOLIC, + ICON_FOLDER, + ICON_FOLDER_DOCUMENTS, + ICON_FOLDER_DOCUMENTS_OPEN, + ICON_FOLDER_DOCUMENTS_SYMBOLIC, + ICON_FOLDER_DOWNLOAD, + ICON_FOLDER_DOWNLOAD_OPEN, + ICON_FOLDER_DOWNLOAD_SYMBOLIC, + ICON_FOLDER_MUSIC, + ICON_FOLDER_MUSIC_OPEN, + ICON_FOLDER_MUSIC_SYMBOLIC, + ICON_FOLDER_OPEN, + ICON_FOLDER_PICTURES, + ICON_FOLDER_PICTURES_OPEN, + ICON_FOLDER_PICTURES_SYMBOLIC, + ICON_FOLDER_PUBLICSHARE, + ICON_FOLDER_PUBLICSHARE_OPEN, + ICON_FOLDER_PUBLICSHARE_SYMBOLIC, + ICON_FOLDER_RECENT, + ICON_FOLDER_RECENT_SYMBOLIC, + ICON_FOLDER_REMOTE, + ICON_FOLDER_REMOTE_OPEN, + ICON_FOLDER_SAVED_SEARCH, + ICON_FOLDER_SYMBOLIC, + ICON_FOLDER_TAG, + ICON_FOLDER_TEMPLATES, + ICON_FOLDER_TEMPLATES_OPEN, + ICON_FOLDER_TEMPLATES_SYMBOLIC, + ICON_FOLDER_VIDEOS, + ICON_FOLDER_VIDEOS_OPEN, + ICON_FOLDER_VIDEOS_SYMBOLIC, + ICON_INTERNET_RADIO, + ICON_INTERNET_RADIO_SYMBOLIC, + ICON_LIBRARY_AUDIOBOOK, + ICON_LIBRARY_PLACES, + ICON_LIBRARY_PODCAST, + ICON_MAIL_INBOX, + ICON_MAIL_INBOX_SYMBOLIC, + ICON_MAIL_MAILBOX, + ICON_MAIL_MAILBOX_SYMBOLIC, + ICON_MAIL_OUTBOX, + ICON_MAIL_OUTBOX_SYMBOLIC, + ICON_NETWORK_SERVER_SYMBOLIC, + ICON_PLAYLIST, + ICON_PLAYLIST_AUTOMATIC, + ICON_PLAYLIST_QUEUE, + ICON_PLAYLIST_QUEUE_SYMBOLIC, + ICON_PLAYLIST_SIMILAR, + ICON_PLAYLIST_SYMBOLIC, + ICON_TAG_SYMBOLIC, + ICON_USER_BOOKMARKS_SYMBOLIC, + ICON_USER_HOME, + ICON_USER_HOME_OPEN, + ICON_USER_HOME_SYMBOLIC, + ICON_USER_TRASH, + ICON_USER_TRASH_FULL, + ICON_USER_TRASH_SYMBOLIC, + ICON_AIRPLANE_MODE, + ICON_AIRPLANE_MODE_SYMBOLIC, + ICON_ALARM_SYMBOLIC, + ICON_APPOINTMENT_MISSED, + ICON_APPOINTMENT_MISSED_SYMBOLIC, + ICON_APPOINTMENT_SOON, + ICON_APPOINTMENT_SOON_SYMBOLIC, + ICON_AUDIO_VOLUME_HIGH_SYMBOLIC, + ICON_AUDIO_VOLUME_LOW_SYMBOLIC, + ICON_AUDIO_VOLUME_MEDIUM_SYMBOLIC, + ICON_AUDIO_VOLUME_MUTED_BLOCKING_SYMBOLIC, + ICON_AUDIO_VOLUME_MUTED_SYMBOLIC, + ICON_AVATAR_DEFAULT, + ICON_AVATAR_DEFAULT_SYMBOLIC, + ICON_BATTERY_AC_ADAPTER, + ICON_BATTERY_AC_ADAPTER_SYMBOLIC, + ICON_BATTERY_CAUTION, + ICON_BATTERY_CAUTION_CHARGING, + ICON_BATTERY_CAUTION_CHARGING_SYMBOLIC, + ICON_BATTERY_CAUTION_SYMBOLIC, + ICON_BATTERY_EMPTY, + ICON_BATTERY_EMPTY_CHARGING, + ICON_BATTERY_EMPTY_CHARGING_SYMBOLIC, + ICON_BATTERY_EMPTY_SYMBOLIC, + ICON_BATTERY_FULL, + ICON_BATTERY_FULL_CHARGED, + ICON_BATTERY_FULL_CHARGED_SYMBOLIC, + ICON_BATTERY_FULL_CHARGING, + ICON_BATTERY_FULL_CHARGING_SYMBOLIC, + ICON_BATTERY_FULL_SYMBOLIC, + ICON_BATTERY_GOOD, + ICON_BATTERY_GOOD_CHARGING, + ICON_BATTERY_GOOD_CHARGING_SYMBOLIC, + ICON_BATTERY_GOOD_SYMBOLIC, + ICON_BATTERY_LOW, + ICON_BATTERY_LOW_CHARGING, + ICON_BATTERY_LOW_CHARGING_SYMBOLIC, + ICON_BATTERY_LOW_SYMBOLIC, + ICON_BATTERY_MISSING, + ICON_BATTERY_MISSING_SYMBOLIC, + ICON_BLUETOOTH_ACTIVE_SYMBOLIC, + ICON_BLUETOOTH_DISABLED, + ICON_BLUETOOTH_DISABLED_10_SYMBOLIC, + ICON_BLUETOOTH_DISABLED_20_SYMBOLIC, + ICON_BLUETOOTH_DISABLED_30_SYMBOLIC, + ICON_BLUETOOTH_DISABLED_40_SYMBOLIC, + ICON_BLUETOOTH_DISABLED_50_SYMBOLIC, + ICON_BLUETOOTH_DISABLED_60_SYMBOLIC, + ICON_BLUETOOTH_DISABLED_70_SYMBOLIC, + ICON_BLUETOOTH_DISABLED_80_SYMBOLIC, + ICON_BLUETOOTH_DISABLED_90_SYMBOLIC, + ICON_BLUETOOTH_DISABLED_SYMBOLIC, + ICON_BLUETOOTH_PAIRED_SYMBOLIC, + ICON_CALL_MISSED_SYMBOLIC, + ICON_CHANGES_ALLOW, + ICON_CHANGES_ALLOW_SYMBOLIC, + ICON_CHANGES_PREVENT_SYMBOLIC, + ICON_CHANNEL_INSECURE_SYMBOLIC, + ICON_CHANNEL_SECURE_SYMBOLIC, + ICON_CHECK_ACTIVE_SYMBOLIC, + ICON_CHECK_MIXED_SYMBOLIC, + ICON_CHECKBOX_CHECKED_SYMBOLIC, + ICON_CHECKBOX_MIXED_SYMBOLIC, + ICON_CHECKBOX_SYMBOLIC, + ICON_COMPUTER_FAIL_SYMBOLIC, + ICON_CONTENT_LOADING_SYMBOLIC, + ICON_DAYTIME_SUNRISE_SYMBOLIC, + ICON_DAYTIME_SUNSET_SYMBOLIC, + ICON_DIALOG_ERROR, + ICON_DIALOG_ERROR_SYMBOLIC, + ICON_DIALOG_INFORMATION, + ICON_DIALOG_INFORMATION_SYMBOLIC, + ICON_DIALOG_PASSWORD, + ICON_DIALOG_PASSWORD_SYMBOLIC, + ICON_DIALOG_WARNING, + ICON_DIALOG_WARNING_SYMBOLIC, + ICON_DISPLAY_BRIGHTNESS_SYMBOLIC, + ICON_FOLDER_OPEN_SYMBOLIC, + ICON_FOLDER_VISITING_SYMBOLIC, + ICON_IMAGE_LOADING, + ICON_IMAGE_MISSING, + ICON_INPUT_KEYBOARD_CAPSLOCK_SYMBOLIC, + ICON_INPUT_KEYBOARD_NUMLOCK_SYMBOLIC, + ICON_KEYBOARD_BRIGHTNESS_SYMBOLIC, + ICON_LOCATION_ACTIVE_SYMBOLIC, + ICON_LOCATION_DISABLED_SYMBOLIC, + ICON_LOCATION_INACTIVE_SYMBOLIC, + ICON_LOCKED, + ICON_MAIL_ATTACHMENT_SYMBOLIC, + ICON_MAIL_FORWARDED_SYMBOLIC, + ICON_MAIL_IMPORTANT_SYMBOLIC, + ICON_MAIL_READ_SYMBOLIC, + ICON_MAIL_REPLIED_SYMBOLIC, + ICON_MAIL_UNREAD, + ICON_MAIL_UNREAD_SYMBOLIC, + ICON_MEDIA_PLAYLIST_CONSECUTIVE_SYMBOLIC, + ICON_MEDIA_PLAYLIST_NO_REPEAT_SYMBOLIC, + ICON_MEDIA_PLAYLIST_REPEAT, + ICON_MEDIA_PLAYLIST_REPEAT_SONG_SYMBOLIC, + ICON_MEDIA_PLAYLIST_REPEAT_SYMBOLIC, + ICON_MEDIA_PLAYLIST_SHUFFLE_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_HIGH_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_LOW_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MEDIUM_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_10_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_20_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_30_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_40_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_50_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_60_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_70_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_80_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_90_SYMBOLIC, + ICON_MICROPHONE_SENSITIVITY_MUTED_SYMBOLIC, + ICON_NETWORK_CELLULAR_ACQUIRING_SYMBOLIC, + ICON_NETWORK_CELLULAR_CONNECTED_SYMBOLIC, + ICON_NETWORK_CELLULAR_NO_ROUTE_SYMBOLIC, + ICON_NETWORK_CELLULAR_OFFLINE_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_EXCELLENT_SECURE_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_EXCELLENT_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_GOOD_SECURE_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_GOOD_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_NONE_SECURE_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_NONE_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_OK_SECURE_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_OK_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_WEAK_SECURE_SYMBOLIC, + ICON_NETWORK_CELLULAR_SIGNAL_WEAK_SYMBOLIC, + ICON_NETWORK_ERROR, + ICON_NETWORK_ERROR_SYMBOLIC, + ICON_NETWORK_IDLE, + ICON_NETWORK_OFFLINE_SYMBOLIC, + ICON_NETWORK_VPN_ACQUIRING_SYMBOLIC, + ICON_NETWORK_VPN_LOCK_SYMBOLIC, + ICON_NETWORK_VPN_SYMBOLIC, + ICON_NETWORK_WIRED_ACQUIRING_SYMBOLIC, + ICON_NETWORK_WIRED_DISCONNECTED, + ICON_NETWORK_WIRED_NO_ROUTE_SYMBOLIC, + ICON_NETWORK_WIRED_OFFLINE_SYMBOLIC, + ICON_NETWORK_WIRED_SYMBOLIC, + ICON_NETWORK_WIRELESS_ACQUIRING_SYMBOLIC, + ICON_NETWORK_WIRELESS_CONNECTED_SYMBOLIC, + ICON_NETWORK_WIRELESS_ENCRYPTED_SYMBOLIC, + ICON_NETWORK_WIRELESS_HOTSPOT_SYMBOLIC, + ICON_NETWORK_WIRELESS_NO_ROUTE_SYMBOLIC, + ICON_NETWORK_WIRELESS_OFFLINE_SYMBOLIC, + ICON_NETWORK_WIRELESS_SIGNAL_EXCELLENT_SECURE_SYMBOLIC, + ICON_NETWORK_WIRELESS_SIGNAL_EXCELLENT_SYMBOLIC, + ICON_NETWORK_WIRELESS_SIGNAL_GOOD_SECURE_SYMBOLIC, + ICON_NETWORK_WIRELESS_SIGNAL_GOOD_SYMBOLIC, + ICON_NETWORK_WIRELESS_SIGNAL_NONE_SYMBOLIC, + ICON_NETWORK_WIRELESS_SIGNAL_OK_SECURE_SYMBOLIC, + ICON_NETWORK_WIRELESS_SIGNAL_OK_SYMBOLIC, + ICON_NETWORK_WIRELESS_SIGNAL_WEAK_SECURE_SYMBOLIC, + ICON_NETWORK_WIRELESS_SIGNAL_WEAK_SYMBOLIC, + ICON_NETWORK_WIRELESS_SYMBOLIC, + ICON_NIGHT_LIGHT, + ICON_NIGHT_LIGHT_DISABLED_10_SYMBOLIC, + ICON_NIGHT_LIGHT_DISABLED_20_SYMBOLIC, + ICON_NIGHT_LIGHT_DISABLED_30_SYMBOLIC, + ICON_NIGHT_LIGHT_DISABLED_40_SYMBOLIC, + ICON_NIGHT_LIGHT_DISABLED_50_SYMBOLIC, + ICON_NIGHT_LIGHT_DISABLED_60_SYMBOLIC, + ICON_NIGHT_LIGHT_DISABLED_70_SYMBOLIC, + ICON_NIGHT_LIGHT_DISABLED_80_SYMBOLIC, + ICON_NIGHT_LIGHT_DISABLED_90_SYMBOLIC, + ICON_NIGHT_LIGHT_DISABLED_SYMBOLIC, + ICON_NIGHT_LIGHT_SYMBOLIC, + ICON_NM_NO_CONNECTION, + ICON_NM_SIGNAL_0, + ICON_NM_SIGNAL_0_SECURE, + ICON_NM_SIGNAL_100, + ICON_NM_SIGNAL_100_SECURE, + ICON_NM_SIGNAL_25, + ICON_NM_SIGNAL_25_SECURE, + ICON_NM_SIGNAL_50, + ICON_NM_SIGNAL_50_SECURE, + ICON_NM_SIGNAL_75, + ICON_NM_SIGNAL_75_SECURE, + ICON_NM_VPN_ACTIVE_LOCK, + ICON_NM_VPN_LOCK, + ICON_NON_STARRED, + ICON_NON_STARRED_SYMBOLIC, + ICON_NOTIFICATION_AUDIO_VOLUME_HIGH, + ICON_NOTIFICATION_AUDIO_VOLUME_LOW, + ICON_NOTIFICATION_AUDIO_VOLUME_MEDIUM, + ICON_NOTIFICATION_AUDIO_VOLUME_MUTED, + ICON_NOTIFICATION_DEVICE_EJECT, + ICON_NOTIFICATION_DISABLED, + ICON_NOTIFICATION_DISABLED_10_SYMBOLIC, + ICON_NOTIFICATION_DISABLED_20_SYMBOLIC, + ICON_NOTIFICATION_DISABLED_30_SYMBOLIC, + ICON_NOTIFICATION_DISABLED_40_SYMBOLIC, + ICON_NOTIFICATION_DISABLED_50_SYMBOLIC, + ICON_NOTIFICATION_DISABLED_60_SYMBOLIC, + ICON_NOTIFICATION_DISABLED_70_SYMBOLIC, + ICON_NOTIFICATION_DISABLED_80_SYMBOLIC, + ICON_NOTIFICATION_DISABLED_90_SYMBOLIC, + ICON_NOTIFICATION_DISABLED_SYMBOLIC, + ICON_NOTIFICATION_DISPLAY_BRIGHTNESS, + ICON_NOTIFICATION_KEYBOARD_BRIGHTNESS, + ICON_NOTIFICATION_NETWORK_ETHERNET_DISCONNECTED, + ICON_NOTIFICATION_NETWORK_WIRED, + ICON_NOTIFICATION_NETWORK_WIRELESS, + ICON_NOTIFICATION_NETWORK_WIRELESS_DISCONNECTED, + ICON_NOTIFICATION_NETWORK_WIRELESS_DISCONNECTED_SYMBOLIC, + ICON_NOTIFICATION_NETWORK_WIRELESS_SYMBOLIC, + ICON_NOTIFICATION_NEW_10_SYMBOLIC, + ICON_NOTIFICATION_NEW_20_SYMBOLIC, + ICON_NOTIFICATION_NEW_30_SYMBOLIC, + ICON_NOTIFICATION_NEW_40_SYMBOLIC, + ICON_NOTIFICATION_NEW_50_SYMBOLIC, + ICON_NOTIFICATION_NEW_60_SYMBOLIC, + ICON_NOTIFICATION_NEW_70_SYMBOLIC, + ICON_NOTIFICATION_NEW_80_SYMBOLIC, + ICON_NOTIFICATION_NEW_90_SYMBOLIC, + ICON_NOTIFICATION_NEW_SYMBOLIC, + ICON_NOTIFICATION_SYMBOLIC, + ICON_PAGER_CHECKED_SYMBOLIC, + ICON_PRINTER_ERROR, + ICON_PRINTER_ERROR_SYMBOLIC, + ICON_PRINTER_PRINTING_SYMBOLIC, + ICON_PRINTER_WARNING_SYMBOLIC, + ICON_PROCESS_COMPLETED, + ICON_PROCESS_COMPLETED_SYMBOLIC, + ICON_PROCESS_ERROR_SYMBOLIC, + ICON_PROCESS_WORKING_SYMBOLIC, + ICON_RADIO_CHECKED_SYMBOLIC, + ICON_RADIO_MIXED_SYMBOLIC, + ICON_RADIO_SYMBOLIC, + ICON_ROTATION_ALLOWED_SYMBOLIC, + ICON_ROTATION_LOCKED_SYMBOLIC, + ICON_SECURITY_HIGH, + ICON_SECURITY_HIGH_SYMBOLIC, + ICON_SECURITY_LOW, + ICON_SECURITY_LOW_SYMBOLIC, + ICON_SECURITY_MEDIUM, + ICON_SECURITY_MEDIUM_SYMBOLIC, + ICON_SEMI_STARRED, + ICON_SEMI_STARRED_SYMBOLIC, + ICON_SOFTWARE_UPDATE_AVAILABLE_SYMBOLIC, + ICON_SOFTWARE_UPDATE_URGENT_SYMBOLIC, + ICON_STARRED, + ICON_STARRED_SYMBOLIC, + ICON_TASK_DUE_SYMBOLIC, + ICON_TASK_PAST_DUE_SYMBOLIC, + ICON_TOUCHPAD_DISABLED_SYMBOLIC, + ICON_USER_AVAILABLE, + ICON_USER_AVAILABLE_SYMBOLIC, + ICON_USER_AWAY, + ICON_USER_AWAY_SYMBOLIC, + ICON_USER_BUSY, + ICON_USER_BUSY_SYMBOLIC, + ICON_USER_IDLE_SYMBOLIC, + ICON_USER_INVISIBLE, + ICON_USER_INVISIBLE_SYMBOLIC, + ICON_USER_OFFLINE, + ICON_USER_OFFLINE_SYMBOLIC, + ICON_USER_STATUS_PENDING_SYMBOLIC, + ICON_USER_TRASH_FULL_SYMBOLIC, + ICON_USER_TYPING, + ICON_VIEW_PRIVATE, + ICON_VIEW_PRIVATE_SYMBOLIC, + ICON_VIEW_WRAPPED_SYMBOLIC, + ICON_WEATHER_CLEAR_NIGHT_SYMBOLIC, + ICON_WEATHER_CLEAR_SYMBOLIC, + ICON_WEATHER_FEW_CLOUDS_NIGHT_SYMBOLIC, + ICON_WEATHER_FEW_CLOUDS_SYMBOLIC, + ICON_WEATHER_FOG_NIGHT_SYMBOLIC, + ICON_WEATHER_FOG_SYMBOLIC, + ICON_WEATHER_OVERCAST_NIGHT_SYMBOLIC, + ICON_WEATHER_OVERCAST_SYMBOLIC, + ICON_WEATHER_SEVERE_ALERT_SYMBOLIC, + ICON_WEATHER_SHOWERS_NIGHT_SYMBOLIC, + ICON_WEATHER_SHOWERS_SCATTERED_NIGHT_SYMBOLIC, + ICON_WEATHER_SHOWERS_SCATTERED_SYMBOLIC, + ICON_WEATHER_SHOWERS_SYMBOLIC, + ICON_WEATHER_SNOW_NIGHT_SYMBOLIC, + ICON_WEATHER_SNOW_SYMBOLIC, + ICON_WEATHER_STORM_NIGHT_SYMBOLIC, + ICON_WEATHER_STORM_SYMBOLIC, + ICON_WEATHER_STORM_TORNADO_NIGHT_SYMBOLIC, + ICON_WEATHER_STORM_TORNADO_SYMBOLIC, + ICON_WEATHER_WINDY_SYMBOLIC, +} -OS :: "essence"; +FatalError :: enum { + FATAL_ERROR_INVALID_BUFFER, + FATAL_ERROR_UNKNOWN_SYSCALL, + FATAL_ERROR_INVALID_MEMORY_REGION, + FATAL_ERROR_MEMORY_REGION_LOCKED_BY_KERNEL, + FATAL_ERROR_PATH_LENGTH_EXCEEDS_LIMIT, + FATAL_ERROR_INVALID_HANDLE, + FATAL_ERROR_MUTEX_NOT_ACQUIRED_BY_THREAD, + FATAL_ERROR_MUTEX_ALREADY_ACQUIRED, + FATAL_ERROR_BUFFER_NOT_ACCESSIBLE, + FATAL_ERROR_SHARED_MEMORY_REGION_TOO_LARGE, + FATAL_ERROR_SHARED_MEMORY_STILL_MAPPED, + FATAL_ERROR_COULD_NOT_LOAD_FONT, + FATAL_ERROR_COULD_NOT_DRAW_FONT, + FATAL_ERROR_COULD_NOT_ALLOCATE_MEMORY, + FATAL_ERROR_INCORRECT_FILE_ACCESS, + FATAL_ERROR_TOO_MANY_WAIT_OBJECTS, + FATAL_ERROR_INCORRECT_NODE_TYPE, + FATAL_ERROR_PROCESSOR_EXCEPTION, + FATAL_ERROR_UNKNOWN, + FATAL_ERROR_RECURSIVE_BATCH, + FATAL_ERROR_CORRUPT_HEAP, + FATAL_ERROR_CORRUPT_LINKED_LIST, + FATAL_ERROR_INDEX_OUT_OF_BOUNDS, + FATAL_ERROR_INVALID_STRING_LENGTH, + FATAL_ERROR_SPINLOCK_NOT_ACQUIRED, + FATAL_ERROR_UNKNOWN_SNAPSHOT_TYPE, + FATAL_ERROR_PROCESS_ALREADY_ATTACHED, + FATAL_ERROR_INTERNAL, + FATAL_ERROR_INSUFFICIENT_PERMISSIONS, + FATAL_ERROR_ABORT, + FATAL_ERROR_COUNT, +} -foreign import api "system:api" +SyscallType :: enum { + SYSCALL_ALLOCATE, + SYSCALL_FREE, + SYSCALL_SHARE_MEMORY, + SYSCALL_MAP_OBJECT, + SYSCALL_OPEN_SHARED_MEMORY, + SYSCALL_CREATE_PROCESS, + SYSCALL_GET_CREATION_ARGUMENT, + SYSCALL_TERMINATE_THREAD, + SYSCALL_CREATE_THREAD, + SYSCALL_WAIT, + SYSCALL_TERMINATE_PROCESS, + SYSCALL_CREATE_EVENT, + SYSCALL_SET_EVENT, + SYSCALL_RESET_EVENT, + SYSCALL_POLL_EVENT, + SYSCALL_PAUSE_PROCESS, + SYSCALL_CRASH_PROCESS, + SYSCALL_GET_THREAD_ID, + SYSCALL_GET_PROCESS_STATE, + SYSCALL_YIELD_SCHEDULER, + SYSCALL_SLEEP, + SYSCALL_OPEN_PROCESS, + SYSCALL_SET_TLS, + SYSCALL_TIMER_SET, + SYSCALL_TIMER_CREATE, + SYSCALL_GET_PROCESS_STATUS, + SYSCALL_CREATE_SURFACE, + SYSCALL_GET_LINEAR_BUFFER, + SYSCALL_INVALIDATE_RECTANGLE, + SYSCALL_COPY_TO_SCREEN, + SYSCALL_FORCE_SCREEN_UPDATE, + SYSCALL_FILL_RECTANGLE, + SYSCALL_COPY_SURFACE, + SYSCALL_CLEAR_MODIFIED_REGION, + SYSCALL_DRAW_SURFACE, + SYSCALL_REDRAW_ALL, + SYSCALL_DRAW_BOX, + SYSCALL_DRAW_BITMAP, + SYSCALL_SURFACE_RESET, + SYSCALL_SURFACE_SHARE, + SYSCALL_DRAW_STYLED_BOX, + SYSCALL_SURFACE_SCROLL, + SYSCALL_RESIZE_SURFACE, + SYSCALL_GET_MESSAGE, + SYSCALL_POST_MESSAGE, + SYSCALL_POST_MESSAGE_REMOTE, + SYSCALL_WAIT_MESSAGE, + SYSCALL_CREATE_WINDOW, + SYSCALL_UPDATE_WINDOW, + SYSCALL_SET_CURSOR_STYLE, + SYSCALL_MOVE_WINDOW, + SYSCALL_GET_WINDOW_BOUNDS, + SYSCALL_RESET_CLICK_CHAIN, + SYSCALL_GET_CURSOR_POSITION, + SYSCALL_SET_CURSOR_POSITION, + SYSCALL_COPY, + SYSCALL_GET_CLIPBOARD_HEADER, + SYSCALL_PASTE_TEXT, + SYSCALL_SET_FOCUSED_WINDOW, + SYSCALL_SET_WINDOW_TITLE, + SYSCALL_GET_SCREEN_BOUNDS, + SYSCALL_WINDOW_OPEN, + SYSCALL_WINDOW_SET_BLEND_BOUNDS, + SYSCALL_WINDOW_GET_BLEND_BOUNDS, + SYSCALL_WINDOW_GET_ID, + SYSCALL_SET_WINDOW_ALPHA, + SYSCALL_DOCKED_WINDOW_CREATE, + SYSCALL_WINDOW_SHARE, + SYSCALL_SET_EMBED_WINDOW, + SYSCALL_OPEN_NODE, + SYSCALL_READ_FILE_SYNC, + SYSCALL_WRITE_FILE_SYNC, + SYSCALL_RESIZE_FILE, + SYSCALL_REFRESH_NODE_INFORMATION, + SYSCALL_ENUMERATE_DIRECTORY_CHILDREN, + SYSCALL_DELETE_NODE, + SYSCALL_MOVE_NODE, + SYSCALL_READ_CONSTANT_BUFFER, + SYSCALL_SHARE_CONSTANT_BUFFER, + SYSCALL_CREATE_CONSTANT_BUFFER, + SYSCALL_EXECUTE, + SYSCALL_INSTANCE_CREATE_REMOTE, + SYSCALL_MAILSLOT_SEND_DATA, + SYSCALL_MAILSLOT_SEND_MESSAGE, + SYSCALL_MAILSLOT_SHARE, + SYSCALL_PIPE_CREATE, + SYSCALL_PIPE_WRITE, + SYSCALL_PIPE_READ, + SYSCALL_USER_GET_HOME_FOLDER, + SYSCALL_USER_LOGIN, + SYSCALL_GET_SYSTEM_CONSTANTS, + SYSCALL_TAKE_SYSTEM_SNAPSHOT, + SYSCALL_SET_SYSTEM_CONSTANT, + SYSCALL_GET_SYSTEM_INFORMATION, + SYSCALL_PRINT, + SYSCALL_CLOSE_HANDLE, + SYSCALL_BATCH, + SYSCALL_SHUTDOWN, + SYSCALL_POSIX, + SYSCALL_COUNT, +} -Handle :: distinct int; -Errno :: distinct int; +StandardFont :: enum { + STANDARD_FONT_REGULAR, + STANDARD_FONT_BOLD, + STANDARD_FONT_MONOSPACED, +} -O_RDONLY :: 0x00001; -O_WRONLY :: 0x00002; -O_RDWR :: 0x00003; -O_CREATE :: 0x00040; -O_EXCL :: 0x00080; -O_TRUNC :: 0x00200; -O_APPEND :: 0x00400; +MessageType :: enum { + MESSAGE_WM_START = 0x1000, + MESSAGE_MOUSE_MOVED = 0x1001, + MESSAGE_WINDOW_ACTIVATED = 0x1003, + MESSAGE_WINDOW_DEACTIVATED = 0x1004, + MESSAGE_WINDOW_DESTROYED = 0x1005, + MESSAGE_MOUSE_EXIT = 0x1006 , + MESSAGE_CLICK_REPEAT = 0x1009, + MESSAGE_WINDOW_RESIZED = 0x100B, + MESSAGE_MOUSE_LEFT_PRESSED = 0x100C , + MESSAGE_MOUSE_LEFT_RELEASED = 0x100D, + MESSAGE_MOUSE_RIGHT_PRESSED = 0x100E, + MESSAGE_MOUSE_RIGHT_RELEASED = 0x100F, + MESSAGE_MOUSE_MIDDLE_PRESSED = 0x1010, + MESSAGE_MOUSE_MIDDLE_RELEASED = 0x1011 , + MESSAGE_KEY_PRESSED = 0x1012, + MESSAGE_KEY_RELEASED = 0x1013, + MESSAGE_UPDATE_WINDOW = 0x1014, + MESSAGE_WM_END = 0x13FF, + MESSAGE_PAINT = 0x2000 , + MESSAGE_DESTROY = 0x2001 , + MESSAGE_MEASURE = 0x2002 , + MESSAGE_SIZE = 0x2003 , + MESSAGE_ADD_CHILD = 0x2004 , + MESSAGE_REMOVE_CHILD = 0x2005 , + MESSAGE_HIT_TEST = 0x2006 , + MESSAGE_HOVERED_START = 0x2007 , + MESSAGE_HOVERED_END = 0x2008 , + MESSAGE_PRESSED_START = 0x2009 , + MESSAGE_PRESSED_END = 0x200A , + MESSAGE_FOCUSED_START = 0x200B , + MESSAGE_FOCUSED_END = 0x200C , + MESSAGE_FOCUS_WITHIN_START = 0x200D , + MESSAGE_FOCUS_WITHIN_END = 0x200E , + MESSAGE_Z_ORDER = 0x2010 , + MESSAGE_ANIMATE = 0x2011 , + MESSAGE_MOUSE_DRAGGED = 0x2012 , + MESSAGE_KEY_TYPED = 0x2013 , + MESSAGE_PAINT_BACKGROUND = 0x2014 , + MESSAGE_PAINT_FOREGROUND = 0x2015 , + MESSAGE_ENSURE_VISIBLE = 0x2016 , + MESSAGE_GET_CURSOR = 0x2017 , + MESSAGE_WINDOW_CREATED = 0x2018 , + MESSAGE_CLICKED = 0x3000 , + MESSAGE_SCROLLBAR_MOVED = 0x3001 , + MESSAGE_RECALCULATE_CONTENT_SIZE = 0x3002 , + MESSAGE_TEXTBOX_UPDATED = 0x3003 , + MESSAGE_DESKTOP_EXECUTE = 0x4800, + MESSAGE_POWER_BUTTON_PRESSED = 0x4801, + MESSAGE_TASKBAR_WINDOW_ADD = 0x4804, + MESSAGE_TASKBAR_WINDOW_REMOVE = 0x4805, + MESSAGE_TASKBAR_WINDOW_ACTIVATE = 0x4806, + MESSAGE_TASKBAR_WINDOW_SET_TITLE = 0x4807, + MESSAGE_DOCKED_WINDOW_CREATE = 0x4808, + MESSAGE_PROGRAM_CRASH = 0x4C00, + MESSAGE_PROGRAM_FAILED_TO_START = 0x4C01, + MESSAGE_RECEIVE_DATA = 0x5100, + MESSAGE_MAILSLOT_CLOSED = 0x5101, + MESSAGE_CLIPBOARD_UPDATED = 0x5001, + MESSAGE_SYSTEM_CONSTANT_UPDATED = 0x5004, + MESSAGE_TIMER = 0x5006, + MESSAGE_OBJECT_DESTROY = 0x5007, + MESSAGE_LIST_VIEW_GET_ITEM_CONTENT = 0x6000, + MESSAGE_LIST_VIEW_SET_ITEM_STATE = 0x6001, + MESSAGE_LIST_VIEW_GET_ITEM_STATE = 0x6002, + MESSAGE_LIST_VIEW_PAINT_ITEM = 0x6003 , + MESSAGE_LIST_VIEW_PAINT_CELL = 0x6004 , + MESSAGE_LIST_VIEW_SORT_COLUMN = 0x6005, + MESSAGE_LIST_VIEW_CHOOSE_ITEM = 0x6006, + MESSAGE_LIST_VIEW_FIND_ITEM = 0x6007, + MESSAGE_LIST_VIEW_TOGGLE_DISCLOSURE = 0x6008, + MESSAGE_LIST_VIEW_MEASURE_ITEM_HEIGHT = 0x6009, + MESSAGE_LIST_VIEW_LAYOUT_ITEM = 0x600A, + MESSAGE_LIST_VIEW_SET_ITEM_VISIBILITY = 0x600B, + MESSAGE_LIST_VIEW_RELAY_MESSAGE = 0x600C, + MESSAGE_LIST_VIEW_SET_ITEM_POSITION = 0x600D, + MESSAGE_PROGRAM_START = 0x7000, + MESSAGE_PROGRAM_EXIT = 0x7001, + MESSAGE_USER_START = 0x8000, + MESSAGE_USER_END = 0xBFFF, +} -ERROR_NONE :: Errno(-1); -ERROR_UNKNOWN_OPERATION_FAILURE :: Errno(-7); -ERROR_PATH_NOT_WITHIN_MOUNTED_VOLUME :: Errno(-14); -ERROR_PATH_NOT_FOUND :: Errno(-15); -ERROR_FILE_EXISTS :: Errno(-19); -ERROR_FILE_NOT_FOUND :: Errno(-20); -ERROR_DRIVE_ERROR_FILE_DAMAGED :: Errno(-21); -ERROR_ACCESS_NOT_WITHIN_FILE_BOUNDS :: Errno(-22); -ERROR_ACCESS_DENIED :: Errno(-23); -ERROR_FILE_IN_EXCLUSIVE_USE :: Errno(-24); -ERROR_FILE_CANNOT_GET_EXCLUSIVE_USE :: Errno(-25); -ERROR_INCORRECT_NODE_TYPE :: Errno(-26); -ERROR_EVENT_NOT_SET :: Errno(-27); -ERROR_TIMEOUT_REACHED :: Errno(-29); -ERROR_REQUEST_CLOSED_BEFORE_COMPLETE :: Errno(-30); -ERROR_NO_CHARACTER_AT_COORDINATE :: Errno(-31); -ERROR_FILE_ON_READ_ONLY_VOLUME :: Errno(-32); -ERROR_USER_CANCELED_IO :: Errno(-33); -ERROR_DRIVE_CONTROLLER_REPORTED :: Errno(-35); -ERROR_COULD_NOT_ISSUE_PACKET :: Errno(-36); - -ERROR_NOT_IMPLEMENTED :: Errno(1); - -OS_Node_Type :: enum i32 { - File = 0, - Directory = 1, -} - -OS_Node_Information :: struct { - handle: Handle, - id: [16]byte, - ntype: OS_Node_Type, - size: i64, - - // Our additions.. - position: i64, -} - -foreign api { - @(link_name="OSPrintDirect") OSPrintDirect :: proc(str: ^u8, length: int) ---; - @(link_name="malloc") OSMalloc :: proc(bytes: int) -> rawptr ---; - @(link_name="free") OSFree :: proc(address: rawptr) ---; - @(link_name="OSOpenNode") OSOpenNode :: proc(path: ^u8, path_length: int, flags: u64, information: ^OS_Node_Information) -> Errno ---; - @(link_name="OSResizeFile") OSResizeFile :: proc(handle: Handle, new_size: u64) -> Errno ---; - @(link_name="OSCloseHandle") OSCloseHandle :: proc(handle: Handle) ---; - @(link_name="OSWriteFileSync") OSWriteFileSync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---; - @(link_name="OSReadFileSync") OSReadFileSync :: proc(handle: Handle, offset: i64, size: i64, buffer: rawptr) -> i64 ---; - @(link_name="realloc") OSRealloc :: proc(address: rawptr, size: int) -> rawptr ---; - @(link_name="OSGetThreadID") OSGetThreadID :: proc(handle: Handle) -> int ---; - @(link_name="OSRefreshNodeInformation") OSRefreshNodeInformation :: proc(information: ^OS_Node_Information) ---; -} - -stdin := Handle(-1); // Not implemented -stdout := Handle(0); -stderr := Handle(0); - -is_path_separator :: proc(r: rune) -> bool { - return r == '/'; +DrawMode :: enum { + DRAW_MODE_REPEAT_FIRST = 1 , + DRAW_MODE_STRECH, + DRAW_MODE_REPEAT, + DRAW_MODE_NONE, } -current_thread_id :: proc "contextless" () -> int { - return OSGetThreadID(Handle(0x1000)); +ClipboardFormat :: enum { + CLIPBOARD_FORMAT_EMPTY, + CLIPBOARD_FORMAT_TEXT, + CLIPBOARD_FORMAT_FILE_LIST, } -heap_alloc :: proc(size: int) -> rawptr { - return OSMalloc(size); +ColorFormat :: enum { + COLOR_FORMAT_32_XRGB, } -heap_free :: proc(address: rawptr) { - OSFree(address); +CursorStyle :: enum { + CURSOR_NORMAL, + CURSOR_TEXT, + CURSOR_RESIZE_VERTICAL, + CURSOR_RESIZE_HORIZONTAL, + CURSOR_RESIZE_DIAGONAL_1, + CURSOR_RESIZE_DIAGONAL_2, + CURSOR_SPLIT_VERTICAL, + CURSOR_SPLIT_HORIZONTAL, + CURSOR_HAND_HOVER, + CURSOR_HAND_DRAG, + CURSOR_HAND_POINT, + CURSOR_SCROLL_UP_LEFT, + CURSOR_SCROLL_UP, + CURSOR_SCROLL_UP_RIGHT, + CURSOR_SCROLL_LEFT, + CURSOR_SCROLL_CENTER, + CURSOR_SCROLL_RIGHT, + CURSOR_SCROLL_DOWN_LEFT, + CURSOR_SCROLL_DOWN, + CURSOR_SCROLL_DOWN_RIGHT, + CURSOR_SELECT_LINES, + CURSOR_DROP_TEXT, + CURSOR_CROSS_HAIR_PICK, + CURSOR_CROSS_HAIR_RESIZE, + CURSOR_MOVE_HOVER, + CURSOR_MOVE_DRAG, + CURSOR_ROTATE_HOVER, + CURSOR_ROTATE_DRAG, + CURSOR_BLANK, } -heap_resize :: proc(address: rawptr, new_size: int) -> rawptr { - return OSRealloc(address, new_size); +WindowStyle :: enum { + WINDOW_NORMAL, + WINDOW_CONTAINER, + WINDOW_MENU, } -open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) { - flags : u64 = 0; +NODE_FILE :: (0); +NODE_DIRECTORY :: (0x4000); +NODE_INVALID :: (0x8000); +BatchCall :: struct { + index :SyscallType, + stopBatchIfError :bool, + using _ : struct #raw_union { + argument0 :uintptr, + returnValue :uintptr, + }, - if mode & O_CREATE == O_CREATE { - flags = flags | 0x9000; // Fail if found and create directories leading to the file if they don't exist - } else { - flags = flags | 0x2000; // Fail if not found - } + argument1 :uintptr, + argument2 :uintptr, + argument3 :uintptr, +} - if mode & O_EXCL == O_EXCL { - flags = flags | 0x111; // Block opening the node for any reason - } +ThreadInformation :: struct { + handle :Handle, + tid :uintptr, +} - if mode & O_RDONLY == O_RDONLY { - flags = flags | 0x2; // Read access - } +ProcessInformation :: struct { + handle :Handle, + pid :uintptr, + mainThread :ThreadInformation, +} - if mode & O_WRONLY == O_WRONLY { - flags = flags | 0x220; // Write and resize access - } +UniqueIdentifier :: struct { + using _ : struct #raw_union { + d :[16]u8, + }, - if mode & O_TRUNC == O_TRUNC { - flags = flags | 0x200; // Resize access - } +} - information := new(OS_Node_Information); - error := OSOpenNode(&path[0], len(path), flags, information); +NodeInformation :: struct { + handle :Handle, + type :NodeType, + fileSize :FileOffset, + directoryChildren :FileOffset, +} - if error < ERROR_NONE { - free(information); - return 0, error; - } +DirectoryChild :: struct { + name :[MAX_DIRECTORY_CHILD_NAME_LENGTH]i8, + nameBytes :uintptr, + information :NodeInformation, +} - if mode & O_TRUNC == O_TRUNC { - error := OSResizeFile(information.handle, 0); - if error < ERROR_NONE do return 0, ERROR_UNKNOWN_OPERATION_FAILURE; - } +Point :: struct { + x :i32, + y :i32, +} - if mode & O_APPEND == O_APPEND { - information.position = information.size; - } else { - information.position = 0; - } +Rectangle :: struct { + left :i32, + right :i32, + top :i32, + bottom :i32, +} - return Handle(uintptr(information)), ERROR_NONE; +Rectangle16 :: struct { + left :i16, + right :i16, + top :i16, + bottom :i16, } -close :: proc(fd: Handle) { - information := (^OS_Node_Information)(uintptr(fd)); - OSCloseHandle(information.handle); - free(information); +Color :: struct { + using _ : struct #raw_union { + using _ : struct { + blue :u8, + green :u8, + red :u8, + }, + + combined :u32, + }, + } -file_size :: proc(fd: Handle) -> (i64, Errno) { - x: OS_Node_Information; - OSRefreshNodeInformation(&x); - return x.size, ERROR_NONE; +LinearBuffer :: struct { + width :uintptr, + height :uintptr, + stride :uintptr, + colorFormat :ColorFormat, + handle :Handle, } -write :: proc(fd: Handle, data: []byte) -> (int, Errno) { - if fd == 0 { - OSPrintDirect(&data[0], len(data)); - return len(data), ERROR_NONE; - } else if fd == 1 { - assert(false); - return 0, ERROR_NOT_IMPLEMENTED; - } +RectangleAndColor :: struct { + rectangle :Rectangle, + color :Color, +} + +StyledBoxData :: struct { + backgroundColor :u32, + borderColor :u32, + borders :Rectangle16, + cornerRadius :u8, + roundedCornersToExclude :u8, + ox :i32, + oy :i32, + width :i32, + height :i32, + clip :Rectangle, +} - information := (^OS_Node_Information)(uintptr(fd)); - count := OSWriteFileSync(information.handle, information.position, i64(len(data)), &data[0]); - if count < 0 do return 0, 1; - information.position += count; - return int(count), 0; +InstanceCreateRemoteArguments :: struct { + what :^i8, + argument :^i8, + whatBytes :uintptr, + argumentBytes :uintptr, + modalWindowParent :Handle, + apiInstance :^rawptr, } +DrawSurfaceArguments :: struct { + source :Rectangle, + destination :Rectangle, + border :Rectangle, + alpha :u16, +} + +Spinlock :: struct { + state :u8, +} + +Mutex :: struct { + event :Handle, + spinlock :Spinlock, + state :u8, + queued :u32, +} + +CrashReason :: struct { + errorCode :Error, +} + +ProcessState :: struct { + crashReason :CrashReason, + creationArgument :Generic, + id :uintptr, + executableState :uintptr, + flags :u8, +} + +IORequestProgress :: struct { + accessed :FileOffset, + progress :FileOffset, + completed :bool, + cancelled :bool, + error :Error, +} + +ClipboardHeader :: struct { + customBytes :uintptr, + format :ClipboardFormat, + textBytes :uintptr, + unused :uintptr, +} + +Painter :: struct { + surface :Handle, + clip :Rectangle, + offsetX :i32, + offsetY :i32, + fullAlpha :bool, +} + +Message :: struct { + type :MessageType, + _context :Generic, + using _ : struct #raw_union { + _argument :^rawptr, + mouseMoved : struct { + oldPositionX :i32, + newPositionX :i32, + oldPositionY :i32, + newPositionY :i32, + newPositionXScreen :i32, + newPositionYScreen :i32, + }, + + mouseDragged : struct { + oldPositionX :i32, + newPositionX :i32, + oldPositionY :i32, + newPositionY :i32, + originalPositionX :i32, + originalPositionY :i32, + }, + + mousePressed : struct { + positionX :i32, + positionY :i32, + positionXScreen :i32, + positionYScreen :i32, + clickChainCount :u8, + activationClick :u8, + alt :u8, + ctrl :u8, + shift :u8, + }, + + keyboard : struct { + scancode :u32, + alt :u8, + ctrl :u8, + shift :u8, + numpad :u8, + notHandledBy :Object, + }, + + crash : struct { + reason :CrashReason, + process :Handle, + processNameBuffer :Handle, + processNameBytes :uintptr, + pid :uintptr, + }, + + clipboard :ClipboardHeader, + receive : struct { + buffer :Handle, + bytes :uintptr, + }, + + animate : struct { + deltaMcs :i64, + waitMcs :i64, + complete :bool, + }, + + systemConstantUpdated : struct { + index :uintptr, + newValue :u64, + }, + + desktopExecute : struct { + whatBuffer :Handle, + argumentBuffer :Handle, + mailslot :Handle, + whatBytes :uintptr, + argumentBytes :uintptr, + modalWindowParent :u64, + }, + + dockedWindowCreate : struct { + pipe :Handle, + }, + + taskbar : struct { + id :u64, + buffer :Handle, + bytes :uintptr, + }, + + windowResized : struct { + content :Rectangle, + }, + + painter :^Painter, + measure : struct { + width :i32, + height :i32, + }, + + child :Object, + size : struct { + width :i32, + height :i32, + }, + + hitTest : struct { + x :i32, + y :i32, + inside :bool, + }, + + zOrder : struct { + index :uintptr, + child :^Element, + }, + + scrollbarMoved : struct { + scroll :i32, + }, + + ensureVisible : struct { + child :^Element, + }, + + cursorStyle :CursorStyle, + getItemContent : struct { + mask :u32, + index :ListViewIndex, + column :ListViewIndex, + group :ListViewIndex, + text :^i8, + textBytes :uintptr, + iconID :u16, + iconSize :u16, + indentation :u16, + spaceAfterIcon :u16, + }, + + accessItemState : struct { + mask :u32, + state :u32, + iIndexFrom :ListViewIndex, + eIndexTo :ListViewIndex, + group :ListViewIndex, + }, + + measureItemHeight : struct { + iIndexFrom :ListViewIndex, + eIndexTo :ListViewIndex, + group :ListViewIndex, + height :i32, + }, + + layoutItem : struct { + index :ListViewIndex, + group :ListViewIndex, + knownIndex :ListViewIndex, + knownGroup :ListViewIndex, + bounds :Rectangle, + }, + + toggleItemDisclosure : struct { + index :ListViewIndex, + group :ListViewIndex, + }, + + findItem : struct { + type :u8, + backwards :u8, + inclusive :bool, + indexFrom :ListViewIndex, + groupFrom :ListViewIndex, + foundIndex :ListViewIndex, + foundGroup :ListViewIndex, + using _ : struct #raw_union { + using _ : struct { + prefix :^i8, + prefixBytes :uintptr, + }, + + using _ : struct { + yPosition :i32, + yPositionOfIndexFrom :i32, + offsetIntoItem :i32, + }, + + }, + + }, + + listViewColumn : struct { + index :ListViewIndex, + descending :bool, + }, + + setItemVisibility : struct { + index :ListViewIndex, + group :ListViewIndex, + visible :bool, + }, + + setItemPosition : struct { + index :ListViewIndex, + group :ListViewIndex, + bounds :Rectangle, + }, + + listViewPaint : struct { + painter :^Painter, + width :i32, + height :i32, + index :ListViewIndex, + group :ListViewIndex, + column :ListViewIndex, + }, + + }, + +} + +DebuggerMessage :: struct { + process :Handle, + reason :CrashReason, +} + +DriveInformation :: struct { + name :[64]i8, + nameBytes :uintptr, + mountpoint :[256]i8, + mountpointBytes :uintptr, +} + +SnapshotProcessesItem :: struct { + pid :i64, + memoryUsage :i64, + cpuTimeSlices :i64, + name :[SNAPSHOT_MAX_PROCESS_NAME_LENGTH]i8, + nameLength :uintptr, + internal :u64, +} + +SystemInformation :: struct { + processCount :uintptr, + threadCount :uintptr, + handleCount :uintptr, + commitLimit :uintptr, + commit :uintptr, + countZeroedPages :uintptr, + countFreePages :uintptr, + countStandbyPages :uintptr, + countModifiedPages :uintptr, + countActivePages :uintptr, + coreHeapSize :uintptr, + coreHeapAllocations :uintptr, + fixedHeapSize :uintptr, + fixedHeapAllocations :uintptr, + coreRegions :uintptr, + kernelRegions :uintptr, +} + +SnapshotProcesses :: struct { + count :uintptr, + processes :[]SnapshotProcessesItem, +} + +POSIXSyscall :: struct { + index :int, + arguments :[7]int, +} + +ProcessCreationArguments :: struct { + executablePath :^i8, + executablePathBytes :uintptr, + environmentBlock :^rawptr, + environmentBlockBytes :uintptr, + creationArgument :Generic, + permissions :u64, +} + +UserLoginArguments :: struct { + name :^i8, + nameBytes :uintptr, + home :^i8, + homeBytes :uintptr, +} + +Instance :: struct { + _private :^rawptr, +} + +ListViewColumn :: struct { + title :^i8, + titleBytes :uintptr, + width :i32, + minimumWidth :i32, + flags :u32, +} + +ListViewStyle :: struct { + fixedWidth :i32, + fixedHeight :i32, + groupHeaderHeight :i32, + gapX :i32, + gapY :i32, + margin :Rectangle16, + columns :^ListViewColumn, + columnCount :uintptr, + emptyMessage :^i8, + emptyMessageBytes :uintptr, +} + +NumericEntryProperties :: struct { + value :i32, + dp :i32, + delta :i32, + speed :i32, + minimum :i32, + maximum :i32, + cPrefix :^i8, + cSuffix :^i8, +} + +Batch :: inline proc (calls :^BatchCall, count :uintptr){ addr := 0x1000 + 0 * size_of(int); ((proc (^BatchCall, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(calls, count); } +ProcessCreate :: inline proc (executablePath :^i8, executablePathLength :uintptr, information :^ProcessInformation, argument :Generic) -> Error{ addr := 0x1000 + 1 * size_of(int); return ((proc (^i8, uintptr, ^ProcessInformation, Generic) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(executablePath, executablePathLength, information, argument); } +ThreadCreate :: inline proc (entryFunction :ThreadEntryFunction, information :^ThreadInformation, argument :Generic) -> Error{ addr := 0x1000 + 2 * size_of(int); return ((proc (ThreadEntryFunction, ^ThreadInformation, Generic) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(entryFunction, information, argument); } +SurfaceCreate :: inline proc (width :uintptr, height :uintptr, flags :u32) -> Handle{ addr := 0x1000 + 3 * size_of(int); return ((proc (uintptr, uintptr, u32) -> Handle)(rawptr(((^uintptr)(uintptr(addr)))^)))(width, height, flags); } +EventCreate :: inline proc (autoReset :bool) -> Handle{ addr := 0x1000 + 4 * size_of(int); return ((proc (bool) -> Handle)(rawptr(((^uintptr)(uintptr(addr)))^)))(autoReset); } +ThreadLocalStorageSetAddress :: inline proc (address :^rawptr){ addr := 0x1000 + 5 * size_of(int); ((proc (^rawptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(address); } +ConstantBufferRead :: inline proc (constantBuffer :Handle, output :^rawptr){ addr := 0x1000 + 6 * size_of(int); ((proc (Handle, ^rawptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(constantBuffer, output); } +ConstantBufferShare :: inline proc (constantBuffer :Handle, targetProcess :Handle) -> Handle{ addr := 0x1000 + 7 * size_of(int); return ((proc (Handle, Handle) -> Handle)(rawptr(((^uintptr)(uintptr(addr)))^)))(constantBuffer, targetProcess); } +ConstantBufferCreate :: inline proc (data :^rawptr, dataBytes :uintptr, targetProcess :Handle) -> Handle{ addr := 0x1000 + 8 * size_of(int); return ((proc (^rawptr, uintptr, Handle) -> Handle)(rawptr(((^uintptr)(uintptr(addr)))^)))(data, dataBytes, targetProcess); } +ProcessOpen :: inline proc (pid :u64) -> Handle{ addr := 0x1000 + 9 * size_of(int); return ((proc (u64) -> Handle)(rawptr(((^uintptr)(uintptr(addr)))^)))(pid); } +HandleClose :: inline proc (handle :Handle) -> Error{ addr := 0x1000 + 10 * size_of(int); return ((proc (Handle) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(handle); } +TakeSystemSnapshot :: inline proc (type :i32, bufferSize :^uintptr) -> Handle{ addr := 0x1000 + 11 * size_of(int); return ((proc (i32, ^uintptr) -> Handle)(rawptr(((^uintptr)(uintptr(addr)))^)))(type, bufferSize); } +GetSystemInformation :: inline proc (systemInformation :^SystemInformation){ addr := 0x1000 + 12 * size_of(int); ((proc (^SystemInformation))(rawptr(((^uintptr)(uintptr(addr)))^)))(systemInformation); } +NodeOpen :: inline proc (path :^i8, pathLength :uintptr, flags :u64, information :^NodeInformation) -> Error{ addr := 0x1000 + 13 * size_of(int); return ((proc (^i8, uintptr, u64, ^NodeInformation) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(path, pathLength, flags, information); } +NodeFindUniqueName :: inline proc (buffer :^i8, originalBytes :uintptr, bufferBytes :uintptr) -> uintptr{ addr := 0x1000 + 14 * size_of(int); return ((proc (^i8, uintptr, uintptr) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(buffer, originalBytes, bufferBytes); } +FileReadAll :: inline proc (filePath :^i8, filePathLength :uintptr, fileSize :^uintptr){ addr := 0x1000 + 15 * size_of(int); ((proc (^i8, uintptr, ^uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(filePath, filePathLength, fileSize); } +FileReadSync :: inline proc (file :Handle, offset :FileOffset, size :uintptr, buffer :^rawptr) -> uintptr{ addr := 0x1000 + 16 * size_of(int); return ((proc (Handle, FileOffset, uintptr, ^rawptr) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(file, offset, size, buffer); } +FileWriteSync :: inline proc (file :Handle, offset :FileOffset, size :uintptr, buffer :^rawptr) -> uintptr{ addr := 0x1000 + 17 * size_of(int); return ((proc (Handle, FileOffset, uintptr, ^rawptr) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(file, offset, size, buffer); } +FileResize :: inline proc (file :Handle, newSize :FileOffset) -> Error{ addr := 0x1000 + 18 * size_of(int); return ((proc (Handle, FileOffset) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(file, newSize); } +NodeRefreshInformation :: inline proc (information :^NodeInformation){ addr := 0x1000 + 19 * size_of(int); ((proc (^NodeInformation))(rawptr(((^uintptr)(uintptr(addr)))^)))(information); } +DirectoryEnumerateChildren :: inline proc (directory :Handle, buffer :^DirectoryChild, bufferCount :uintptr) -> int{ addr := 0x1000 + 20 * size_of(int); return ((proc (Handle, ^DirectoryChild, uintptr) -> int)(rawptr(((^uintptr)(uintptr(addr)))^)))(directory, buffer, bufferCount); } +NodeDelete :: inline proc (node :Handle) -> Error{ addr := 0x1000 + 21 * size_of(int); return ((proc (Handle) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(node); } +NodeMove :: inline proc (node :Handle, newDirectory :Handle, newName :^i8, newNameLength :uintptr) -> Error{ addr := 0x1000 + 22 * size_of(int); return ((proc (Handle, Handle, ^i8, uintptr) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(node, newDirectory, newName, newNameLength); } +ThreadTerminate :: inline proc (thread :Handle){ addr := 0x1000 + 23 * size_of(int); ((proc (Handle))(rawptr(((^uintptr)(uintptr(addr)))^)))(thread); } +ProcessTerminate :: inline proc (process :Handle, status :i32){ addr := 0x1000 + 24 * size_of(int); ((proc (Handle, i32))(rawptr(((^uintptr)(uintptr(addr)))^)))(process, status); } +ProcessTerminateCurrent :: inline proc (){ addr := 0x1000 + 25 * size_of(int); ((proc ())(rawptr(((^uintptr)(uintptr(addr)))^)))(); } +ProcessPause :: inline proc (process :Handle, resume :bool){ addr := 0x1000 + 26 * size_of(int); ((proc (Handle, bool))(rawptr(((^uintptr)(uintptr(addr)))^)))(process, resume); } +ProcessCrash :: inline proc (error :Error, message :^i8, messageBytes :uintptr){ addr := 0x1000 + 27 * size_of(int); ((proc (Error, ^i8, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(error, message, messageBytes); } +ThreadGetID :: inline proc (thread :Handle) -> uintptr{ addr := 0x1000 + 28 * size_of(int); return ((proc (Handle) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(thread); } +ProcessGetID :: inline proc (process :Handle) -> uintptr{ addr := 0x1000 + 29 * size_of(int); return ((proc (Handle) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(process); } +SpinlockRelease :: inline proc (spinlock :^Spinlock){ addr := 0x1000 + 30 * size_of(int); ((proc (^Spinlock))(rawptr(((^uintptr)(uintptr(addr)))^)))(spinlock); } +SpinlockAcquire :: inline proc (spinlock :^Spinlock){ addr := 0x1000 + 31 * size_of(int); ((proc (^Spinlock))(rawptr(((^uintptr)(uintptr(addr)))^)))(spinlock); } +MutexRelease :: inline proc (mutex :^Mutex){ addr := 0x1000 + 32 * size_of(int); ((proc (^Mutex))(rawptr(((^uintptr)(uintptr(addr)))^)))(mutex); } +MutexAcquire :: inline proc (mutex :^Mutex){ addr := 0x1000 + 33 * size_of(int); ((proc (^Mutex))(rawptr(((^uintptr)(uintptr(addr)))^)))(mutex); } +MutexDestroy :: inline proc (mutex :^Mutex){ addr := 0x1000 + 34 * size_of(int); ((proc (^Mutex))(rawptr(((^uintptr)(uintptr(addr)))^)))(mutex); } +SchedulerYield :: inline proc (){ addr := 0x1000 + 35 * size_of(int); ((proc ())(rawptr(((^uintptr)(uintptr(addr)))^)))(); } +EventSet :: inline proc (event :Handle){ addr := 0x1000 + 36 * size_of(int); ((proc (Handle))(rawptr(((^uintptr)(uintptr(addr)))^)))(event); } +EventReset :: inline proc (event :Handle){ addr := 0x1000 + 37 * size_of(int); ((proc (Handle))(rawptr(((^uintptr)(uintptr(addr)))^)))(event); } +EventPoll :: inline proc (event :Handle) -> Error{ addr := 0x1000 + 38 * size_of(int); return ((proc (Handle) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(event); } +Wait :: inline proc (objects :^Handle, objectCount :uintptr, timeoutMs :uintptr) -> uintptr{ addr := 0x1000 + 39 * size_of(int); return ((proc (^Handle, uintptr, uintptr) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(objects, objectCount, timeoutMs); } +Sleep :: inline proc (milliseconds :u64){ addr := 0x1000 + 40 * size_of(int); ((proc (u64))(rawptr(((^uintptr)(uintptr(addr)))^)))(milliseconds); } +MemoryOpen :: inline proc (size :uintptr, name :^i8, nameLength :uintptr, flags :u32) -> Handle{ addr := 0x1000 + 41 * size_of(int); return ((proc (uintptr, ^i8, uintptr, u32) -> Handle)(rawptr(((^uintptr)(uintptr(addr)))^)))(size, name, nameLength, flags); } +MemoryShare :: inline proc (sharedMemoryRegion :Handle, targetProcess :Handle, readOnly :bool) -> Handle{ addr := 0x1000 + 42 * size_of(int); return ((proc (Handle, Handle, bool) -> Handle)(rawptr(((^uintptr)(uintptr(addr)))^)))(sharedMemoryRegion, targetProcess, readOnly); } +ObjectMap :: inline proc (object :Handle, offset :uintptr, size :uintptr, flags :u32){ addr := 0x1000 + 43 * size_of(int); ((proc (Handle, uintptr, uintptr, u32))(rawptr(((^uintptr)(uintptr(addr)))^)))(object, offset, size, flags); } +MemoryAllocate :: inline proc (size :uintptr){ addr := 0x1000 + 44 * size_of(int); ((proc (uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(size); } +MemoryFree :: inline proc (address :^rawptr) -> Error{ addr := 0x1000 + 45 * size_of(int); return ((proc (^rawptr) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(address); } +GetCreationArgument :: inline proc (object :Handle) -> Generic{ addr := 0x1000 + 46 * size_of(int); return ((proc (Handle) -> Generic)(rawptr(((^uintptr)(uintptr(addr)))^)))(object); } +ProcessGetState :: inline proc (process :Handle, state :^ProcessState){ addr := 0x1000 + 47 * size_of(int); ((proc (Handle, ^ProcessState))(rawptr(((^uintptr)(uintptr(addr)))^)))(process, state); } +SurfaceGetLinearBuffer :: inline proc (surface :Handle, linearBuffer :^LinearBuffer){ addr := 0x1000 + 48 * size_of(int); ((proc (Handle, ^LinearBuffer))(rawptr(((^uintptr)(uintptr(addr)))^)))(surface, linearBuffer); } +RectangleInvalidate :: inline proc (surface :Handle, rectangle :Rectangle){ addr := 0x1000 + 49 * size_of(int); ((proc (Handle, Rectangle))(rawptr(((^uintptr)(uintptr(addr)))^)))(surface, rectangle); } +CopyToScreen :: inline proc (source :Handle, point :Point, depth :u16){ addr := 0x1000 + 50 * size_of(int); ((proc (Handle, Point, u16))(rawptr(((^uintptr)(uintptr(addr)))^)))(source, point, depth); } +ForceScreenUpdate :: inline proc (){ addr := 0x1000 + 51 * size_of(int); ((proc ())(rawptr(((^uintptr)(uintptr(addr)))^)))(); } +DrawRectangle :: inline proc (surface :Handle, rectangle :Rectangle, color :Color){ addr := 0x1000 + 52 * size_of(int); ((proc (Handle, Rectangle, Color))(rawptr(((^uintptr)(uintptr(addr)))^)))(surface, rectangle, color); } +DrawRectangleClipped :: inline proc (surface :Handle, rectangle :Rectangle, color :Color, clipRegion :Rectangle){ addr := 0x1000 + 53 * size_of(int); ((proc (Handle, Rectangle, Color, Rectangle))(rawptr(((^uintptr)(uintptr(addr)))^)))(surface, rectangle, color, clipRegion); } +DrawSurfaceBlit :: inline proc (destination :Handle, source :Handle, destinationPoint :Point){ addr := 0x1000 + 54 * size_of(int); ((proc (Handle, Handle, Point))(rawptr(((^uintptr)(uintptr(addr)))^)))(destination, source, destinationPoint); } +DrawSurface :: inline proc (destination :Handle, source :Handle, destinationRegion :Rectangle, sourceRegion :Rectangle, borderRegion :Rectangle, mode :DrawMode, alpha :u16) -> Error{ addr := 0x1000 + 55 * size_of(int); return ((proc (Handle, Handle, Rectangle, Rectangle, Rectangle, DrawMode, u16) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(destination, source, destinationRegion, sourceRegion, borderRegion, mode, alpha); } +DrawSurfaceClipped :: inline proc (destination :Handle, source :Handle, destinationRegion :Rectangle, sourceRegion :Rectangle, borderRegion :Rectangle, mode :DrawMode, alpha :u16, clipRegion :Rectangle) -> Error{ addr := 0x1000 + 56 * size_of(int); return ((proc (Handle, Handle, Rectangle, Rectangle, Rectangle, DrawMode, u16, Rectangle) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(destination, source, destinationRegion, sourceRegion, borderRegion, mode, alpha, clipRegion); } +DrawBitmap :: inline proc (destination :Handle, destinationPoint :Point, bitmap :^rawptr, width :uintptr, height :uintptr, stride :uintptr, colorFormat :ColorFormat){ addr := 0x1000 + 57 * size_of(int); ((proc (Handle, Point, ^rawptr, uintptr, uintptr, uintptr, ColorFormat))(rawptr(((^uintptr)(uintptr(addr)))^)))(destination, destinationPoint, bitmap, width, height, stride, colorFormat); } +SurfaceClearInvalidatedRegion :: inline proc (surface :Handle){ addr := 0x1000 + 58 * size_of(int); ((proc (Handle))(rawptr(((^uintptr)(uintptr(addr)))^)))(surface); } +RectangleClip :: inline proc (parent :Rectangle, rectangle :Rectangle, output :^Rectangle) -> bool{ addr := 0x1000 + 59 * size_of(int); return ((proc (Rectangle, Rectangle, ^Rectangle) -> bool)(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, rectangle, output); } +DrawBox :: inline proc (surface :Handle, rectangle :Rectangle, style :u8, color :u32, clipRegion :Rectangle){ addr := 0x1000 + 60 * size_of(int); ((proc (Handle, Rectangle, u8, u32, Rectangle))(rawptr(((^uintptr)(uintptr(addr)))^)))(surface, rectangle, style, color, clipRegion); } +RedrawAll :: inline proc (){ addr := 0x1000 + 61 * size_of(int); ((proc ())(rawptr(((^uintptr)(uintptr(addr)))^)))(); } +MessagePost :: inline proc (message :^Message) -> Error{ addr := 0x1000 + 62 * size_of(int); return ((proc (^Message) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(message); } +MessagePostRemote :: inline proc (process :Handle, message :^Message) -> Error{ addr := 0x1000 + 63 * size_of(int); return ((proc (Handle, ^Message) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(process, message); } +ExtractArguments :: inline proc (string :^i8, bytes :uintptr, delimiterByte :u8, replacementDelimiter :u8, argvAllocated :uintptr, argv :^^i8, argc :^uintptr) -> bool{ addr := 0x1000 + 64 * size_of(int); return ((proc (^i8, uintptr, u8, u8, uintptr, ^^i8, ^uintptr) -> bool)(rawptr(((^uintptr)(uintptr(addr)))^)))(string, bytes, delimiterByte, replacementDelimiter, argvAllocated, argv, argc); } +CStringLength :: inline proc (string :^i8) -> uintptr{ addr := 0x1000 + 65 * size_of(int); return ((proc (^i8) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(string); } +StringLength :: inline proc (string :^i8, end :u8) -> uintptr{ addr := 0x1000 + 66 * size_of(int); return ((proc (^i8, u8) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(string, end); } +MemoryCopy :: inline proc (destination :^rawptr, source :^rawptr, bytes :uintptr){ addr := 0x1000 + 67 * size_of(int); ((proc (^rawptr, ^rawptr, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(destination, source, bytes); } +MemoryMove :: inline proc (_start :^rawptr, _end :^rawptr, amount :int, zeroEmptySpace :bool){ addr := 0x1000 + 68 * size_of(int); ((proc (^rawptr, ^rawptr, int, bool))(rawptr(((^uintptr)(uintptr(addr)))^)))(_start, _end, amount, zeroEmptySpace); } +MemoryCopyReverse :: inline proc (_destination :^rawptr, _source :^rawptr, bytes :uintptr){ addr := 0x1000 + 69 * size_of(int); ((proc (^rawptr, ^rawptr, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(_destination, _source, bytes); } +MemoryZero :: inline proc (destination :^rawptr, bytes :uintptr){ addr := 0x1000 + 70 * size_of(int); ((proc (^rawptr, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(destination, bytes); } +MemoryCompare :: inline proc (a :^rawptr, b :^rawptr, bytes :uintptr) -> i32{ addr := 0x1000 + 71 * size_of(int); return ((proc (^rawptr, ^rawptr, uintptr) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(a, b, bytes); } +MemorySumBytes :: inline proc (data :^u8, bytes :uintptr) -> u8{ addr := 0x1000 + 72 * size_of(int); return ((proc (^u8, uintptr) -> u8)(rawptr(((^uintptr)(uintptr(addr)))^)))(data, bytes); } +PrintDirect :: inline proc (string :^i8, stringLength :uintptr){ addr := 0x1000 + 73 * size_of(int); ((proc (^i8, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(string, stringLength); } +StringFormat :: inline proc (buffer :^i8, bufferLength :uintptr, format :^i8, args : ..any) -> uintptr{ addr := 0x1000 + 74 * size_of(int); return ((proc (^i8, uintptr, ^i8, ..any) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(buffer, bufferLength, format, ); } +StringFormatAppend :: inline proc (buffer :^i8, bufferLength :uintptr, bufferPosition :^uintptr, format :^i8, args : ..any){ addr := 0x1000 + 75 * size_of(int); ((proc (^i8, uintptr, ^uintptr, ^i8, ..any))(rawptr(((^uintptr)(uintptr(addr)))^)))(buffer, bufferLength, bufferPosition, format, ); } +PrintHelloWorld :: inline proc (){ addr := 0x1000 + 76 * size_of(int); ((proc ())(rawptr(((^uintptr)(uintptr(addr)))^)))(); } +GetRandomByte :: inline proc () -> u8{ addr := 0x1000 + 77 * size_of(int); return ((proc () -> u8)(rawptr(((^uintptr)(uintptr(addr)))^)))(); } +Sort :: inline proc (_base :^rawptr, nmemb :uintptr, size :uintptr, compar :ComparisonCallbackFunction, argument :Generic){ addr := 0x1000 + 78 * size_of(int); ((proc (^rawptr, uintptr, uintptr, ComparisonCallbackFunction, Generic))(rawptr(((^uintptr)(uintptr(addr)))^)))(_base, nmemb, size, compar, argument); } +SortWithSwapCallback :: inline proc (_base :^rawptr, nmemb :uintptr, size :uintptr, compar :ComparisonCallbackFunction, argument :Generic, swap :SwapCallbackFunction){ addr := 0x1000 + 79 * size_of(int); ((proc (^rawptr, uintptr, uintptr, ComparisonCallbackFunction, Generic, SwapCallbackFunction))(rawptr(((^uintptr)(uintptr(addr)))^)))(_base, nmemb, size, compar, argument, swap); } +StringCompare :: inline proc (s1 :^i8, s2 :^i8, length1 :uintptr, length2 :uintptr) -> i32{ addr := 0x1000 + 80 * size_of(int); return ((proc (^i8, ^i8, uintptr, uintptr) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(s1, s2, length1, length2); } +IntegerParse :: inline proc (text :^i8, bytes :uintptr) -> i64{ addr := 0x1000 + 81 * size_of(int); return ((proc (^i8, uintptr) -> i64)(rawptr(((^uintptr)(uintptr(addr)))^)))(text, bytes); } +CRTmemset :: inline proc (s :^rawptr, c :i32, n :uintptr){ addr := 0x1000 + 82 * size_of(int); ((proc (^rawptr, i32, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(s, c, n); } +CRTmemcpy :: inline proc (dest :^rawptr, src :^rawptr, n :uintptr){ addr := 0x1000 + 83 * size_of(int); ((proc (^rawptr, ^rawptr, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(dest, src, n); } +CRTmemmove :: inline proc (dest :^rawptr, src :^rawptr, n :uintptr){ addr := 0x1000 + 84 * size_of(int); ((proc (^rawptr, ^rawptr, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(dest, src, n); } +CRTstrlen :: inline proc (s :^i8) -> uintptr{ addr := 0x1000 + 85 * size_of(int); return ((proc (^i8) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(s); } +CRTstrnlen :: inline proc (s :^i8, maxlen :uintptr) -> uintptr{ addr := 0x1000 + 86 * size_of(int); return ((proc (^i8, uintptr) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(s, maxlen); } +CRTmalloc :: inline proc (size :uintptr){ addr := 0x1000 + 87 * size_of(int); ((proc (uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(size); } +CRTcalloc :: inline proc (num :uintptr, size :uintptr){ addr := 0x1000 + 88 * size_of(int); ((proc (uintptr, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(num, size); } +CRTfree :: inline proc (ptr :^rawptr){ addr := 0x1000 + 89 * size_of(int); ((proc (^rawptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(ptr); } +CRTabs :: inline proc (n :i32) -> i32{ addr := 0x1000 + 90 * size_of(int); return ((proc (i32) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(n); } +CRTrealloc :: inline proc (ptr :^rawptr, size :uintptr){ addr := 0x1000 + 91 * size_of(int); ((proc (^rawptr, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(ptr, size); } +CRTgetenv :: inline proc (name :^i8) -> ^i8{ addr := 0x1000 + 92 * size_of(int); return ((proc (^i8) -> ^i8)(rawptr(((^uintptr)(uintptr(addr)))^)))(name); } +CRTstrncmp :: inline proc (s1 :^i8, s2 :^i8, n :uintptr) -> i32{ addr := 0x1000 + 93 * size_of(int); return ((proc (^i8, ^i8, uintptr) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(s1, s2, n); } +CRTmemcmp :: inline proc (s1 :^rawptr, s2 :^rawptr, n :uintptr) -> i32{ addr := 0x1000 + 94 * size_of(int); return ((proc (^rawptr, ^rawptr, uintptr) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(s1, s2, n); } +CRTqsort :: inline proc (_base :^rawptr, nmemb :uintptr, size :uintptr, compar :CRTComparisonCallback){ addr := 0x1000 + 95 * size_of(int); ((proc (^rawptr, uintptr, uintptr, CRTComparisonCallback))(rawptr(((^uintptr)(uintptr(addr)))^)))(_base, nmemb, size, compar); } +CRTstrcmp :: inline proc (s1 :^i8, s2 :^i8) -> i32{ addr := 0x1000 + 96 * size_of(int); return ((proc (^i8, ^i8) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(s1, s2); } +CRTstrstr :: inline proc (haystack :^i8, needle :^i8) -> ^i8{ addr := 0x1000 + 97 * size_of(int); return ((proc (^i8, ^i8) -> ^i8)(rawptr(((^uintptr)(uintptr(addr)))^)))(haystack, needle); } +CRTstrcpy :: inline proc (dest :^i8, src :^i8) -> ^i8{ addr := 0x1000 + 98 * size_of(int); return ((proc (^i8, ^i8) -> ^i8)(rawptr(((^uintptr)(uintptr(addr)))^)))(dest, src); } +CRTisalpha :: inline proc (c :i32) -> i32{ addr := 0x1000 + 99 * size_of(int); return ((proc (i32) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(c); } +CRTmemchr :: inline proc (_s :^rawptr, _c :i32, n :uintptr){ addr := 0x1000 + 100 * size_of(int); ((proc (^rawptr, i32, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(_s, _c, n); } +CRTisdigit :: inline proc (c :i32) -> i32{ addr := 0x1000 + 101 * size_of(int); return ((proc (i32) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(c); } +CRTstrcat :: inline proc (dest :^i8, src :^i8) -> ^i8{ addr := 0x1000 + 102 * size_of(int); return ((proc (^i8, ^i8) -> ^i8)(rawptr(((^uintptr)(uintptr(addr)))^)))(dest, src); } +CRTtolower :: inline proc (c :i32) -> i32{ addr := 0x1000 + 103 * size_of(int); return ((proc (i32) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(c); } +CRTstrncpy :: inline proc (dest :^i8, src :^i8, n :uintptr) -> ^i8{ addr := 0x1000 + 104 * size_of(int); return ((proc (^i8, ^i8, uintptr) -> ^i8)(rawptr(((^uintptr)(uintptr(addr)))^)))(dest, src, n); } +CRTstrtoul :: inline proc (nptr :^i8, endptr :^^i8, base :i32) -> u64{ addr := 0x1000 + 105 * size_of(int); return ((proc (^i8, ^^i8, i32) -> u64)(rawptr(((^uintptr)(uintptr(addr)))^)))(nptr, endptr, base); } +Execute :: inline proc (what :^i8, whatBytes :uintptr, argument :^i8, argumentBytes :uintptr){ addr := 0x1000 + 106 * size_of(int); ((proc (^i8, uintptr, ^i8, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(what, whatBytes, argument, argumentBytes); } +Abort :: inline proc (){ addr := 0x1000 + 107 * size_of(int); ((proc ())(rawptr(((^uintptr)(uintptr(addr)))^)))(); } +MailslotSendData :: inline proc (mailslot :Handle, data :^rawptr, bytes :uintptr) -> bool{ addr := 0x1000 + 108 * size_of(int); return ((proc (Handle, ^rawptr, uintptr) -> bool)(rawptr(((^uintptr)(uintptr(addr)))^)))(mailslot, data, bytes); } +CRTfloorf :: inline proc (x :f32) -> f32{ addr := 0x1000 + 109 * size_of(int); return ((proc (f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +CRTceilf :: inline proc (x :f32) -> f32{ addr := 0x1000 + 110 * size_of(int); return ((proc (f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +CRTsinf :: inline proc (x :f32) -> f32{ addr := 0x1000 + 111 * size_of(int); return ((proc (f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +CRTcosf :: inline proc (x :f32) -> f32{ addr := 0x1000 + 112 * size_of(int); return ((proc (f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +CRTatan2f :: inline proc (y :f32, x :f32) -> f32{ addr := 0x1000 + 113 * size_of(int); return ((proc (f32, f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(y, x); } +CRTfmodf :: inline proc (x :f32, y :f32) -> f32{ addr := 0x1000 + 114 * size_of(int); return ((proc (f32, f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(x, y); } +CRTacosf :: inline proc (x :f32) -> f32{ addr := 0x1000 + 115 * size_of(int); return ((proc (f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +CRTasinf :: inline proc (x :f32) -> f32{ addr := 0x1000 + 116 * size_of(int); return ((proc (f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +CRTatanf :: inline proc (x :f32) -> f32{ addr := 0x1000 + 117 * size_of(int); return ((proc (f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +RandomSeed :: inline proc (x :u64){ addr := 0x1000 + 118 * size_of(int); ((proc (u64))(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +CRTsqrtf :: inline proc (x :f32) -> f32{ addr := 0x1000 + 119 * size_of(int); return ((proc (f32) -> f32)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +CRTsqrtl :: inline proc (x :LongDouble) -> LongDouble{ addr := 0x1000 + 120 * size_of(int); return ((proc (LongDouble) -> LongDouble)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +CRTfabsl :: inline proc (x :LongDouble) -> LongDouble{ addr := 0x1000 + 121 * size_of(int); return ((proc (LongDouble) -> LongDouble)(rawptr(((^uintptr)(uintptr(addr)))^)))(x); } +Syscall :: inline proc (a :uintptr, b :uintptr, c :uintptr, d :uintptr, e :uintptr, f :uintptr) -> uintptr{ addr := 0x1000 + 122 * size_of(int); return ((proc (uintptr, uintptr, uintptr, uintptr, uintptr, uintptr) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(a, b, c, d, e, f); } +ProcessorReadTimeStamp :: inline proc () -> u64{ addr := 0x1000 + 123 * size_of(int); return ((proc () -> u64)(rawptr(((^uintptr)(uintptr(addr)))^)))(); } +HeapAllocate :: inline proc (size :uintptr, zeroMemory :bool){ addr := 0x1000 + 124 * size_of(int); ((proc (uintptr, bool))(rawptr(((^uintptr)(uintptr(addr)))^)))(size, zeroMemory); } +HeapFree :: inline proc (address :^rawptr){ addr := 0x1000 + 125 * size_of(int); ((proc (^rawptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(address); } +Print :: inline proc (format :^i8, args : ..any){ addr := 0x1000 + 126 * size_of(int); ((proc (^i8, ..any))(rawptr(((^uintptr)(uintptr(addr)))^)))(format, ); } +MemoryFill :: inline proc (from :^rawptr, to :^rawptr, byte :u8){ addr := 0x1000 + 127 * size_of(int); ((proc (^rawptr, ^rawptr, u8))(rawptr(((^uintptr)(uintptr(addr)))^)))(from, to, byte); } +InitialiseCStandardLibrary :: inline proc (argc :^i32, argv :^^^i8){ addr := 0x1000 + 128 * size_of(int); ((proc (^i32, ^^^i8))(rawptr(((^uintptr)(uintptr(addr)))^)))(argc, argv); } +MakeLinuxSystemCall2 :: inline proc (n :int, a1 :int, a2 :int, a3 :int, a4 :int, a5 :int, a6 :int) -> int{ addr := 0x1000 + 129 * size_of(int); return ((proc (int, int, int, int, int, int, int) -> int)(rawptr(((^uintptr)(uintptr(addr)))^)))(n, a1, a2, a3, a4, a5, a6); } +ProcessCreate2 :: inline proc (arguments :^ProcessCreationArguments, information :^ProcessInformation) -> Error{ addr := 0x1000 + 130 * size_of(int); return ((proc (^ProcessCreationArguments, ^ProcessInformation) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(arguments, information); } +CRTatoi :: inline proc (string :^i8) -> i32{ addr := 0x1000 + 131 * size_of(int); return ((proc (^i8) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(string); } +ProcessGetExitStatus :: inline proc (process :Handle) -> i32{ addr := 0x1000 + 132 * size_of(int); return ((proc (Handle) -> i32)(rawptr(((^uintptr)(uintptr(addr)))^)))(process); } +SurfaceReset :: inline proc (surface :Handle){ addr := 0x1000 + 133 * size_of(int); ((proc (Handle))(rawptr(((^uintptr)(uintptr(addr)))^)))(surface); } +TimerCreate :: inline proc () -> Handle{ addr := 0x1000 + 134 * size_of(int); return ((proc () -> Handle)(rawptr(((^uintptr)(uintptr(addr)))^)))(); } +TimerSet :: inline proc (handle :Handle, afterMs :u64, object :Object, argument :Generic){ addr := 0x1000 + 135 * size_of(int); ((proc (Handle, u64, Object, Generic))(rawptr(((^uintptr)(uintptr(addr)))^)))(handle, afterMs, object, argument); } +FileWriteAll :: inline proc (filePath :^i8, filePathLength :uintptr, data :^rawptr, fileSize :uintptr) -> Error{ addr := 0x1000 + 136 * size_of(int); return ((proc (^i8, uintptr, ^rawptr, uintptr) -> Error)(rawptr(((^uintptr)(uintptr(addr)))^)))(filePath, filePathLength, data, fileSize); } +UserGetHomeFolder :: inline proc (buffer :^i8, bufferBytes :uintptr) -> uintptr{ addr := 0x1000 + 137 * size_of(int); return ((proc (^i8, uintptr) -> uintptr)(rawptr(((^uintptr)(uintptr(addr)))^)))(buffer, bufferBytes); } +Assert :: inline proc (expression :bool, failureMessage :^i8){ addr := 0x1000 + 138 * size_of(int); ((proc (bool, ^i8))(rawptr(((^uintptr)(uintptr(addr)))^)))(expression, failureMessage); } +ResizeArray :: inline proc (array :^^rawptr, allocated :^uintptr, needed :uintptr, itemSize :uintptr){ addr := 0x1000 + 139 * size_of(int); ((proc (^^rawptr, ^uintptr, uintptr, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(array, allocated, needed, itemSize); } +MessageLoopEnter :: inline proc (callback :MessageCallbackFunction = nil){ addr := 0x1000 + 140 * size_of(int); ((proc (MessageCallbackFunction))(rawptr(((^uintptr)(uintptr(addr)))^)))(callback); } +InstanceCreate :: inline proc (bytes :uintptr) -> ^Instance{ addr := 0x1000 + 141 * size_of(int); return ((proc (uintptr) -> ^Instance)(rawptr(((^uintptr)(uintptr(addr)))^)))(bytes); } +MouseGetPosition :: inline proc (relativeWindow :^Window = nil) -> Point{ addr := 0x1000 + 142 * size_of(int); return ((proc (^Window) -> Point)(rawptr(((^uintptr)(uintptr(addr)))^)))(relativeWindow); } +MouseSetPosition :: inline proc (relativeWindow :^Window, x :i32, y :i32){ addr := 0x1000 + 143 * size_of(int); ((proc (^Window, i32, i32))(rawptr(((^uintptr)(uintptr(addr)))^)))(relativeWindow, x, y); } +NewPanel :: inline proc (parent :^Element, cStyle :^i8, flags :u64 = FLAGS_DEFAULT) -> ^Panel{ addr := 0x1000 + 144 * size_of(int); return ((proc (^Element, ^i8, u64) -> ^Panel)(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, cStyle, flags); } +NewCustomPanel :: inline proc (parent :^Element, style :Data, flags :u64 = FLAGS_DEFAULT) -> ^Panel{ addr := 0x1000 + 145 * size_of(int); return ((proc (^Element, Data, u64) -> ^Panel)(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, style, flags); } +NewWindow :: inline proc (instance :^Instance, style :WindowStyle = WindowStyle.WINDOW_NORMAL) -> ^Window{ addr := 0x1000 + 146 * size_of(int); return ((proc (^Instance, WindowStyle) -> ^Window)(rawptr(((^uintptr)(uintptr(addr)))^)))(instance, style); } +NewScrollbar :: inline proc (parent :^Element, flags :u64 = FLAGS_DEFAULT, userCallback :UICallbackFunction = nil, _context :Generic = nil) -> ^Scrollbar{ addr := 0x1000 + 147 * size_of(int); return ((proc (^Element, u64, UICallbackFunction, Generic) -> ^Scrollbar)(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, flags, userCallback, _context); } +NewButton :: inline proc (parent :^Element, label :^i8 = nil, labelBytes :int = -1, flags :u64 = FLAGS_DEFAULT, userCallback :UICallbackFunction = nil, _context :Generic = nil) -> ^Button{ addr := 0x1000 + 148 * size_of(int); return ((proc (^Element, ^i8, int, u64, UICallbackFunction, Generic) -> ^Button)(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, label, labelBytes, flags, userCallback, _context); } +NewTextbox :: inline proc (parent :^Element, flags :u64 = FLAGS_DEFAULT, userCallback :UICallbackFunction = nil, _context :Generic = nil) -> ^Textbox{ addr := 0x1000 + 149 * size_of(int); return ((proc (^Element, u64, UICallbackFunction, Generic) -> ^Textbox)(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, flags, userCallback, _context); } +NewNumericEntry :: inline proc (parent :^Element, flags :u64 = FLAGS_DEFAULT, userCallback :UICallbackFunction = nil, _context :Generic = nil) -> ^NumericEntry{ addr := 0x1000 + 150 * size_of(int); return ((proc (^Element, u64, UICallbackFunction, Generic) -> ^NumericEntry)(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, flags, userCallback, _context); } +NewListView :: inline proc (parent :^Element, flags :u64 = FLAGS_DEFAULT, style :^ListViewStyle = nil, userCallback :UICallbackFunction = nil, _context :Generic = nil) -> ^ListView{ addr := 0x1000 + 151 * size_of(int); return ((proc (^Element, u64, ^ListViewStyle, UICallbackFunction, Generic) -> ^ListView)(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, flags, style, userCallback, _context); } +NewMenu :: inline proc (parent :^Element, flags :u64 = FLAGS_DEFAULT, userCallback :MenuCallbackFunction = nil, _context :Generic = nil) -> ^Menu{ addr := 0x1000 + 152 * size_of(int); return ((proc (^Element, u64, MenuCallbackFunction, Generic) -> ^Menu)(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, flags, userCallback, _context); } +NewMenuItem :: inline proc (parent :^Element, flags :u64, label :^i8, labelBytes :int = -1, callback :MenuCallbackFunction = nil, _context :Generic = nil){ addr := 0x1000 + 153 * size_of(int); ((proc (^Element, u64, ^i8, int, MenuCallbackFunction, Generic))(rawptr(((^uintptr)(uintptr(addr)))^)))(parent, flags, label, labelBytes, callback, _context); } +ElementGetInstance :: inline proc (element :^Element) -> ^INSTANCE_TYPE{ addr := 0x1000 + 154 * size_of(int); return ((proc (^Element) -> ^INSTANCE_TYPE)(rawptr(((^uintptr)(uintptr(addr)))^)))(element); } +ElementFocus :: inline proc (element :^Element, ensureVisible :bool){ addr := 0x1000 + 155 * size_of(int); ((proc (^Element, bool))(rawptr(((^uintptr)(uintptr(addr)))^)))(element, ensureVisible); } +ElementSetDisabled :: inline proc (element :^Element, disabled :bool){ addr := 0x1000 + 156 * size_of(int); ((proc (^Element, bool))(rawptr(((^uintptr)(uintptr(addr)))^)))(element, disabled); } +ElementSetCallback :: inline proc (element :^Element, callback :UICallbackFunction, _context :Generic){ addr := 0x1000 + 157 * size_of(int); ((proc (^Element, UICallbackFunction, Generic))(rawptr(((^uintptr)(uintptr(addr)))^)))(element, callback, _context); } +ScrollbarSetMeasurements :: inline proc (scrollbar :^Scrollbar, viewportSize :i32, contentSize :i32){ addr := 0x1000 + 158 * size_of(int); ((proc (^Scrollbar, i32, i32))(rawptr(((^uintptr)(uintptr(addr)))^)))(scrollbar, viewportSize, contentSize); } +ScrollbarSetPosition :: inline proc (scrollbar :^Scrollbar, position :f32, sendMovedMessage :bool, smoothScroll :bool){ addr := 0x1000 + 159 * size_of(int); ((proc (^Scrollbar, f32, bool, bool))(rawptr(((^uintptr)(uintptr(addr)))^)))(scrollbar, position, sendMovedMessage, smoothScroll); } +WindowGetBounds :: inline proc (window :^Window) -> Rectangle{ addr := 0x1000 + 160 * size_of(int); return ((proc (^Window) -> Rectangle)(rawptr(((^uintptr)(uintptr(addr)))^)))(window); } +WindowGetToolbar :: inline proc (window :^Window) -> ^Element{ addr := 0x1000 + 161 * size_of(int); return ((proc (^Window) -> ^Element)(rawptr(((^uintptr)(uintptr(addr)))^)))(window); } +ListViewInsert :: inline proc (listView :^ListView, group :ListViewIndex, index :ListViewIndex, count :uintptr){ addr := 0x1000 + 162 * size_of(int); ((proc (^ListView, ListViewIndex, ListViewIndex, uintptr))(rawptr(((^uintptr)(uintptr(addr)))^)))(listView, group, index, count); } +ListViewInsertGroup :: inline proc (listView :^ListView, group :ListViewIndex){ addr := 0x1000 + 163 * size_of(int); ((proc (^ListView, ListViewIndex))(rawptr(((^uintptr)(uintptr(addr)))^)))(listView, group); } +ListViewRemove :: inline proc (listView :^ListView, group :ListViewIndex, index :ListViewIndex, count :int, removedHeight :i32){ addr := 0x1000 + 164 * size_of(int); ((proc (^ListView, ListViewIndex, ListViewIndex, int, i32))(rawptr(((^uintptr)(uintptr(addr)))^)))(listView, group, index, count, removedHeight); } +ListViewRemoveGroup :: inline proc (listView :^ListView, group :ListViewIndex){ addr := 0x1000 + 165 * size_of(int); ((proc (^ListView, ListViewIndex))(rawptr(((^uintptr)(uintptr(addr)))^)))(listView, group); } +ListViewInvalidate :: inline proc (listView :^ListView, deltaHeight :i32, recalculateHeight :bool){ addr := 0x1000 + 166 * size_of(int); ((proc (^ListView, i32, bool))(rawptr(((^uintptr)(uintptr(addr)))^)))(listView, deltaHeight, recalculateHeight); } +ListViewEnsureVisible :: inline proc (listView :^ListView, group :ListViewIndex, index :ListViewIndex){ addr := 0x1000 + 167 * size_of(int); ((proc (^ListView, ListViewIndex, ListViewIndex))(rawptr(((^uintptr)(uintptr(addr)))^)))(listView, group, index); } +ListViewResetSearchBuffer :: inline proc (listView :^ListView){ addr := 0x1000 + 168 * size_of(int); ((proc (^ListView))(rawptr(((^uintptr)(uintptr(addr)))^)))(listView); } +ButtonSetIcon :: inline proc (button :^Button, iconID :u32){ addr := 0x1000 + 169 * size_of(int); ((proc (^Button, u32))(rawptr(((^uintptr)(uintptr(addr)))^)))(button, iconID); } +DataParse :: inline proc (cFormat :^i8, args : ..any) -> Data{ addr := 0x1000 + 170 * size_of(int); return ((proc (^i8, ..any) -> Data)(rawptr(((^uintptr)(uintptr(addr)))^)))(cFormat, ); } + + +////////////////////////////////////////////////////// + +Errno :: distinct i32; + +stdin: Handle = 0; +stdout: Handle = 1; +stderr: Handle = 2; + +O_RDONLY :: 0x00000; +O_WRONLY :: 0x00001; +O_RDWR :: 0x00002; +O_CREATE :: 0x00040; +O_EXCL :: 0x00080; +O_NOCTTY :: 0x00100; +O_TRUNC :: 0x00200; +O_NONBLOCK :: 0x00800; +O_APPEND :: 0x00400; +O_SYNC :: 0x01000; +O_ASYNC :: 0x02000; +O_CLOEXEC :: 0x80000; + +ERROR_SUCCESS :: 0; +ERROR_UNSUPPORTED :: 1; + read :: proc(fd: Handle, data: []byte) -> (int, Errno) { - if (fd == 0 || fd == 1) { - assert(false); - return 0, ERROR_NOT_IMPLEMENTED; + return -1, ERROR_UNSUPPORTED; +} + +write :: proc(fd: Handle, data: []byte) -> (int, Errno) { + if (fd == stdout) { + PrintDirect((^i8)(&data[0]), (uintptr)(len(data))); + return len(data), ERROR_SUCCESS; } - information := (^OS_Node_Information)(uintptr(fd)); - count := OSReadFileSync(information.handle, information.position, i64(len(data)), &data[0]); - if count < 0 do return 0, ERROR_UNKNOWN_OPERATION_FAILURE; - information.position += count; - return int(count), ERROR_NONE; + return -1, ERROR_UNSUPPORTED; +} + +open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { + return INVALID_HANDLE, ERROR_UNSUPPORTED; } + +close :: proc(fd: Handle) -> Errno { + return ERROR_UNSUPPORTED; +} + +file_size :: proc(fd: Handle) -> (i64, Errno) { + return 0, ERROR_UNSUPPORTED; +} + +heap_alloc :: proc(size: int) -> rawptr { + return nil; +} + +heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { + return nil; +} + +heap_free :: proc(ptr: rawptr) { +} + +current_thread_id :: proc "contextless" () -> int { + // return int(EsThreadGetID(ES_CURRENT_THREAD)); + return -1; +} + +OS :: "essence"; diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin index 1587e2ad1..13eab5def 100644 --- a/core/os/os_linux.odin +++ b/core/os/os_linux.odin @@ -392,9 +392,9 @@ dlerror :: proc() -> string { _alloc_command_line_arguments :: proc() -> []string { - args := make([]string, len(runtime.args__)); + res := make([]string, len(runtime.args__)); for arg, i in runtime.args__ { - args[i] = string(arg); + res[i] = string(arg); } - return args; + return res; } diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin index 9618f83be..e45cf9f5f 100644 --- a/core/os/os_windows.odin +++ b/core/os/os_windows.odin @@ -322,4 +322,4 @@ is_windows_8_1 :: proc() -> bool { is_windows_10 :: proc() -> bool { osvi := get_windows_version_ansi(); return (osvi.major_version == 10 && osvi.minor_version == 0); -}
\ No newline at end of file +} diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin new file mode 100644 index 000000000..4bc9a2225 --- /dev/null +++ b/core/reflect/reflect.odin @@ -0,0 +1,396 @@ +package reflect + +import "core:runtime" +import "core:mem" + + +Type_Kind :: enum { + Invalid, + + Named, + Integer, + Rune, + Float, + Complex, + Quaternion, + String, + Boolean, + Any, + Type_Id, + Pointer, + Procedure, + Array, + Dynamic_Array, + Slice, + Tuple, + Struct, + Union, + Enum, + Map, + Bit_Field, + Bit_Set, + Opaque, + Simd_Vector, +} + + +type_kind :: proc(T: typeid) -> Type_Kind { + ti := type_info_of(T); + if ti != nil { + #complete switch _ in ti.variant { + case runtime.Type_Info_Named: return .Named; + case runtime.Type_Info_Integer: return .Integer; + 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; + case runtime.Type_Info_Type_Id: return .Type_Id; + case runtime.Type_Info_Pointer: return .Pointer; + case runtime.Type_Info_Procedure: return .Procedure; + case runtime.Type_Info_Array: return .Array; + case runtime.Type_Info_Dynamic_Array: return .Dynamic_Array; + case runtime.Type_Info_Slice: return .Slice; + case runtime.Type_Info_Tuple: return .Tuple; + case runtime.Type_Info_Struct: return .Struct; + case runtime.Type_Info_Union: return .Union; + case runtime.Type_Info_Enum: return .Enum; + case runtime.Type_Info_Map: return .Map; + case runtime.Type_Info_Bit_Field: return .Bit_Field; + case runtime.Type_Info_Bit_Set: return .Bit_Set; + case runtime.Type_Info_Opaque: return .Opaque; + case runtime.Type_Info_Simd_Vector: return .Simd_Vector; + } + + } + return .Invalid; +} + +// TODO(bill): Better name +underlying_type_kind :: proc(T: typeid) -> Type_Kind { + return type_kind(runtime.typeid_base(T)); +} + +// TODO(bill): Better name +backing_type_kind :: proc(T: typeid) -> Type_Kind { + return type_kind(runtime.typeid_core(T)); +} + + + +size_of_typeid :: proc(T: typeid) -> int { + if ti := type_info_of(T); ti != nil { + return ti.size; + } + return 0; +} + +align_of_typeid :: proc(T: typeid) -> int { + if ti := type_info_of(T); ti != nil { + return ti.align; + } + return 1; +} + +to_bytes :: proc(v: any) -> []byte { + if v != nil { + sz := size_of_typeid(v.id); + return mem.slice_ptr((^byte)(v.data), sz); + } + return nil; +} + +any_data :: inline proc(v: any) -> (data: rawptr, id: typeid) { + return v.data, v.id; +} + +is_nil :: proc(v: any) -> bool { + data := to_bytes(v); + if data != nil { + return true; + } + for v in data do if v != 0 { + return false; + } + return true; +} + +length :: proc(val: any) -> int { + if val == nil do return 0; + + v := val; + v.id = runtime.typeid_base(v.id); + switch a in v { + case runtime.Type_Info_Array: + return a.count; + + case runtime.Type_Info_Slice: + return (^mem.Raw_Slice)(v.data).len; + + case runtime.Type_Info_Dynamic_Array: + return (^mem.Raw_Dynamic_Array)(v.data).len; + + case runtime.Type_Info_String: + if a.is_cstring { + return len((^cstring)(v.data)^); + } else { + return (^mem.Raw_String)(v.data).len; + } + } + return 0; +} + + +index :: proc(val: any, i: int, loc := #caller_location) -> any { + if val == nil do return nil; + + v := val; + v.id = runtime.typeid_base(v.id); + switch a in v { + case runtime.Type_Info_Array: + runtime.bounds_check_error_loc(loc, i, a.count); + offset := uintptr(a.elem.size * i); + data := rawptr(uintptr(v.data) + offset); + return any{data, a.elem.id}; + + case runtime.Type_Info_Slice: + raw := (^mem.Raw_Slice)(v.data); + runtime.bounds_check_error_loc(loc, i, raw.len); + offset := uintptr(a.elem.size * i); + data := rawptr(uintptr(raw.data) + offset); + return any{data, a.elem.id}; + + case runtime.Type_Info_Dynamic_Array: + raw := (^mem.Raw_Dynamic_Array)(v.data); + runtime.bounds_check_error_loc(loc, i, raw.len); + offset := uintptr(a.elem.size * i); + data := rawptr(uintptr(raw.data) + offset); + return any{data, a.elem.id}; + + case runtime.Type_Info_String: + if a.is_cstring do return nil; + + raw := (^mem.Raw_String)(v.data); + runtime.bounds_check_error_loc(loc, i, raw.len); + offset := uintptr(size_of(u8) * i); + data := rawptr(uintptr(raw.data) + offset); + return any{data, typeid_of(u8)}; + } + return nil; +} + + + + +Struct_Tag :: distinct string; + +Struct_Field :: struct { + name: string, + type: typeid, + tag: Struct_Tag, + offset: uintptr, +} + +struct_field_at :: proc(T: typeid, i: int) -> (field: Struct_Field) { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + if 0 <= i && i < len(s.names) { + field.name = s.names[i]; + field.type = s.types[i].id; + field.tag = Struct_Tag(s.tags[i]); + field.offset = s.offsets[i]; + } + } + return; +} + +struct_field_by_name :: proc(T: typeid, name: string) -> (field: Struct_Field) { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + for fname, i in s.names { + if fname == name { + field.name = s.names[i]; + field.type = s.types[i].id; + field.tag = Struct_Tag(s.tags[i]); + field.offset = s.offsets[i]; + break; + } + } + } + return; +} + +struct_field_value_by_name :: proc(a: any, field: string, recurse := false) -> any { + if a == nil do return nil; + + ti := runtime.type_info_base(type_info_of(a.id)); + + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + for name, i in s.names { + if name == field { + return any{ + rawptr(uintptr(a.data) + s.offsets[i]), + s.types[i].id, + }; + } + + if recurse && s.usings[i] { + f := any{ + rawptr(uintptr(a.data) + s.offsets[i]), + s.types[i].id, + }; + + if res := struct_field_value_by_name(f, field, recurse); res != nil { + return res; + } + } + } + } + return nil; +} + + + +struct_field_names :: proc(T: typeid) -> []string { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + return s.names; + } + return nil; +} + +struct_field_types :: proc(T: typeid) -> []^runtime.Type_Info { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + return s.types; + } + return nil; +} + + +struct_field_tags :: proc(T: typeid) -> []Struct_Tag { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + return transmute([]Struct_Tag)s.tags; + } + return nil; +} + +struct_field_offsets :: proc(T: typeid) -> []uintptr { + ti := runtime.type_info_base(type_info_of(T)); + if s, ok := ti.variant.(runtime.Type_Info_Struct); ok { + return s.offsets; + } + return nil; +} + + + +struct_tag_get :: proc(tag: Struct_Tag, key: string) -> (value: string) { + value, _ = struct_tag_lookup(tag, key); + return; +} + +struct_tag_lookup :: proc(tag: Struct_Tag, key: string) -> (value: string, ok: bool) { + for t := tag; t != ""; /**/ { + i := 0; + for i < len(t) && t[i] == ' ' { // Skip whitespace + i += 1; + } + t = t[i:]; + if len(t) == 0 do break; + + i = 0; + loop: for i < len(t) { + switch t[i] { + case ':', '"': + break loop; + case 0x00 ..< ' ', 0x7f .. 0x9f: // break if control character is found + break loop; + } + i += 1; + } + + if i == 0 do break; + if i+1 >= len(t) do break; + + if t[i] != ':' || t[i+1] != '"' { + break; + } + name := string(t[:i]); + t = t[i+1:]; + + i = 1; + for i < len(t) && t[i] != '"' { // find closing quote + if t[i] == '\\' do i += 1; // Skip escaped characters + i += 1; + } + + if i >= len(t) do break; + + val := string(t[:i+1]); + t = t[i+1:]; + + if key == name { + return val[1:i], true; + } + } + return; +} + + +enum_string :: proc(a: any) -> string { + if a == nil do return ""; + ti := runtime.type_info_base(type_info_of(a.id)); + if e, ok := ti.variant.(runtime.Type_Info_Enum); ok { + for _, i in e.values { + value := &e.values[i]; + n := mem.compare_byte_ptrs((^byte)(a.data), (^byte)(value), ti.size); + if n == 0 { + return e.names[i]; + } + } + } else { + panic("expected an enum to reflect.enum_string"); + } + + return ""; +} + +union_variant_type_info :: proc(a: any) -> ^runtime.Type_Info { + id := union_variant_typeid(a); + return type_info_of(id); +} + +union_variant_typeid :: proc(a: any) -> typeid { + if a == nil do return nil; + + ti := runtime.type_info_base(type_info_of(a.id)); + if info, ok := ti.variant.(runtime.Type_Info_Union); ok { + tag_ptr := uintptr(a.data) + info.tag_offset; + tag_any := any{rawptr(tag_ptr), info.tag_type.id}; + + tag: i64 = ---; + switch i in tag_any { + case u8: tag = i64(i); + case i8: tag = i64(i); + case u16: tag = i64(i); + case i16: tag = i64(i); + case u32: tag = i64(i); + case i32: tag = i64(i); + case u64: tag = i64(i); + case i64: tag = i64(i); + case: unimplemented(); + } + + if a.data != nil && tag != 0 { + return info.variants[tag-1].id; + } + } else { + panic("expected a union to reflect.union_variant_typeid"); + } + + return nil; +} diff --git a/core/types/types.odin b/core/reflect/types.odin index dc4db549f..d1678a1b2 100644 --- a/core/types/types.odin +++ b/core/reflect/types.odin @@ -1,6 +1,7 @@ -package types +package reflect import rt "core:runtime" +import "core:strings" are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool { if a == b do return true; @@ -108,9 +109,11 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool { for _, i in x.types { xn, yn := x.names[i], y.names[i]; xt, yt := x.types[i], y.types[i]; + xl, yl := x.tags[i], y.tags[i]; if xn != yn do return false; if !are_types_identical(xt, yt) do return false; + if xl != yl do return false; } return true; @@ -272,3 +275,216 @@ is_simd_vector :: proc(info: ^rt.Type_Info) -> bool { _, ok := rt.type_info_base(info).variant.(rt.Type_Info_Simd_Vector); return ok; } + + + + + + +write_typeid :: proc(buf: ^strings.Builder, id: typeid) { + write_type(buf, type_info_of(id)); +} + +write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) { + using strings; + if ti == nil { + write_string(buf, "nil"); + return; + } + + switch info in ti.variant { + case rt.Type_Info_Named: + write_string(buf, info.name); + case rt.Type_Info_Integer: + switch ti.id { + case int: write_string(buf, "int"); + case uint: write_string(buf, "uint"); + case uintptr: write_string(buf, "uintptr"); + case: + write_byte(buf, info.signed ? 'i' : 'u'); + write_i64(buf, i64(8*ti.size), 10); + switch info.endianness { + case .Little: write_string(buf, "le"); + case .Big: write_string(buf, "be"); + } + } + case rt.Type_Info_Rune: + write_string(buf, "rune"); + case rt.Type_Info_Float: + write_byte(buf, 'f'); + write_i64(buf, i64(8*ti.size), 10); + case rt.Type_Info_Complex: + write_string(buf, "complex"); + write_i64(buf, i64(8*ti.size), 10); + case rt.Type_Info_String: + if info.is_cstring { + write_string(buf, "cstring"); + } else { + write_string(buf, "string"); + } + case rt.Type_Info_Boolean: + switch ti.id { + case bool: write_string(buf, "bool"); + case: + write_byte(buf, 'b'); + write_i64(buf, i64(8*ti.size), 10); + } + case rt.Type_Info_Any: + write_string(buf, "any"); + + case rt.Type_Info_Type_Id: + write_string(buf, "typeid"); + + case rt.Type_Info_Pointer: + if info.elem == nil { + write_string(buf, "rawptr"); + } else { + write_string(buf, "^"); + write_type(buf, info.elem); + } + case rt.Type_Info_Procedure: + write_string(buf, "proc"); + if info.params == nil { + write_string(buf, "()"); + } else { + t := info.params.variant.(rt.Type_Info_Tuple); + write_string(buf, "("); + for t, i in t.types { + if i > 0 do write_string(buf, ", "); + write_type(buf, t); + } + write_string(buf, ")"); + } + if info.results != nil { + write_string(buf, " -> "); + write_type(buf, info.results); + } + case rt.Type_Info_Tuple: + count := len(info.names); + if count != 1 do write_string(buf, "("); + for name, i in info.names { + if i > 0 do write_string(buf, ", "); + + t := info.types[i]; + + if len(name) > 0 { + write_string(buf, name); + write_string(buf, ": "); + } + write_type(buf, t); + } + if count != 1 do write_string(buf, ")"); + + case rt.Type_Info_Array: + write_string(buf, "["); + write_i64(buf, i64(info.count), 10); + write_string(buf, "]"); + write_type(buf, info.elem); + case rt.Type_Info_Dynamic_Array: + write_string(buf, "[dynamic]"); + write_type(buf, info.elem); + case rt.Type_Info_Slice: + write_string(buf, "[]"); + write_type(buf, info.elem); + + case rt.Type_Info_Map: + write_string(buf, "map["); + write_type(buf, info.key); + write_byte(buf, ']'); + write_type(buf, info.value); + + case rt.Type_Info_Struct: + write_string(buf, "struct "); + if info.is_packed do write_string(buf, "#packed "); + if info.is_raw_union do write_string(buf, "#raw_union "); + if info.custom_align { + write_string(buf, "#align "); + write_i64(buf, i64(ti.align), 10); + write_byte(buf, ' '); + } + write_byte(buf, '{'); + for name, i in info.names { + if i > 0 do write_string(buf, ", "); + write_string(buf, name); + write_string(buf, ": "); + write_type(buf, info.types[i]); + } + write_byte(buf, '}'); + + case rt.Type_Info_Union: + write_string(buf, "union "); + if info.custom_align { + write_string(buf, "#align "); + write_i64(buf, i64(ti.align), 10); + write_byte(buf, ' '); + } + write_byte(buf, '{'); + for variant, i in info.variants { + if i > 0 do write_string(buf, ", "); + write_type(buf, variant); + } + write_byte(buf, '}'); + + case rt.Type_Info_Enum: + write_string(buf, "enum "); + write_type(buf, info.base); + write_string(buf, " {"); + for name, i in info.names { + if i > 0 do write_string(buf, ", "); + write_string(buf, name); + } + write_byte(buf, '}'); + + case rt.Type_Info_Bit_Field: + write_string(buf, "bit_field "); + if ti.align != 1 { + write_string(buf, "#align "); + write_i64(buf, i64(ti.align), 10); + write_byte(buf, ' '); + } + write_string(buf, " {"); + for name, i in info.names { + if i > 0 do write_string(buf, ", "); + write_string(buf, name); + write_string(buf, ": "); + write_i64(buf, i64(info.bits[i]), 10); + } + write_byte(buf, '}'); + + case rt.Type_Info_Bit_Set: + write_string(buf, "bit_set["); + switch { + case is_enum(info.elem): + write_type(buf, info.elem); + case is_rune(info.elem): + write_encoded_rune(buf, rune(info.lower)); + write_string(buf, ".."); + write_encoded_rune(buf, rune(info.upper)); + case: + write_i64(buf, info.lower, 10); + write_string(buf, ".."); + write_i64(buf, info.upper, 10); + } + if info.underlying != nil { + write_string(buf, "; "); + write_type(buf, info.underlying); + } + write_byte(buf, ']'); + + case rt.Type_Info_Opaque: + write_string(buf, "opaque "); + write_type(buf, info.elem); + + case rt.Type_Info_Simd_Vector: + if info.is_x86_mmx { + write_string(buf, "intrinsics.x86_mmx"); + } else { + write_string(buf, "intrinsics.vector("); + write_i64(buf, i64(info.count)); + write_string(buf, ", "); + write_type(buf, info.elem); + write_byte(buf, ')'); + } + } +} + diff --git a/core/runtime/core.odin b/core/runtime/core.odin index 623f3725a..7ba98ae30 100644 --- a/core/runtime/core.odin +++ b/core/runtime/core.odin @@ -6,6 +6,7 @@ package runtime import "core:os" import "core:mem" import "core:log" +import "intrinsics" // Naming Conventions: // In general, Ada_Case for types and snake_case for values @@ -40,23 +41,24 @@ Type_Info_Enum_Value :: union { u8, u16, u32, u64, uint, uintptr, }; -Type_Info_Endianness :: enum u8 { +Platform_Endianness :: enum u8 { Platform = 0, Little = 1, Big = 2, } // 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: Platform_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 { @@ -79,8 +81,9 @@ Type_Info_Tuple :: struct { // Only really used for procedures Type_Info_Struct :: struct { types: []^Type_Info, names: []string, - offsets: []uintptr, // offsets may not be used in tuples - usings: []bool, // usings may not be used in tuples + offsets: []uintptr, + usings: []bool, + tags: []string, is_packed: bool, is_raw_union: bool, custom_align: bool, @@ -134,6 +137,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, @@ -162,6 +166,7 @@ Typeid_Kind :: enum u8 { Rune, Float, Complex, + Quaternion, String, Boolean, Any, @@ -183,12 +188,13 @@ Typeid_Kind :: enum u8 { #assert(len(Typeid_Kind) < 32); Typeid_Bit_Field :: bit_field #align align_of(uintptr) { - index: 8*size_of(align_of(uintptr)) - 8, + index: 8*size_of(uintptr) - 8, kind: 5, // Typeid_Kind named: 1, special: 1, // signed, cstring, etc reserved: 1, } +#assert(size_of(Typeid_Bit_Field) == size_of(uintptr)); // NOTE(bill): only the ones that are needed (not all types) // This will be set by the compiler @@ -231,7 +237,22 @@ global_scratch_allocator_data: mem.Scratch_Allocator; +Raw_Slice :: struct { + data: rawptr, + len: int, +} + +Raw_Dynamic_Array :: struct { + data: rawptr, + len: int, + cap: int, + allocator: mem.Allocator, +} +Raw_Map :: struct { + hashes: []int, + entries: Raw_Dynamic_Array, +} INITIAL_MAP_CAP :: 16; @@ -255,7 +276,7 @@ Map_Entry_Header :: struct { } Map_Header :: struct { - m: ^mem.Raw_Map, + m: ^Raw_Map, is_key_string: bool, entry_size: int, @@ -282,19 +303,21 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { } -type_info_base_without_enum :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { +type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info { if info == nil do return nil; base := info; loop: for { switch i in base.variant { - case Type_Info_Named: base = i.base; - case Type_Info_Enum: base = i.base; + case Type_Info_Named: base = i.base; + case Type_Info_Enum: base = i.base; + case Type_Info_Opaque: base = i.elem; case: break loop; } } return base; } +type_info_base_without_enum :: type_info_core; __type_info_of :: proc "contextless" (id: typeid) -> ^Type_Info { data := transmute(Typeid_Bit_Field)id; @@ -310,10 +333,11 @@ typeid_base :: proc "contextless" (id: typeid) -> typeid { ti = type_info_base(ti); return ti.id; } -typeid_base_without_enum :: proc "contextless" (id: typeid) -> typeid { +typeid_core :: proc "contextless" (id: typeid) -> typeid { ti := type_info_base_without_enum(type_info_of(id)); return ti.id; } +typeid_base_without_enum :: typeid_core; @@ -385,7 +409,7 @@ default_assertion_failure_proc :: proc(prefix, message: string, loc: Source_Code @builtin copy :: proc "contextless" (dst, src: $T/[]$E) -> int { n := max(0, min(len(dst), len(src))); - if n > 0 do mem.copy(&dst[0], &src[0], n*size_of(E)); + if n > 0 do mem_copy(&dst[0], &src[0], n*size_of(E)); return n; } @@ -396,7 +420,7 @@ pop :: proc "contextless" (array: ^$T/[dynamic]$E) -> E { if array == nil do return E{}; assert(len(array) > 0); res := array[len(array)-1]; - (^mem.Raw_Dynamic_Array)(array).len -= 1; + (^Raw_Dynamic_Array)(array).len -= 1; return res; } @@ -460,14 +484,11 @@ make :: proc{ mem.make_map, }; - - - @builtin clear_map :: inline proc "contextless" (m: ^$T/map[$K]$V) { if m == nil do return; - raw_map := (^mem.Raw_Map)(m); - entries := (^mem.Raw_Dynamic_Array)(&raw_map.entries); + raw_map := (^Raw_Map)(m); + entries := (^Raw_Dynamic_Array)(&raw_map.entries); entries.len = 0; for _, i in raw_map.hashes { raw_map.hashes[i] = -1; @@ -498,11 +519,11 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) { } arg_len = min(cap(array)-len(array), arg_len); if arg_len > 0 { - a := (^mem.Raw_Dynamic_Array)(array); + a := (^Raw_Dynamic_Array)(array); data := (^E)(a.data); assert(data != nil); val := arg; - mem.copy(mem.ptr_offset(data, a.len), &val, size_of(E)); + mem_copy(mem.ptr_offset(data, a.len), &val, size_of(E)); a.len += arg_len; } } @@ -520,10 +541,10 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) } arg_len = min(cap(array)-len(array), arg_len); if arg_len > 0 { - a := (^mem.Raw_Dynamic_Array)(array); + a := (^Raw_Dynamic_Array)(array); data := (^E)(a.data); assert(data != nil); - mem.copy(mem.ptr_offset(data, a.len), &args[0], size_of(E) * arg_len); + mem_copy(mem.ptr_offset(data, a.len), &args[0], size_of(E) * arg_len); a.len += arg_len; } } @@ -540,13 +561,13 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_ @builtin clear_dynamic_array :: inline proc "contextless" (array: ^$T/[dynamic]$E) { - if array != nil do (^mem.Raw_Dynamic_Array)(array).len = 0; + if array != nil do (^Raw_Dynamic_Array)(array).len = 0; } @builtin reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> bool { if array == nil do return false; - a := (^mem.Raw_Dynamic_Array)(array); + a := (^Raw_Dynamic_Array)(array); if capacity <= a.cap do return true; @@ -573,7 +594,7 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal @builtin resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> bool { if array == nil do return false; - a := (^mem.Raw_Dynamic_Array)(array); + a := (^Raw_Dynamic_Array)(array); if length <= a.cap { a.len = max(length, 0); @@ -666,11 +687,13 @@ card :: proc(s: $S/bit_set[$E; $U]) -> int { @builtin assert :: proc(condition: bool, message := "", loc := #caller_location) -> bool { if !condition { - p := context.assertion_failure_proc; - if p == nil { - p = default_assertion_failure_proc; - } - p("Runtime assertion", message, loc); + proc(message: string, loc: Source_Code_Location) { + p := context.assertion_failure_proc; + if p == nil { + p = default_assertion_failure_proc; + } + p("runtime assertion", message, loc); + }(message, loc); } return condition; } @@ -681,7 +704,7 @@ panic :: proc(message: string, loc := #caller_location) -> ! { if p == nil { p = default_assertion_failure_proc; } - p("Panic", message, loc); + p("panic", message, loc); } @builtin @@ -711,7 +734,7 @@ unreachable :: proc(message := "", loc := #caller_location) -> ! { __dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int, loc := #caller_location) { - array := (^mem.Raw_Dynamic_Array)(array_); + array := (^Raw_Dynamic_Array)(array_); array.allocator = context.allocator; assert(array.allocator.procedure != nil); @@ -722,7 +745,7 @@ __dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, ca } __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int, loc := #caller_location) -> bool { - array := (^mem.Raw_Dynamic_Array)(array_); + array := (^Raw_Dynamic_Array)(array_); if cap <= array.cap do return true; @@ -744,7 +767,7 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: } __dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int, loc := #caller_location) -> bool { - array := (^mem.Raw_Dynamic_Array)(array_); + array := (^Raw_Dynamic_Array)(array_); ok := __dynamic_array_reserve(array_, elem_size, elem_align, len, loc); if ok do array.len = len; @@ -754,7 +777,7 @@ __dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, items: rawptr, item_count: int, loc := #caller_location) -> int { - array := (^mem.Raw_Dynamic_Array)(array_); + array := (^Raw_Dynamic_Array)(array_); if items == nil do return 0; if item_count <= 0 do return 0; @@ -771,13 +794,13 @@ __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, assert(array.data != nil); data := uintptr(array.data) + uintptr(elem_size*array.len); - mem.copy(rawptr(data), items, elem_size * item_count); + mem_copy(rawptr(data), items, elem_size * item_count); array.len += item_count; return array.len; } __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int, loc := #caller_location) -> int { - array := (^mem.Raw_Dynamic_Array)(array_); + array := (^Raw_Dynamic_Array)(array_); ok := true; if array.cap <= array.len+1 { @@ -800,15 +823,14 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in // Map __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { - header := Map_Header{m = (^mem.Raw_Map)(m)}; + header := Map_Header{m = (^Raw_Map)(m)}; Entry :: struct { key: Map_Key, next: int, value: V, - } + }; - _, is_string := type_info_base(type_info_of(K)).variant.(Type_Info_String); - header.is_key_string = is_string; + header.is_key_string = intrinsics.type_is_string(K); header.entry_size = int(size_of(Entry)); header.entry_align = int(align_of(Entry)); header.value_offset = uintptr(offset_of(Entry, value)); @@ -819,33 +841,34 @@ __get_map_header :: proc "contextless" (m: ^$T/map[$K]$V) -> Map_Header { __get_map_key :: proc "contextless" (k: $K) -> Map_Key { key := k; map_key: Map_Key; - ti := type_info_base_without_enum(type_info_of(K)); - switch _ in ti.variant { - case Type_Info_Integer: - switch 8*size_of(key) { - case 8: map_key.hash = u64(( ^u8)(&key)^); - case 16: map_key.hash = u64(( ^u16)(&key)^); - case 32: map_key.hash = u64(( ^u32)(&key)^); - case 64: map_key.hash = u64(( ^u64)(&key)^); - case: panic("Unhandled integer size"); - } - case Type_Info_Rune: + + T :: intrinsics.type_core_type(K); + + when intrinsics.type_is_integer(T) { + sz :: 8*size_of(T); + when sz == 8 do map_key.hash = u64(( ^u8)(&key)^); + else when sz == 16 do map_key.hash = u64((^u16)(&key)^); + else when sz == 32 do map_key.hash = u64((^u32)(&key)^); + else when sz == 64 do map_key.hash = u64((^u64)(&key)^); + else do #assert(false, "Unhandled integer size"); + } else when intrinsics.type_is_rune(T) { map_key.hash = u64((^rune)(&key)^); - case Type_Info_Pointer: + } else when intrinsics.type_is_pointer(T) { map_key.hash = u64(uintptr((^rawptr)(&key)^)); - case Type_Info_Float: - switch 8*size_of(key) { - case 32: map_key.hash = u64((^u32)(&key)^); - case 64: map_key.hash = u64((^u64)(&key)^); - case: panic("Unhandled float size"); - } - case Type_Info_String: + } else when intrinsics.type_is_float(T) { + sz :: 8*size_of(T); + when sz == 32 do map_key.hash = u64((^u32)(&key)^); + else when sz == 64 do map_key.hash = u64((^u64)(&key)^); + else do #assert(false, "Unhandled float size"); + } else when intrinsics.type_is_string(T) { + #assert(T == string); str := (^string)(&key)^; map_key.hash = default_hash_string(str); map_key.str = str; - case: - panic("Unhandled map key type"); + } else { + #assert(false, "Unhandled map key type"); } + return map_key; } @@ -874,7 +897,7 @@ source_code_location_hash :: proc(s: Source_Code_Location) -> u64 { __slice_resize :: proc(array_: ^$T/[]$E, new_count: int, allocator: mem.Allocator, loc := #caller_location) -> bool { - array := (^mem.Raw_Slice)(array_); + array := (^Raw_Slice)(array_); if new_count < array.len do return true; @@ -900,7 +923,7 @@ __dynamic_map_reserve :: proc(using header: Map_Header, cap: int, loc := #caller } __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #caller_location) #no_bounds_check { new_header: Map_Header = header; - nm := mem.Raw_Map{}; + nm := Raw_Map{}; nm.entries.allocator = m.entries.allocator; new_header.m = &nm; @@ -932,7 +955,7 @@ __dynamic_map_rehash :: proc(using header: Map_Header, new_count: int, loc := #c e := __dynamic_map_get_entry(new_header, j); e.next = fr.entry_index; ndata := uintptr(e); - mem.copy(rawptr(ndata+value_offset), rawptr(data+value_offset), value_size); + mem_copy(rawptr(ndata+value_offset), rawptr(data+value_offset), value_size); if __dynamic_map_full(new_header) do __dynamic_map_grow(new_header, loc); } @@ -975,7 +998,7 @@ __dynamic_map_set :: proc(h: Map_Header, key: Map_Key, value: rawptr, loc := #ca e := __dynamic_map_get_entry(h, index); e.key = key; val := (^byte)(uintptr(e) + h.value_offset); - mem.copy(val, value, h.value_size); + mem_copy(val, value, h.value_size); } if __dynamic_map_full(h) { @@ -1054,7 +1077,7 @@ __dynamic_map_erase :: proc(using h: Map_Header, fr: Map_Find_Result) #no_bounds } else { old := __dynamic_map_get_entry(h, fr.entry_index); end := __dynamic_map_get_entry(h, m.entries.len-1); - mem.copy(old, end, entry_size); + mem_copy(old, end, entry_size); if last := __dynamic_map_find(h, old.key); last.entry_prev >= 0 { last_entry := __dynamic_map_get_entry(h, last.entry_prev); diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index d4d7ab84d..164610ae6 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -1,9 +1,22 @@ package runtime -import "core:mem" import "core:os" -import "core:unicode/utf8" +mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr { + if src == nil do return dst; + // NOTE(bill): This _must_ be implemented like C's memmove + foreign _ { + when size_of(rawptr) == 8 { + @(link_name="llvm.memmove.p0i8.p0i8.i64") + llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---; + } else { + @(link_name="llvm.memmove.p0i8.p0i8.i32") + llvm_memmove :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) ---; + } + } + llvm_memmove(dst, src, len, 1, false); + return dst; +} print_u64 :: proc(fd: os.Handle, x: u64) { digits := "0123456789"; @@ -243,6 +256,78 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) { } } +memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check { + x := uintptr(a); + y := uintptr(b); + n := uintptr(n); + + SU :: size_of(uintptr); + fast := uintptr(n/SU + 1); + offset := (fast-1)*SU; + curr_block := uintptr(0); + if n < SU { + fast = 0; + } + + for /**/; curr_block < fast; curr_block += 1 { + va := (^uintptr)(x + curr_block * size_of(uintptr))^; + vb := (^uintptr)(y + curr_block * size_of(uintptr))^; + if va ~ vb != 0 { + for pos := curr_block*SU; pos < n; pos += 1 { + a := (^byte)(x+pos)^; + b := (^byte)(y+pos)^; + if a ~ b != 0 { + return (int(a) - int(b)) < 0 ? -1 : +1; + } + } + } + } + + for /**/; offset < n; offset += 1 { + a := (^byte)(x+offset)^; + b := (^byte)(y+offset)^; + if a ~ b != 0 { + return (int(a) - int(b)) < 0 ? -1 : +1; + } + } + + return 0; +} + +memory_compare_zero :: proc "contextless" (a: rawptr, n: int) -> int #no_bounds_check { + x := uintptr(a); + n := uintptr(n); + + SU :: size_of(uintptr); + fast := uintptr(n/SU + 1); + offset := (fast-1)*SU; + curr_block := uintptr(0); + if n < SU { + fast = 0; + } + + for /**/; curr_block < fast; curr_block += 1 { + va := (^uintptr)(x + curr_block * size_of(uintptr))^; + if va ~ 0 != 0 { + for pos := curr_block*SU; pos < n; pos += 1 { + a := (^byte)(x+pos)^; + if a ~ 0 != 0 { + return int(a) < 0 ? -1 : +1; + } + } + } + } + + for /**/; offset < n; offset += 1 { + a := (^byte)(x+offset)^; + if a ~ 0 != 0 { + return int(a) < 0 ? -1 : +1; + } + } + + return 0; +} + string_eq :: proc "contextless" (a, b: string) -> bool { switch { case len(a) != len(b): return false; @@ -253,7 +338,7 @@ string_eq :: proc "contextless" (a, b: string) -> bool { } string_cmp :: proc "contextless" (a, b: string) -> int { - return mem.compare_byte_ptrs(&a[0], &b[0], min(len(a), len(b))); + return memory_compare(&a[0], &b[0], min(len(a), len(b))); } string_ne :: inline proc "contextless" (a, b: string) -> bool { return !string_eq(a, b); } @@ -263,18 +348,23 @@ string_le :: inline proc "contextless" (a, b: string) -> bool { return string_cm string_ge :: inline proc "contextless" (a, b: string) -> bool { return string_cmp(a, b) >= 0; } cstring_len :: proc "contextless" (s: cstring) -> int { - n := 0; - for p := (^byte)(s); p != nil && p^ != 0; p = mem.ptr_offset(p, 1) { - n += 1; + p0 := uintptr((^byte)(s)); + p := p0; + for p != 0 && (^byte)(p)^ != 0 { + p += 1; } - return n; + return int(p - p0); } cstring_to_string :: proc "contextless" (s: cstring) -> string { + Raw_String :: struct { + data: ^byte, + len: int, + }; if s == nil do return ""; ptr := (^byte)(s); n := cstring_len(s); - return transmute(string)mem.Raw_String{ptr, n}; + return transmute(string)Raw_String{ptr, n}; } @@ -285,6 +375,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) { @@ -358,8 +453,84 @@ type_assertion_check :: proc "contextless" (ok: bool, file: string, line, column handle_error(file, line, column, from, to); } + string_decode_rune :: inline proc "contextless" (s: string) -> (rune, int) { - return utf8.decode_rune_in_string(s); + // NOTE(bill): Duplicated here to remove dependency on package unicode/utf8 + + @static accept_sizes := [256]u8{ + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3f + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4f + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5f + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6f + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7f + + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8f + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9f + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xa0-0xaf + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xb0-0xbf + 0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0-0xcf + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf + 0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef + 0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff + }; + Accept_Range :: struct {lo, hi: u8}; + + @static accept_ranges := [5]Accept_Range{ + {0x80, 0xbf}, + {0xa0, 0xbf}, + {0x80, 0x9f}, + {0x90, 0xbf}, + {0x80, 0x8f}, + }; + + MASKX :: 0b0011_1111; + MASK2 :: 0b0001_1111; + MASK3 :: 0b0000_1111; + MASK4 :: 0b0000_0111; + + LOCB :: 0b1000_0000; + HICB :: 0b1011_1111; + + + RUNE_ERROR :: '\ufffd'; + + n := len(s); + if n < 1 { + return RUNE_ERROR, 0; + } + s0 := s[0]; + x := accept_sizes[s0]; + if x >= 0xF0 { + mask := rune(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff. + return rune(s[0])&~mask | RUNE_ERROR&mask, 1; + } + sz := x & 7; + accept := accept_ranges[x>>4]; + if n < int(sz) { + return RUNE_ERROR, 1; + } + b1 := s[1]; + if b1 < accept.lo || accept.hi < b1 { + return RUNE_ERROR, 1; + } + if sz == 2 { + return rune(s0&MASK2)<<6 | rune(b1&MASKX), 2; + } + b2 := s[2]; + if b2 < LOCB || HICB < b2 { + return RUNE_ERROR, 1; + } + if sz == 3 { + return rune(s0&MASK3)<<12 | rune(b1&MASKX)<<6 | rune(b2&MASKX), 3; + } + b3 := s[3]; + if b3 < LOCB || HICB < b3 { + return RUNE_ERROR, 1; + } + return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4; } bounds_check_error_loc :: inline proc "contextless" (using loc := #caller_location, index, count: int) { @@ -474,9 +645,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)) { @@ -494,7 +672,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)) { @@ -511,3 +689,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/core/sort/sort.odin b/core/sort/sort.odin index 3c8366def..86953d7dd 100644 --- a/core/sort/sort.odin +++ b/core/sort/sort.odin @@ -1,6 +1,7 @@ package sort import "core:mem" +import "intrinsics" bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { assert(f != nil); @@ -26,7 +27,7 @@ bubble_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { } } -bubble_sort :: proc(array: $A/[]$T) { +bubble_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { count := len(array); init_j, last_j := 0, count-1; @@ -73,7 +74,7 @@ quick_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { quick_sort_proc(a[i:n], f); } -quick_sort :: proc(array: $A/[]$T) { +quick_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { a := array; n := len(a); if n < 2 do return; @@ -146,7 +147,7 @@ merge_sort_proc :: proc(array: $A/[]$T, f: proc(T, T) -> int) { if M & 1 == 0 do copy(arr2, arr1); } -merge_sort :: proc(array: $A/[]$T) { +merge_sort :: proc(array: $A/[]$T) where intrinsics.type_is_ordered(T) { merge_slices :: proc(arr1, arr2, out: A) { N1, N2 := len(arr1), len(arr2); i, j := 0, 0; diff --git a/core/decimal/decimal.odin b/core/strconv/decimal/decimal.odin index 97c586635..15853c5a7 100644 --- a/core/decimal/decimal.odin +++ b/core/strconv/decimal/decimal.odin @@ -1,6 +1,6 @@ // Multiple precision decimal numbers // NOTE: This is only for floating point printing and nothing else -package decimal +package strconv_decimal Decimal :: struct { digits: [384]byte, // big-endian digits diff --git a/core/strconv/generic_float.odin b/core/strconv/generic_float.odin index f0e43bcc9..24cbf3b15 100644 --- a/core/strconv/generic_float.odin +++ b/core/strconv/generic_float.odin @@ -1,6 +1,6 @@ package strconv -using import "core:decimal" +using import "decimal" Int_Flag :: enum { Prefix, @@ -105,7 +105,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic Buffer :: struct { b: []byte, n: int, - } + }; to_bytes :: proc(b: Buffer) -> []byte do return b.b[:b.n]; add_bytes :: proc(buf: ^Buffer, bytes: ..byte) { diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index ac78f78dc..447178bc3 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -205,7 +205,11 @@ itoa :: proc(buf: []byte, i: int) -> string { atoi :: proc(s: string) -> int { return parse_int(s); } +atof :: proc(s: string) -> f64 { + return parse_f64(s); +} +ftoa :: append_float; append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { return string(generic_ftoa(buf, f, fmt, prec, bit_size)); } diff --git a/core/strings/builder.odin b/core/strings/builder.odin index 68f1a7ceb..f7dac04ba 100644 --- a/core/strings/builder.odin +++ b/core/strings/builder.odin @@ -21,6 +21,10 @@ grow_builder :: proc(b: ^Builder, cap: int) { reserve(&b.buf, cap); } +reset_builder :: proc(b: ^Builder) { + clear(&b.buf); +} + builder_from_slice :: proc(backing: []byte) -> Builder { s := transmute(mem.Raw_Slice)backing; d := mem.Raw_Dynamic_Array{ diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 8f9db86be..921e4f009 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -155,6 +155,73 @@ concatenate :: proc(a: []string, allocator := context.allocator) -> string { return string(b); } +@private +_split :: proc(s_, sep: string, sep_save, n_: int, allocator := context.allocator) -> []string { + s, n := s_, n_; + + if n == 0 { + return nil; + } + + if sep == "" { + l := utf8.rune_count_in_string(s); + if n < 0 || n > l { + n = l; + } + + res := make([dynamic]string, n, allocator); + for i := 0; i < n-1; i += 1 { + _, w := utf8.decode_rune_in_string(s); + res[i] = s[:w]; + s = s[w:]; + } + if n > 0 { + res[n-1] = s; + } + return res[:]; + } + + if n < 0 { + n = count(s, sep) + 1; + } + + res := make([dynamic]string, n, allocator); + + n -= 1; + + i := 0; + for ; i < n; i += 1 { + m := index(s, sep); + if m < 0 { + break; + } + res[i] = s[:m+sep_save]; + s = s[m+len(sep):]; + } + res[i] = s; + + return res[:i+1]; +} + +split :: inline proc(s, sep: string, allocator := context.allocator) -> []string { + return _split(s, sep, 0, -1, allocator); +} + +split_n :: inline proc(s, sep: string, n: int, allocator := context.allocator) -> []string { + return _split(s, sep, 0, n, allocator); +} + +split_after :: inline proc(s, sep: string, allocator := context.allocator) -> []string { + return _split(s, sep, len(sep), -1, allocator); +} + +split_after_n :: inline proc(s, sep: string, n: int, allocator := context.allocator) -> []string { + return _split(s, sep, len(sep), n, allocator); +} + + + + index_byte :: proc(s: string, c: byte) -> int { for i := 0; i < len(s); i += 1 { if s[i] == c do return i; @@ -170,7 +237,25 @@ last_index_byte :: proc(s: string, c: byte) -> int { return -1; } + + +@private PRIME_RABIN_KARP :: 16777619; + index :: proc(s, substr: string) -> int { + hash_str_rabin_karp :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { + for i := 0; i < len(s); i += 1 { + hash = hash*PRIME_RABIN_KARP + u32(s[i]); + } + sq := u32(PRIME_RABIN_KARP); + for i := len(s); i > 0; i >>= 1 { + if (i & 1) != 0 { + pow *= sq; + } + sq *= sq; + } + return; + } + n := len(substr); switch { case n == 0: @@ -186,9 +271,68 @@ index :: proc(s, substr: string) -> int { return -1; } - for i := 0; i < len(s)-n+1; i += 1 { - x := s[i:i+n]; - if x == substr { + hash, pow := hash_str_rabin_karp(substr); + h: u32; + for i := 0; i < n; i += 1 { + h = h*PRIME_RABIN_KARP + u32(s[i]); + } + if h == hash && s[:n] == substr { + return 0; + } + for i := n; i < len(s); /**/ { + h *= PRIME_RABIN_KARP; + h += u32(s[i]); + h -= pow * u32(s[i-n]); + i += 1; + if h == hash && s[i-n:i] == substr { + return i - n; + } + } + return -1; +} + +last_index :: proc(s, substr: string) -> int { + hash_str_rabin_karp_reverse :: proc(s: string) -> (hash: u32 = 0, pow: u32 = 1) { + for i := len(s) - 1; i >= 0; i -= 1 { + hash = hash*PRIME_RABIN_KARP + u32(s[i]); + } + sq := u32(PRIME_RABIN_KARP); + for i := len(s); i > 0; i >>= 1 { + if (i & 1) != 0 { + pow *= sq; + } + sq *= sq; + } + return; + } + + n := len(substr); + switch { + case n == 0: + return len(s); + case n == 1: + return last_index_byte(s, substr[0]); + case n == len(s): + return substr == s ? 0 : -1; + case n > len(s): + return -1; + } + + hash, pow := hash_str_rabin_karp_reverse(substr); + last := len(s) - n; + h: u32; + for i := len(s)-1; i >= last; i -= 1 { + h = h*PRIME_RABIN_KARP + u32(s[i]); + } + if h == hash && s[last:] == substr { + return last; + } + + for i := last-1; i >= 0; i -= 1 { + h *= PRIME_RABIN_KARP; + h += u32(s[i]); + h -= pow * u32(s[i+n]); + if h == hash && s[i:i+n] == substr { return i; } } diff --git a/core/sys/essence_linker_userland64.ld b/core/sys/essence_linker_userland64.ld deleted file mode 100644 index 5f6d92791..000000000 --- a/core/sys/essence_linker_userland64.ld +++ /dev/null @@ -1,24 +0,0 @@ -ENTRY(_start) - -SECTIONS -{ - . = 0x100000; - .text BLOCK(4K) : ALIGN(4K) - { - *(.text) - } - .rodata BLOCK(4K) : ALIGN(4K) - { - *(.rodata) - } - .data BLOCK(4K) : ALIGN(4K) - { - *(.data) - } - - .bss BLOCK(4K) : ALIGN(4K) - { - *(COMMON) - *(.bss) - } -} diff --git a/core/sys/win32/kernel32.odin b/core/sys/win32/kernel32.odin index 036fc016a..f647ab7e0 100644 --- a/core/sys/win32/kernel32.odin +++ b/core/sys/win32/kernel32.odin @@ -23,7 +23,7 @@ foreign kernel32 { @(link_name="GetModuleFileNameA") get_module_file_name_a :: proc(module: Hmodule, filename: cstring, size: u32) -> u32 ---; @(link_name="GetModuleFileNameW") get_module_file_name_w :: proc(module: Hmodule, filename: Wstring, size: u32) -> u32 ---; - @(link_name="Sleep") sleep :: proc(ms: i32) -> i32 ---; + @(link_name="Sleep") sleep :: proc(ms: u32) ---; @(link_name="QueryPerformanceFrequency") query_performance_frequency :: proc(result: ^i64) -> i32 ---; @(link_name="QueryPerformanceCounter") query_performance_counter :: proc(result: ^i64) -> i32 ---; @(link_name="OutputDebugStringA") output_debug_string_a :: proc(c_str: cstring) ---; diff --git a/core/sys/win32/user32.odin b/core/sys/win32/user32.odin index b8969faf3..5165d9389 100644 --- a/core/sys/win32/user32.odin +++ b/core/sys/win32/user32.odin @@ -182,7 +182,7 @@ foreign user32 { @(link_name="DestroyIcon") destroy_icon :: proc(icon: Hicon) -> Bool ---; @(link_name="LoadCursorA") load_cursor_a :: proc(instance: Hinstance, cursor_name: cstring) -> Hcursor ---; - @(link_name="LoadCursorW") load_cursor_w :: proc(instance: Hinstance, cursor_name: cstring) -> Hcursor ---; + @(link_name="LoadCursorW") load_cursor_w :: proc(instance: Hinstance, cursor_name: Wstring) -> Hcursor ---; @(link_name="GetCursor") get_cursor :: proc() -> Hcursor ---; @(link_name="SetCursor") set_cursor :: proc(cursor: Hcursor) -> Hcursor ---; diff --git a/core/time/time_osx.odin b/core/time/time_darwin.odin index fd499d1ce..fd499d1ce 100644 --- a/core/time/time_osx.odin +++ b/core/time/time_darwin.odin diff --git a/core/time/time_essence.odin b/core/time/time_essence.odin new file mode 100644 index 000000000..11713d84b --- /dev/null +++ b/core/time/time_essence.odin @@ -0,0 +1,2 @@ +package time +IS_SUPPORTED :: false; diff --git a/core/time/time_windows.odin b/core/time/time_windows.odin index 31fbca769..f6bfdd678 100644 --- a/core/time/time_windows.odin +++ b/core/time/time_windows.odin @@ -20,5 +20,5 @@ now :: proc() -> Time { sleep :: proc(d: Duration) { - win32.sleep(i32(d/Millisecond)); + win32.sleep(u32(d/Millisecond)); } diff --git a/core/unicode/utf8/utf8.odin b/core/unicode/utf8/utf8.odin index 33cb48e1c..bbf3994cd 100644 --- a/core/unicode/utf8/utf8.odin +++ b/core/unicode/utf8/utf8.odin @@ -41,23 +41,17 @@ accept_ranges := [5]Accept_Range{ }; accept_sizes := [256]u8{ - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3f - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4f - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5f - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6f - 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7f - - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8f - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9f - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xa0-0xaf - 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xb0-0xbf - 0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xc0-0xcf - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf - 0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef - 0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff + 0x00..0x7f = 0xf0, + 0x80..0xc1 = 0xf1, + 0xc2..0xdf = 0x02, + 0xe0 = 0x13, + 0xe1..0xec = 0x03, + 0xed = 0x23, + 0xee..0xef = 0x03, + 0xf0 = 0x34, + 0xf1..0xf3 = 0x04, + 0xf4 = 0x44, + 0xf5..0xff = 0xf1, }; encode_rune :: proc(c: rune) -> ([4]u8, int) { @@ -167,9 +161,58 @@ decode_last_rune :: proc(s: []u8) -> (rune, int) { return r, size; } +rune_at_pos :: proc(s: string, pos: int) -> rune { + if pos < 0 { + return RUNE_ERROR; + } + i := 0; + for r in s { + if i == pos { + return r; + } + i += 1; + } + return RUNE_ERROR; +} +rune_string_at_pos :: proc(s: string, pos: int) -> string { + if pos < 0 { + return ""; + } + + i := 0; + for c, offset in s { + if i == pos { + w := rune_size(c); + return s[offset:][:w]; + } + i += 1; + } + return ""; +} +rune_at :: proc(s: string, byte_index: int) -> rune { + r, _ := decode_rune_in_string(s[byte_index:]); + return r; +} + +// Returns the byte position of rune at position pos in s with an optional start byte position. +// Returns -1 if it runs out of the string. +rune_offset :: proc(s: string, pos: int, start: int = 0) -> int { + if pos < 0 { + return -1; + } + + i := 0; + for _, offset in s[start:] { + if i == pos { + return offset+start; + } + i += 1; + } + return -1; +} valid_rune :: proc(r: rune) -> bool { if r < 0 { diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index 206fa4a2d..4d14973f6 100644 --- a/examples/demo/demo.odin +++ b/examples/demo/demo.odin @@ -3,14 +3,34 @@ package main import "core:fmt" import "core:mem" import "core:os" +import "core:reflect" +import "intrinsics" when os.OS == "windows" { import "core:thread" } -@(link_name="general_stuff") -general_stuff :: proc() { - fmt.println("# general_stuff"); +/* + The Odin programming language is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals: + * simplicity + * high performance + * built for modern systems + * joy of programming + + # Installing Odin + Getting Started - https://odin-lang.org/docs/install/ + Instructions for downloading and install the Odin compiler and libraries. + + # Learning Odin + Overview of Odin - https://odin-lang.org/docs/overview/ + An overview of the Odin programming language. + Frequently Asked Questions (FAQ) - https://odin-lang.org/docs/faq/ + Answers to common questions about Odin. +*/ + +@(link_name="extra_general_stuff") +extra_general_stuff :: proc() { + fmt.println("# extra_general_stuff"); { // `do` for inline statements rather than block foo :: proc() do fmt.println("Foo!"); if false do foo(); @@ -30,14 +50,12 @@ general_stuff :: proc() { i := i32(137); ptr := &i; - _ = (^f32)(ptr); + _ = (^f32)(ptr); // Call-based syntax // ^f32(ptr) == ^(f32(ptr)) - _ = cast(^f32)ptr; + _ = cast(^f32)ptr; // Operator-based syntax _ = (^f32)(ptr)^; _ = (cast(^f32)ptr)^; - - // Questions: Should there be two ways to do it? } /* @@ -50,7 +68,7 @@ general_stuff :: proc() { Foo :: struct { x: int, b: bool, - } + }; f := Foo{137, true}; x, b := expand_to_tuple(f); fmt.println(f); @@ -191,8 +209,8 @@ union_type :: proc() { } } - Vector3 :: struct {x, y, z: f32}; - Quaternion :: struct {x, y, z, w: f32}; + Vector3 :: distinct [3]f32; + Quaternion :: distinct quaternion128; // More realistic examples { @@ -209,18 +227,18 @@ union_type :: proc() { orientation: Quaternion, derived: any, - } + }; Frog :: struct { using entity: Entity, jump_height: f32, - } + }; Monster :: struct { using entity: Entity, is_robot: bool, is_zombie: bool, - } + }; // See `parametric_polymorphism` procedure for details new_entity :: proc($T: typeid) -> ^Entity { @@ -254,18 +272,18 @@ union_type :: proc() { orientation: Quaternion, derived: union {Frog, Monster}, - } + }; Frog :: struct { using entity: ^Entity, jump_height: f32, - } + }; Monster :: struct { using entity: ^Entity, is_robot: bool, is_zombie: bool, - } + }; // See `parametric_polymorphism` procedure for details new_entity :: proc($T: typeid) -> ^Entity { @@ -302,17 +320,17 @@ union_type :: proc() { /* Entity :: struct { - .. + ... derived: union{^Frog, ^Monster}, } Frog :: struct { using entity: Entity, - .. + ... } Monster :: struct { using entity: Entity, - .. + ... } new_entity :: proc(T: type) -> ^Entity { @@ -325,7 +343,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); @@ -383,13 +401,13 @@ parametric_polymorphism :: proc() { hash: u32, key: Key, value: Value, - } + }; TABLE_SIZE_MIN :: 32; Table :: struct(Key, Value: typeid) { count: int, allocator: mem.Allocator, slots: []Table_Slot(Key, Value), - } + }; // Only allow types that are specializations of a (polymorphic) slice make_slice :: proc($T: typeid/[]$E, len: int) -> T { @@ -513,7 +531,7 @@ parametric_polymorphism :: proc() { Foo1, Foo2, Foo3, - } + }; Para_Union :: union(T: typeid) {T, Error}; r: Para_Union(int); fmt.println(typeid_of(type_of(r))); @@ -521,7 +539,7 @@ parametric_polymorphism :: proc() { fmt.println(r); r = 123; fmt.println(r); - r = Error.Foo0; + r = Error.Foo0; // r = .Foo0; is allow too, see implicit selector expressions below fmt.println(r); } @@ -543,6 +561,30 @@ parametric_polymorphism :: proc() { for v, i in array { assert(v == T(i*i)); } + + // Matrix multiplication + mul :: proc(a: [$M][$N]$T, b: [N][$P]T) -> (c: [M][P]T) { + for i in 0..<M { + for j in 0..<P { + for k in 0..<N { + c[i][j] += a[i][k] * b[k][j]; + } + } + } + return; + } + + x := [2][3]f32{ + {1, 2, 3}, + {3, 2, 1}, + }; + y := [3][2]f32{ + {0, 8}, + {6, 2}, + {8, 4}, + }; + z := mul(x, y); + assert(z == {{36, 24}, {20, 32}}); } } @@ -560,7 +602,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 { @@ -600,7 +642,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}; @@ -645,7 +687,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; @@ -667,7 +709,7 @@ named_proc_return_parameters :: proc() { using_enum :: proc() { - fmt.println("# using enum"); + fmt.println("\n# using enum"); using Foo :: enum {A, B, C}; @@ -679,25 +721,25 @@ 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 { A = 0, B = 1 << 8 - 1, - } + }; Enum_u64 :: enum u64 { A = 0, B = 1 << 64 - 1, - } + }; Enum_i8 :: enum i8 { A = 0, B = -(1 << 7), - } + }; Enum_i64 :: enum i64 { A = 0, B = -(1 << 63), - } + }; map_u8: map[Enum_u8]u8; map_u8[Enum_u8.A] = u8(Enum_u8.B); @@ -721,7 +763,7 @@ map_type :: proc() { demo_struct :: struct { member: Enum_i64, - } + }; map_string: map[string]demo_struct; map_string["Hellope!"] = demo_struct{Enum_i64.B}; @@ -734,7 +776,7 @@ map_type :: proc() { } implicit_selector_expression :: proc() { - fmt.println("# implicit selector expression"); + fmt.println("\n# implicit selector expression"); Foo :: enum {A, B, C}; @@ -762,7 +804,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; @@ -796,14 +838,14 @@ explicit_procedure_overloading :: proc() { } complete_switch :: proc() { - fmt.println("# complete_switch"); + fmt.println("\n# complete_switch"); { // enum using Foo :: enum { A, B, C, D, - } + }; b := Foo.B; f := Foo.A; @@ -829,6 +871,8 @@ complete_switch :: proc() { } cstring_example :: proc() { + fmt.println("\n# cstring_example"); + W :: "Hellope"; X :: cstring(W); Y :: string(X); @@ -860,6 +904,8 @@ deprecated_attribute :: proc() { } bit_set_type :: proc() { + fmt.println("\n# bit_set_type"); + { using Day :: enum { Sunday, @@ -869,7 +915,7 @@ bit_set_type :: proc() { Thursday, Friday, Saturday, - } + }; Days :: distinct bit_set[Day]; WEEKEND :: Days{Sunday, Saturday}; @@ -921,6 +967,8 @@ bit_set_type :: proc() { } diverging_procedures :: proc() { + fmt.println("\n# diverging_procedures"); + // Diverging procedures may never return foo :: proc() -> ! { fmt.println("I'm a diverging procedure"); @@ -930,6 +978,8 @@ diverging_procedures :: proc() { } deferred_procedure_associations :: proc() { + fmt.println("\n# deferred_procedure_associations"); + @(deferred_out=closure) open :: proc(s: string) -> bool { fmt.println(s); @@ -945,9 +995,247 @@ deferred_procedure_associations :: proc() { } } +reflection :: proc() { + fmt.println("\n# reflection"); + + Foo :: struct { + x: int `tag1`, + y: string `json:"y_field"`, + z: bool, // no tag + }; + + id := typeid_of(Foo); + names := reflect.struct_field_names(id); + types := reflect.struct_field_types(id); + tags := reflect.struct_field_tags(id); + + assert(len(names) == len(types) && len(names) == len(tags)); + + fmt.println("Foo :: struct {"); + for tag, i in tags { + name, type := names[i], types[i]; + if tag != "" { + fmt.printf("\t%s: %T `%s`,\n", name, type, tag); + } else { + fmt.printf("\t%s: %T,\n", name, type); + } + } + fmt.println("}"); + + + for tag, i in tags { + if val, ok := reflect.struct_tag_lookup(tag, "json"); ok { + fmt.printf("json: %s -> %s\n", names[i], val); + } + } +} + +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); + } + { // 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"); + + // 'inline for' works the same as if the 'inline' prefix did not + // exist but these ranged loops are explicitly unrolled which can + // be very very useful for certain optimizations + + 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); + } +} + +where_clauses :: proc() { + fmt.println("\n#procedure 'where' clauses"); + + { // Sanity checks + simple_sanity_check :: proc(x: [2]int) + where len(x) > 1, + type_of(x) == [2]int { + fmt.println(x); + } + } + { // Parametric polymorphism checks + cross_2d :: proc(a, b: $T/[2]$E) -> E + where intrinsics.type_is_numeric(E) { + return a.x*b.y - a.y*b.x; + } + cross_3d :: proc(a, b: $T/[3]$E) -> T + where intrinsics.type_is_numeric(E) { + x := a.y*b.z - a.z*b.y; + y := a.z*b.x - a.x*b.z; + z := a.x*b.y - a.y*b.z; + return T{x, y, z}; + } + + a := [2]int{1, 2}; + b := [2]int{5, -3}; + fmt.println(cross_2d(a, b)); + + x := [3]f32{1, 4, 9}; + y := [3]f32{-5, 0, 3}; + fmt.println(cross_3d(x, y)); + + // Failure case + // i := [2]bool{true, false}; + // j := [2]bool{false, true}; + // fmt.println(cross_2d(i, j)); + + } + + { // Procedure groups usage + foo :: proc(x: [$N]int) -> bool + where N > 2 { + fmt.println(#procedure, "was called with the parameter", x); + return true; + } + + bar :: proc(x: [$N]int) -> bool + where 0 < N, + N <= 2 { + fmt.println(#procedure, "was called with the parameter", x); + return false; + } + + baz :: proc{foo, bar}; + + x := [3]int{1, 2, 3}; + y := [2]int{4, 9}; + ok_x := baz(x); + ok_y := baz(y); + assert(ok_x == true); + assert(ok_y == false); + } + + { // Record types + Foo :: struct(T: typeid, N: int) + where intrinsics.type_is_integer(T), + N > 2 { + x: [N]T, + y: [N-2]T, + }; + + T :: i32; + N :: 5; + f: Foo(T, N); + #assert(size_of(f) == (N+N-2)*size_of(T)); + } +} + +ranged_fields_for_array_compound_literals :: proc() { + fmt.println("\n#ranged fields for array compound literals"); + { // Normal Array Literal + foo := [?]int{1, 4, 9, 16}; + fmt.println(foo); + } + { // Indexed + foo := [?]int{ + 3 = 16, + 1 = 4, + 2 = 9, + 0 = 1, + }; + fmt.println(foo); + } + { // Ranges + i := 2; + foo := [?]int { + 0 = 123, + 5..9 = 54, + 10..<16 = i*3 + (i-1)*2, + }; + #assert(len(foo) == 16); + fmt.println(foo); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] + } + { // Slice and Dynamic Array support + i := 2; + foo_slice := []int { + 0 = 123, + 5..9 = 54, + 10..<16 = i*3 + (i-1)*2, + }; + assert(len(foo_slice) == 16); + fmt.println(foo_slice); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] + + foo_dynamic_array := [dynamic]int { + 0 = 123, + 5..9 = 54, + 10..<16 = i*3 + (i-1)*2, + }; + assert(len(foo_dynamic_array) == 16); + fmt.println(foo_dynamic_array); // [123, 0, 0, 0, 0, 54, 54, 54, 54, 54, 8, 8, 8, 8, 8] + } +} + main :: proc() { when true { - general_stuff(); + extra_general_stuff(); union_type(); parametric_polymorphism(); threading_example(); @@ -963,5 +1251,11 @@ main :: proc() { bit_set_type(); diverging_procedures(); deferred_procedure_associations(); + reflection(); + quaternions(); + inline_for_statement(); + where_clauses(); + ranged_fields_for_array_compound_literals(); } } + diff --git a/misc/logo-slim.png b/misc/logo-slim.png Binary files differindex 6e33dc6b8..ed9b6061e 100644 --- a/misc/logo-slim.png +++ b/misc/logo-slim.png diff --git a/examples/old_demos/demo001.odin b/misc/old_demos/demo001.odin index a3aea1cb7..a3aea1cb7 100644 --- a/examples/old_demos/demo001.odin +++ b/misc/old_demos/demo001.odin diff --git a/examples/old_demos/demo002.odin b/misc/old_demos/demo002.odin index a790aadf3..a790aadf3 100644 --- a/examples/old_demos/demo002.odin +++ b/misc/old_demos/demo002.odin diff --git a/examples/old_demos/demo004.odin b/misc/old_demos/demo004.odin index c9acc9a15..c9acc9a15 100644 --- a/examples/old_demos/demo004.odin +++ b/misc/old_demos/demo004.odin diff --git a/examples/old_demos/demo005.odin b/misc/old_demos/demo005.odin index c8273b03b..c8273b03b 100644 --- a/examples/old_demos/demo005.odin +++ b/misc/old_demos/demo005.odin diff --git a/examples/old_demos/demo006.odin b/misc/old_demos/demo006.odin index c2f64151b..c2f64151b 100644 --- a/examples/old_demos/demo006.odin +++ b/misc/old_demos/demo006.odin diff --git a/examples/old_demos/demo007.odin b/misc/old_demos/demo007.odin index d19446ecb..d19446ecb 100644 --- a/examples/old_demos/demo007.odin +++ b/misc/old_demos/demo007.odin diff --git a/examples/old_demos/demo008.odin b/misc/old_demos/demo008.odin index 7916cd5e9..7916cd5e9 100644 --- a/examples/old_demos/demo008.odin +++ b/misc/old_demos/demo008.odin diff --git a/examples/old_demos/old_runtime.odin b/misc/old_demos/old_runtime.odin index e605e7820..e605e7820 100644 --- a/examples/old_demos/old_runtime.odin +++ b/misc/old_demos/old_runtime.odin diff --git a/examples/old_stuff/demo_backup.odin b/misc/old_stuff/demo_backup.odin index b8bbbb02d..b8bbbb02d 100644 --- a/examples/old_stuff/demo_backup.odin +++ b/misc/old_stuff/demo_backup.odin diff --git a/misc/shell.bat b/misc/shell.bat index 36db84570..85a7949c6 100644 --- a/misc/shell.bat +++ b/misc/shell.bat @@ -1,8 +1,8 @@ @echo off rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL -call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL -rem call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL +rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL set _NO_DEBUG_HEAP=1 diff --git a/src/build_settings.cpp b/src/build_settings.cpp index 992443f7c..5d1c07a67 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -2,7 +2,7 @@ enum TargetOsKind { TargetOs_Invalid, TargetOs_windows, - TargetOs_osx, + TargetOs_darwin, TargetOs_linux, TargetOs_essence, @@ -30,7 +30,7 @@ enum TargetEndianKind { String target_os_names[TargetOs_COUNT] = { str_lit(""), str_lit("windows"), - str_lit("osx"), + str_lit("darwin"), str_lit("linux"), str_lit("essence"), }; @@ -55,9 +55,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = { -String const ODIN_VERSION = str_lit("0.10.1"); -String cross_compile_target = str_lit(""); -String cross_compile_lib_dir = str_lit(""); +String const ODIN_VERSION = str_lit("0.10.2"); @@ -66,6 +64,7 @@ struct TargetMetrics { TargetArchKind arch; isize word_size; isize max_align; + String target_triplet; }; @@ -109,6 +108,7 @@ struct BuildContext { bool has_resource; String opt_flags; String llc_flags; + String target_triplet; String link_flags; bool is_dll; bool generate_docs; @@ -121,6 +121,7 @@ struct BuildContext { bool no_crt; bool use_lld; bool vet; + bool cross_compiling; QueryDataSetSettings query_data_set_settings; @@ -135,18 +136,19 @@ struct BuildContext { gb_global BuildContext build_context = {0}; - gb_global TargetMetrics target_windows_386 = { TargetOs_windows, TargetArch_386, 4, 8, + str_lit("i686-pc-windows"), }; gb_global TargetMetrics target_windows_amd64 = { TargetOs_windows, TargetArch_amd64, 8, 16, + str_lit("x86_64-pc-windows-gnu"), }; gb_global TargetMetrics target_linux_386 = { @@ -154,23 +156,47 @@ gb_global TargetMetrics target_linux_386 = { TargetArch_386, 4, 8, + str_lit("i686-pc-linux-gnu"), }; gb_global TargetMetrics target_linux_amd64 = { TargetOs_linux, TargetArch_amd64, 8, 16, + str_lit("x86_64-pc-linux-gnu"), +}; + +gb_global TargetMetrics target_darwin_amd64 = { + TargetOs_darwin, + TargetArch_amd64, + 8, + 16, + str_lit("x86_64-apple-darwin"), }; -gb_global TargetMetrics target_osx_amd64 = { - TargetOs_osx, +gb_global TargetMetrics target_essence_amd64 = { + TargetOs_essence, TargetArch_amd64, 8, 16, + str_lit("x86_64-pc-none-elf"), }; +struct NamedTargetMetrics { + String name; + TargetMetrics *metrics; +}; +gb_global NamedTargetMetrics named_targets[] = { + { str_lit("essence_amd64"), &target_essence_amd64 }, + { str_lit("darwin_amd64"), &target_darwin_amd64 }, + { str_lit("linux_386"), &target_linux_386 }, + { str_lit("linux_amd64"), &target_linux_amd64 }, + { str_lit("windows_386"), &target_windows_386 }, + { str_lit("windows_amd64"), &target_windows_amd64 }, +}; +NamedTargetMetrics *selected_target_metrics; TargetOsKind get_target_os_from_string(String str) { for (isize i = 0; i < TargetOs_COUNT; i++) { @@ -522,7 +548,7 @@ String get_fullpath_core(gbAllocator a, String path) { -void init_build_context(void) { +void init_build_context(TargetMetrics *cross_target) { BuildContext *bc = &build_context; gb_affinity_init(&bc->affinity); @@ -540,7 +566,7 @@ void init_build_context(void) { #if defined(GB_SYSTEM_WINDOWS) metrics = target_windows_amd64; #elif defined(GB_SYSTEM_OSX) - metrics = target_osx_amd64; + metrics = target_darwin_amd64; #else metrics = target_linux_amd64; #endif @@ -554,8 +580,9 @@ void init_build_context(void) { #endif #endif - if (cross_compile_target.len) { - bc->ODIN_OS = cross_compile_target; + if (cross_target) { + metrics = *cross_target; + bc->cross_compiling = true; } GB_ASSERT(metrics.os != TargetOs_Invalid); @@ -573,6 +600,7 @@ void init_build_context(void) { bc->max_align = metrics.max_align; bc->link_flags = str_lit(" "); bc->opt_flags = str_lit(" "); + bc->target_triplet = metrics.target_triplet; gbString llc_flags = gb_string_make_reserve(heap_allocator(), 64); @@ -590,7 +618,7 @@ void init_build_context(void) { case TargetOs_windows: bc->link_flags = str_lit("/machine:x64 "); break; - case TargetOs_osx: + case TargetOs_darwin: break; case TargetOs_linux: bc->link_flags = str_lit("-arch x86-64 "); @@ -603,7 +631,7 @@ void init_build_context(void) { case TargetOs_windows: bc->link_flags = str_lit("/machine:x86 "); break; - case TargetOs_osx: + case TargetOs_darwin: gb_printf_err("Unsupported architecture\n"); gb_exit(1); break; diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 1a6fad918..ebfc29899 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; + } } @@ -112,7 +121,8 @@ void check_init_variables(CheckerContext *ctx, Entity **lhs, isize lhs_count, Ar isize rhs_count = operands.count; for_array(i, operands) { if (operands[i].mode == Addressing_Invalid) { - rhs_count--; + // TODO(bill): Should I ignore invalid parameters? + // rhs_count--; } } @@ -239,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); @@ -247,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); @@ -265,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) { @@ -274,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 @@ -362,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; } @@ -654,7 +675,6 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { bool is_foreign = e->Procedure.is_foreign; bool is_export = e->Procedure.is_export; - bool is_require_results = (pl->tags & ProcTag_require_results) != 0; if (e->pkg != nullptr && e->token.string == "main") { if (pt->param_count != 0 || @@ -714,10 +734,10 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } - if (pt->result_count == 0 && is_require_results) { - error(pl->type, "'#require_results' is not needed on a procedure with no results"); + if (pt->result_count == 0 && ac.require_results) { + error(pl->type, "'require_results' is not needed on a procedure with no results"); } else { - pt->require_results = is_require_results; + pt->require_results = ac.require_results; } if (ac.link_name.len > 0) { @@ -791,7 +811,7 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { } } -void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) { +void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_expr) { GB_ASSERT(e->type == nullptr); GB_ASSERT(e->kind == Entity_Variable); @@ -805,6 +825,7 @@ void check_var_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init_ex ac.init_expr_list_count = init_expr != nullptr ? 1 : 0; DeclInfo *decl = decl_info_of_entity(e); + GB_ASSERT(decl == ctx->decl); if (decl != nullptr) { check_decl_attributes(ctx, decl->attributes, var_decl_attribute, &ac); } @@ -936,7 +957,6 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) ptr_set_destroy(&entity_set); - for_array(j, pge->entities) { Entity *p = pge->entities[j]; if (p->type == t_invalid) { @@ -962,27 +982,40 @@ void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) defer (end_error_block()); ProcTypeOverloadKind kind = are_proc_types_overload_safe(p->type, q->type); - switch (kind) { + bool both_have_where_clauses = false; + if (p->decl_info->proc_lit != nullptr && q->decl_info->proc_lit != nullptr) { + GB_ASSERT(p->decl_info->proc_lit->kind == Ast_ProcLit); + GB_ASSERT(q->decl_info->proc_lit->kind == Ast_ProcLit); + auto pl = &p->decl_info->proc_lit->ProcLit; + auto ql = &q->decl_info->proc_lit->ProcLit; + + // Allow collisions if the procedures both have 'where' clauses and are both polymorphic + bool pw = pl->where_token.kind != Token_Invalid && is_type_polymorphic(p->type, true); + bool qw = ql->where_token.kind != Token_Invalid && is_type_polymorphic(q->type, true); + both_have_where_clauses = pw && qw; + } + + if (!both_have_where_clauses) switch (kind) { case ProcOverload_Identical: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; // case ProcOverload_CallingConvention: - // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name)); + // error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); // is_invalid = true; // break; case ProcOverload_ParamVariadic: - error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in this scope", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' as the same type as another procedure in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_ResultCount: case ProcOverload_ResultTypes: - error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in this scope", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' as the same parameters but different results in the procedure group '%.*s'", LIT(name), LIT(proc_group_name)); is_invalid = true; break; case ProcOverload_Polymorphic: #if 0 - error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in this scope which is not allowed", LIT(name)); + error(p->token, "Overloaded procedure '%.*s' has a polymorphic counterpart in the procedure group '%.*s' which is not allowed", LIT(name), LIT(proc_group_name)); is_invalid = true; #endif break; @@ -1051,13 +1084,13 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_ switch (e->kind) { case Entity_Variable: - check_var_decl(&c, e, d->type_expr, d->init_expr); + check_global_variable_decl(&c, e, d->type_expr, d->init_expr); break; case Entity_Constant: 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: @@ -1074,6 +1107,11 @@ void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_ } +struct ProcUsingVar { + Entity *e; + Entity *uvar; +}; + void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *type, Ast *body) { if (body == nullptr) { @@ -1098,76 +1136,117 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty ctx->curr_proc_decl = decl; ctx->curr_proc_sig = type; - GB_ASSERT(type->kind == Type_Proc); - if (type->Proc.param_count > 0) { - TypeTuple *params = &type->Proc.params->Tuple; - for_array(i, params->variables) { - Entity *e = params->variables[i]; - if (e->kind != Entity_Variable) { - continue; - } - if (!(e->flags & EntityFlag_Using)) { - continue; - } - bool is_immutable = e->Variable.is_immutable; - bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type); - String name = e->token.string; - Type *t = base_type(type_deref(e->type)); - if (t->kind == Type_Struct) { - Scope *scope = t->Struct.scope; - if (scope == nullptr) { - scope = scope_of_node(t->Struct.node); + ast_node(bs, BlockStmt, body); + + Array<ProcUsingVar> using_entities = {}; + using_entities.allocator = heap_allocator(); + defer (array_free(&using_entities)); + + { + GB_ASSERT(type->kind == Type_Proc); + if (type->Proc.param_count > 0) { + TypeTuple *params = &type->Proc.params->Tuple; + for_array(i, params->variables) { + Entity *e = params->variables[i]; + if (e->kind != Entity_Variable) { + continue; + } + if (!(e->flags & EntityFlag_Using)) { + continue; } - GB_ASSERT(scope != nullptr); - for_array(i, scope->elements.entries) { - Entity *f = scope->elements.entries[i].value; - if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); - uvar->Variable.is_immutable = is_immutable; - if (is_value) uvar->flags |= EntityFlag_Value; - - Entity *prev = scope_insert(ctx->scope, uvar); - if (prev != nullptr) { - error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(name), LIT(prev->token.string)); - break; + bool is_immutable = e->Variable.is_immutable; + bool is_value = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type); + String name = e->token.string; + Type *t = base_type(type_deref(e->type)); + if (t->kind == Type_Struct) { + Scope *scope = t->Struct.scope; + if (scope == nullptr) { + scope = scope_of_node(t->Struct.node); + } + GB_ASSERT(scope != nullptr); + for_array(i, scope->elements.entries) { + Entity *f = scope->elements.entries[i].value; + if (f->kind == Entity_Variable) { + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr); + uvar->Variable.is_immutable = is_immutable; + if (is_value) uvar->flags |= EntityFlag_Value; + + ProcUsingVar puv = {e, uvar}; + array_add(&using_entities, puv); + } } + } else { + error(e->token, "'using' can only be applied to variables of type struct"); + break; } - } else { - error(e->token, "'using' can only be applied to variables of type struct"); - break; } } } - ast_node(bs, BlockStmt, body); - // check_open_scope(ctx, body); - check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls); - if (type->Proc.result_count > 0) { - if (!check_is_terminating(body)) { - if (token.kind == Token_Ident) { - error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string)); - } else { - // NOTE(bill): Anonymous procedure (lambda) - error(bs->close, "Missing return statement at the end of the procedure"); + + for_array(i, using_entities) { + Entity *e = using_entities[i].e; + Entity *uvar = using_entities[i].uvar; + Entity *prev = scope_insert(ctx->scope, uvar); + if (prev != nullptr) { + error(e->token, "Namespace collision while 'using' '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string)); + break; + } + } + + + bool where_clause_ok = evaluate_where_clauses(ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, true); + if (!where_clause_ok) { + // NOTE(bill, 2019-08-31): Don't check the body as the where clauses failed + return; + } + + check_open_scope(ctx, body); + { + for_array(i, using_entities) { + Entity *e = using_entities[i].e; + Entity *uvar = using_entities[i].uvar; + Entity *prev = scope_insert(ctx->scope, uvar); + // NOTE(bill): Don't err here + } + + check_stmt_list(ctx, bs->stmts, Stmt_CheckScopeDecls); + + if (type->Proc.result_count > 0) { + if (!check_is_terminating(body)) { + if (token.kind == Token_Ident) { + error(bs->close, "Missing return statement at the end of the procedure '%.*s'", LIT(token.string)); + } else { + // NOTE(bill): Anonymous procedure (lambda) + error(bs->close, "Missing return statement at the end of the procedure"); + } } } } - // check_close_scope(ctx); + check_close_scope(ctx); check_scope_usage(ctx->checker, ctx->scope); +#if 1 if (decl->parent != nullptr) { - // NOTE(bill): Add the dependencies from the procedure literal (lambda) - for_array(i, decl->deps.entries) { - Entity *e = decl->deps.entries[i].ptr; - ptr_set_add(&decl->parent->deps, e); - } - for_array(i, decl->type_info_deps.entries) { - Type *t = decl->type_info_deps.entries[i].ptr; - ptr_set_add(&decl->parent->type_info_deps, t); + Scope *ps = decl->parent->scope; + if (ps->flags & (ScopeFlag_File & ScopeFlag_Pkg & ScopeFlag_Global)) { + return; + } else { + // NOTE(bill): Add the dependencies from the procedure literal (lambda) + // But only at the procedure level + for_array(i, decl->deps.entries) { + Entity *e = decl->deps.entries[i].ptr; + ptr_set_add(&decl->parent->deps, e); + } + for_array(i, decl->type_info_deps.entries) { + Type *t = decl->type_info_deps.entries[i].ptr; + ptr_set_add(&decl->parent->type_info_deps, t); + } } } +#endif } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 4c5ca2348..fae38fa01 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -62,7 +62,7 @@ Type * make_optional_ok_type (Type *value); void check_type_decl (CheckerContext *c, Entity *e, Ast *type_expr, Type *def); Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint); Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name); -Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> ordered_operands); +Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure); void check_not_tuple (CheckerContext *c, Operand *operand); void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type); gbString expr_to_string (Ast *expression); @@ -89,8 +89,8 @@ Type * check_init_variable (CheckerContext *c, Entity *e, Operand * Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCallingConvention cc); bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type *abi_return_type); -void set_procedure_abi_types(CheckerContext *c, Type *type); - +void set_procedure_abi_types(gbAllocator a, Type *type); +void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type); Entity *entity_from_expr(Ast *expr) { expr = unparen_expr(expr); @@ -159,7 +159,7 @@ isize check_is_assignable_to_using_subtype(Type *src, Type *dst, isize level = 0 src = base_type(src); if (!is_type_struct(src)) { - return false; + return 0; } for_array(i, src->Struct.fields) { @@ -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; } @@ -497,6 +501,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; } } @@ -664,17 +676,6 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co return; } - #if 0 - if (operand->mode == Addressing_Type) { - Type *t = base_type(type); - if (t->kind == Type_Pointer && - t->Pointer.elem == t_type_info) { - add_type_info_type(c, type); - return; - } - } - #endif - if (is_type_untyped(operand->type)) { Type *target_type = type; if (type == nullptr || is_type_any(type)) { @@ -747,7 +748,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); @@ -778,6 +784,7 @@ void check_assignment(CheckerContext *c, Operand *operand, Type *type, String co op_type_str, type_str, LIT(context_name)); + check_assignment_error_suggestion(c, operand, type); break; } operand->mode = Addressing_Invalid; @@ -956,7 +963,7 @@ bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, Type *source, } if (modify_type) { - set_procedure_abi_types(c, source); + set_procedure_abi_types(c->allocator, source); } return true; @@ -1438,7 +1445,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; @@ -1450,6 +1457,36 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ } 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) { return true; @@ -1471,28 +1508,97 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ return false; } + +void check_assignment_error_suggestion(CheckerContext *c, Operand *o, Type *type) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + ); + + Type *src = base_type(o->type); + Type *dst = base_type(type); + + if (is_type_array(src) && is_type_slice(dst)) { + Type *s = src->Array.elem; + Type *d = dst->Slice.elem; + if (are_types_identical(s, d)) { + error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); + } + } else if (are_types_identical(src, dst)) { + error_line("\tSuggestion: the expression may be directly casted to type %s\n", b); + } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { + error_line("\tSuggestion: a string may be casted to %s\n", a, b); + } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { + error_line("\tSuggestion: the expression may be casted to %s\n", b); + } +} + +void check_cast_error_suggestion(CheckerContext *c, Operand *o, Type *type) { + gbString a = expr_to_string(o->expr); + gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + ); + + Type *src = base_type(o->type); + Type *dst = base_type(type); + + if (is_type_array(src) && is_type_slice(dst)) { + Type *s = src->Array.elem; + Type *d = dst->Slice.elem; + if (are_types_identical(s, d)) { + error_line("\tSuggestion: the array expression may be sliced with %s[:]\n", a); + } + } else if (is_type_pointer(o->type) && is_type_integer(type)) { + if (is_type_uintptr(type)) { + error_line("\tSuggestion: a pointer may be directly casted to %s\n", b); + } else { + error_line("\tSuggestion: for a pointer to be casted to an integer, it must be converted to 'uintptr' first\n"); + i64 x = type_size_of(o->type); + i64 y = type_size_of(type); + if (x != y) { + error_line("\tNote: the type of expression and the type of the cast have a different size in bytes, %lld vs %lld\n", x, y); + } + } + } else if (is_type_integer(o->type) && is_type_pointer(type)) { + if (is_type_uintptr(o->type)) { + error_line("\tSuggestion: %a may be directly casted to %s\n", a, b); + } else { + error_line("\tSuggestion: for an integer to be casted to a pointer, it must be converted to 'uintptr' first\n"); + } + } else if (are_types_identical(src, t_string) && is_type_u8_slice(dst)) { + error_line("\tSuggestion: a string may be casted to %s\n", a, b); + } else if (is_type_u8_slice(src) && are_types_identical(dst, t_string)) { + error_line("\tSuggestion: the expression may be casted to %s\n", b); + } +} + + void check_is_expressible(CheckerContext *c, Operand *o, Type *type) { - GB_ASSERT(is_type_constant_type(type)); GB_ASSERT(o->mode == Addressing_Constant); - if (!check_representable_as_constant(c, o->value, type, &o->value)) { + if (!is_type_constant_type(type) || !check_representable_as_constant(c, o->value, type, &o->value)) { gbString a = expr_to_string(o->expr); gbString b = type_to_string(type); + defer( + gb_string_free(b); + gb_string_free(a); + o->mode = Addressing_Invalid; + ); + if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { error(o->expr, "'%s' truncated to '%s'", a, b); } else { - 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); + error(o->expr, "Cannot convert '%s' to '%s'", a, b); + check_assignment_error_suggestion(c, o, type); } } else { error(o->expr, "Cannot convert '%s' to '%s'", a, b); + check_assignment_error_suggestion(c, o, type); } - - gb_string_free(b); - gb_string_free(a); - o->mode = Addressing_Invalid; } } @@ -1623,6 +1729,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) { @@ -1759,6 +1882,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; + } } } @@ -1767,7 +1905,7 @@ void check_comparison(CheckerContext *c, Operand *x, Operand *y, TokenKind op) { } -void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { +void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *type_hint) { GB_ASSERT(node->kind == Ast_BinaryExpr); ast_node(be, BinaryExpr, node); @@ -1845,6 +1983,9 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node) { info->is_lhs = true; } x->mode = Addressing_Value; + if (type_hint && is_type_integer(type_hint)) { + x->type = type_hint; + } // x->value = x_val; return; } @@ -1975,6 +2116,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; } @@ -2097,6 +2246,8 @@ void check_cast(CheckerContext *c, Operand *x, Type *type) { gb_string_free(to_type); gb_string_free(expr_str); + check_cast_error_suggestion(c, x, type); + x->mode = Addressing_Invalid; return; } @@ -2167,7 +2318,7 @@ bool check_binary_array_expr(CheckerContext *c, Token op, Operand *x, Operand *y } -void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as_type_hint=false) { +void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, Type *type_hint, bool use_lhs_as_type_hint=false) { GB_ASSERT(node->kind == Ast_BinaryExpr); Operand y_ = {}, *y = &y_; @@ -2178,15 +2329,23 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as case Token_CmpEq: case Token_NotEq: { // NOTE(bill): Allow comparisons between types - check_expr_or_type(c, x, be->left); + check_expr_or_type(c, x, be->left, type_hint); check_expr_or_type(c, y, be->right, x->type); bool xt = x->mode == Addressing_Type; bool yt = y->mode == Addressing_Type; // 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; @@ -2278,11 +2437,11 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as return; default: - check_expr(c, x, be->left); + check_expr_with_type_hint(c, x, be->left, type_hint); if (use_lhs_as_type_hint) { check_expr_with_type_hint(c, y, be->right, x->type); } else { - check_expr(c, y, be->right); + check_expr_with_type_hint(c, y, be->right, type_hint); } break; } @@ -2307,7 +2466,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as } if (token_is_shift(op.kind)) { - check_shift(c, x, y, node); + check_shift(c, x, y, node, type_hint); return; } @@ -2554,6 +2713,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; } @@ -2649,6 +2810,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; @@ -2895,7 +3057,6 @@ Entity *check_selector(CheckerContext *c, Operand *operand, Ast *node, Type *typ } if (selector->kind != Ast_Ident) { - // if (selector->kind != Ast_Ident) { error(selector, "Illegal selector kind: '%.*s'", LIT(ast_strings[selector->kind])); operand->mode = Addressing_Invalid; operand->expr = node; @@ -3131,8 +3292,59 @@ bool check_identifier_exists(Scope *s, Ast *node, bool nested = false, Scope **o return false; } +typedef bool (BuiltinTypeIsProc)(Type *t); + +BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_end - BuiltinProc__type_begin - 1] = { + nullptr, // BuiltinProc_type_base_type + nullptr, // BuiltinProc_type_core_type + nullptr, // BuiltinProc_type_elem_type + + is_type_boolean, + is_type_integer, + is_type_rune, + is_type_float, + is_type_complex, + is_type_quaternion, + is_type_string, + is_type_typeid, + is_type_any, + is_type_endian_little, + is_type_endian_big, + is_type_numeric, + is_type_ordered, + is_type_ordered_numeric, + is_type_indexable, + is_type_sliceable, + is_type_simple_compare, + is_type_dereferenceable, + is_type_valid_for_keys, + + is_type_named, + is_type_pointer, + is_type_opaque, + is_type_array, + is_type_slice, + is_type_dynamic_array, + + is_type_map, + is_type_struct, + is_type_union, + is_type_enum, + is_type_proc, + is_type_bit_field, + is_type_bit_field_value, + is_type_bit_set, + is_type_simd_vector, + + type_has_nil, + + nullptr, // BuiltinProc_type_proc_parameter_count + nullptr, // BuiltinProc_type_proc_return_count +}; + -bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id) { + +bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) { ast_node(ce, CallExpr, call); if (ce->inlining != ProcInlining_none) { error(call, "Inlining operators are not allowed on built-in procedures"); @@ -3178,7 +3390,9 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 /*fallthrough*/ } default: - if (ce->args.count > 0) { + if (BuiltinProc__type_begin < id && id < BuiltinProc__type_end) { + check_expr_or_type(c, operand, ce->args[0]); + } else if (ce->args.count > 0) { check_multi_expr(c, operand, ce->args[0]); } break; @@ -3306,19 +3520,43 @@ 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); } operand->type = t_untyped_bool; operand->mode = Addressing_Constant; + } else if (name == "panic") { + if (ce->args.count != 1) { + error(call, "'#panic' expects 1 argument, got %td", ce->args.count); + return false; + } + if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) { + gbString str = expr_to_string(ce->args[0]); + error(call, "'%s' is not a constant string", str); + gb_string_free(str); + return false; + } + error(call, "Compile time panic: %.*s", LIT(operand->value.value_string)); + 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); + } + operand->type = t_invalid; + operand->mode = Addressing_NoValue; } else if (name == "defined") { if (ce->args.count != 1) { error(call, "'#defined' expects 1 argument, got %td", ce->args.count); return false; } Ast *arg = unparen_expr(ce->args[0]); - if (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr) { - error(call, "'#defined' expects an identifier or selector expression, got %s", LIT(ast_strings[arg->kind])); + if (arg == nullptr || (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr)) { + error(call, "'#defined' expects an identifier or selector expression, got %.*s", LIT(ast_strings[arg->kind])); return false; } @@ -3644,6 +3882,10 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 } operand->mode = Addressing_Value; + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } @@ -3690,7 +3932,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; @@ -3705,13 +3949,109 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 default: GB_PANIC("Invalid type"); break; } + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + 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; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + 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)) { @@ -3719,7 +4059,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; @@ -3727,9 +4072,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; } @@ -3747,10 +4092,68 @@ 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; + } + + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + + 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; } + if (type_hint != nullptr && check_is_castable_to(c, operand, type_hint)) { + operand->type = type_hint; + } + break; } @@ -3761,12 +4164,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); @@ -3959,6 +4374,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *bt = base_type(operands[0].type); if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "min_f32"); if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "min_f64"); + + operand->type = operands[0].type; } } break; @@ -4119,6 +4536,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 Type *bt = base_type(operands[0].type); if (are_types_identical(bt, t_f32)) add_package_dependency(c, "runtime", "max_f32"); if (are_types_identical(bt, t_f64)) add_package_dependency(c, "runtime", "max_f64"); + + operand->type = operands[0].type; } } break; @@ -4161,6 +4580,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"); } } @@ -4266,6 +4687,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 add_package_dependency(c, "runtime", "min_f64"); add_package_dependency(c, "runtime", "max_f64"); } + + operand->type = ops[0]->type; } } @@ -4439,6 +4862,123 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 break; } break; + + case BuiltinProc_type_base_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = base_type(operand->type); + } + operand->mode = Addressing_Type; + break; + case BuiltinProc_type_core_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + operand->type = core_type(operand->type); + } + operand->mode = Addressing_Type; + break; + case BuiltinProc_type_elem_type: + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + switch (bt->kind) { + case Type_Basic: + switch (bt->Basic.kind) { + case Basic_complex64: operand->type = t_f32; break; + case Basic_complex128: operand->type = t_f64; break; + } + break; + case Type_Pointer: operand->type = bt->Pointer.elem; break; + case Type_Opaque: operand->type = bt->Opaque.elem; break; + case Type_Array: operand->type = bt->Array.elem; break; + case Type_Slice: operand->type = bt->Slice.elem; break; + case Type_DynamicArray: operand->type = bt->DynamicArray.elem; break; + } + } + operand->mode = Addressing_Type; + break; + + + case BuiltinProc_type_is_boolean: + case BuiltinProc_type_is_integer: + case BuiltinProc_type_is_rune: + case BuiltinProc_type_is_float: + case BuiltinProc_type_is_complex: + case BuiltinProc_type_is_quaternion: + case BuiltinProc_type_is_string: + case BuiltinProc_type_is_typeid: + case BuiltinProc_type_is_any: + case BuiltinProc_type_is_endian_little: + case BuiltinProc_type_is_endian_big: + case BuiltinProc_type_is_numeric: + case BuiltinProc_type_is_ordered: + case BuiltinProc_type_is_ordered_numeric: + case BuiltinProc_type_is_indexable: + case BuiltinProc_type_is_sliceable: + case BuiltinProc_type_is_simple_compare: + case BuiltinProc_type_is_dereferenceable: + case BuiltinProc_type_is_valid_map_key: + case BuiltinProc_type_is_named: + case BuiltinProc_type_is_pointer: + case BuiltinProc_type_is_opaque: + case BuiltinProc_type_is_array: + case BuiltinProc_type_is_slice: + case BuiltinProc_type_is_dynamic_array: + case BuiltinProc_type_is_map: + case BuiltinProc_type_is_struct: + case BuiltinProc_type_is_union: + case BuiltinProc_type_is_enum: + case BuiltinProc_type_is_proc: + case BuiltinProc_type_is_bit_field: + case BuiltinProc_type_is_bit_field_value: + case BuiltinProc_type_is_bit_set: + case BuiltinProc_type_is_simd_vector: + case BuiltinProc_type_has_nil: + GB_ASSERT(BuiltinProc__type_begin < id && id < BuiltinProc__type_end); + operand->value = exact_value_bool(false); + if (operand->mode != Addressing_Type) { + gbString str = expr_to_string(ce->args[0]); + error(operand->expr, "Expected a type for '%.*s', got '%s'", LIT(builtin_name), str); + gb_string_free(str); + } else { + i32 i = id - (BuiltinProc__type_begin+1); + auto procedure = builtin_type_is_procs[i]; + GB_ASSERT_MSG(procedure != nullptr, "%.*s", LIT(builtin_name)); + operand->value = exact_value_bool(procedure(operand->type)); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_bool; + break; + + case BuiltinProc_type_proc_parameter_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.param_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; + case BuiltinProc_type_proc_return_count: + operand->value = exact_value_i64(0); + if (operand->mode != Addressing_Type) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else if (!is_type_proc(operand->type)) { + error(operand->expr, "Expected a procedure type for '%.*s'", LIT(builtin_name)); + } else { + Type *bt = base_type(operand->type); + operand->value = exact_value_i64(bt->Proc.result_count); + } + operand->mode = Addressing_Constant; + operand->type = t_untyped_integer; + break; } return true; @@ -4455,7 +4995,7 @@ isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs c->decl = decl; // will be reset by the 'defer' any way for_array(k, decl->deps.entries) { Entity *dep = decl->deps.entries[k].ptr; - add_declaration_dependency(c, dep); // TODO(bill): Should this be here? + add_declaration_dependency(c, dep); // TODO(bill): Should this be here? } } } @@ -4774,6 +5314,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) { @@ -4816,6 +5361,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { if (is_type_any(elem)) { add_type_info_type(c, o.type); } + if (o.mode == Addressing_Type && is_type_typeid(t)) { + add_type_info_type(c, o.type); + add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); + } } } } @@ -5016,6 +5565,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) { @@ -5061,6 +5615,69 @@ Entity **populate_proc_parameter_list(CheckerContext *c, Type *proc_type, isize return lhs; } + +bool evaluate_where_clauses(CheckerContext *ctx, Scope *scope, Array<Ast *> *clauses, bool print_err) { + if (clauses != nullptr) { + for_array(i, *clauses) { + Ast *clause = (*clauses)[i]; + Operand o = {}; + check_expr(ctx, &o, clause); + if (o.mode != Addressing_Constant) { + if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation"); + return false; + } else if (o.value.kind != ExactValue_Bool) { + if (print_err) error(clause, "'where' clauses expect a constant boolean evaluation"); + return false; + } else if (!o.value.value_bool) { + if (print_err) { + gbString str = expr_to_string(clause); + error(clause, "'where' clause evaluated to false:\n\t%s", str); + gb_string_free(str); + + if (scope != nullptr) { + isize print_count = 0; + for_array(j, scope->elements.entries) { + Entity *e = scope->elements.entries[j].value; + switch (e->kind) { + case Entity_TypeName: { + if (print_count == 0) error_line("\n\tWith the following definitions:\n"); + + gbString str = type_to_string(e->type); + error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str); + gb_string_free(str); + print_count += 1; + break; + } + case Entity_Constant: { + if (print_count == 0) error_line("\n\tWith the following definitions:\n"); + + gbString str = exact_value_to_string(e->Constant.value); + if (is_type_untyped(e->type)) { + error_line("\t\t%.*s :: %s;\n", LIT(e->token.string), str); + } else { + gbString t = type_to_string(e->type); + error_line("\t\t%.*s : %s : %s;\n", LIT(e->token.string), t, str); + gb_string_free(t); + } + gb_string_free(str); + + print_count += 1; + break; + } + } + } + } + + } + return false; + } + } + } + + return true; +} + + CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type *proc_type, Ast *call) { ast_node(ce, CallExpr, call); @@ -5238,6 +5855,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } + if (proc_arg_count >= 0 && proc_arg_count_all_equal) { lhs_count = proc_arg_count; if (lhs_count > 0) { @@ -5281,9 +5899,8 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type gb_free(heap_allocator(), lhs); } - ValidIndexAndScore *valids = gb_alloc_array(heap_allocator(), ValidIndexAndScore, procs.count); - isize valid_count = 0; - defer (gb_free(heap_allocator(), valids)); + auto valids = array_make<ValidIndexAndScore>(heap_allocator(), 0, procs.count); + defer (array_free(&valids)); gbString expr_name = expr_to_string(operand->expr); defer (gb_string_free(expr_name)); @@ -5298,36 +5915,54 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type ctx.no_polymorphic_errors = true; ctx.allow_polymorphic_types = is_type_polymorphic(pt); + ctx.hide_polymorphic_errors = true; err = call_checker(&ctx, call, pt, p, operands, CallArgumentMode_NoErrors, &data); - if (err == CallArgumentError_None) { - valids[valid_count].index = i; - valids[valid_count].score = data.score; - valid_count++; + if (err != CallArgumentError_None) { + continue; + } + + if (data.gen_entity != nullptr) { + Entity *e = data.gen_entity; + DeclInfo *decl = data.gen_entity->decl_info; + ctx.scope = decl->scope; + ctx.decl = decl; + ctx.proc_name = e->token.string; + ctx.curr_proc_decl = decl; + ctx.curr_proc_sig = e->type; + + GB_ASSERT(decl->proc_lit->kind == Ast_ProcLit); + if (!evaluate_where_clauses(&ctx, decl->scope, &decl->proc_lit->ProcLit.where_clauses, false)) { + continue; + } } + + ValidIndexAndScore item = {}; + item.index = i; + item.score = data.score; + array_add(&valids, item); } } - if (valid_count > 1) { - gb_sort_array(valids, valid_count, valid_index_and_score_cmp); + if (valids.count > 1) { + gb_sort_array(valids.data, valids.count, valid_index_and_score_cmp); i64 best_score = valids[0].score; Entity *best_entity = procs[valids[0].index]; - for (isize i = 1; i < valid_count; i++) { + for (isize i = 1; i < valids.count; i++) { if (best_score > valids[i].score) { - valid_count = i; + valids.count = i; break; } if (best_entity == procs[valids[i].index]) { - valid_count = i; + valids.count = i; break; } - best_score = valids[i].score; } } - if (valid_count == 0) { + if (valids.count == 0) { begin_error_block(); defer (end_error_block()); @@ -5382,7 +6017,7 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } result_type = t_invalid; - } else if (valid_count > 1) { + } else if (valids.count > 1) { begin_error_block(); defer (end_error_block()); @@ -5397,11 +6032,11 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type } error_line(")\n"); - for (isize i = 0; i < valid_count; i++) { + for (isize i = 0; i < valids.count; i++) { Entity *proc = procs[valids[i].index]; TokenPos pos = proc->token.pos; Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); - gbString pt; + gbString pt = nullptr; defer (gb_string_free(pt)); if (t->Proc.node != nullptr) { pt = expr_to_string(t->Proc.node); @@ -5413,7 +6048,29 @@ CallArgumentData check_call_arguments(CheckerContext *c, Operand *operand, Type if (proc->kind == Entity_Variable) { sep = ":="; } - error_line("\t%.*s %s %s at %.*s(%td:%td)\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column); + error_line("\t%.*s %s %s ", LIT(name), sep, pt); + if (proc->decl_info->proc_lit != nullptr) { + GB_ASSERT(proc->decl_info->proc_lit->kind == Ast_ProcLit); + auto *pl = &proc->decl_info->proc_lit->ProcLit; + if (pl->where_token.kind != Token_Invalid) { + error_line("\n\t\twhere "); + for_array(j, pl->where_clauses) { + Ast *clause = pl->where_clauses[j]; + if (j != 0) { + error_line("\t\t "); + } + gbString str = expr_to_string(clause); + error_line("%s", str); + gb_string_free(str); + + if (j != pl->where_clauses.count-1) { + error_line(","); + } + } + error_line("\n\t"); + } + } + error_line("at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); // error_line("\t%.*s %s %s at %.*s(%td:%td) %lld\n", LIT(name), sep, pt, LIT(pos.file), pos.line, pos.column, valids[i].score); } result_type = t_invalid; @@ -5492,24 +6149,57 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper defer (array_free(&operands)); bool named_fields = false; + { + // NOTE(bill, 2019-10-26): Allow a cycle in the parameters but not in the fields themselves + auto prev_type_path = c->type_path; + c->type_path = new_checker_type_path(); + defer ({ + destroy_checker_type_path(c->type_path); + c->type_path = prev_type_path; + }); + + if (is_call_expr_field_value(ce)) { + named_fields = true; + operands = array_make<Operand>(heap_allocator(), ce->args.count); + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + ast_node(fv, FieldValue, arg); + + if (fv->field->kind == Ast_Ident) { + String name = fv->field->Ident.token.string; + isize index = lookup_polymorphic_record_parameter(original_type, name); + if (index >= 0) { + TypeTuple *params = get_record_polymorphic_params(original_type); + Entity *e = params->variables[i]; + if (e->kind == Entity_Constant) { + check_expr_with_type_hint(c, &operands[i], fv->value, e->type); + } + } - if (is_call_expr_field_value(ce)) { - named_fields = true; - operands = array_make<Operand>(heap_allocator(), ce->args.count); - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - ast_node(fv, FieldValue, arg); - check_expr_or_type(c, &operands[i], fv->value); - } + } + check_expr_or_type(c, &operands[i], fv->value); + } + + bool vari_expand = (ce->ellipsis.pos.line != 0); + if (vari_expand) { + error(ce->ellipsis, "Invalid use of '..' in a polymorphic type call'"); + } - bool vari_expand = (ce->ellipsis.pos.line != 0); - if (vari_expand) { - error(ce->ellipsis, "Invalid use of '..' in a polymorphic type call'"); + } else { + operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count); + + Entity **lhs = nullptr; + isize lhs_count = -1; + + TypeTuple *params = get_record_polymorphic_params(original_type); + if (params != nullptr) { + lhs = params->variables.data; + lhs_count = params->variables.count; + } + + check_unpack_arguments(c, lhs, lhs_count, &operands, ce->args, false, false); } - } else { - operands = array_make<Operand>(heap_allocator(), 0, 2*ce->args.count); - check_unpack_arguments(c, nullptr, -1, &operands, ce->args, false, false); } CallArgumentError err = CallArgumentError_None; @@ -5638,12 +6328,16 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper } score += s; } + + // NOTE(bill): Add type info the parameters + add_type_info_type(c, o->type); } { gbAllocator a = c->allocator; - Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands); + bool failure = false; + Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure); if (found_entity) { operand->mode = Addressing_Type; operand->type = found_entity->type; @@ -5692,13 +6386,13 @@ CallArgumentError check_polymorphic_record_type(CheckerContext *c, Operand *oper -ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { +ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) { ast_node(ce, CallExpr, call); if (ce->proc != nullptr && ce->proc->kind == Ast_BasicDirective) { ast_node(bd, BasicDirective, ce->proc); String name = bd->name; - if (name == "location" || name == "assert" || name == "defined" || name == "load") { + if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "load") { operand->mode = Addressing_Builtin; operand->builtin_id = BuiltinProc_DIRECTIVE; operand->expr = ce->proc; @@ -5761,7 +6455,8 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { Ast *s = ident->SelectorExpr.selector; ident = s; } - Type *ot = operand->type; GB_ASSERT(ot->kind == Type_Named); + Type *ot = operand->type; + GB_ASSERT(ot->kind == Type_Named); Entity *e = ot->Named.type_name; add_entity_use(c, ident, e); add_type_and_value(&c->checker->info, call, Addressing_Type, ot, empty_exact_value); @@ -5799,7 +6494,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call) { if (operand->mode == Addressing_Builtin) { i32 id = operand->builtin_id; - if (!check_builtin_procedure(c, operand, call, id)) { + if (!check_builtin_procedure(c, operand, call, id, type_hint)) { operand->mode = Addressing_Invalid; } operand->expr = call; @@ -5825,17 +6520,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); @@ -5975,6 +6659,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; @@ -6037,32 +6813,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]; - switch (r) { - case 'i': t = t_untyped_complex; 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); @@ -6107,6 +6877,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type decl = make_decl_info(ctx.allocator, ctx.scope, ctx.decl); decl->proc_lit = node; ctx.decl = decl; + defer (ctx.decl = ctx.decl->parent); if (pl->tags != 0) { error(node, "A procedure literal cannot have tags"); @@ -6412,7 +7183,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type i64 max = 0; - isize index = 0; Type *bet = base_type(elem_type); if (!elem_type_can_be_constant(bet)) { @@ -6423,40 +7193,152 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type break; } - for (; index < cl->elems.count; index++) { - Ast *e = cl->elems[index]; - if (e == nullptr) { - error(node, "Invalid literal element"); - continue; - } + if (cl->elems.count > 0 && cl->elems[0]->kind == Ast_FieldValue) { + if (is_type_simd_vector(t)) { + error(cl->elems[0], "'field = value' is not allowed for SIMD vector literals"); + } else { + RangeCache rc = range_cache_make(heap_allocator()); + defer (range_cache_destroy(&rc)); - if (e->kind == Ast_FieldValue) { - error(e, "'field = value' is only allowed in struct literals"); - continue; - } + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind != Ast_FieldValue) { + error(elem, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + ast_node(fv, FieldValue, elem); + + if (is_ast_range(fv->field)) { + Token op = fv->field->BinaryExpr.op; + + 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; + } + + if (y.mode != Addressing_Constant || !is_type_integer(core_type(y.type))) { + error(y.expr, "Expected a constant integer as an array field"); + continue; + } + + i64 lo = exact_value_to_i64(x.value); + i64 hi = exact_value_to_i64(y.value); + i64 max_index = hi; + if (op.kind == Token_RangeHalf) { + hi -= 1; + } + + 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_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 < hi) { + 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; + } - if (0 <= max_type_count && max_type_count <= index) { - error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + 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+1) { + max = index+1; + } + + 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_count = max; } - Operand operand = {}; - check_expr_with_type_hint(c, &operand, e, elem_type); - check_assignment(c, &operand, elem_type, context_name); - is_constant = is_constant && operand.mode == Addressing_Constant; - } - if (max < index) { - max = index; + } else { + isize index = 0; + for (; index < cl->elems.count; index++) { + Ast *e = cl->elems[index]; + if (e == nullptr) { + error(node, "Invalid literal element"); + continue; + } + + if (e->kind == Ast_FieldValue) { + error(e, "Mixture of 'field = value' and value elements in a literal is not allowed"); + continue; + } + + if (0 <= max_type_count && max_type_count <= index) { + error(e, "Index %lld is out of bounds (>= %lld) for %.*s", index, max_type_count, LIT(context_name)); + } + + Operand operand = {}; + check_expr_with_type_hint(c, &operand, e, elem_type); + check_assignment(c, &operand, elem_type, context_name); + + is_constant = is_constant && operand.mode == Addressing_Constant; + } + + if (max < index) { + max = index; + } } + if (t->kind == Type_Array) { if (is_to_be_determined_array_count) { t->Array.count = max; - } else if (0 < max && max < t->Array.count) { - error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } else if (cl->elems.count > 0 && cl->elems[0]->kind != Ast_FieldValue) { + if (0 < max && max < t->Array.count) { + error(node, "Expected %lld values for this array literal, got %lld", cast(long long)t->Array.count, cast(long long)max); + } } } + if (t->kind == Type_SimdVector) { if (!is_constant) { error(node, "Expected all constant elements for a simd vector"); @@ -6828,7 +7710,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(be, BinaryExpr, node); - check_binary_expr(c, o, node, true); + check_binary_expr(c, o, node, type_hint, true); if (o->mode == Addressing_Invalid) { o->expr = node; return kind; @@ -6992,7 +7874,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type if (o->mode == Addressing_Constant) { max_count = o->value.value_string.len; } - o->type = t_string; + o->type = type_deref(o->type); } break; @@ -7072,7 +7954,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_ast_node(ce, CallExpr, node); - return check_call_expr(c, o, node); + return check_call_expr(c, o, node, type_hint); case_end; case_ast_node(de, DerefExpr, node); @@ -7111,6 +7993,9 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case Ast_UnionType: case Ast_EnumType: case Ast_MapType: + case Ast_OpaqueType: + case Ast_BitSetType: + case Ast_BitFieldType: o->mode = Addressing_Type; o->type = check_type(c, node); break; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index e2090688f..d4398664b 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; @@ -492,8 +496,7 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b for_array(i, found->elements.entries) { Entity *f = found->elements.entries[i].value; if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); - uvar->using_expr = expr; + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, expr); Entity *prev = scope_insert(ctx->scope, uvar); if (prev != nullptr) { gbString expr_str = expr_to_string(expr); @@ -587,6 +590,162 @@ void add_constant_switch_case(CheckerContext *ctx, Map<TypeAndToken> *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 = {}; + Operand y = {}; + + bool ok = check_range(ctx, expr, &x, &y, &inline_for_depth); + if (!ok) { + goto skip_expr; + } + + val0 = x.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); @@ -971,7 +1130,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { GB_PANIC("Unknown type to type switch statement"); } - if (ptr_set_exists(&seen, y.type)) { + if (type_ptr_set_exists(&seen, y.type)) { TokenPos pos = cc->token.pos; gbString expr_str = expr_to_string(y.expr); error(y.expr, @@ -1023,7 +1182,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) { for_array(i, variants) { Type *t = variants[i]; - if (!ptr_set_exists(&seen, t)) { + if (!type_ptr_set_exists(&seen, t)) { array_add(&unhandled, t); } } @@ -1132,7 +1291,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { isize rhs_count = rhs_operands.count; for_array(i, rhs_operands) { if (rhs_operands[i].mode == Addressing_Invalid) { - rhs_count--; + // TODO(bill): Should I ignore invalid parameters? + // rhs_count--; } } @@ -1168,7 +1328,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { be->right = as->rhs[0]; check_expr(ctx, &lhs, as->lhs[0]); - check_binary_expr(ctx, &rhs, &binary_expr, true); + check_binary_expr(ctx, &rhs, &binary_expr, nullptr, true); if (rhs.mode == Addressing_Invalid) { return; } @@ -1298,6 +1458,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 +1481,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 +1517,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 +1543,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 +1560,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 +1610,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}; @@ -1507,7 +1661,12 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { } for (isize i = 0; i < entity_count; i++) { - add_entity(ctx->checker, ctx->scope, entities[i]->identifier, entities[i]); + Entity *e = entities[i]; + DeclInfo *d = decl_info_of_entity(e); + GB_ASSERT(d == nullptr); + add_entity(ctx->checker, ctx->scope, e->identifier, e); + d = make_decl_info(ctx->allocator, ctx->scope, ctx->decl); + add_entity_and_decl_info(ctx, e->identifier, e, d); } check_stmt(ctx, rs->body, new_flags); @@ -1515,6 +1674,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; @@ -1814,7 +1977,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { // TODO(bill): Should a 'continue' happen here? } - for (isize entity_index = 0; entity_index < entity_count; entity_index++) { + for (isize entity_index = 0; entity_index < 1; entity_index++) { Entity *e = entities[entity_index]; if (e == nullptr) { continue; @@ -1833,7 +1996,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { for_array(i, scope->elements.entries) { Entity *f = scope->elements.entries[i].value; if (f->kind == Entity_Variable) { - Entity *uvar = alloc_entity_using_variable(e, f->token, f->type); + Entity *uvar = alloc_entity_using_variable(e, f->token, f->type, nullptr); uvar->Variable.is_immutable = is_immutable; Entity *prev = scope_insert(ctx->scope, uvar); if (prev != nullptr) { diff --git a/src/check_type.cpp b/src/check_type.cpp index f4e5b49d0..9f8310e44 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -110,9 +110,10 @@ bool does_field_type_allow_using(Type *t) { return false; } -void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<Ast *> const ¶ms, +void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields, Array<String> *tags, Array<Ast *> const ¶ms, isize init_field_capacity, Type *struct_type, String context) { *fields = array_make<Entity *>(heap_allocator(), 0, init_field_capacity); + *tags = array_make<String>(heap_allocator(), 0, init_field_capacity); GB_ASSERT(node->kind == Ast_StructType); GB_ASSERT(struct_type->kind == Type_Struct); @@ -171,6 +172,7 @@ void check_struct_fields(CheckerContext *ctx, Ast *node, Array<Entity *> *fields Entity *field = alloc_entity_field(ctx->scope, name_token, type, is_using, field_src_index); add_entity(ctx->checker, ctx->scope, name, field); array_add(fields, field); + array_add(tags, p->tag.string); field_src_index += 1; } @@ -246,38 +248,51 @@ bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_) { } -Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> ordered_operands) { +Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure) { auto *found_gen_types = map_get(&ctx->checker->info.gen_types, hash_pointer(original_type)); if (found_gen_types != nullptr) { for_array(i, *found_gen_types) { Entity *e = (*found_gen_types)[i]; Type *t = base_type(e->type); TypeTuple *tuple = get_record_polymorphic_params(t); - bool ok = true; GB_ASSERT(param_count == tuple->variables.count); + + bool skip = false; + for (isize j = 0; j < param_count; j++) { Entity *p = tuple->variables[j]; Operand o = ordered_operands[j]; + Entity *oe = entity_of_node(o.expr); + if (p == oe) { + // NOTE(bill): This is the same type, make sure that it will be be same thing and use that + // Saves on a lot of checking too below + continue; + } + if (p->kind == Entity_TypeName) { if (is_type_polymorphic(o.type)) { // NOTE(bill): Do not add polymorphic version to the gen_types - ok = false; + skip = true; + break; } if (!are_types_identical(o.type, p->type)) { - ok = false; + skip = true; + break; } } else if (p->kind == Entity_Constant) { - if (!are_types_identical(o.type, p->type)) { - ok = false; - } if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) { - ok = false; + skip = true; + break; + } + if (!are_types_identical(o.type, p->type)) { + skip = true; + break; } } else { GB_PANIC("Unknown entity kind"); } } - if (ok) { + if (!skip) { return e; } } @@ -439,8 +454,6 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< if (poly_operands != nullptr) { Operand operand = (*poly_operands)[entities.count]; if (is_type_param) { - GB_ASSERT(operand.mode == Addressing_Type || - operand.mode == Addressing_Invalid); if (is_type_polymorphic(base_type(operand.type))) { is_polymorphic = true; can_check_fields = false; @@ -448,6 +461,10 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< e = alloc_entity_type_name(scope, token, operand.type); e->TypeName.is_type_alias = true; } else { + if (is_type_polymorphic(base_type(operand.type))) { + is_polymorphic = true; + can_check_fields = false; + } e = alloc_entity_constant(scope, token, operand.type, operand.value); } } else { @@ -502,9 +519,13 @@ void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array< struct_type->Struct.polymorphic_params = polymorphic_params; struct_type->Struct.is_poly_specialized = is_poly_specialized; - if (!is_polymorphic) { - check_struct_fields(ctx, node, &struct_type->Struct.fields, st->fields, min_field_count, struct_type, context); + if (st->where_clauses.count > 0 && st->polymorphic_params == nullptr) { + error(st->where_clauses[0], "'where' clauses can only be used on structures with polymorphic parameters"); + } else { + bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &st->where_clauses, true); + } + check_struct_fields(ctx, node, &struct_type->Struct.fields, &struct_type->Struct.tags, st->fields, min_field_count, struct_type, context); } if (st->align != nullptr) { @@ -686,6 +707,13 @@ void check_union_type(CheckerContext *ctx, Type *union_type, Ast *node, Array<Op union_type->Union.is_polymorphic = is_polymorphic; union_type->Union.is_poly_specialized = is_poly_specialized; + if (ut->where_clauses.count > 0 && ut->polymorphic_params == nullptr) { + error(ut->where_clauses[0], "'where' clauses can only be used on unions with polymorphic parameters"); + } else { + bool where_clause_ok = evaluate_where_clauses(ctx, ctx->scope, &ut->where_clauses, true); + } + + for_array(i, ut->variants) { Ast *node = ut->variants[i]; Type *t = check_type_expr(ctx, node, nullptr); @@ -1252,20 +1280,21 @@ bool check_type_specialization_to(CheckerContext *ctx, Type *specialization, Typ Type *determine_type_from_polymorphic(CheckerContext *ctx, Type *poly_type, Operand operand) { bool modify_type = !ctx->no_polymorphic_errors; + bool show_error = modify_type && !ctx->hide_polymorphic_errors; if (!is_operand_value(operand)) { - if (modify_type) { + if (show_error) { error(operand.expr, "Cannot determine polymorphic type from parameter"); } return t_invalid; } if (is_polymorphic_type_assignable(ctx, poly_type, operand.type, false, modify_type)) { - if (modify_type) { - set_procedure_abi_types(ctx, poly_type); + if (show_error) { + set_procedure_abi_types(ctx->allocator, poly_type); } return poly_type; } - if (modify_type) { + if (show_error) { gbString pts = type_to_string(poly_type); gbString ots = type_to_string(operand.type); defer (gb_string_free(pts)); @@ -1538,7 +1567,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is } if (is_poly_name) { - if (type != nullptr && type_expr->kind == Ast_TypeidType) { + if (type_expr != nullptr && type_expr->kind == Ast_TypeidType) { is_type_param = true; } else { if (param_value.kind != ParameterValue_Invalid) { @@ -1622,6 +1651,7 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is if (op.mode == Addressing_Constant) { poly_const = op.value; } else { + error(op.expr, "Expected a constant value for this polymorphic name parameter"); success = false; } } @@ -1825,6 +1855,282 @@ Type *check_get_results(CheckerContext *ctx, Scope *scope, Ast *_results) { return tuple; } +Array<Type *> systemv_distribute_struct_fields(Type *t) { + Type *bt = core_type(t); + + + isize distributed_cap = 1; + if (bt->kind == Type_Struct) { + distributed_cap = bt->Struct.fields.count; + } + auto distributed = array_make<Type *>(heap_allocator(), 0, distributed_cap); + + i64 sz = type_size_of(bt); + switch (bt->kind) { + case Type_Basic: + switch (bt->Basic.kind){ + case Basic_complex64: + array_add(&distributed, t_f32); + array_add(&distributed, t_f32); + break; + case Basic_complex128: + array_add(&distributed, t_f64); + array_add(&distributed, t_f64); + break; + case Basic_quaternion128: + array_add(&distributed, t_f32); + array_add(&distributed, t_f32); + array_add(&distributed, t_f32); + array_add(&distributed, t_f32); + break; + case Basic_quaternion256: + goto DEFAULT; + case Basic_string: + array_add(&distributed, t_u8_ptr); + array_add(&distributed, t_int); + break; + case Basic_any: + GB_ASSERT(type_size_of(t_uintptr) == type_size_of(t_typeid)); + array_add(&distributed, t_rawptr); + array_add(&distributed, t_uintptr); + break; + + case Basic_u128: + case Basic_i128: + if (build_context.ODIN_OS == "windows") { + array_add(&distributed, alloc_type_simd_vector(2, t_u64)); + } else { + array_add(&distributed, bt); + } + break; + + default: + goto DEFAULT; + } + break; + + case Type_Struct: + if (bt->Struct.is_raw_union) { + goto DEFAULT; + } else { + // IMPORTANT TOOD(bill): handle #packed structs correctly + // IMPORTANT TODO(bill): handle #align structs correctly + for_array(field_index, bt->Struct.fields) { + Entity *f = bt->Struct.fields[field_index]; + auto nested = systemv_distribute_struct_fields(f->type); + array_add_elems(&distributed, nested.data, nested.count); + array_free(&nested); + } + } + break; + + case Type_Array: + for (i64 i = 0; i < bt->Array.count; i++) { + array_add(&distributed, bt->Array.elem); + } + break; + + case Type_BitSet: + array_add(&distributed, bit_set_to_int(bt)); + break; + + case Type_Tuple: + GB_PANIC("Invalid struct field type"); + break; + + case Type_Slice: + array_add(&distributed, t_rawptr); + array_add(&distributed, t_int); + break; + + case Type_Union: + case Type_DynamicArray: + case Type_Map: + case Type_BitField: // TODO(bill): Ignore? + // NOTE(bill, 2019-10-10): Odin specific, don't worry about C calling convention yet + goto DEFAULT; + + case Type_Pointer: + case Type_Proc: + case Type_SimdVector: // TODO(bill): Is this correct logic? + default: + DEFAULT:; + if (sz > 0) { + array_add(&distributed, bt); + } + break; + } + + return distributed; +} + +Type *struct_type_from_systemv_distribute_struct_fields(Type *abi_type) { + GB_ASSERT(is_type_tuple(abi_type)); + Type *final_type = alloc_type_struct(); + final_type->Struct.fields = abi_type->Tuple.variables; + return final_type; +} + + +Type *handle_single_distributed_type_parameter(Array<Type *> const &types, bool packed, isize *offset) { + GB_ASSERT(types.count > 0); + + if (types.count == 1) { + if (offset) *offset = 1; + + i64 sz = type_size_of(types[0]); + + if (is_type_float(types[0])) { + return types[0]; + } + switch (sz) { + case 0: + GB_PANIC("Zero sized type found!"); + case 1: return t_u8; + case 2: return t_u16; + case 4: return t_u32; + case 8: return t_u64; + default: + return types[0]; + } + } else if (types.count >= 2) { + if (types[0] == t_f32 && types[1] == t_f32) { + if (offset) *offset = 2; + return alloc_type_simd_vector(2, t_f32); + } else if (type_size_of(types[0]) == 8) { + if (offset) *offset = 1; + return types[0]; + } + + i64 total_size = 0; + isize i = 0; + if (packed) { + for (; i < types.count && total_size < 8; i += 1) { + Type *t = types[i]; + i64 s = type_size_of(t); + total_size += s; + } + } else { + for (; i < types.count && total_size < 8; i += 1) { + Type *t = types[i]; + i64 s = gb_max(type_size_of(t), 0); + i64 a = gb_max(type_align_of(t), 1); + isize ts = align_formula(total_size, a); + if (ts >= 8) { + break; + } + total_size = ts + s; + } + } + if (offset) *offset = i; + switch (total_size) { + case 1: return t_u8; + case 2: return t_u16; + case 4: return t_u32; + case 8: return t_u64; + } + return t_u64; + } + + return nullptr; +} + +Type *handle_struct_system_v_amd64_abi_type(Type *t) { + if (type_size_of(t) > 16) { + return alloc_type_pointer(t); + } + Type *original_type = t; + Type *bt = core_type(t); + t = base_type(t); + i64 size = type_size_of(bt); + + switch (t->kind) { + case Type_Slice: + case Type_Struct: + break; + + case Type_Basic: + switch (bt->Basic.kind) { + case Basic_string: + case Basic_any: + case Basic_complex64: + case Basic_complex128: + case Basic_quaternion128: + break; + default: + return original_type; + } + break; + + default: + return original_type; + } + + bool is_packed = false; + if (is_type_struct(bt)) { + is_packed = bt->Struct.is_packed; + } + + if (is_type_raw_union(bt)) { + // TODO(bill): Handle raw union correctly for + return t; + } else { + auto field_types = systemv_distribute_struct_fields(bt); + defer (array_free(&field_types)); + + GB_ASSERT(field_types.count <= 16); + + Type *final_type = nullptr; + + if (field_types.count == 0) { + final_type = t; + } else if (field_types.count == 1) { + final_type = field_types[0]; + } else { + if (size <= 8) { + isize offset = 0; + final_type = handle_single_distributed_type_parameter(field_types, is_packed, &offset); + } else { + isize offset = 0; + isize next_offset = 0; + Type *two_types[2] = {}; + + two_types[0] = handle_single_distributed_type_parameter(field_types, is_packed, &offset); + auto remaining = array_slice(field_types, offset, field_types.count); + two_types[1] = handle_single_distributed_type_parameter(remaining, is_packed, &next_offset); + GB_ASSERT(offset + next_offset == field_types.count); + + auto variables = array_make<Entity *>(heap_allocator(), 2); + variables[0] = alloc_entity_param(nullptr, empty_token, two_types[0], false, false); + variables[1] = alloc_entity_param(nullptr, empty_token, two_types[1], false, false); + final_type = alloc_type_tuple(); + final_type->Tuple.variables = variables; + if (t->kind == Type_Struct) { + // NOTE(bill): Make this packed + final_type->Tuple.is_packed = t->Struct.is_packed; + } + } + } + + + GB_ASSERT(final_type != nullptr); + i64 ftsz = type_size_of(final_type); + i64 otsz = type_size_of(original_type); + if (ftsz != otsz) { + // TODO(bill): Handle this case which will be caused by #packed most likely + switch (otsz) { + case 1: + case 2: + case 4: + case 8: + GB_PANIC("Incorrectly handled case for handle_struct_system_v_amd64_abi_type, %s %lld vs %s %lld", type_to_string(final_type), ftsz, type_to_string(original_type), otsz); + } + } + + return final_type; + } +} + Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCallingConvention cc) { Type *new_type = original_type; @@ -1889,6 +2195,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall { i64 align = type_align_of(original_type); i64 size = type_size_of(original_type); + switch (8*size) { case 8: new_type = t_u8; break; case 16: new_type = t_u16; break; @@ -1903,7 +2210,7 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall } } } else if (build_context.ODIN_OS == "linux" || - build_context.ODIN_OS == "osx") { + build_context.ODIN_OS == "darwin") { Type *bt = core_type(original_type); switch (bt->kind) { // Okay to pass by value (usually) @@ -1920,18 +2227,17 @@ Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type, ProcCall case Type_Pointer: break; case Type_Proc: break; // NOTE(bill): Just a pointer - // Odin specific - case Type_Slice: - case Type_Array: - case Type_DynamicArray: - case Type_Map: - case Type_Union: - // Could be in C too - case Type_Struct: { - i64 align = type_align_of(original_type); - i64 size = type_size_of(original_type); - if (8*size > 16) { + default: { + i64 size = type_size_of(original_type); + if (size > 16) { new_type = alloc_type_pointer(original_type); + } else if (build_context.ODIN_ARCH == "amd64") { + // NOTE(bill): System V AMD64 ABI + new_type = handle_struct_system_v_amd64_abi_type(bt); + if (are_types_identical(core_type(original_type), new_type)) { + new_type = original_type; + } + return new_type; } break; @@ -2004,8 +2310,10 @@ Type *type_to_abi_compat_result_type(gbAllocator a, Type *original_type, ProcCal break; } } - } else if (build_context.ODIN_OS == "linux") { + } else if (build_context.ODIN_OS == "linux" || build_context.ODIN_OS == "darwin") { + if (build_context.ODIN_ARCH == "amd64") { + } } else { // IMPORTANT TODO(bill): figure out the ABI settings for Linux, OSX etc. for // their architectures @@ -2071,25 +2379,39 @@ bool abi_compat_return_by_pointer(gbAllocator a, ProcCallingConvention cc, Type return false; } -void set_procedure_abi_types(CheckerContext *c, Type *type) { +void set_procedure_abi_types(gbAllocator allocator, Type *type) { type = base_type(type); if (type->kind != Type_Proc) { return; } - type->Proc.abi_compat_params = array_make<Type *>(c->allocator, cast(isize)type->Proc.param_count); + if (type->Proc.abi_types_set) { + return; + } + + type->Proc.abi_compat_params = array_make<Type *>(allocator, cast(isize)type->Proc.param_count); for (i32 i = 0; i < type->Proc.param_count; i++) { Entity *e = type->Proc.params->Tuple.variables[i]; if (e->kind == Entity_Variable) { Type *original_type = e->type; - Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type, type->Proc.calling_convention); + Type *new_type = type_to_abi_compat_param_type(allocator, original_type, type->Proc.calling_convention); type->Proc.abi_compat_params[i] = new_type; + switch (type->Proc.calling_convention) { + case ProcCC_Odin: + case ProcCC_Contextless: + if (is_type_pointer(new_type) & !is_type_pointer(e->type)) { + e->flags |= EntityFlag_ImplicitReference; + } + break; + } } } // NOTE(bill): The types are the same - type->Proc.abi_compat_result_type = type_to_abi_compat_result_type(c->allocator, type->Proc.results, type->Proc.calling_convention); - type->Proc.return_by_pointer = abi_compat_return_by_pointer(c->allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type); + type->Proc.abi_compat_result_type = type_to_abi_compat_result_type(allocator, type->Proc.results, type->Proc.calling_convention); + type->Proc.return_by_pointer = abi_compat_return_by_pointer(allocator, type->Proc.calling_convention, type->Proc.abi_compat_result_type); + + type->Proc.abi_types_set = true; } // NOTE(bill): 'operands' is for generating non generic procedure type @@ -2187,8 +2509,6 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, } type->Proc.is_polymorphic = is_polymorphic; - set_procedure_abi_types(c, type); - return success; } @@ -2537,7 +2857,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t generic_type = o.type; } if (count < 0) { - error(at->count, "... can only be used in conjuction with compound literals"); + error(at->count, "? can only be used in conjuction with compound literals"); count = 0; } Type *elem = check_type_expr(ctx, at->elem, nullptr); diff --git a/src/checker.cpp b/src/checker.cpp index 9abd8c499..e7b347f1a 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -85,9 +85,13 @@ int entity_graph_node_cmp(EntityGraphNode **data, isize i, isize j) { EntityGraphNode *y = data[j]; isize a = x->entity->order_in_src; isize b = y->entity->order_in_src; - if (x->dep_count < y->dep_count) return -1; - if (x->dep_count > y->dep_count) return +1; - return a < b ? -1 : b > a; + if (x->dep_count < y->dep_count) { + return -1; + } + if (x->dep_count == y->dep_count) { + return a < b ? -1 : b > a; + } + return +1; } void entity_graph_node_swap(EntityGraphNode **data, isize i, isize j) { @@ -400,6 +404,15 @@ Entity *scope_insert_with_name(Scope *s, String name, Entity *entity) { if (found) { return *found; } + if (s->parent != nullptr && (s->parent->flags & ScopeFlag_Proc) != 0) { + Entity **found = map_get(&s->parent->elements, key); + if (found) { + if ((*found)->flags & EntityFlag_Result) { + return *found; + } + } + } + map_set(&s->elements, key, entity); if (entity->scope == nullptr) { entity->scope = s; @@ -1044,21 +1057,37 @@ bool redeclaration_error(String name, Entity *prev, Entity *found) { // NOTE(bill): Error should have been handled already return false; } - error(prev->token, - "Redeclaration of '%.*s' in this scope through 'using'\n" - "\tat %.*s(%td:%td)", - LIT(name), - LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); + if (found->flags & EntityFlag_Result) { + error(prev->token, + "Direct shadowing of the named return value '%.*s' in this scope through 'using'\n" + "\tat %.*s(%td:%td)", + LIT(name), + LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); + } else { + error(prev->token, + "Redeclaration of '%.*s' in this scope through 'using'\n" + "\tat %.*s(%td:%td)", + LIT(name), + LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); + } } else { if (pos == prev->token.pos) { // NOTE(bill): Error should have been handled already return false; } - error(prev->token, - "Redeclaration of '%.*s' in this scope\n" - "\tat %.*s(%td:%td)", - LIT(name), - LIT(pos.file), pos.line, pos.column); + if (found->flags & EntityFlag_Result) { + error(prev->token, + "Direct shadowing of the named return value '%.*s' in this scope\n" + "\tat %.*s(%td:%td)", + LIT(name), + LIT(pos.file), pos.line, pos.column); + } else { + error(prev->token, + "Redeclaration of '%.*s' in this scope\n" + "\tat %.*s(%td:%td)", + LIT(name), + LIT(pos.file), pos.line, pos.column); + } } return false; } @@ -1139,6 +1168,7 @@ void add_entity_and_decl_info(CheckerContext *c, Ast *identifier, Entity *e, Dec add_entity_definition(&c->checker->info, identifier, e); GB_ASSERT(e->decl_info == nullptr); e->decl_info = d; + d->entity = e; array_add(&c->checker->info.entities, e); e->order_in_src = c->checker->info.entities.count; e->pkg = c->pkg; @@ -1415,6 +1445,14 @@ void add_min_dep_type_info(Checker *c, Type *t) { add_min_dep_type_info(c, t_type_info_float); add_min_dep_type_info(c, t_f64); break; + case Basic_quaternion128: + add_min_dep_type_info(c, t_type_info_float); + add_min_dep_type_info(c, t_f32); + break; + case Basic_quaternion256: + add_min_dep_type_info(c, t_type_info_float); + add_min_dep_type_info(c, t_f64); + break; } break; @@ -1577,6 +1615,7 @@ void generate_minimum_dependency_set(Checker *c, Entity *start) { str_lit("args__"), str_lit("type_table"), + str_lit("__type_info_of"), str_lit("global_scratch_allocator"), str_lit("Type_Info"), @@ -1585,9 +1624,17 @@ 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("cstring_to_string"), str_lit("umodti3"), str_lit("udivti3"), + + str_lit("memory_compare"), + str_lit("memory_compare_zero"), }; for (isize i = 0; i < gb_count_of(required_runtime_entities); i++) { add_dependency_to_set(c, scope_lookup(c->info.runtime_package->scope, required_runtime_entities[i])); @@ -1656,9 +1703,10 @@ bool is_entity_a_dependency(Entity *e) { if (e == nullptr) return false; switch (e->kind) { case Entity_Procedure: - case Entity_Variable: - case Entity_Constant: return true; + case Entity_Constant: + case Entity_Variable: + return e->pkg != nullptr; } return false; } @@ -1685,18 +1733,17 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) { EntityGraphNode *n = M.entries[i].value; DeclInfo *decl = decl_info_of_entity(e); - if (decl != nullptr) { - for_array(j, decl->deps.entries) { - auto entry = decl->deps.entries[j]; - Entity *dep = entry.ptr; - if (dep && is_entity_a_dependency(dep)) { - EntityGraphNode **m_ = map_get(&M, hash_pointer(dep)); - if (m_ != nullptr) { - EntityGraphNode *m = *m_; - entity_graph_node_set_add(&n->succ, m); - entity_graph_node_set_add(&m->pred, n); - } - } + GB_ASSERT(decl != nullptr); + + for_array(j, decl->deps.entries) { + Entity *dep = decl->deps.entries[j].ptr; + GB_ASSERT(dep != nullptr); + if (is_entity_a_dependency(dep)) { + EntityGraphNode **m_ = map_get(&M, hash_pointer(dep)); + GB_ASSERT(m_ != nullptr); + EntityGraphNode *m = *m_; + entity_graph_node_set_add(&n->succ, m); + entity_graph_node_set_add(&m->pred, n); } } } @@ -1741,6 +1788,7 @@ Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInfo *info) { EntityGraphNode *n = G[i]; n->index = i; n->dep_count = n->succ.entries.count; + GB_ASSERT(n->dep_count >= 0); } @@ -1863,6 +1911,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")); @@ -1887,6 +1936,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); @@ -2138,6 +2188,12 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) { error(elem, "Expected a string value for '%.*s'", LIT(name)); } return true; + } else if (name == "require_results") { + if (value != nullptr) { + error(elem, "Expected no value for '%.*s'", LIT(name)); + } + ac->require_results = true; + return true; } return false; } @@ -2528,6 +2584,7 @@ void check_collect_value_decl(CheckerContext *c, Ast *decl) { Ast *init_expr = value; DeclInfo *d = make_decl_info(heap_allocator(), c->scope, c->decl); + d->entity = e; d->type_expr = vd->type; d->init_expr = init_expr; d->attributes = vd->attributes; @@ -2557,14 +2614,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"); @@ -2591,19 +2648,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; @@ -3101,7 +3154,7 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) { Entity *e = scope->elements.entries[elem_index].value; if (e->scope == parent_scope) continue; - if (is_entity_exported(e)) { + if (is_entity_exported(e, true)) { Entity *found = scope_lookup_current(parent_scope, name); if (found != nullptr) { // NOTE(bill): @@ -3315,8 +3368,9 @@ bool collect_file_decls(CheckerContext *ctx, Array<Ast *> const &decls) { if (es->expr->kind == Ast_CallExpr) { ast_node(ce, CallExpr, es->expr); if (ce->proc->kind == Ast_BasicDirective) { - Operand o = {}; - check_expr(ctx, &o, es->expr); + if (ctx->collect_delayed_decls) { + array_add(&ctx->scope->delayed_directives, es->expr); + } } } case_end; @@ -3473,12 +3527,18 @@ void check_import_entities(Checker *c) { for_array(i, pkg->files) { AstFile *f = pkg->files[i]; CheckerContext ctx = c->init_ctx; - add_curr_ast_file(&ctx, f); + for_array(j, f->scope->delayed_imports) { Ast *decl = f->scope->delayed_imports[j]; check_add_import_decl(&ctx, decl); } + } + for_array(i, pkg->files) { + AstFile *f = pkg->files[i]; + CheckerContext ctx = c->init_ctx; + add_curr_ast_file(&ctx, f); + for_array(j, f->scope->delayed_directives) { Ast *expr = f->scope->delayed_directives[j]; Operand o = {}; @@ -3542,7 +3602,6 @@ void calculate_global_init_order(Checker *c) { #define TIME_SECTION(str) #endif - CheckerInfo *info = &c->info; TIME_SECTION("generate entity dependency graph"); @@ -3584,21 +3643,26 @@ void calculate_global_init_order(Checker *c) { for_array(i, n->pred.entries) { EntityGraphNode *p = n->pred.entries[i].ptr; - p->dep_count -= gb_max(p->dep_count-1, 0); + p->dep_count -= 1; + p->dep_count = gb_max(p->dep_count, 0); priority_queue_fix(&pq, p->index); } - if (e == nullptr || e->kind != Entity_Variable) { + DeclInfo *d = decl_info_of_entity(e); + if (e->kind != Entity_Variable) { continue; } - DeclInfo *d = decl_info_of_entity(e); - + // IMPORTANT NOTE(bill, 2019-08-29): Just add it regardless of the ordering + // because it does not need any initialization other than zero + // if (!decl_info_has_init(d)) { + // continue; + // } if (ptr_set_exists(&emitted, d)) { continue; } ptr_set_add(&emitted, d); - d->entity = e; + array_add(&info->variable_init_order, d); } @@ -3637,6 +3701,14 @@ void check_proc_info(Checker *c, ProcInfo pi) { return; } + if (pt->is_polymorphic && pt->is_poly_specialized) { + Entity *e = pi.decl->entity; + if ((e->flags & EntityFlag_Used) == 0) { + // NOTE(bill, 2019-08-31): It was never used, don't check + return; + } + } + bool bounds_check = (pi.tags & ProcTag_bounds_check) != 0; bool no_bounds_check = (pi.tags & ProcTag_no_bounds_check) != 0; diff --git a/src/checker.hpp b/src/checker.hpp index 66b68c35c..c33514511 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -59,218 +59,8 @@ struct BuiltinProc { BuiltinProcPkg pkg; }; -enum BuiltinProcId { - BuiltinProc_Invalid, - - BuiltinProc_len, - BuiltinProc_cap, - - BuiltinProc_size_of, - BuiltinProc_align_of, - BuiltinProc_offset_of, - BuiltinProc_type_of, - BuiltinProc_type_info_of, - BuiltinProc_typeid_of, - - BuiltinProc_swizzle, - - BuiltinProc_complex, - BuiltinProc_real, - BuiltinProc_imag, - BuiltinProc_conj, - - BuiltinProc_expand_to_tuple, - - BuiltinProc_min, - BuiltinProc_max, - BuiltinProc_abs, - BuiltinProc_clamp, - - BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures - - // "Intrinsics" - BuiltinProc_vector, - - BuiltinProc_atomic_fence, - BuiltinProc_atomic_fence_acq, - BuiltinProc_atomic_fence_rel, - BuiltinProc_atomic_fence_acqrel, - - BuiltinProc_atomic_store, - BuiltinProc_atomic_store_rel, - BuiltinProc_atomic_store_relaxed, - BuiltinProc_atomic_store_unordered, - - BuiltinProc_atomic_load, - BuiltinProc_atomic_load_acq, - BuiltinProc_atomic_load_relaxed, - BuiltinProc_atomic_load_unordered, - - BuiltinProc_atomic_add, - BuiltinProc_atomic_add_acq, - BuiltinProc_atomic_add_rel, - BuiltinProc_atomic_add_acqrel, - BuiltinProc_atomic_add_relaxed, - BuiltinProc_atomic_sub, - BuiltinProc_atomic_sub_acq, - BuiltinProc_atomic_sub_rel, - BuiltinProc_atomic_sub_acqrel, - BuiltinProc_atomic_sub_relaxed, - BuiltinProc_atomic_and, - BuiltinProc_atomic_and_acq, - BuiltinProc_atomic_and_rel, - BuiltinProc_atomic_and_acqrel, - BuiltinProc_atomic_and_relaxed, - BuiltinProc_atomic_nand, - BuiltinProc_atomic_nand_acq, - BuiltinProc_atomic_nand_rel, - BuiltinProc_atomic_nand_acqrel, - BuiltinProc_atomic_nand_relaxed, - BuiltinProc_atomic_or, - BuiltinProc_atomic_or_acq, - BuiltinProc_atomic_or_rel, - BuiltinProc_atomic_or_acqrel, - BuiltinProc_atomic_or_relaxed, - BuiltinProc_atomic_xor, - BuiltinProc_atomic_xor_acq, - BuiltinProc_atomic_xor_rel, - BuiltinProc_atomic_xor_acqrel, - BuiltinProc_atomic_xor_relaxed, - - BuiltinProc_atomic_xchg, - BuiltinProc_atomic_xchg_acq, - BuiltinProc_atomic_xchg_rel, - BuiltinProc_atomic_xchg_acqrel, - BuiltinProc_atomic_xchg_relaxed, - - BuiltinProc_atomic_cxchg, - BuiltinProc_atomic_cxchg_acq, - BuiltinProc_atomic_cxchg_rel, - BuiltinProc_atomic_cxchg_acqrel, - BuiltinProc_atomic_cxchg_relaxed, - BuiltinProc_atomic_cxchg_failrelaxed, - BuiltinProc_atomic_cxchg_failacq, - BuiltinProc_atomic_cxchg_acq_failrelaxed, - BuiltinProc_atomic_cxchg_acqrel_failrelaxed, - - BuiltinProc_atomic_cxchgweak, - BuiltinProc_atomic_cxchgweak_acq, - BuiltinProc_atomic_cxchgweak_rel, - BuiltinProc_atomic_cxchgweak_acqrel, - BuiltinProc_atomic_cxchgweak_relaxed, - BuiltinProc_atomic_cxchgweak_failrelaxed, - BuiltinProc_atomic_cxchgweak_failacq, - BuiltinProc_atomic_cxchgweak_acq_failrelaxed, - BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed, - - BuiltinProc_COUNT, -}; -gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { - {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_builtin}, - - {STR_LIT("len"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("cap"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("size_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("align_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("offset_of"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("type_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("type_info_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("typeid_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("swizzle"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("complex"), 2, 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("conj"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("expand_to_tuple"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("abs"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("clamp"), 3, false, Expr_Expr, BuiltinProcPkg_builtin}, - - {STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE - - - // "Intrinsics" - {STR_LIT("vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type - - - {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - - {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, - {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, -}; + +#include "checker_builtin_procs.hpp" // Operand is used as an intermediate value whilst checking @@ -307,6 +97,7 @@ struct DeferredProcedure { struct AttributeContext { bool is_export; bool is_static; + bool require_results; String link_name; String link_prefix; isize init_expr_list_count; @@ -431,6 +222,7 @@ struct ForeignContext { typedef Array<Entity *> CheckerTypePath; typedef Array<Type *> CheckerPolyPath; + // CheckerInfo stores all the symbol information for a type-checked program struct CheckerInfo { Map<ExprInfo> untyped; // Key: Ast * | Expression -> ExprInfo @@ -486,10 +278,14 @@ 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; bool no_polymorphic_errors; + bool hide_polymorphic_errors; bool in_polymorphic_specialization; Scope * polymorphic_scope; }; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp new file mode 100644 index 000000000..50d27d715 --- /dev/null +++ b/src/checker_builtin_procs.hpp @@ -0,0 +1,320 @@ +// checker_builtin_procs.hpp + +enum BuiltinProcId { + BuiltinProc_Invalid, + + BuiltinProc_len, + BuiltinProc_cap, + + BuiltinProc_size_of, + BuiltinProc_align_of, + BuiltinProc_offset_of, + BuiltinProc_type_of, + BuiltinProc_type_info_of, + BuiltinProc_typeid_of, + + BuiltinProc_swizzle, + + BuiltinProc_complex, + BuiltinProc_quaternion, + BuiltinProc_real, + BuiltinProc_imag, + BuiltinProc_jmag, + BuiltinProc_kmag, + BuiltinProc_conj, + + BuiltinProc_expand_to_tuple, + + BuiltinProc_min, + BuiltinProc_max, + BuiltinProc_abs, + BuiltinProc_clamp, + + BuiltinProc_DIRECTIVE, // NOTE(bill): This is used for specialized hash-prefixed procedures + + // "Intrinsics" + BuiltinProc_vector, + + BuiltinProc_atomic_fence, + BuiltinProc_atomic_fence_acq, + BuiltinProc_atomic_fence_rel, + BuiltinProc_atomic_fence_acqrel, + + BuiltinProc_atomic_store, + BuiltinProc_atomic_store_rel, + BuiltinProc_atomic_store_relaxed, + BuiltinProc_atomic_store_unordered, + + BuiltinProc_atomic_load, + BuiltinProc_atomic_load_acq, + BuiltinProc_atomic_load_relaxed, + BuiltinProc_atomic_load_unordered, + + BuiltinProc_atomic_add, + BuiltinProc_atomic_add_acq, + BuiltinProc_atomic_add_rel, + BuiltinProc_atomic_add_acqrel, + BuiltinProc_atomic_add_relaxed, + BuiltinProc_atomic_sub, + BuiltinProc_atomic_sub_acq, + BuiltinProc_atomic_sub_rel, + BuiltinProc_atomic_sub_acqrel, + BuiltinProc_atomic_sub_relaxed, + BuiltinProc_atomic_and, + BuiltinProc_atomic_and_acq, + BuiltinProc_atomic_and_rel, + BuiltinProc_atomic_and_acqrel, + BuiltinProc_atomic_and_relaxed, + BuiltinProc_atomic_nand, + BuiltinProc_atomic_nand_acq, + BuiltinProc_atomic_nand_rel, + BuiltinProc_atomic_nand_acqrel, + BuiltinProc_atomic_nand_relaxed, + BuiltinProc_atomic_or, + BuiltinProc_atomic_or_acq, + BuiltinProc_atomic_or_rel, + BuiltinProc_atomic_or_acqrel, + BuiltinProc_atomic_or_relaxed, + BuiltinProc_atomic_xor, + BuiltinProc_atomic_xor_acq, + BuiltinProc_atomic_xor_rel, + BuiltinProc_atomic_xor_acqrel, + BuiltinProc_atomic_xor_relaxed, + + BuiltinProc_atomic_xchg, + BuiltinProc_atomic_xchg_acq, + BuiltinProc_atomic_xchg_rel, + BuiltinProc_atomic_xchg_acqrel, + BuiltinProc_atomic_xchg_relaxed, + + BuiltinProc_atomic_cxchg, + BuiltinProc_atomic_cxchg_acq, + BuiltinProc_atomic_cxchg_rel, + BuiltinProc_atomic_cxchg_acqrel, + BuiltinProc_atomic_cxchg_relaxed, + BuiltinProc_atomic_cxchg_failrelaxed, + BuiltinProc_atomic_cxchg_failacq, + BuiltinProc_atomic_cxchg_acq_failrelaxed, + BuiltinProc_atomic_cxchg_acqrel_failrelaxed, + + BuiltinProc_atomic_cxchgweak, + BuiltinProc_atomic_cxchgweak_acq, + BuiltinProc_atomic_cxchgweak_rel, + BuiltinProc_atomic_cxchgweak_acqrel, + BuiltinProc_atomic_cxchgweak_relaxed, + BuiltinProc_atomic_cxchgweak_failrelaxed, + BuiltinProc_atomic_cxchgweak_failacq, + BuiltinProc_atomic_cxchgweak_acq_failrelaxed, + BuiltinProc_atomic_cxchgweak_acqrel_failrelaxed, + + + // Constant type tests +BuiltinProc__type_begin, + + BuiltinProc_type_base_type, + BuiltinProc_type_core_type, + BuiltinProc_type_elem_type, + + BuiltinProc_type_is_boolean, + BuiltinProc_type_is_integer, + BuiltinProc_type_is_rune, + BuiltinProc_type_is_float, + BuiltinProc_type_is_complex, + BuiltinProc_type_is_quaternion, + BuiltinProc_type_is_string, + BuiltinProc_type_is_typeid, + BuiltinProc_type_is_any, + + BuiltinProc_type_is_endian_little, + BuiltinProc_type_is_endian_big, + BuiltinProc_type_is_numeric, + BuiltinProc_type_is_ordered, + BuiltinProc_type_is_ordered_numeric, + BuiltinProc_type_is_indexable, + BuiltinProc_type_is_sliceable, + BuiltinProc_type_is_simple_compare, // easily compared using memcmp + BuiltinProc_type_is_dereferenceable, + BuiltinProc_type_is_valid_map_key, + + BuiltinProc_type_is_named, + BuiltinProc_type_is_pointer, + BuiltinProc_type_is_opaque, + BuiltinProc_type_is_array, + BuiltinProc_type_is_slice, + BuiltinProc_type_is_dynamic_array, + BuiltinProc_type_is_map, + BuiltinProc_type_is_struct, + BuiltinProc_type_is_union, + BuiltinProc_type_is_enum, + BuiltinProc_type_is_proc, + BuiltinProc_type_is_bit_field, + BuiltinProc_type_is_bit_field_value, + BuiltinProc_type_is_bit_set, + BuiltinProc_type_is_simd_vector, + + BuiltinProc_type_has_nil, + + BuiltinProc_type_proc_parameter_count, + BuiltinProc_type_proc_return_count, + +BuiltinProc__type_end, + + BuiltinProc_COUNT, +}; +gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_builtin}, + + {STR_LIT("len"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("cap"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT("size_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("align_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("offset_of"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("type_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("type_info_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("typeid_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {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}, + + {STR_LIT("min"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("max"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("abs"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("clamp"), 3, false, Expr_Expr, BuiltinProcPkg_builtin}, + + {STR_LIT(""), 0, true, Expr_Expr, BuiltinProcPkg_builtin}, // DIRECTIVE + + + // "Intrinsics" + {STR_LIT("vector"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, // Type + + {STR_LIT("atomic_fence"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_fence_acq"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_fence_rel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_fence_acqrel"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_rel"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_relaxed"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_store_unordered"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_acq"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_relaxed"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_load_unordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_add"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_add_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_sub_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_and_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_nand_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_or_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xor_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_xchg"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_acq"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_rel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_acqrel"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_xchg_relaxed"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_cxchg"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchg_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("atomic_cxchgweak"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_rel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acqrel"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_relaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_failacq"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acq_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("atomic_cxchgweak_acqrel_failrelaxed"), 3, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_base_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_core_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_elem_type"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_is_boolean"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_integer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_rune"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_float"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_complex"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_quaternion"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_string"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_typeid"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_any"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_is_endian_little"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_endian_big"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_numeric"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_ordered"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_ordered_numeric"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_indexable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_sliceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_simple_compare"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_dereferenceable"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_valid_map_key"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_opaque"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_slice"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_dynamic_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_map"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_struct"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_union"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_enum"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_proc"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_field"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_field_value"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_bit_set"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_is_simd_vector"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_has_nil"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("type_proc_return_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, +}; diff --git a/src/common.cpp b/src/common.cpp index 8085e895c..b034ad720 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -10,13 +10,11 @@ #define GB_IMPLEMENTATION #include "gb/gb.h" - #include <wchar.h> #include <stdio.h> #include <math.h> - template <typename U, typename V> gb_inline U bit_cast(V &v) { return reinterpret_cast<U &>(v); } @@ -145,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; @@ -331,7 +332,7 @@ void mul_overflow_u64(u64 x, u64 y, u64 *lo, u64 *hi) { #include "ptr_set.cpp" #include "string_set.cpp" #include "priority_queue.cpp" - +#include "thread_pool.cpp" gb_global String global_module_path = {0}; @@ -873,7 +874,6 @@ ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) { info.size = size; info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; array_add(fi, info); - } while (FindNextFileW(find_file, &file_data)); if (fi->count == 0) { diff --git a/src/entity.cpp b/src/entity.cpp index 3318eac24..bbea68e8b 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -48,7 +48,8 @@ enum EntityFlag { EntityFlag_NotExported = 1<<14, EntityFlag_Static = 1<<16, - // EntityFlag_Reference = 1<<17, + + EntityFlag_ImplicitReference = 1<<17, // NOTE(bill): equivalent to `const &` in C++ EntityFlag_CVarArg = 1<<20, EntityFlag_AutoCast = 1<<21, @@ -183,10 +184,11 @@ bool is_entity_exported(Entity *e, bool allow_builtin = false) { } String name = e->token.string; - if (name.len == 0) { - return false; + switch (name.len) { + case 0: return false; + case 1: return name[0] != '_'; } - return name[0] != '_'; + return true; } bool entity_has_deferred_procedure(Entity *e) { @@ -219,12 +221,13 @@ Entity *alloc_entity_variable(Scope *scope, Token token, Type *type, bool is_imm return entity; } -Entity *alloc_entity_using_variable(Entity *parent, Token token, Type *type) { +Entity *alloc_entity_using_variable(Entity *parent, Token token, Type *type, Ast *using_expr) { GB_ASSERT(parent != nullptr); token.pos = parent->token.pos; Entity *entity = alloc_entity(Entity_Variable, parent->scope, token, type); entity->using_parent = parent; entity->parent_proc_decl = parent->parent_proc_decl; + entity->using_expr = using_expr; entity->flags |= EntityFlag_Using; entity->flags |= EntityFlag_Used; entity->state = EntityState_Resolved; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index 428690291..42b22c8ef 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,9 +34,11 @@ 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? + ExactValue_Typeid, ExactValue_Count, }; @@ -37,8 +52,10 @@ struct ExactValue { f64 value_float; i64 value_pointer; Complex128 value_complex; + Quaternion256 value_quaternion; Ast * value_compound; Ast * value_procedure; + Type * value_typeid; }; }; @@ -53,25 +70,22 @@ 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: 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: return hash_pointer(v.value_procedure); + case ExactValue_Typeid: + return hash_pointer(v.value_typeid); } return hashing_proc(&v, gb_size_of(ExactValue)); @@ -122,6 +136,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; @@ -135,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); @@ -259,14 +289,16 @@ 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); + 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: @@ -324,11 +356,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) { @@ -337,6 +384,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; @@ -349,6 +398,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; @@ -367,6 +444,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) { @@ -395,6 +498,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; @@ -419,6 +523,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; } @@ -469,8 +580,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"); @@ -491,7 +604,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: @@ -505,6 +618,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; @@ -515,6 +631,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; } @@ -612,6 +739,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; @@ -647,6 +824,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); @@ -719,8 +900,61 @@ 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"); 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=36) { + return write_exact_value_to_string(gb_string_make(heap_allocator(), ""), v, string_limit); +} diff --git a/src/gb/gb.h b/src/gb/gb.h index adeb554b2..60303729f 100644 --- a/src/gb/gb.h +++ b/src/gb/gb.h @@ -918,7 +918,7 @@ GB_DEF void gb_lfence (void); #if defined(GB_SYSTEM_WINDOWS) -typedef struct gbSemaphore { void *win32_handle; } gbSemaphore; +typedef struct gbSemaphore { void *win32_handle;} gbSemaphore; #elif defined(GB_SYSTEM_OSX) typedef struct gbSemaphore { semaphore_t osx_handle; } gbSemaphore; #elif defined(GB_SYSTEM_UNIX) @@ -930,7 +930,7 @@ typedef struct gbSemaphore { sem_t unix_handle; } gbSemaphore; GB_DEF void gb_semaphore_init (gbSemaphore *s); GB_DEF void gb_semaphore_destroy(gbSemaphore *s); GB_DEF void gb_semaphore_post (gbSemaphore *s, i32 count); -GB_DEF void gb_semaphore_release(gbSemaphore *s); // NOTE(bill): gb_semaphore_post(s, 1) +GB_DEF void gb_semaphore_release(gbSemaphore *s); GB_DEF void gb_semaphore_wait (gbSemaphore *s); @@ -975,10 +975,10 @@ typedef struct gbThread { pthread_t posix_handle; #endif - gbThreadProc *proc; - void * user_data; - isize user_index; - isize return_value; + gbThreadProc * proc; + void * user_data; + isize user_index; + isize volatile return_value; gbSemaphore semaphore; isize stack_size; @@ -4588,10 +4588,18 @@ gb_inline void gb_lfence(void) { gb_inline void gb_semaphore_release(gbSemaphore *s) { gb_semaphore_post(s, 1); } #if defined(GB_SYSTEM_WINDOWS) - gb_inline void gb_semaphore_init (gbSemaphore *s) { s->win32_handle = CreateSemaphoreA(NULL, 0, I32_MAX, NULL); } - gb_inline void gb_semaphore_destroy(gbSemaphore *s) { CloseHandle(s->win32_handle); } - gb_inline void gb_semaphore_post (gbSemaphore *s, i32 count) { ReleaseSemaphore(s->win32_handle, count, NULL); } - gb_inline void gb_semaphore_wait (gbSemaphore *s) { WaitForSingleObject(s->win32_handle, INFINITE); } + gb_inline void gb_semaphore_init(gbSemaphore *s) { + s->win32_handle = CreateSemaphoreA(NULL, 0, I32_MAX, NULL); + } + gb_inline void gb_semaphore_destroy(gbSemaphore *s) { + CloseHandle(s->win32_handle); + } + gb_inline void gb_semaphore_post(gbSemaphore *s, i32 count) { + ReleaseSemaphore(s->win32_handle, count, NULL); + } + gb_inline void gb_semaphore_wait(gbSemaphore *s) { + WaitForSingleObjectEx(s->win32_handle, INFINITE, FALSE); + } #elif defined(GB_SYSTEM_OSX) gb_inline void gb_semaphore_init (gbSemaphore *s) { semaphore_create(mach_task_self(), &s->osx_handle, SYNC_POLICY_FIFO, 0); } @@ -8975,7 +8983,7 @@ gb_inline void gb_exit(u32 code) { exit(code); } gb_inline void gb_yield(void) { #if defined(GB_SYSTEM_WINDOWS) - Sleep(0); + YieldProcessor(); #else sched_yield(); #endif diff --git a/src/ir.cpp b/src/ir.cpp index 82f9fa755..e7317a960 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -147,6 +147,7 @@ struct irProcedure { Array<irContextData> context_stack; + i32 parameter_count; irValue *return_ptr_hint_value; Ast * return_ptr_hint_ast; @@ -174,6 +175,7 @@ gbAllocator ir_allocator(void) { #define IR_TYPE_INFO_NAMES_NAME "__$type_info_names_data" #define IR_TYPE_INFO_OFFSETS_NAME "__$type_info_offsets_data" #define IR_TYPE_INFO_USINGS_NAME "__$type_info_usings_data" +#define IR_TYPE_INFO_TAGS_NAME "__$type_info_tags_data" #define IR_INSTR_KINDS \ @@ -424,6 +426,7 @@ enum irParamPasskind { irParamPass_Integer, // Pass as an integer of the same size irParamPass_ConstRef, // Pass as a pointer but the value is immutable irParamPass_BitCast, // Pass by value and bit cast to the correct type + irParamPass_Tuple, // Pass across multiple parameters (System V AMD64, up to 2) }; struct irValueParam { @@ -432,6 +435,7 @@ struct irValueParam { Entity * entity; Type * type; Type * original_type; + i32 index; Array<irValue *> referrers; }; @@ -913,15 +917,18 @@ irValue *ir_value_global(Entity *e, irValue *value) { if (value) value->uses += 1; return v; } -irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type) { +irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type, i32 index) { irValue *v = ir_alloc_value(irValue_Param); v->Param.kind = irParamPass_Value; v->Param.parent = parent; - v->Param.entity = e; - v->Param.original_type = e->type; + if (e != nullptr) { + v->Param.entity = e; + v->Param.original_type = e->type; + } v->Param.type = abi_type; + v->Param.index = index; - if (abi_type != e->type) { + if (e != nullptr && abi_type != e->type) { if (is_type_pointer(abi_type)) { GB_ASSERT(e->kind == Entity_Variable); v->Param.kind = irParamPass_Pointer; @@ -934,8 +941,12 @@ irValue *ir_value_param(irProcedure *parent, Entity *e, Type *abi_type) { v->Param.kind = irParamPass_Value; } else if (is_type_simd_vector(abi_type)) { v->Param.kind = irParamPass_BitCast; + } else if (is_type_float(abi_type)) { + v->Param.kind = irParamPass_BitCast; + } else if (is_type_tuple(abi_type)) { + v->Param.kind = irParamPass_Tuple; } else { - GB_PANIC("Invalid abi type pass kind"); + GB_PANIC("Invalid abi type pass kind %s", type_to_string(abi_type)); } } array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here @@ -1428,7 +1439,7 @@ irValue *ir_value_procedure(irModule *m, Entity *entity, Type *type, Ast *type_e Type *t = base_type(type); GB_ASSERT(is_type_proc(t)); - array_init(&v->Proc.params, ir_allocator(), 0, t->Proc.param_count); + array_init(&v->Proc.params, heap_allocator(), 0, t->Proc.param_count); return v; } @@ -1498,7 +1509,9 @@ void ir_start_block(irProcedure *proc, irBlock *block) { } - +irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t); +irValue *ir_address_from_load_or_generate_local(irProcedure *proc, irValue *val); +irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index); @@ -1539,6 +1552,7 @@ irValue *ir_add_module_constant(irModule *m, Type *type, ExactValue value) { if (count == 0) { return ir_value_nil(type); } + count = gb_max(cl->max_count, count); Type *elem = base_type(type)->Slice.elem; Type *t = alloc_type_array(elem, count); irValue *backing_array = ir_add_module_constant(m, t, value); @@ -1712,9 +1726,12 @@ irValue *ir_add_global_generated(irModule *m, Type *type, irValue *value) { irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i32 index) { - irValue *v = ir_value_param(proc, e, abi_type); + irValue *v = ir_value_param(proc, e, abi_type, index); + array_add(&proc->params, v); irValueParam *p = &v->Param; + irValue *res = nullptr; + ir_push_debug_location(proc->module, e ? e->identifier : nullptr, proc->debug_scope, e); defer (ir_pop_debug_location(proc->module)); @@ -1749,6 +1766,24 @@ irValue *ir_add_param(irProcedure *proc, Entity *e, Ast *expr, Type *abi_type, i ir_emit_store(proc, l, x); return x; } + case irParamPass_Tuple: { + irValue *l = ir_add_local(proc, e, expr, true, index); + Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); + irValue *ptr = ir_emit_bitcast(proc, l, alloc_type_pointer(st)); + if (abi_type->Tuple.variables.count > 0) { + array_pop(&proc->params); + } + for_array(i, abi_type->Tuple.variables) { + Type *t = abi_type->Tuple.variables[i]->type; + + irValue *elem = ir_value_param(proc, nullptr, t, index+cast(i32)i); + array_add(&proc->params, elem); + + irValue *dst = ir_emit_struct_ep(proc, ptr, cast(i32)i); + ir_emit_store(proc, dst, elem); + } + return ir_emit_load(proc, l); + } } @@ -1872,10 +1907,12 @@ irDebugEncoding ir_debug_encoding_for_basic(BasicKind kind) { case Basic_string: case Basic_any: case Basic_rawptr: + case Basic_quaternion128: + case Basic_quaternion256: break; // not a "DIBasicType" } - GB_PANIC("Unreachable"); + GB_PANIC("Unreachable %d", kind); return irDebugBasicEncoding_Invalid; } @@ -2221,27 +2258,11 @@ irDebugInfo *ir_add_debug_info_type_complex(irModule *module, Type *type) { di->CompositeType.tag = irDebugBasicEncoding_structure_type; di->CompositeType.size = ir_debug_size_bits(type); - Type *field_type = nullptr; - if (type->Basic.kind == Basic_complex64) { - field_type = t_f32; - } else if (type->Basic.kind == Basic_complex128) { - field_type = t_f64; - } else { - GB_PANIC("Unreachable"); - } + Type *field_type = base_complex_elem_type(type); - // Field "real" - irDebugInfo *real_di = ir_add_debug_info_field_internal(module, str_lit("real"), field_type, - 0, - nullptr, - di); + irDebugInfo *real_di = ir_add_debug_info_field_internal(module, str_lit("real"), field_type, 0*cast(i32)type_size_of(field_type), nullptr, di); + irDebugInfo *imag_di = ir_add_debug_info_field_internal(module, str_lit("imag"), field_type, 1*cast(i32)type_size_of(field_type), nullptr, di); map_set(&module->debug_info, hash_pointer(real_di), real_di); - - // Field "imag" - irDebugInfo *imag_di = ir_add_debug_info_field_internal(module, str_lit("imag"), field_type, - real_di->DerivedType.size, - nullptr, - di); map_set(&module->debug_info, hash_pointer(imag_di), imag_di); irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 2); @@ -2253,6 +2274,40 @@ irDebugInfo *ir_add_debug_info_type_complex(irModule *module, Type *type) { return di; } +irDebugInfo *ir_add_debug_info_type_quaternion(irModule *module, Type *type) { + GB_ASSERT(type->kind == Type_Basic && is_type_quaternion(type)); + + irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompositeType); + map_set(&module->debug_info, hash_type(type), di); + + di->CompositeType.name = type->Basic.name; + di->CompositeType.tag = irDebugBasicEncoding_structure_type; + di->CompositeType.size = ir_debug_size_bits(type); + + Type *field_type = base_complex_elem_type(type); + + // @QuaternionLayout + irDebugInfo *imag_di = ir_add_debug_info_field_internal(module, str_lit("imag"), field_type, 0*cast(i32)type_size_of(field_type), nullptr, di); + irDebugInfo *jmag_di = ir_add_debug_info_field_internal(module, str_lit("jmag"), field_type, 1*cast(i32)type_size_of(field_type), nullptr, di); + irDebugInfo *kmag_di = ir_add_debug_info_field_internal(module, str_lit("kmag"), field_type, 2*cast(i32)type_size_of(field_type), nullptr, di); + irDebugInfo *real_di = ir_add_debug_info_field_internal(module, str_lit("real"), field_type, 3*cast(i32)type_size_of(field_type), nullptr, di); + + map_set(&module->debug_info, hash_pointer(imag_di), imag_di); + map_set(&module->debug_info, hash_pointer(jmag_di), jmag_di); + map_set(&module->debug_info, hash_pointer(kmag_di), kmag_di); + map_set(&module->debug_info, hash_pointer(real_di), real_di); + + irDebugInfo *elements_di = ir_add_debug_info_array(module, 0, 4); + array_add(&elements_di->DebugInfoArray.elements, imag_di); + array_add(&elements_di->DebugInfoArray.elements, jmag_di); + array_add(&elements_di->DebugInfoArray.elements, kmag_di); + array_add(&elements_di->DebugInfoArray.elements, real_di); + di->CompositeType.elements = elements_di; + map_set(&module->debug_info, hash_pointer(elements_di), elements_di); + + return di; +} + irDebugInfo *ir_add_debug_info_proc_type(irModule *module, Type *type) { GB_ASSERT(type->kind == Type_Proc); @@ -2377,10 +2432,14 @@ irDebugInfo *ir_add_debug_info_type(irModule *module, Type *type, Entity *e, irD if (type->kind == Type_Basic) { switch (type->Basic.kind) { // Composite basic types - case Basic_complex64: - case Basic_complex128: return ir_add_debug_info_type_complex(module, type); - case Basic_string: return ir_add_debug_info_type_string(module, scope, e, type); - case Basic_any: return ir_add_debug_info_type_any(module); + case Basic_complex64: case Basic_complex128: + return ir_add_debug_info_type_complex(module, type); + case Basic_quaternion128: case Basic_quaternion256: + return ir_add_debug_info_type_quaternion(module, type); + case Basic_string: + return ir_add_debug_info_type_string(module, scope, e, type); + case Basic_any: + return ir_add_debug_info_type_any(module); // Derived basic types case Basic_cstring: @@ -2920,10 +2979,6 @@ void ir_emit_unreachable(irProcedure *proc) { ir_emit(proc, ir_instr_unreachable(proc)); } -irValue *ir_emit_transmute(irProcedure *proc, irValue *value, Type *t); -irValue *ir_address_from_load_or_generate_local(irProcedure *proc, irValue *val); -irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index); - irValue *ir_get_package_value(irModule *m, String package_name, String entity_name) { AstPackage *rt_pkg = get_core_package(m->info, package_name); @@ -2973,7 +3028,7 @@ Array<irValue *> ir_value_to_array(irProcedure *p, irValue *value) { } -irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false) { +irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> const &args, ProcInlining inlining = ProcInlining_none, bool use_return_ptr_hint = false) { Type *pt = base_type(ir_type(value)); GB_ASSERT(pt->kind == Type_Proc); Type *results = pt->Proc.results; @@ -2983,6 +3038,8 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro context_ptr = ir_find_or_generate_context_ptr(p); } + set_procedure_abi_types(heap_allocator(), pt); + bool is_c_vararg = pt->Proc.c_vararg; isize param_count = pt->Proc.param_count; if (is_c_vararg) { @@ -2991,9 +3048,13 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro } else { GB_ASSERT_MSG(param_count == args.count, "%td == %td", param_count, args.count); } + + auto processed_args = array_make<irValue *>(heap_allocator(), 0, args.count); + for (isize i = 0; i < param_count; i++) { Entity *e = pt->Proc.params->Tuple.variables[i]; if (e->kind != Entity_Variable) { + array_add(&processed_args, args[i]); continue; } GB_ASSERT(e->flags & EntityFlag_Param); @@ -3003,21 +3064,32 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro Type *arg_type = ir_type(args[i]); if (are_types_identical(arg_type, new_type)) { // NOTE(bill): Done + array_add(&processed_args, args[i]); } else if (!are_types_identical(original_type, new_type)) { - if (is_type_pointer(new_type) && !is_type_pointer(original_type)) { - if (e->flags&EntityFlag_Value) { - args[i] = ir_address_from_load_or_generate_local(p, args[i]); + if (e->flags&EntityFlag_ImplicitReference) { + array_add(&processed_args, ir_address_from_load_or_generate_local(p, args[i])); } else if (!is_type_pointer(arg_type)) { - args[i] = ir_copy_value_to_ptr(p, args[i], original_type, 16); + array_add(&processed_args, ir_copy_value_to_ptr(p, args[i], original_type, 16)); } } else if (is_type_integer(new_type)) { - args[i] = ir_emit_transmute(p, args[i], new_type); + array_add(&processed_args, ir_emit_transmute(p, args[i], new_type)); } else if (new_type == t_llvm_bool) { - args[i] = ir_emit_conv(p, args[i], new_type); + array_add(&processed_args, ir_emit_conv(p, args[i], new_type)); } else if (is_type_simd_vector(new_type)) { - args[i] = ir_emit_bitcast(p, args[i], new_type); + array_add(&processed_args, ir_emit_bitcast(p, args[i], new_type)); + } else if (is_type_tuple(new_type)) { + Type *abi_type = pt->Proc.abi_compat_params[i]; + Type *st = struct_type_from_systemv_distribute_struct_fields(abi_type); + irValue *x = ir_emit_transmute(p, args[i], st); + for (isize j = 0; j < new_type->Tuple.variables.count; j++) { + irValue *xx = ir_emit_struct_ev(p, x, cast(i32)j); + array_add(&processed_args, xx); + } } + } else { + irValue *x = ir_emit_conv(p, args[i], new_type); + array_add(&processed_args, x); } } @@ -3042,10 +3114,10 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro return_ptr = ir_add_local_generated(p, rt, true); } GB_ASSERT(is_type_pointer(ir_type(return_ptr))); - ir_emit(p, ir_instr_call(p, value, return_ptr, args, nullptr, context_ptr, inlining)); + ir_emit(p, ir_instr_call(p, value, return_ptr, processed_args, nullptr, context_ptr, inlining)); result = ir_emit_load(p, return_ptr); } else { - result = ir_emit(p, ir_instr_call(p, value, nullptr, args, abi_rt, context_ptr, inlining)); + result = ir_emit(p, ir_instr_call(p, value, nullptr, processed_args, abi_rt, context_ptr, inlining)); if (abi_rt != results) { result = ir_emit_transmute(p, result, rt); } @@ -3054,7 +3126,7 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro if (value->kind == irValue_Proc) { irProcedure *the_proc = &value->Proc; Entity *e = the_proc->entity; - if (entity_has_deferred_procedure(e)) { + if (e != nullptr && entity_has_deferred_procedure(e)) { DeferredProcedureKind kind = e->Procedure.deferred_procedure.kind; Entity *deferred_entity = e->Procedure.deferred_procedure.entity; irValue **deferred_found = map_get(&p->module->values, hash_entity(deferred_entity)); @@ -3653,12 +3725,12 @@ struct irLoopData { irBlock *loop; }; -irLoopData ir_loop_start(irProcedure *proc, isize count) { +irLoopData ir_loop_start(irProcedure *proc, isize count, Type *index_type=t_int) { irLoopData data = {}; irValue *max = ir_const_int(count); - data.idx_addr = ir_add_local_generated(proc, t_int, true); + data.idx_addr = ir_add_local_generated(proc, index_type, true); data.body = ir_new_block(proc, nullptr, "loop.body"); data.done = ir_new_block(proc, nullptr, "loop.done"); @@ -3728,7 +3800,7 @@ irValue *ir_emit_unary_arith(irProcedure *proc, TokenKind op, irValue *x, Type * ir_emit_store(proc, ir_emit_array_epi(proc, res, i), z); } } else { - auto loop_data = ir_loop_start(proc, count); + auto loop_data = ir_loop_start(proc, count, t_i32); irValue *e = ir_emit_load(proc, ir_emit_array_ep(proc, val, loop_data.idx)); irValue *z = ir_emit_unary_arith(proc, op, e, elem_type); @@ -3787,7 +3859,7 @@ irValue *ir_emit_arith(irProcedure *proc, TokenKind op, irValue *left, irValue * ir_emit_store(proc, ir_emit_array_epi(proc, res, i), z); } } else { - auto loop_data = ir_loop_start(proc, count); + auto loop_data = ir_loop_start(proc, count, t_i32); irValue *x = ir_emit_load(proc, ir_emit_array_ep(proc, lhs, loop_data.idx)); irValue *y = ir_emit_load(proc, ir_emit_array_ep(proc, rhs, loop_data.idx)); @@ -3853,6 +3925,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<irValue *>(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<irValue *>(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) { @@ -4018,6 +4144,15 @@ irValue *ir_emit_comp_against_nil(irProcedure *proc, TokenKind op_kind, irValue } else if (is_type_typeid(t)) { irValue *invalid_typeid = ir_value_constant(t_typeid, exact_value_i64(0)); return ir_emit_comp(proc, op_kind, x, invalid_typeid); + } else if (is_type_bit_field(t)) { + auto args = array_make<irValue *>(heap_allocator(), 2); + irValue *lhs = ir_address_from_load_or_generate_local(proc, x); + args[0] = ir_emit_conv(proc, lhs, t_rawptr); + args[1] = ir_const_int(type_size_of(t)); + irValue *val = ir_emit_runtime_call(proc, "memory_compare_zero", args); + irValue *res = ir_emit_comp(proc, op_kind, val, v_zero); + return ir_emit_conv(proc, res, t_bool); + } return nullptr; } @@ -4105,6 +4240,9 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal if (op_kind == Token_NotEq) { res = v_false; cmp_op = Token_Or; + } else if (op_kind == Token_CmpEq) { + res = v_true; + cmp_op = Token_And; } bool inline_array_arith = type_size_of(tl) <= build_context.max_align; @@ -4112,28 +4250,43 @@ irValue *ir_emit_comp(irProcedure *proc, TokenKind op_kind, irValue *left, irVal if (inline_array_arith) { // inline + irValue *val = ir_add_local_generated(proc, t_bool, false); + ir_emit_store(proc, val, res); for (i32 i = 0; i < count; i++) { irValue *x = ir_emit_load(proc, ir_emit_array_epi(proc, lhs, i)); irValue *y = ir_emit_load(proc, ir_emit_array_epi(proc, rhs, i)); irValue *cmp = ir_emit_comp(proc, op_kind, x, y); - res = ir_emit_arith(proc, cmp_op, res, cmp, t_bool); - } - - return ir_emit_conv(proc, res, t_bool); - } else { - irValue *val = ir_add_local_generated(proc, t_bool, false); - ir_emit_store(proc, val, res); - auto loop_data = ir_loop_start(proc, count); - { - irValue *x = ir_emit_load(proc, ir_emit_array_ep(proc, lhs, loop_data.idx)); - irValue *y = ir_emit_load(proc, ir_emit_array_ep(proc, rhs, loop_data.idx)); - irValue *cmp = ir_emit_comp(proc, op_kind, x, y); - irValue *new_res = ir_emit_arith(proc, cmp_op, res, cmp, t_bool); + irValue *new_res = ir_emit_arith(proc, cmp_op, ir_emit_load(proc, val), cmp, t_bool); ir_emit_store(proc, val, ir_emit_conv(proc, new_res, t_bool)); } - ir_loop_end(proc, loop_data); return ir_emit_load(proc, val); + } else { + if (is_type_simple_compare(tl) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) { + // TODO(bill): Test to see if this is actually faster!!!! + auto args = array_make<irValue *>(heap_allocator(), 3); + args[0] = ir_emit_conv(proc, lhs, t_rawptr); + args[1] = ir_emit_conv(proc, rhs, t_rawptr); + args[2] = ir_const_int(type_size_of(tl)); + irValue *val = ir_emit_runtime_call(proc, "memory_compare", args); + irValue *res = ir_emit_comp(proc, op_kind, val, v_zero); + return ir_emit_conv(proc, res, t_bool); + } else { + irValue *val = ir_add_local_generated(proc, t_bool, false); + ir_emit_store(proc, val, res); + auto loop_data = ir_loop_start(proc, count, t_i32); + { + irValue *i = loop_data.idx; + irValue *x = ir_emit_load(proc, ir_emit_array_ep(proc, lhs, i)); + irValue *y = ir_emit_load(proc, ir_emit_array_ep(proc, rhs, i)); + irValue *cmp = ir_emit_comp(proc, op_kind, x, y); + irValue *new_res = ir_emit_arith(proc, cmp_op, ir_emit_load(proc, val), cmp, t_bool); + ir_emit_store(proc, val, ir_emit_conv(proc, new_res, t_bool)); + } + ir_loop_end(proc, loop_data); + + return ir_emit_load(proc, val); + } } } @@ -4161,7 +4314,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: @@ -4185,6 +4338,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<irValue *>(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: @@ -4268,11 +4446,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) { @@ -4343,6 +4528,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: @@ -4401,13 +4597,14 @@ irValue *ir_emit_deep_field_gep(irProcedure *proc, irValue *e, Selection sel) { if (is_type_pointer(type)) { type = type_deref(type); e = ir_emit_load(proc, e); - e = ir_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? + // e = ir_emit_ptr_offset(proc, e, v_zero); // TODO(bill): Do I need these copies? } type = core_type(type); if (is_type_raw_union(type)) { type = type->Struct.fields[index]->type; - e = ir_emit_conv(proc, e, alloc_type_pointer(type)); + GB_ASSERT(is_type_pointer(ir_type(e))); + e = ir_emit_bitcast(proc, e, alloc_type_pointer(type)); } else if (is_type_struct(type)) { type = type->Struct.fields[index]->type; e = ir_emit_struct_ep(proc, e, index); @@ -4725,6 +4922,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); @@ -4848,6 +5047,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; @@ -5245,12 +5487,14 @@ gb_global irValue *ir_global_type_info_member_types = nullptr; gb_global irValue *ir_global_type_info_member_names = nullptr; gb_global irValue *ir_global_type_info_member_offsets = nullptr; gb_global irValue *ir_global_type_info_member_usings = nullptr; +gb_global irValue *ir_global_type_info_member_tags = nullptr; gb_global i32 ir_global_type_info_data_index = 0; gb_global i32 ir_global_type_info_member_types_index = 0; gb_global i32 ir_global_type_info_member_names_index = 0; gb_global i32 ir_global_type_info_member_offsets_index = 0; gb_global i32 ir_global_type_info_member_usings_index = 0; +gb_global i32 ir_global_type_info_member_tags_index = 0; isize ir_type_info_count(CheckerInfo *info) { return info->minimum_dependency_type_info_set.entries.count+1; @@ -5286,6 +5530,7 @@ enum Typeid_Kind : u8 { Typeid_Rune, Typeid_Float, Typeid_Complex, + Typeid_Quaternion, Typeid_String, Typeid_Boolean, Typeid_Any, @@ -5402,7 +5647,8 @@ irValue *ir_emit_logical_binary_expr(irProcedure *proc, TokenKind op, Ast *left, } ir_start_block(proc, rhs); - array_add(&edges, ir_build_expr(proc, right)); + irValue *edge = ir_build_expr(proc, right); + array_add(&edges, edge); ir_emit_jump(proc, done); ir_start_block(proc, done); @@ -5411,8 +5657,6 @@ irValue *ir_emit_logical_binary_expr(irProcedure *proc, TokenKind op, Ast *left, irValue *ir_emit_logical_binary_expr(irProcedure *proc, Ast *expr) { ast_node(be, BinaryExpr, expr); - irBlock *rhs = ir_new_block(proc, nullptr, "logical.cmp.rhs"); - irBlock *done = ir_new_block(proc, nullptr, "logical.cmp.done"); Type *type = type_of_expr(expr); type = default_type(type); @@ -6038,17 +6282,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: { @@ -6063,6 +6367,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); } @@ -6133,7 +6451,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<irValue *>(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<irValue *>(ir_allocator(), 1); args[0] = x; @@ -6312,6 +6639,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); } @@ -6546,9 +6878,6 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case_end; case_ast_node(be, BinaryExpr, expr); - irValue *left = ir_build_expr(proc, be->left); - Type *type = default_type(tv.type); - switch (be->op.kind) { case Token_Add: case Token_Sub: @@ -6562,6 +6891,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_AndNot: case Token_Shl: case Token_Shr: { + irValue *left = ir_build_expr(proc, be->left); + Type *type = default_type(tv.type); irValue *right = ir_build_expr(proc, be->right); return ir_emit_arith(proc, be->op.kind, left, right, type); } @@ -6573,10 +6904,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_LtEq: case Token_Gt: case Token_GtEq: { + irValue *left = ir_build_expr(proc, be->left); + Type *type = default_type(tv.type); irValue *right = ir_build_expr(proc, be->right); irValue *cmp = ir_emit_comp(proc, be->op.kind, left, right); return ir_emit_conv(proc, cmp, type); - break; } case Token_CmpAnd: @@ -6586,6 +6918,8 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case Token_in: case Token_notin: { + irValue *left = ir_build_expr(proc, be->left); + Type *type = default_type(tv.type); irValue *right = ir_build_expr(proc, be->right); Type *rt = base_type(ir_type(right)); switch (rt->kind) { @@ -6687,7 +7021,34 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { } // NOTE(bill): Regular call - irValue *value = ir_build_expr(proc, ce->proc); + irValue *value = nullptr; + Ast *proc_expr = unparen_expr(ce->proc); + if (proc_expr->tav.mode == Addressing_Constant) { + ExactValue v = proc_expr->tav.value; + switch (v.kind) { + case ExactValue_Integer: + { + u64 u = big_int_to_u64(&v.value_integer); + irValue *x = ir_const_uintptr(u); + x = ir_emit_conv(proc, x, t_rawptr); + value = ir_emit_conv(proc, x, proc_expr->tav.type); + break; + } + case ExactValue_Pointer: + { + u64 u = cast(u64)v.value_pointer; + irValue *x = ir_const_uintptr(u); + x = ir_emit_conv(proc, x, t_rawptr); + value = ir_emit_conv(proc, x, proc_expr->tav.type); + break; + } + } + } + + if (value == nullptr) { + value = ir_build_expr(proc, proc_expr); + } + GB_ASSERT(value != nullptr); Type *proc_type_ = base_type(ir_type(value)); GB_ASSERT(proc_type_->kind == Type_Proc); @@ -7501,13 +7862,55 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { // NOTE(bill): Separate value, gep, store into their own chunks for_array(i, cl->elems) { Ast *elem = cl->elems[i]; - if (ir_is_elem_const(proc->module, elem, et)) { - continue; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + if (ir_is_elem_const(proc->module, fv->value, et)) { + 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; + } + + 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)) { + continue; + } + irCompoundLitElemTempData data = {}; + data.expr = elem; + data.elem_index = cast(i32)i; + array_add(&temp_data, data); } - irCompoundLitElemTempData data = {}; - data.expr = elem; - data.elem_index = cast(i32)i; - array_add(&temp_data, data); } for_array(i, temp_data) { @@ -7522,12 +7925,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; 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); @@ -7560,18 +7966,64 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { for_array(i, cl->elems) { Ast *elem = cl->elems[i]; - if (ir_is_elem_const(proc->module, elem, et)) { - continue; - } - irValue *field_expr = ir_build_expr(proc, elem); - Type *t = ir_type(field_expr); - GB_ASSERT(t->kind != Type_Tuple); - irValue *ev = ir_emit_conv(proc, field_expr, et); + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + + if (ir_is_elem_const(proc->module, fv->value, et)) { + 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; + } + + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); + + for (i64 k = lo; k < hi; k++) { + irCompoundLitElemTempData data = {}; + data.value = value; + data.elem_index = cast(i32)k; + array_add(&temp_data, data); + } + + } 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)i; - 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; + } + irValue *field_expr = ir_build_expr(proc, elem); + 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)i; + array_add(&temp_data, data); + } } for_array(i, temp_data) { @@ -7592,28 +8044,64 @@ irAddr ir_build_addr(irProcedure *proc, Ast *expr) { if (cl->elems.count == 0) { break; } - Type *elem = bt->DynamicArray.elem; + Type *et = bt->DynamicArray.elem; gbAllocator a = ir_allocator(); - irValue *size = ir_const_int(type_size_of(elem)); - irValue *align = ir_const_int(type_align_of(elem)); + irValue *size = ir_const_int(type_size_of(et)); + irValue *align = ir_const_int(type_align_of(et)); + + i64 item_count = gb_max(cl->max_count, cl->elems.count); { + auto args = array_make<irValue *>(a, 5); args[0] = ir_emit_conv(proc, v, t_rawptr); args[1] = size; args[2] = align; - args[3] = ir_const_int(2*cl->elems.count); + args[3] = ir_const_int(2*item_count); // TODO(bill): Is this too much waste? args[4] = ir_emit_source_code_location(proc, proc_name, pos); ir_emit_runtime_call(proc, "__dynamic_array_reserve", args); } - i64 item_count = cl->elems.count; - irValue *items = ir_generate_array(proc->module, elem, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr); + irValue *items = ir_generate_array(proc->module, et, item_count, str_lit("dacl$"), cast(i64)cast(intptr)expr); + + for_array(i, cl->elems) { + Ast *elem = cl->elems[i]; + if (elem->kind == Ast_FieldValue) { + ast_node(fv, FieldValue, elem); + 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; + } - for_array(field_index, cl->elems) { - Ast *f = cl->elems[field_index]; - irValue *value = ir_emit_conv(proc, ir_build_expr(proc, f), elem); - irValue *ep = ir_emit_array_epi(proc, items, cast(i32)field_index); - ir_emit_store(proc, ep, value); + irValue *value = ir_emit_conv(proc, ir_build_expr(proc, fv->value), et); + + 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); + ir_emit_store(proc, ep, value); + } } { @@ -7907,24 +8395,27 @@ void ir_build_constant_value_decl(irProcedure *proc, AstValueDecl *vd) { name = e->Procedure.link_name; } + HashKey key = hash_string(name); + irValue **prev_value = map_get(&proc->module->members, key); + if (prev_value != nullptr) { + // NOTE(bill): Don't do mutliple declarations in the IR + return; + } + + irValue *value = ir_value_procedure(proc->module, e, e->type, pl->type, pl->body, name); value->Proc.tags = pl->tags; value->Proc.inlining = pl->inlining; - ir_module_add_value(proc->module, e, value); - ir_build_proc(value, proc); - if (value->Proc.is_foreign || value->Proc.is_export) { - HashKey key = hash_string(name); - irValue **prev_value = map_get(&proc->module->members, key); - if (prev_value == nullptr) { - // NOTE(bill): Don't do mutliple declarations in the IR - map_set(&proc->module->members, key, value); - } + map_set(&proc->module->members, key, value); } else { array_add(&proc->children, &value->Proc); } + + ir_module_add_value(proc->module, e, value); + ir_build_proc(value, proc); } } } @@ -8806,6 +9297,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) { @@ -9084,6 +9719,7 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) { //////////////////////////////////////////////////////////////// void ir_number_proc_registers(irProcedure *proc) { + // i32 reg_index = proc->parameter_count; i32 reg_index = 0; for_array(i, proc->blocks) { irBlock *b = proc->blocks[i]; @@ -9139,13 +9775,15 @@ void ir_begin_procedure_body(irProcedure *proc) { proc->entry_block = ir_new_block(proc, proc->type_expr, "entry"); ir_start_block(proc, proc->entry_block); + i32 parameter_index = 0; + if (proc->type->Proc.return_by_pointer) { // NOTE(bill): this must be the first parameter stored Type *ptr_type = alloc_type_pointer(reduce_tuple_to_single_type(proc->type->Proc.results)); Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("agg.result")), ptr_type, false, false); e->flags |= EntityFlag_Sret | EntityFlag_NoAlias; - irValue *param = ir_value_param(proc, e, ptr_type); + irValue *param = ir_value_param(proc, e, ptr_type, -1); param->Param.kind = irParamPass_Pointer; ir_module_add_value(proc->module, e, param); @@ -9174,13 +9812,19 @@ void ir_begin_procedure_body(irProcedure *proc) { Entity *e = params->variables[i]; if (e->kind != Entity_Variable) { + parameter_index += 1; continue; } Type *abi_type = proc->type->Proc.abi_compat_params[i]; - if (e->token.string != "" && !is_blank_ident(e->token)) { - irValue *param = ir_add_param(proc, e, name, abi_type, cast(i32)(i+1)); - array_add(&proc->params, param); + if (e->token.string != "") { + ir_add_param(proc, e, name, abi_type, parameter_index); + } + + if (is_type_tuple(abi_type)) { + parameter_index += cast(i32)abi_type->Tuple.variables.count; + } else { + parameter_index += 1; } } } else { @@ -9189,15 +9833,20 @@ void ir_begin_procedure_body(irProcedure *proc) { for_array(i, params->variables) { Entity *e = params->variables[i]; if (e->kind != Entity_Variable) { + parameter_index += 1; continue; } Type *abi_type = e->type; if (abi_types.count > 0) { abi_type = abi_types[i]; } - if (e->token.string != "" && !is_blank_ident(e->token)) { - irValue *param = ir_add_param(proc, e, nullptr, abi_type, cast(i32)(i+1)); - array_add(&proc->params, param); + if (e->token.string != "") { + ir_add_param(proc, e, nullptr, abi_type, parameter_index); + } + if (is_type_tuple(abi_type)) { + parameter_index += cast(i32)abi_type->Tuple.variables.count; + } else { + parameter_index += 1; } } } @@ -9239,11 +9888,13 @@ void ir_begin_procedure_body(irProcedure *proc) { if (proc->type->Proc.calling_convention == ProcCC_Odin) { Entity *e = alloc_entity_param(nullptr, make_token_ident(str_lit("__.context_ptr")), t_context_ptr, false, false); e->flags |= EntityFlag_NoAlias; - irValue *param = ir_value_param(proc, e, e->type); + irValue *param = ir_value_param(proc, e, e->type, -1); ir_module_add_value(proc->module, e, param); irContextData ctx = {param, proc->scope_index}; array_add(&proc->context_stack, ctx); } + + proc->parameter_count = parameter_index; } @@ -9370,6 +10021,8 @@ void ir_insert_code_before_proc(irProcedure* proc, irProcedure *parent) { void ir_build_proc(irValue *value, irProcedure *parent) { irProcedure *proc = &value->Proc; + set_procedure_abi_types(heap_allocator(), proc->type); + proc->parent = parent; if (proc->body != nullptr) { @@ -9561,13 +10214,27 @@ void ir_init_module(irModule *m, Checker *c) { map_set(&m->members, hash_string(name), g); ir_global_type_info_member_usings = g; } + + { + String name = str_lit(IR_TYPE_INFO_TAGS_NAME); + Entity *e = alloc_entity_variable(nullptr, make_token_ident(name), + alloc_type_array(t_string, count), false); + irValue *g = ir_value_global(e, nullptr); + ir_module_add_value(m, e, g); + map_set(&m->members, hash_string(name), g); + ir_global_type_info_member_tags = g; + } } } } { irDebugInfo *di = ir_alloc_debug_info(irDebugInfo_CompileUnit); - di->CompileUnit.file = m->info->files.entries[0].value; // Zeroth is the init file + + GB_ASSERT(m->info->files.entries.count > 0); + AstFile *file = m->info->files.entries[0].value; + + di->CompileUnit.file = file; // Zeroth is the init file di->CompileUnit.producer = str_lit("odin"); map_set(&m->debug_info, hash_pointer(m), di); @@ -9586,6 +10253,13 @@ void ir_init_module(irModule *m, Checker *c) { array_init(&m->debug_location_stack, heap_allocator()); // TODO(lachsinc): ir_allocator() ?? } + + { + for_array(i, m->info->files.entries) { + AstFile *file = m->info->files.entries[i].value; + ir_add_debug_info_file(m, file); + } + } } void ir_destroy_module(irModule *m) { @@ -9694,6 +10368,11 @@ irValue *ir_type_info_member_usings_offset(irProcedure *proc, isize count) { ir_global_type_info_member_usings_index += cast(i32)count; return offset; } +irValue *ir_type_info_member_tags_offset(irProcedure *proc, isize count) { + irValue *offset = ir_emit_array_epi(proc, ir_global_type_info_member_tags, ir_global_type_info_member_tags_index); + ir_global_type_info_member_tags_index += cast(i32)count; + return offset; +} @@ -9832,6 +10511,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; @@ -10039,9 +10723,9 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *is_packed = ir_const_bool(t->Struct.is_packed); irValue *is_raw_union = ir_const_bool(t->Struct.is_raw_union); irValue *is_custom_align = ir_const_bool(t->Struct.custom_align != 0); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 4), is_packed); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 5), is_raw_union); - ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_custom_align); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 5), is_packed); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 6), is_raw_union); + ir_emit_store(proc, ir_emit_struct_ep(proc, tag, 7), is_custom_align); } isize count = t->Struct.fields.count; @@ -10050,6 +10734,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *memory_names = ir_type_info_member_names_offset (proc, count); irValue *memory_offsets = ir_type_info_member_offsets_offset(proc, count); irValue *memory_usings = ir_type_info_member_usings_offset (proc, count); + irValue *memory_tags = ir_type_info_member_tags_offset (proc, count); type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet for (isize source_index = 0; source_index < count; source_index++) { @@ -10065,7 +10750,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info irValue *index = ir_const_int(source_index); irValue *type_info = ir_emit_ptr_offset(proc, memory_types, index); irValue *offset = ir_emit_ptr_offset(proc, memory_offsets, index); - irValue *is_using = ir_emit_ptr_offset(proc, memory_usings, index); + irValue *is_using = ir_emit_ptr_offset(proc, memory_usings, index); ir_emit_store(proc, type_info, ir_type_info(proc, f->type)); if (f->token.string.len > 0) { @@ -10074,6 +10759,15 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info } ir_emit_store(proc, offset, ir_const_uintptr(foffset)); ir_emit_store(proc, is_using, ir_const_bool((f->flags&EntityFlag_Using) != 0)); + + if (t->Struct.tags.count > 0) { + String tag_string = t->Struct.tags[source_index]; + if (tag_string.len > 0) { + irValue *tag_ptr = ir_emit_ptr_offset(proc, memory_tags, index); + ir_emit_store(proc, tag_ptr, ir_const_string(tag_string)); + } + } + } irValue *cv = ir_const_int(count); @@ -10081,6 +10775,7 @@ void ir_setup_type_info_data(irProcedure *proc) { // NOTE(bill): Setup type_info ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 1), memory_names, cv); ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 2), memory_offsets, cv); ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 3), memory_usings, cv); + ir_fill_slice(proc, ir_emit_struct_ep(proc, tag, 4), memory_tags, cv); } break; } @@ -10482,12 +11177,14 @@ void ir_gen_tree(irGen *s) { // main :: proc(argc: i32, argv: ^^u8) -> i32 String name = str_lit("main"); +#if 0 if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) { // This is a bit hacky, // because this makes this function the first function run in the executable // so it won't actually have the argc/argv arguments. name = str_lit("ProgramEntry"); } +#endif Type *proc_params = alloc_type_tuple(); Type *proc_results = alloc_type_tuple(); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index b008fa682..d47dfc898 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -323,12 +323,15 @@ void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) { void ir_print_proc_type_without_pointer(irFileBuffer *f, irModule *m, Type *t) { + set_procedure_abi_types(heap_allocator(), t); + i64 word_bits = 8*build_context.word_size; t = base_type(t); GB_ASSERT(is_type_proc(t)); isize param_count = t->Proc.param_count; isize result_count = t->Proc.result_count; + ir_print_proc_results(f, m, t); ir_write_string(f, str_lit(" (")); if (t->Proc.return_by_pointer) { @@ -418,20 +421,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_quaternion128: ir_write_str_lit(f, "%..quaternion128"); return; + case Basic_quaternion256: ir_write_str_lit(f, "%..quaternion256"); 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_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; + case Basic_typeid: ir_write_str_lit(f, "%..typeid"); return; } break; @@ -767,15 +773,17 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * case ExactValue_Float: { GB_ASSERT_MSG(is_type_float(type), "%s", type_to_string(type)); type = core_type(type); - u64 u = bit_cast<u64>(value.value_float); + u64 u_64 = bit_cast<u64>(value.value_float); + u32 u_32 = bit_cast<u32>(cast(f32)value.value_float); + #if 0 switch (type->Basic.kind) { case Basic_f32: // IMPORTANT NOTE(bill): LLVM requires all floating point constants to be // a 64 bit number if bits_of(float type) <= 64. // https://groups.google.com/forum/#!topic/llvm-dev/IlqV3TbSk6M - // 64 bit mantissa: 52 bits - // 32 bit mantissa: 23 bits - // 16 bit mantissa: 10 bits + // 64 bit mantissa: 52 bits ==> 52-52 == 0 + // 32 bit mantissa: 23 bits ==> 52-23 == 29 + // 16 bit mantissa: 10 bits ==> 52=10 == 42 // 29 == 52-23 u >>= 29; u <<= 29; @@ -792,9 +800,24 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_fprintf(f, "0x%016llx", u); break; } + #else + switch (type->Basic.kind) { + case Basic_f32: { + ir_fprintf(f, "bitcast (i32 %u to float)", u_32); + break; + } + case Basic_f64: + ir_fprintf(f, "0x%016llx", u_64); + break; + default: + ir_fprintf(f, "0x%016llx", u_64); + break; + } + #endif 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); @@ -807,6 +830,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)) { @@ -836,22 +879,86 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * ir_write_str_lit(f, "zeroinitializer"); break; } - GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); + if (cl->elems[0]->kind == Ast_FieldValue) { + // TODO(bill): This is O(N*M) and will be quite slow; it should probably be sorted before hand + ir_write_byte(f, '['); + for (i64 i = 0; i < type->Array.count; i++) { + if (i > 0) ir_write_str_lit(f, ", "); + + bool found = false; + + for (isize j = 0; j < elem_count; j++) { + Ast *elem = cl->elems[j]; + ast_node(fv, FieldValue, elem); + 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_write_byte(f, '['); + if (!found) { + ir_print_type(f, m, elem_type); + ir_write_byte(f, ' '); + ir_write_str_lit(f, "zeroinitializer"); + } + } + ir_write_byte(f, ']'); + } else { + GB_ASSERT_MSG(elem_count == type->Array.count, "%td != %td", elem_count, type->Array.count); - for (isize i = 0; i < elem_count; i++) { - if (i > 0) ir_write_str_lit(f, ", "); - TypeAndValue tav = cl->elems[i]->tav; - GB_ASSERT(tav.mode != Addressing_Invalid); - ir_print_compound_element(f, m, tav.value, elem_type); - } - for (isize i = elem_count; i < type->Array.count; i++) { - if (i >= elem_count) ir_write_str_lit(f, ", "); - ir_print_compound_element(f, m, empty_exact_value, elem_type); - } + ir_write_byte(f, '['); - ir_write_byte(f, ']'); + for (isize i = 0; i < elem_count; i++) { + if (i > 0) ir_write_str_lit(f, ", "); + TypeAndValue tav = cl->elems[i]->tav; + GB_ASSERT(tav.mode != Addressing_Invalid); + ir_print_compound_element(f, m, tav.value, elem_type); + } + for (isize i = elem_count; i < type->Array.count; i++) { + if (i >= elem_count) ir_write_str_lit(f, ", "); + ir_print_compound_element(f, m, empty_exact_value, elem_type); + } + + ir_write_byte(f, ']'); + } } else if (is_type_simd_vector(type)) { ast_node(cl, CompoundLit, value.value_compound); @@ -927,7 +1034,8 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * if (type->Struct.is_packed) ir_write_byte(f, '<'); ir_write_byte(f, '{'); if (type->Struct.custom_align > 0) { - ir_fprintf(f, "[0 x <%lld x i8>] zeroinitializer", cast(i64)type->Struct.custom_align); + ir_print_alignment_prefix_hack(f, cast(i64)type->Struct.custom_align); + ir_write_str_lit(f, " zeroinitializer"); if (value_count > 0) { ir_write_string(f, str_lit(", ")); } @@ -1089,7 +1197,11 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin break; } case irValue_Param: - ir_print_encoded_local(f, value->Param.entity->token.string); + if (value->Param.index >= 0) { + ir_fprintf(f, "%%_.%d", value->Param.index); + } else { + ir_print_encoded_local(f, value->Param.entity->token.string); + } break; case irValue_SourceCodeLocation: { irValue *file = value->SourceCodeLocation.file; @@ -1124,7 +1236,8 @@ void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConven switch (cc) { case ProcCC_Odin: ir_write_str_lit(f, ""); break; case ProcCC_Contextless: ir_write_str_lit(f, ""); break; - case ProcCC_CDecl: ir_write_str_lit(f, "ccc "); break; + // case ProcCC_CDecl: ir_write_str_lit(f, "ccc "); break; + case ProcCC_CDecl: ir_write_str_lit(f, ""); break; case ProcCC_StdCall: ir_write_str_lit(f, "cc 64 "); break; case ProcCC_FastCall: ir_write_str_lit(f, "cc 65 "); break; case ProcCC_None: ir_write_str_lit(f, ""); break; @@ -1134,8 +1247,8 @@ void ir_print_calling_convention(irFileBuffer *f, irModule *m, ProcCallingConven void ir_print_context_parameter_prefix(irFileBuffer *f, irModule *m) { ir_print_type(f, m, t_context_ptr); - ir_write_str_lit(f, " noalias nonnull nocapture inreg "); - // ir_write_str_lit(f, " noalias nonnull nocapture "); + // ir_write_str_lit(f, " noalias nonnull nocapture inreg "); + ir_write_str_lit(f, " noalias nonnull nocapture "); } void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { @@ -1186,6 +1299,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { ir_write_str_lit(f, ", "); ir_print_type(f, m, type); ir_fprintf(f, "* %%%d, align 1", instr->ZeroInit.address->index); + // ir_fprintf(f, "* %%%d", instr->ZeroInit.address->index); break; } @@ -1888,11 +2002,11 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { if (e->flags&EntityFlag_NoAlias) { ir_write_str_lit(f, " noalias"); } + if (e->flags&EntityFlag_ImplicitReference) { + ir_write_str_lit(f, " nonnull dereferenceable"); + } ir_write_byte(f, ' '); irValue *arg = call->args[i]; - if (is_type_boolean(t)) { - - } ir_print_value(f, m, arg, t); param_index++; } @@ -1907,24 +2021,43 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { param_index++; } } else { - GB_ASSERT(call->args.count == params->variables.count); + // GB_ASSERT(call->args.count == params->variables.count); + isize arg_index = 0; for_array(i, params->variables) { Entity *e = params->variables[i]; GB_ASSERT(e != nullptr); - if (e->kind != Entity_Variable) continue; + if (e->kind != Entity_Variable) { + arg_index++; + continue; + } if (param_index > 0) ir_write_str_lit(f, ", "); - irValue *arg = call->args[i]; Type *t = proc_type->Proc.abi_compat_params[i]; - - ir_print_type(f, m, t); - if (e->flags&EntityFlag_NoAlias) { - ir_write_str_lit(f, " noalias"); + if (is_type_tuple(t)) { + for_array(j, t->Tuple.variables) { + if (j > 0) ir_write_str_lit(f, ", "); + + irValue *arg = call->args[arg_index++]; + + ir_print_type(f, m, t->Tuple.variables[j]->type); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + ir_write_byte(f, ' '); + ir_print_value(f, m, arg, t); + param_index++; + } + } else { + irValue *arg = call->args[arg_index++]; + ir_print_type(f, m, t); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + ir_write_byte(f, ' '); + ir_print_value(f, m, arg, t); + param_index++; } - ir_write_byte(f, ' '); - ir_print_value(f, m, arg, t); - param_index++; } } } @@ -1995,6 +2128,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) { void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { + set_procedure_abi_types(heap_allocator(), proc->type); + if (proc->body == nullptr) { ir_write_str_lit(f, "declare "); // if (proc->tags & ProcTag_dll_import) { @@ -2043,7 +2178,8 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { if (param_count > 0) { TypeTuple *params = &proc_type->params->Tuple; - for (isize i = 0; i < param_count; i++) { + isize parameter_index = 0; + for (isize i = 0; i < param_count; i++, parameter_index++) { Entity *e = params->variables[i]; Type *original_type = e->type; Type *abi_type = proc_type->abi_compat_params[i]; @@ -2053,16 +2189,29 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) { if (i+1 == params->variables.count && proc_type->c_vararg) { ir_write_str_lit(f, " ..."); } else { - ir_print_type(f, m, abi_type); - if (e->flags&EntityFlag_NoAlias) { - ir_write_str_lit(f, " noalias"); - } - if (proc->body != nullptr) { - if (e->token.string != "" && !is_blank_ident(e->token)) { - ir_write_byte(f, ' '); - ir_print_encoded_local(f, e->token.string); - } else { - ir_fprintf(f, " %%_.param_%td", i); + if (is_type_tuple(abi_type)) { + for_array(j, abi_type->Tuple.variables) { + if (j > 0) ir_write_string(f, str_lit(", ")); + + Type *tft = abi_type->Tuple.variables[j]->type; + ir_print_type(f, m, tft); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + + if (proc->body != nullptr) { + ir_fprintf(f, " %%_.%td", parameter_index+j); + } + } + parameter_index += abi_type->Tuple.variables.count-1; + param_index += abi_type->Tuple.variables.count-1; + } else { + ir_print_type(f, m, abi_type); + if (e->flags&EntityFlag_NoAlias) { + ir_write_str_lit(f, " noalias"); + } + if (proc->body != nullptr) { + ir_fprintf(f, " %%_.%td", parameter_index); } } } @@ -2176,6 +2325,22 @@ void ir_print_type_name(irFileBuffer *f, irModule *m, irValue *v) { ir_write_byte(f, '\n'); } +bool ir_print_global_type_allowed(Type *t) { + if (t == nullptr) { + return true; + } + t = core_type(t); + switch (t->kind) { + case Type_DynamicArray: + case Type_Map: + case Type_Union: + case Type_BitField: + return false; + } + + return true; +} + void print_llvm_ir(irGen *ir) { irModule *m = &ir->module; @@ -2184,9 +2349,11 @@ void print_llvm_ir(irGen *ir) { defer (ir_file_buffer_destroy(f)); i32 word_bits = cast(i32)(8*build_context.word_size); - if (build_context.ODIN_OS == "osx" || build_context.ODIN_OS == "macos") { + if (build_context.ODIN_OS == "darwin") { GB_ASSERT(word_bits == 64); - ir_write_str_lit(f, "target triple = \"x86_64-apple-macosx10.8\"\n\n"); + ir_write_str_lit(f, "target datalayout = \"e-m:o-i64:64-f80:128-n8:16:32:64-S128\"\n"); + ir_write_str_lit(f, "target triple = \"x86_64-apple-macosx10.8\"\n"); + ir_write_str_lit(f, "\n"); } else if (build_context.ODIN_OS == "windows") { ir_fprintf(f, "target triple = \"x86%s-pc-windows-msvc\"\n\n", word_bits == 64 ? "_64" : ""); if (word_bits == 64 && build_context.metrics.arch == TargetArch_amd64) { @@ -2210,6 +2377,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); @@ -2339,7 +2513,7 @@ void print_llvm_ir(irGen *ir) { ir_print_type(f, m, g->entity->type); ir_write_byte(f, ' '); if (!g->is_foreign) { - if (g->value != nullptr) { + if (g->value != nullptr && ir_print_global_type_allowed(g->entity->type)) { ir_print_value(f, m, g->value, g->entity->type); } else { ir_write_string(f, str_lit("zeroinitializer")); @@ -2383,11 +2557,13 @@ void print_llvm_ir(irGen *ir) { for_array(di_index, m->debug_info.entries) { irDebugInfo *di = m->debug_info.entries[di_index].value; + GB_ASSERT_MSG(di != nullptr, "Invalid irDebugInfo"); ir_fprintf(f, "!%d = ", di->id); - switch (di->kind) { case irDebugInfo_CompileUnit: { - irDebugInfo *file = *map_get(&m->debug_info, hash_pointer(di->CompileUnit.file)); + irDebugInfo **found = map_get(&m->debug_info, hash_pointer(di->CompileUnit.file)); + GB_ASSERT_MSG(found != nullptr, "Missing debug info for: %.*s\n", LIT(di->CompileUnit.file->fullpath)); + irDebugInfo *file = *found; ir_fprintf(f, "distinct !DICompileUnit(" "language: DW_LANG_C_plus_plus" // Is this good enough? diff --git a/src/main.cpp b/src/main.cpp index 05fa6c366..acb580ca2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -75,7 +75,7 @@ i32 system_exec_command_line_app(char *name, char *fmt, ...) { va_end(va); cmd = make_string(cast(u8 *)&cmd_line, cmd_len-1); - //printf("do: %s\n", cmd_line); + // printf("do: %s\n", cmd_line); exit_code = system(&cmd_line[0]); // pid_t pid = fork(); @@ -160,8 +160,9 @@ void usage(String argv0) { print_usage_line(0, "Usage:"); print_usage_line(1, "%.*s command [arguments]", LIT(argv0)); print_usage_line(0, "Commands:"); - print_usage_line(1, "build compile .odin file as executable"); - print_usage_line(1, "run compile and run .odin file"); + print_usage_line(1, "build compile .odin file, or directory of .odin files, as an executable."); + print_usage_line(1, " one must contain the program's entry point, all must be in the same package."); + print_usage_line(1, "run same as 'build', but also then runs the newly compiled executable."); print_usage_line(1, "check parse and type check .odin file"); print_usage_line(1, "query parse, type check, and output a .json file containing information about the program"); print_usage_line(1, "docs generate documentation for a .odin file"); @@ -210,9 +211,8 @@ enum BuildFlagKind { BuildFlag_Collection, BuildFlag_Define, BuildFlag_BuildMode, + BuildFlag_Target, BuildFlag_Debug, - BuildFlag_CrossCompile, - BuildFlag_CrossLibDir, BuildFlag_NoBoundsCheck, BuildFlag_NoCRT, BuildFlag_UseLLD, @@ -298,9 +298,8 @@ bool parse_build_flags(Array<String> args) { add_flag(&build_flags, BuildFlag_Collection, str_lit("collection"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_Define, str_lit("define"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_BuildMode, str_lit("build-mode"), BuildFlagParam_String); + add_flag(&build_flags, BuildFlag_Target, str_lit("target"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_Debug, str_lit("debug"), BuildFlagParam_None); - add_flag(&build_flags, BuildFlag_CrossCompile, str_lit("cross-compile"), BuildFlagParam_String); - add_flag(&build_flags, BuildFlag_CrossLibDir, str_lit("cross-lib-dir"), BuildFlagParam_String); add_flag(&build_flags, BuildFlag_NoBoundsCheck, str_lit("no-bounds-check"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None); add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None); @@ -447,7 +446,7 @@ bool parse_build_flags(Array<String> args) { path = substring(path, 0, string_extension_position(path)); } #endif - build_context.out_filepath = path; + build_context.out_filepath = path_to_full_path(heap_allocator(), path); } else { gb_printf_err("Invalid -out path, got %.*s\n", LIT(path)); bad_flags = true; @@ -478,33 +477,6 @@ bool parse_build_flags(Array<String> args) { build_context.keep_temp_files = true; break; - case BuildFlag_CrossCompile: { - GB_ASSERT(value.kind == ExactValue_String); - cross_compile_target = value.value_string; - #if defined(GB_SYSTEM_UNIX) && defined(GB_ARCH_64_BIT) - if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) { - - } else - #endif - { - gb_printf_err("Unsupported cross compilation target '%.*s'\n", LIT(cross_compile_target)); - gb_printf_err("Currently supported targets: Essence (from 64-bit Unixes only)\n"); - bad_flags = true; - } - break; - } - - case BuildFlag_CrossLibDir: { - GB_ASSERT(value.kind == ExactValue_String); - if (cross_compile_lib_dir.len) { - gb_printf_err("Multiple cross compilation library directories\n"); - bad_flags = true; - } else { - cross_compile_lib_dir = concatenate_strings(heap_allocator(), str_lit("-L"), value.value_string); - } - break; - } - case BuildFlag_Collection: { GB_ASSERT(value.kind == ExactValue_String); String str = value.value_string; @@ -623,7 +595,25 @@ bool parse_build_flags(Array<String> args) { break; } + case BuildFlag_Target: { + String str = value.value_string; + bool found = false; + + for (int i = 0; i < sizeof(named_targets) / sizeof(named_targets[0]); i++) { + if (str_eq_ignore_case(str, named_targets[i].name)) { + found = true; + selected_target_metrics = named_targets + i; + break; + } + } + + if (!found) { + gb_printf_err("Unknown target '%.*s'\n", LIT(str)); + bad_flags = true; + } + break; + } case BuildFlag_BuildMode: { GB_ASSERT(value.kind == ExactValue_String); @@ -635,7 +625,7 @@ bool parse_build_flags(Array<String> args) { break; } - if (str == "dll") { + if (str == "dll" || str == "shared") { build_context.is_dll = true; } else if (str == "exe") { build_context.is_dll = false; @@ -889,8 +879,8 @@ i32 exec_llvm_opt(String output_base) { } i32 exec_llvm_llc(String output_base) { -#if defined(GB_SYSTEM_WINDOWS) // For more arguments: http://llvm.org/docs/CommandGuide/llc.html +#if defined(GB_SYSTEM_WINDOWS) return system_exec_command_line_app("llvm-llc", "\"%.*sbin\\llc\" \"%.*s.bc\" -filetype=obj -O%d " "-o \"%.*s.obj\" " @@ -903,21 +893,19 @@ i32 exec_llvm_llc(String output_base) { LIT(build_context.llc_flags)); #else // NOTE(zangent): Linux / Unix is unfinished and not tested very well. - // For more arguments: http://llvm.org/docs/CommandGuide/llc.html return system_exec_command_line_app("llc", "llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d " "%.*s " - "%s" - "", + "%s%.*s", LIT(output_base), build_context.optimization_level, LIT(build_context.llc_flags), - str_eq_ignore_case(cross_compile_target, str_lit("Essence")) ? "-mtriple=x86_64-pc-none-elf" : ""); + build_context.cross_compiling ? "-mtriple=" : "", + (int) (build_context.cross_compiling ? build_context.target_triplet.len : 0), + build_context.target_triplet.text); #endif } - - int main(int arg_count, char **arg_ptr) { if (arg_count < 2) { usage(make_string_c(arg_ptr[0])); @@ -1026,7 +1014,7 @@ int main(int arg_count, char **arg_ptr) { } - init_build_context(); + init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr); if (build_context.word_size == 4) { print_usage_line(0, "%s 32-bit is not yet supported", args[0]); return 1; @@ -1121,6 +1109,17 @@ int main(int arg_count, char **arg_ptr) { return exit_code; } + if (build_context.cross_compiling) { + if (0) { +#ifdef GB_SYSTEM_UNIX + } else if (selected_target_metrics->metrics == &target_essence_amd64) { + system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s", + LIT(output_base), LIT(output_base), LIT(build_context.link_flags)); +#endif + } else { + gb_printf_err("Don't know how to cross compile to selected target.\n"); + } + } else { #if defined(GB_SYSTEM_WINDOWS) timings_start_section(&timings, str_lit("msvc-link")); @@ -1219,7 +1218,7 @@ int main(int arg_count, char **arg_ptr) { remove_temp_files(output_base); if (run_output) { - system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string)); + return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string)); } #else timings_start_section(&timings, str_lit("ld-link")); @@ -1241,15 +1240,17 @@ int main(int arg_count, char **arg_ptr) { // This allows you to specify '-f' in a #foreign_system_library, // without having to implement any new syntax specifically for MacOS. #if defined(GB_SYSTEM_OSX) - if (lib.len > 2 && lib[0] == '-' && lib[1] == 'f') { + if (string_ends_with(lib, str_lit(".framework"))) { // framework thingie - lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", (int)(lib.len) - 2, lib.text + 2); + String lib_name = lib; + lib_name = remove_extension_from_path(lib_name); + lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name)); } else if (string_ends_with(lib, str_lit(".a"))) { // static libs, absolute full path relative to the file in which the lib was imported from lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib)); } else if (string_ends_with(lib, str_lit(".dylib"))) { - // dynamic lib, relative path to executable - lib_str = gb_string_append_fmt(lib_str, " -l:%s/%.*s ", cwd, LIT(lib)); + // dynamic lib + lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); } else { // dynamic or static system lib, just link regularly searching system library paths lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib)); @@ -1309,11 +1310,7 @@ int main(int arg_count, char **arg_ptr) { // It probably has to do with including the entire CRT, but // that's quite a complicated issue to solve while remaining distro-agnostic. // Clang can figure out linker flags for us, and that's good enough _for now_. - if (str_eq_ignore_case(cross_compile_target, str_lit("Essence"))) { - linker = "x86_64-elf-gcc -T core/sys/essence_linker_userland64.ld -ffreestanding -nostdlib -lgcc -g -z max-page-size=0x1000 -Wno-unused-command-line-argument"; - } else { - linker = "clang -Wno-unused-command-line-argument"; - } + linker = "clang -Wno-unused-command-line-argument"; #endif exit_code = system_exec_command_line_app("ld-link", @@ -1321,7 +1318,6 @@ int main(int arg_count, char **arg_ptr) { " %s " " %.*s " " %s " - " %.*s " #if defined(GB_SYSTEM_OSX) // This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit. // NOTE: If you change this (although this minimum is as low as you can go with Odin working) @@ -1332,11 +1328,9 @@ int main(int arg_count, char **arg_ptr) { #endif , linker, LIT(output_base), LIT(output_base), LIT(output_ext), lib_str, - str_eq_ignore_case(cross_compile_target, str_lit("Essence")) ? "-lfreetype -lglue" : "-lc -lm", + "-lc -lm", LIT(build_context.link_flags), - link_settings, - LIT(cross_compile_lib_dir) - ); + link_settings); if (exit_code != 0) { return exit_code; } @@ -1366,9 +1360,10 @@ int main(int arg_count, char **arg_ptr) { //NOTE(thebirk): This whole thing is a little leaky String complete_path = concatenate_strings(heap_allocator(), output_base, output_ext); complete_path = path_to_full_path(heap_allocator(), complete_path); - system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string)); + return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string)); } #endif + } return 0; } diff --git a/src/parser.cpp b/src/parser.cpp index a377b773c..10aa10119 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; @@ -143,6 +144,7 @@ Ast *clone_ast(Ast *node) { case Ast_ProcLit: n->ProcLit.type = clone_ast(n->ProcLit.type); n->ProcLit.body = clone_ast(n->ProcLit.body); + n->ProcLit.where_clauses = clone_ast_array(n->ProcLit.where_clauses); break; case Ast_CompoundLit: n->CompoundLit.type = clone_ast(n->CompoundLit.type); @@ -257,6 +259,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); @@ -341,10 +349,12 @@ Ast *clone_ast(Ast *node) { n->StructType.fields = clone_ast_array(n->StructType.fields); n->StructType.polymorphic_params = clone_ast(n->StructType.polymorphic_params); n->StructType.align = clone_ast(n->StructType.align); + n->StructType.where_clauses = clone_ast_array(n->StructType.where_clauses); break; case Ast_UnionType: n->UnionType.variants = clone_ast_array(n->UnionType.variants); n->UnionType.polymorphic_params = clone_ast(n->UnionType.polymorphic_params); + n->UnionType.where_clauses = clone_ast_array(n->UnionType.where_clauses); break; case Ast_EnumType: n->EnumType.base_type = clone_ast(n->EnumType.base_type); @@ -417,7 +427,7 @@ void syntax_error(Ast *node, char *fmt, ...) { bool ast_node_expect(Ast *node, AstKind kind) { if (node->kind != kind) { - error(node, "Expected %.*s, got %.*s", LIT(ast_strings[kind]), LIT(ast_strings[node->kind])); + syntax_error(node, "Expected %.*s, got %.*s", LIT(ast_strings[kind]), LIT(ast_strings[node->kind])); return false; } return true; @@ -578,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; } @@ -605,11 +616,13 @@ Ast *ast_proc_group(AstFile *f, Token token, Token open, Token close, Array<Ast return result; } -Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags) { +Ast *ast_proc_lit(AstFile *f, Ast *type, Ast *body, u64 tags, Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_ProcLit); result->ProcLit.type = type; result->ProcLit.body = body; result->ProcLit.tags = tags; + result->ProcLit.where_token = where_token; + result->ProcLit.where_clauses = where_clauses; return result; } @@ -748,6 +761,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; @@ -805,13 +830,14 @@ Ast *ast_bad_decl(AstFile *f, Token begin, Token end) { return result; } -Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, - CommentGroup *docs, CommentGroup *comment) { +Ast *ast_field(AstFile *f, Array<Ast *> names, Ast *type, Ast *default_value, u32 flags, Token tag, + CommentGroup *docs, CommentGroup *comment) { Ast *result = alloc_ast_node(f, Ast_Field); result->Field.names = names; result->Field.type = type; result->Field.default_value = default_value; result->Field.flags = flags; + result->Field.tag = tag; result->Field.docs = docs; result->Field.comment = comment; return result; @@ -898,7 +924,8 @@ Ast *ast_dynamic_array_type(AstFile *f, Token token, Ast *elem) { Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_count, Ast *polymorphic_params, bool is_packed, bool is_raw_union, - Ast *align) { + Ast *align, + Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_StructType); result->StructType.token = token; result->StructType.fields = fields; @@ -907,17 +934,22 @@ Ast *ast_struct_type(AstFile *f, Token token, Array<Ast *> fields, isize field_c result->StructType.is_packed = is_packed; result->StructType.is_raw_union = is_raw_union; result->StructType.align = align; + result->StructType.where_token = where_token; + result->StructType.where_clauses = where_clauses; return result; } -Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil) { +Ast *ast_union_type(AstFile *f, Token token, Array<Ast *> variants, Ast *polymorphic_params, Ast *align, bool no_nil, + Token where_token, Array<Ast *> const &where_clauses) { Ast *result = alloc_ast_node(f, Ast_UnionType); result->UnionType.token = token; result->UnionType.variants = variants; result->UnionType.polymorphic_params = polymorphic_params; result->UnionType.align = align; result->UnionType.no_nil = no_nil; + result->UnionType.where_token = where_token; + result->UnionType.where_clauses = where_clauses; return result; } @@ -1118,6 +1150,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) { @@ -1302,7 +1345,8 @@ bool is_semicolon_optional_for_node(AstFile *f, Ast *s) { case Ast_UnionType: case Ast_EnumType: case Ast_BitFieldType: - return true; + // Require semicolon within a procedure body + return f->curr_proc == nullptr; case Ast_ProcLit: return true; @@ -1336,10 +1380,6 @@ void expect_semicolon(AstFile *f, Ast *s) { return; } - switch (f->curr_token.kind) { - case Token_EOF: - return; - } if (s != nullptr) { if (prev_token.pos.line != f->curr_token.pos.line) { @@ -1352,12 +1392,21 @@ void expect_semicolon(AstFile *f, Ast *s) { case Token_CloseParen: case Token_else: return; + case Token_EOF: + if (is_semicolon_optional_for_node(f, s)) { + return; + } + break; } } String node_string = ast_strings[s->kind]; syntax_error(prev_token, "Expected ';' after %.*s, got %.*s", - LIT(node_string), LIT(token_strings[prev_token.kind])); + LIT(node_string), LIT(token_strings[f->curr_token.kind])); } else { + switch (f->curr_token.kind) { + case Token_EOF: + return; + } syntax_error(prev_token, "Expected ';'"); } fix_advance_to_next_stmt(f); @@ -1461,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; } @@ -1614,7 +1666,7 @@ void check_polymorphic_params_for_type(AstFile *f, Ast *polymorphic_params, Toke for_array(i, field->Field.names) { Ast *name = field->Field.names[i]; if (name->kind == Ast_PolyType) { - error(name, "Polymorphic names are not needed for %.*s parameters", LIT(token.string)); + syntax_error(name, "Polymorphic names are not needed for %.*s parameters", LIT(token.string)); return; // TODO(bill): Err multiple times or just the once? } } @@ -1687,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); @@ -1796,15 +1849,41 @@ Ast *parse_operand(AstFile *f, bool lhs) { } Ast *type = parse_proc_type(f, token); + Token where_token = {}; + Array<Ast *> where_clauses = {}; + u64 tags = 0; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + parse_proc_tags(f, &tags); + if ((tags & ProcTag_require_results) != 0) { + syntax_error(f->curr_token, "#require_results has now been replaced as an attribute @(require_results) on the declaration"); + tags &= ~ProcTag_require_results; + } + GB_ASSERT(type->kind == Ast_ProcType); + type->ProcType.tags = tags; if (f->allow_type && f->expr_level < 0) { + if (tags != 0) { + syntax_error(token, "A procedure type cannot have suffix tags"); + } + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure types"); + } return type; } - u64 tags = type->ProcType.tags; - if (allow_token(f, Token_Undef)) { - return ast_proc_lit(f, type, nullptr, tags); + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure literals without a defined body (replaced with ---)"); + } + return ast_proc_lit(f, type, nullptr, tags, where_token, where_clauses); } else if (f->curr_token.kind == Token_OpenBrace) { Ast *curr_proc = f->curr_proc; Ast *body = nullptr; @@ -1812,7 +1891,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = parse_body(f); f->curr_proc = curr_proc; - return ast_proc_lit(f, type, body, tags); + return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } else if (allow_token(f, Token_do)) { Ast *curr_proc = f->curr_proc; Ast *body = nullptr; @@ -1820,11 +1899,14 @@ Ast *parse_operand(AstFile *f, bool lhs) { body = convert_stmt_to_body(f, parse_stmt(f)); f->curr_proc = curr_proc; - return ast_proc_lit(f, type, body, tags); + return ast_proc_lit(f, type, body, tags, where_token, where_clauses); } if (tags != 0) { - syntax_error(token, "A procedure type cannot have tags"); + syntax_error(token, "A procedure type cannot have suffix tags"); + } + if (where_token.kind != Token_Invalid) { + syntax_error(where_token, "'where' clauses are not allowed on procedure types"); } return type; @@ -1847,10 +1929,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; @@ -1952,6 +2030,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { syntax_error(token, "'#raw_union' cannot also be '#packed'"); } + Token where_token = {}; + Array<Ast *> where_clauses = {}; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + Token open = expect_token_after(f, Token_OpenBrace, "struct"); isize name_count = 0; @@ -1964,7 +2054,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { decls = fields->FieldList.list; } - return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, align); + return ast_struct_type(f, token, decls, name_count, polymorphic_params, is_packed, is_raw_union, align, where_token, where_clauses); } break; case Token_union: { @@ -2005,6 +2095,18 @@ Ast *parse_operand(AstFile *f, bool lhs) { } } + Token where_token = {}; + Array<Ast *> where_clauses = {}; + + if (f->curr_token.kind == Token_where) { + where_token = expect_token(f, Token_where); + isize prev_level = f->expr_level; + f->expr_level = -1; + where_clauses = parse_rhs_expr_list(f); + f->expr_level = prev_level; + } + + Token open = expect_token_after(f, Token_OpenBrace, "union"); while (f->curr_token.kind != Token_CloseBrace && @@ -2020,7 +2122,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { Token close = expect_token(f, Token_CloseBrace); - return ast_union_type(f, token, variants, polymorphic_params, align, no_nil); + return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, where_token, where_clauses); } break; case Token_enum: { @@ -2091,7 +2193,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); @@ -2383,17 +2485,18 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_LtEq: case Token_GtEq: return 5; + case Token_in: case Token_notin: - if (f->expr_level >= 0 || f->allow_in_expr) { - return 6; + if (f->expr_level < 0 && !f->allow_in_expr) { + return 0; } - return 0; + /*fallthrough*/ case Token_Add: case Token_Sub: case Token_Or: case Token_Xor: - return 7; + return 6; case Token_Mul: case Token_Quo: case Token_Mod: @@ -2402,7 +2505,7 @@ i32 token_precedence(AstFile *f, TokenKind t) { case Token_AndNot: case Token_Shl: case Token_Shr: - return 8; + return 7; } return 0; } @@ -2649,7 +2752,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<Ast *>(heap_allocator(), 0, 1); @@ -2740,7 +2843,8 @@ Ast *parse_results(AstFile *f, bool *diverging) { Array<Ast *> empty_names = {}; auto list = array_make<Ast *>(heap_allocator(), 0, 1); Ast *type = parse_type(f); - array_add(&list, ast_field(f, empty_names, type, nullptr, 0, nullptr, nullptr)); + Token tag = {}; + array_add(&list, ast_field(f, empty_names, type, nullptr, 0, tag, nullptr, nullptr)); return ast_field_list(f, begin_token, list); } @@ -2795,8 +2899,6 @@ Ast *parse_proc_type(AstFile *f, Token proc_token) { results = parse_results(f, &diverging); u64 tags = 0; - parse_proc_tags(f, &tags); - bool is_generic = false; for_array(i, params->FieldList.list) { @@ -3095,6 +3197,7 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi Ast *type = nullptr; Ast *default_value = nullptr; + Token tag = {}; expect_token_after(f, Token_Colon, "field list"); if (f->curr_token.kind != Token_Eq) { @@ -3132,16 +3235,25 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value"); } + if (type != nullptr && default_value == nullptr) { + if (f->curr_token.kind == Token_String) { + tag = expect_token(f, Token_String); + if ((allowed_flags & FieldFlag_Tags) == 0) { + syntax_error(tag, "Field tags are only allowed within structures"); + } + } + } + parse_expect_field_separator(f, type); - Ast *param = ast_field(f, names, type, default_value, set_flags, docs, f->line_comment); + Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); while (f->curr_token.kind != follow && f->curr_token.kind != Token_EOF) { CommentGroup *docs = f->lead_comment; - u32 set_flags = parse_field_prefixes(f); + Token tag = {}; Array<Ast *> names = parse_ident_list(f, allow_poly_names); if (names.count == 0) { syntax_error(f->curr_token, "Empty field declaration"); @@ -3184,9 +3296,18 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi syntax_error(f->curr_token, "Extra parameter after ellipsis without a default value"); } + if (type != nullptr && default_value == nullptr) { + if (f->curr_token.kind == Token_String) { + tag = expect_token(f, Token_String); + if ((allowed_flags & FieldFlag_Tags) == 0) { + syntax_error(tag, "Field tags are only allowed within structures"); + } + } + } + bool ok = parse_expect_field_separator(f, param); - Ast *param = ast_field(f, names, type, default_value, set_flags, docs, f->line_comment); + Ast *param = ast_field(f, names, type, default_value, set_flags, tag, docs, f->line_comment); array_add(¶ms, param); if (!ok) { @@ -3210,8 +3331,8 @@ Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKi token.pos = ast_token(type).pos; names[0] = ast_ident(f, token); u32 flags = check_field_prefixes(f, list.count, allowed_flags, list[i].flags); - - Ast *param = ast_field(f, names, list[i].node, nullptr, flags, docs, f->line_comment); + Token tag = {}; + Ast *param = ast_field(f, names, list[i].node, nullptr, flags, tag, docs, f->line_comment); array_add(¶ms, param); } @@ -3758,9 +3879,55 @@ Ast *parse_stmt(AstFile *f) { Token token = f->curr_token; switch (token.kind) { // Operands - case Token_context: // Also allows for `context =` 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<Ast *> 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: @@ -3808,34 +3975,6 @@ Ast *parse_stmt(AstFile *f) { return s; } - // case Token_static: { - // CommentGroup *docs = f->lead_comment; - // Token token = expect_token(f, Token_static); - - // Ast *decl = nullptr; - // Array<Ast *> list = parse_lhs_expr_list(f); - // if (list.count == 0) { - // syntax_error(token, "Illegal use of 'static' statement"); - // expect_semicolon(f, nullptr); - // return ast_bad_stmt(f, token, f->curr_token); - // } - - // expect_token_after(f, Token_Colon, "identifier list"); - // decl = parse_value_decl(f, list, docs); - - // if (decl != nullptr && decl->kind == Ast_ValueDecl) { - // if (decl->ValueDecl.is_mutable) { - // decl->ValueDecl.is_static = true; - // } else { - // error(token, "'static' may only be currently used with variable declaration"); - // } - // return decl; - // } - - // syntax_error(token, "Illegal use of 'static' statement"); - // return ast_bad_stmt(f, token, f->curr_token); - // } break; - case Token_using: { CommentGroup *docs = f->lead_comment; Token token = expect_token(f, Token_using); @@ -3909,12 +4048,10 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "assert") { Ast *t = ast_basic_directive(f, hash_token, tag); return ast_expr_stmt(f, parse_call_expr(f, t)); - } /* else if (name.string == "no_deferred") { - s = parse_stmt(f); - s->stmt_state_flags |= StmtStateFlag_no_deferred; - } */ - - if (tag == "include") { + } else if (tag == "panic") { + Ast *t = ast_basic_directive(f, hash_token, tag); + return ast_expr_stmt(f, parse_call_expr(f, t)); + } else if (tag == "include") { syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?"); s = ast_bad_stmt(f, token, f->curr_token); } else { @@ -4037,7 +4174,6 @@ bool init_parser(Parser *p) { map_init(&p->package_map, heap_allocator()); array_init(&p->packages, heap_allocator()); array_init(&p->package_imports, heap_allocator()); - array_init(&p->files_to_process, heap_allocator()); gb_mutex_init(&p->file_add_mutex); gb_mutex_init(&p->file_decl_mutex); return true; @@ -4060,7 +4196,6 @@ void destroy_parser(Parser *p) { #endif array_free(&p->packages); array_free(&p->package_imports); - array_free(&p->files_to_process); string_set_destroy(&p->imported_files); map_destroy(&p->package_map); gb_mutex_destroy(&p->file_add_mutex); @@ -4077,19 +4212,32 @@ void parser_add_package(Parser *p, AstPackage *pkg) { if (found) { GB_ASSERT(pkg->files.count > 0); AstFile *f = pkg->files[0]; - error(f->package_token, "Non-unique package name '%.*s'", LIT(pkg->name)); + syntax_error(f->package_token, "Non-unique package name '%.*s'", LIT(pkg->name)); GB_ASSERT((*found)->files.count > 0); TokenPos pos = (*found)->files[0]->package_token.pos; - gb_printf_err("\tpreviously declared at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); + error_line("\tpreviously declared at %.*s(%td:%td)\n", LIT(pos.file), pos.line, pos.column); } else { map_set(&p->package_map, key, pkg); } } } +ParseFileError process_imported_file(Parser *p, ImportedFile const &imported_file); + +WORKER_TASK_PROC(parser_worker_proc) { + ParserWorkerData *wd = cast(ParserWorkerData *)data; + ParseFileError err = process_imported_file(wd->parser, wd->imported_file); + return cast(isize)err; +} + + void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) { - ImportedFile f = {pkg, fi, pos, p->files_to_process.count}; - array_add(&p->files_to_process, f); + // TODO(bill): Use a better allocator + ImportedFile f = {pkg, fi, pos, p->file_to_process_count++}; + auto wd = gb_alloc_item(heap_allocator(), ParserWorkerData); + wd->parser = p; + wd->imported_file = f; + thread_pool_add_task(&parser_thread_pool, parser_worker_proc, wd); } @@ -4140,22 +4288,22 @@ bool try_add_import_path(Parser *p, String const &path, String const &rel_path, switch (rd_err) { case ReadDirectory_InvalidPath: - error(pos, "Invalid path: %.*s", LIT(rel_path)); + syntax_error(pos, "Invalid path: %.*s", LIT(rel_path)); return false; case ReadDirectory_NotExists: - error(pos, "Path does not exist: %.*s", LIT(rel_path)); + syntax_error(pos, "Path does not exist: %.*s", LIT(rel_path)); return false; case ReadDirectory_Permission: - error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); + syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); return false; case ReadDirectory_NotDir: - error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path)); + syntax_error(pos, "Expected a directory for a package, got a file: %.*s", LIT(rel_path)); return false; case ReadDirectory_Empty: - error(pos, "Empty directory: %.*s", LIT(rel_path)); + syntax_error(pos, "Empty directory: %.*s", LIT(rel_path)); return false; case ReadDirectory_Unknown: - error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); + syntax_error(pos, "Unknown error whilst reading path %.*s", LIT(rel_path)); return false; } @@ -4257,7 +4405,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, String file_str = {}; if (colon_pos == 0) { - error(node, "Expected a collection name"); + syntax_error(node, "Expected a collection name"); return false; } @@ -4272,32 +4420,19 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, if (has_windows_drive) { String sub_file_path = substring(file_str, 3, file_str.len); if (!is_import_path_valid(sub_file_path)) { - error(node, "Invalid import path: '%.*s'", LIT(file_str)); + syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } } else if (!is_import_path_valid(file_str)) { - error(node, "Invalid import path: '%.*s'", LIT(file_str)); + syntax_error(node, "Invalid import path: '%.*s'", LIT(file_str)); return false; } - if (is_package_name_reserved(file_str)) { - *path = file_str; - return true; - } - - if (file_mutex) gb_mutex_lock(file_mutex); - defer (if (file_mutex) gb_mutex_unlock(file_mutex)); - - - if (node->kind == Ast_ForeignImportDecl) { - node->ForeignImportDecl.collection_name = collection_name; - } - if (collection_name.len > 0) { if (collection_name == "system") { if (node->kind != Ast_ForeignImportDecl) { - error(node, "The library collection 'system' is restrict for 'foreign_library'"); + syntax_error(node, "The library collection 'system' is restrict for 'foreign_library'"); return false; } else { *path = file_str; @@ -4305,7 +4440,7 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, } } else if (!find_library_collection_path(collection_name, &base_dir)) { // NOTE(bill): It's a naughty name - error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); + syntax_error(node, "Unknown library collection: '%.*s'", LIT(collection_name)); return false; } } else { @@ -4324,6 +4459,20 @@ bool determine_path_from_string(gbMutex *file_mutex, Ast *node, String base_dir, #endif } + + if (is_package_name_reserved(file_str)) { + *path = file_str; + return true; + } + + if (file_mutex) gb_mutex_lock(file_mutex); + defer (if (file_mutex) gb_mutex_unlock(file_mutex)); + + + if (node->kind == Ast_ForeignImportDecl) { + node->ForeignImportDecl.collection_name = collection_name; + } + if (has_windows_drive) { *path = file_str; } else { @@ -4412,7 +4561,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, Array<Ast *> array_add(&fl->fullpaths, fullpath); } if (fl->fullpaths.count == 0) { - error(decls[i], "No foreign paths found"); + syntax_error(decls[i], "No foreign paths found"); decls[i] = ast_bad_decl(f, fl->filepaths[0], fl->filepaths[fl->filepaths.count-1]); goto end; } @@ -4461,7 +4610,7 @@ bool parse_build_tag(Token token_for_pos, String s) { is_notted = true; p = substring(p, 1, p.len); if (p.len == 0) { - error(token_for_pos, "Expected a build platform after '!'"); + syntax_error(token_for_pos, "Expected a build platform after '!'"); break; } } @@ -4490,7 +4639,7 @@ bool parse_build_tag(Token token_for_pos, String s) { } } if (os == TargetOs_Invalid && arch == TargetArch_Invalid) { - error(token_for_pos, "Invalid build tag platform: %.*s", LIT(p)); + syntax_error(token_for_pos, "Invalid build tag platform: %.*s", LIT(p)); break; } } @@ -4536,11 +4685,11 @@ bool parse_file(Parser *p, AstFile *f) { Token package_name = expect_token_after(f, Token_Ident, "package"); if (package_name.kind == Token_Ident) { if (package_name.string == "_") { - error(package_name, "Invalid package name '_'"); + syntax_error(package_name, "Invalid package name '_'"); } else if (f->pkg->kind != Package_Runtime && package_name.string == "runtime") { - error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); + syntax_error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); } else if (is_package_name_reserved(package_name.string)) { - error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); + syntax_error(package_name, "Use of reserved package name '%.*s'", LIT(package_name.string)); } } f->package_name = package_name.string; @@ -4590,9 +4739,9 @@ bool parse_file(Parser *p, AstFile *f) { } -ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) { +ParseFileError process_imported_file(Parser *p, ImportedFile const &imported_file) { AstPackage *pkg = imported_file.pkg; - FileInfo *fi = &imported_file.fi; + FileInfo const *fi = &imported_file.fi; TokenPos pos = imported_file.pos; AstFile *file = gb_alloc_item(heap_allocator(), AstFile); @@ -4607,38 +4756,35 @@ ParseFileError process_imported_file(Parser *p, ImportedFile imported_file) { if (err != ParseFile_None) { if (err == ParseFile_EmptyFile) { if (fi->fullpath == p->init_fullpath) { - error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath)); + syntax_error(pos, "Initial file is empty - %.*s\n", LIT(p->init_fullpath)); gb_exit(1); } - goto skip; - } + } else { + switch (err) { + case ParseFile_WrongExtension: + syntax_error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name)); + break; + case ParseFile_InvalidFile: + syntax_error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name)); + break; + case ParseFile_Permission: + syntax_error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name)); + break; + case ParseFile_NotFound: + syntax_error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath)); + break; + case ParseFile_InvalidToken: + syntax_error(err_pos, "Failed to parse file: %.*s; invalid token found in file", LIT(fi->name)); + break; + case ParseFile_EmptyFile: + syntax_error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name)); + break; + } - switch (err) { - case ParseFile_WrongExtension: - error(pos, "Failed to parse file: %.*s; invalid file extension: File must have the extension '.odin'", LIT(fi->name)); - break; - case ParseFile_InvalidFile: - error(pos, "Failed to parse file: %.*s; invalid file or cannot be found", LIT(fi->name)); - break; - case ParseFile_Permission: - error(pos, "Failed to parse file: %.*s; file permissions problem", LIT(fi->name)); - break; - case ParseFile_NotFound: - error(pos, "Failed to parse file: %.*s; file cannot be found ('%.*s')", LIT(fi->name), LIT(fi->fullpath)); - break; - case ParseFile_InvalidToken: - error(err_pos, "Failed to parse file: %.*s; invalid token found in file", LIT(fi->name)); - break; - case ParseFile_EmptyFile: - error(pos, "Failed to parse file: %.*s; file contains no tokens", LIT(fi->name)); - break; + return err; } - - return err; } - -skip: if (parse_file(p, file)) { gb_mutex_lock(&p->file_add_mutex); defer (gb_mutex_unlock(&p->file_add_mutex)); @@ -4648,42 +4794,33 @@ skip: if (pkg->name.len == 0) { pkg->name = file->package_name; } else if (file->tokens.count > 0 && pkg->name != file->package_name) { - error(file->package_token, "Different package name, expected '%.*s', got '%.*s'", LIT(pkg->name), LIT(file->package_name)); + syntax_error(file->package_token, "Different package name, expected '%.*s', got '%.*s'", LIT(pkg->name), LIT(file->package_name)); } p->total_line_count += file->tokenizer.line_count; p->total_token_count += file->tokens.count; } + return ParseFile_None; } -GB_THREAD_PROC(parse_worker_file_proc) { - if (thread == nullptr) return 0; - auto *p = cast(Parser *)thread->user_data; - isize index = thread->user_index; - gb_mutex_lock(&p->file_add_mutex); - auto file_to_process = p->files_to_process[index]; - gb_mutex_unlock(&p->file_add_mutex); - ParseFileError err = process_imported_file(p, file_to_process); - return cast(isize)err; -} - ParseFileError parse_packages(Parser *p, String init_filename) { GB_ASSERT(init_filename.text[init_filename.len] == 0); - // char *fullpath_str = gb_path_get_full_name(heap_allocator(), cast(char const *)&init_filename[0]); - // String init_fullpath = string_trim_whitespace(make_string_c(fullpath_str)); + isize thread_count = gb_max(build_context.thread_count, 1); + isize worker_count = thread_count-1; // NOTE(bill): The main thread will also be used for work + thread_pool_init(&parser_thread_pool, heap_allocator(), worker_count, "ParserWork"); + String init_fullpath = path_to_full_path(heap_allocator(), init_filename); if (!path_is_directory(init_fullpath)) { String const ext = str_lit(".odin"); if (!string_ends_with(init_fullpath, ext)) { - gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); return ParseFile_WrongExtension; } } - TokenPos init_pos = {}; if (!build_context.generate_docs) { String s = get_fullpath_core(heap_allocator(), str_lit("runtime")); @@ -4693,76 +4830,17 @@ ParseFileError parse_packages(Parser *p, String init_filename) { try_add_import_path(p, init_fullpath, init_fullpath, init_pos, Package_Init); p->init_fullpath = init_fullpath; -#if 1 - isize thread_count = gb_max(build_context.thread_count, 1); - if (thread_count > 1) { - isize volatile curr_import_index = 0; - isize initial_file_count = p->files_to_process.count; - // NOTE(bill): Make sure that these are in parsed in this order - for (isize i = 0; i < initial_file_count; i++) { - ParseFileError err = process_imported_file(p, p->files_to_process[i]); - if (err != ParseFile_None) { - return err; - } - curr_import_index++; - } - - auto worker_threads = array_make<gbThread>(heap_allocator(), thread_count); - defer (array_free(&worker_threads)); - - for_array(i, worker_threads) { - gbThread *t = &worker_threads[i]; - gb_thread_init(t); - } - defer (for_array(i, worker_threads) { - gb_thread_destroy(&worker_threads[i]); - }); + thread_pool_start(&parser_thread_pool); + thread_pool_wait_to_process(&parser_thread_pool); - auto errors = array_make<ParseFileError>(heap_allocator(), 0, 16); - - for (;;) { - bool are_any_alive = false; - for_array(i, worker_threads) { - gbThread *t = &worker_threads[i]; - if (gb_thread_is_running(t)) { - are_any_alive = true; - } else if (curr_import_index < p->files_to_process.count) { - auto curr_err = cast(ParseFileError)t->return_value; - if (curr_err != ParseFile_None) { - array_add(&errors, curr_err); - } else { - t->user_index = curr_import_index; - curr_import_index++; - gb_thread_start(t, parse_worker_file_proc, p); - are_any_alive = true; - } - } - } - if (!are_any_alive && curr_import_index >= p->files_to_process.count) { - break; - } - } - - if (errors.count > 0) { - return errors[errors.count-1]; - } - } else { - for_array(i, p->files_to_process) { - ParseFileError err = process_imported_file(p, p->files_to_process[i]); - if (err != ParseFile_None) { - return err; - } - } - } -#else - for_array(i, p->files_to_process) { - ImportedFile f = p->files_to_process[i]; - ParseFileError err = process_imported_file(p, f); + // NOTE(bill): Get the last error and use that + for (isize i = parser_thread_pool.task_tail-1; i >= 0; i--) { + WorkerTask *task = &parser_thread_pool.tasks[i]; + ParseFileError err = cast(ParseFileError)task->result; if (err != ParseFile_None) { return err; } } -#endif return ParseFile_None; } diff --git a/src/parser.hpp b/src/parser.hpp index 9f4f68a7b..f07f3ce0d 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -134,13 +134,26 @@ struct Parser { Map<AstPackage *> package_map; // Key: String (package name) Array<AstPackage *> packages; Array<ImportedPackage> package_imports; - Array<ImportedFile> files_to_process; + isize file_to_process_count; isize total_token_count; isize total_line_count; gbMutex file_add_mutex; gbMutex file_decl_mutex; }; + +gb_global ThreadPool parser_thread_pool = {}; + +struct ParserWorkerData { + Parser *parser; + ImportedFile imported_file; +}; + + + + + + enum ProcInlining { ProcInlining_none = 0, ProcInlining_inline = 1, @@ -186,11 +199,12 @@ enum FieldFlag { FieldFlag_c_vararg = 1<<3, FieldFlag_auto_cast = 1<<4, + FieldFlag_Tags = 1<<10, FieldFlag_Results = 1<<16, FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_auto_cast, - FieldFlag_Struct = FieldFlag_using, + FieldFlag_Struct = FieldFlag_using|FieldFlag_Tags, }; enum StmtAllowFlag { @@ -208,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; \ @@ -228,11 +243,14 @@ enum StmtAllowFlag { Ast *body; \ u64 tags; \ ProcInlining inlining; \ + Token where_token; \ + Array<Ast *> where_clauses; \ }) \ AST_KIND(CompoundLit, "compound literal", struct { \ Ast *type; \ Array<Ast *> elems; \ Token open, close; \ + i64 max_count; \ }) \ AST_KIND(_ExprBegin, "", bool) \ AST_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ @@ -325,6 +343,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<Ast *> list; \ @@ -412,6 +439,7 @@ AST_KIND(_DeclEnd, "", bool) \ Array<Ast *> names; \ Ast * type; \ Ast * default_value; \ + Token tag; \ u32 flags; \ CommentGroup * docs; \ CommentGroup * comment; \ @@ -465,20 +493,24 @@ AST_KIND(_TypeBegin, "", bool) \ Ast *elem; \ }) \ AST_KIND(StructType, "struct type", struct { \ - Token token; \ - Array<Ast *> fields; \ - isize field_count; \ - Ast *polymorphic_params; \ - Ast *align; \ - bool is_packed; \ - bool is_raw_union; \ + Token token; \ + Array<Ast *> fields; \ + isize field_count; \ + Ast *polymorphic_params; \ + Ast *align; \ + Token where_token; \ + Array<Ast *> where_clauses; \ + bool is_packed; \ + bool is_raw_union; \ }) \ AST_KIND(UnionType, "union type", struct { \ - Token token; \ - Array<Ast *> variants; \ - Ast *polymorphic_params; \ - Ast * align; \ - bool no_nil; \ + Token token; \ + Array<Ast *> variants; \ + Ast *polymorphic_params; \ + Ast * align; \ + bool no_nil; \ + Token where_token; \ + Array<Ast *> where_clauses; \ }) \ AST_KIND(EnumType, "enum type", struct { \ Token token; \ diff --git a/src/priority_queue.cpp b/src/priority_queue.cpp index 7c36e6a22..aee2061b5 100644 --- a/src/priority_queue.cpp +++ b/src/priority_queue.cpp @@ -20,7 +20,7 @@ bool priority_queue_shift_down(PriorityQueue<T> *pq, isize i0, isize n) { if (j2 < n && pq->cmp(&pq->queue[0], j2, j1) < 0) { j = j2; } - if (pq->cmp(&pq->queue[0], i, j) < 0) break; + if (pq->cmp(&pq->queue[0], j, i) >= 0) break; pq->swap(&pq->queue[0], i, j); i = j; @@ -32,7 +32,7 @@ template <typename T> void priority_queue_shift_up(PriorityQueue<T> *pq, isize j) { while (0 <= j && j < pq->queue.count) { isize i = (j-1)/2; - if (i == j || pq->cmp(&pq->queue[0], i, j) < 0) { + if (i == j || pq->cmp(&pq->queue[0], j, i) >= 0) { break; } pq->swap(&pq->queue[0], i, j); diff --git a/src/range_cache.cpp b/src/range_cache.cpp new file mode 100644 index 000000000..9701fb432 --- /dev/null +++ b/src/range_cache.cpp @@ -0,0 +1,70 @@ + +// Integers only +struct RangeValue { + i64 lo; + i64 hi; +}; + +struct RangeCache { + Array<RangeValue> ranges; +}; + + +RangeCache range_cache_make(gbAllocator a) { + RangeCache cache = {}; + array_init(&cache.ranges, a); + return cache; +} + +void range_cache_destroy(RangeCache *c) { + array_free(&c->ranges); +} + +bool range_cache_add_index(RangeCache *c, i64 index) { + for_array(i, c->ranges) { + RangeValue v = c->ranges[i]; + if (v.lo <= index && index <= v.hi) { + return false; + } + } + RangeValue v = {index, index}; + array_add(&c->ranges, v); + return true; +} + + +bool range_cache_add_range(RangeCache *c, i64 lo, i64 hi) { + GB_ASSERT(lo <= hi); + for_array(i, c->ranges) { + RangeValue v = c->ranges[i]; + if (hi < v.lo) { + continue; + } + if (lo > v.hi) { + continue; + } + + if (v.hi < hi) { + v.hi = hi; + } + if (lo < v.lo) { + v.lo = lo; + } + c->ranges[i] = v; + return false; + } + RangeValue v = {lo, hi}; + array_add(&c->ranges, v); + return true; +} + + +bool range_cache_index_exists(RangeCache *c, i64 index) { + for_array(i, c->ranges) { + RangeValue v = c->ranges[i]; + if (v.lo <= index && index <= v.hi) { + return true; + } + } + return false; +} diff --git a/src/string.cpp b/src/string.cpp index 774061edf..6812c5c39 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -404,7 +404,7 @@ String16 string_to_string16(gbAllocator a, String s) { } text[len] = 0; - return make_string16(text, len-1); + return make_string16(text, len); } @@ -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<u8>(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/thread_pool.cpp b/src/thread_pool.cpp new file mode 100644 index 000000000..2467ba609 --- /dev/null +++ b/src/thread_pool.cpp @@ -0,0 +1,184 @@ +// worker_queue.cpp + +#define WORKER_TASK_PROC(name) isize name(void *data) +typedef WORKER_TASK_PROC(WorkerTaskProc); + +struct WorkerTask { + WorkerTaskProc *do_work; + void *data; + isize result; +}; + + +struct ThreadPool { + gbMutex mutex; + gbSemaphore sem_available; + gbAtomic32 processing_work_count; + bool is_running; + + gbAllocator allocator; + + WorkerTask *tasks; + isize volatile task_head; + isize volatile task_tail; + isize volatile task_capacity; + + gbThread *threads; + isize thread_count; + + char worker_prefix[10]; + i32 worker_prefix_len; +}; + +void thread_pool_init(ThreadPool *pool, gbAllocator const &a, isize thread_count, char const *worker_prefix = nullptr); +void thread_pool_destroy(ThreadPool *pool); +void thread_pool_start(ThreadPool *pool); +void thread_pool_join(ThreadPool *pool); +void thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data); +void thread_pool_kick(ThreadPool *pool); +void thread_pool_kick_and_wait(ThreadPool *pool); +GB_THREAD_PROC(worker_thread_internal); + +void thread_pool_init(ThreadPool *pool, gbAllocator const &a, isize thread_count, char const *worker_prefix) { + pool->allocator = a; + pool->task_head = 0; + pool->task_tail = 0; + pool->task_capacity = 1024; + pool->tasks = gb_alloc_array(a, WorkerTask, pool->task_capacity); + pool->thread_count = gb_max(thread_count, 0); + pool->threads = gb_alloc_array(a, gbThread, pool->thread_count); + gb_mutex_init(&pool->mutex); + gb_semaphore_init(&pool->sem_available); + pool->is_running = true; + + pool->worker_prefix_len = 0; + if (worker_prefix) { + i32 worker_prefix_len = cast(i32)gb_strlen(worker_prefix); + worker_prefix_len = gb_min(worker_prefix_len, 10); + gb_memmove(pool->worker_prefix, worker_prefix, worker_prefix_len); + pool->worker_prefix_len = worker_prefix_len; + } + + for (isize i = 0; i < pool->thread_count; i++) { + gbThread *t = &pool->threads[i]; + gb_thread_init(t); + t->user_index = i; + #if 0 + // TODO(bill): Fix this on Linux as it causes a seg-fault + if (pool->worker_prefix_len > 0) { + char worker_name[16] = {}; + gb_snprintf(worker_name, gb_size_of(worker_name), "%.*s%u", pool->worker_prefix_len, pool->worker_prefix, cast(u16)i); + gb_thread_set_name(t, worker_name); + } + #endif + } +} + +void thread_pool_start(ThreadPool *pool) { + for (isize i = 0; i < pool->thread_count; i++) { + gbThread *t = &pool->threads[i]; + gb_thread_start(t, worker_thread_internal, pool); + } +} + +void thread_pool_join(ThreadPool *pool) { + pool->is_running = false; + + gb_semaphore_post(&pool->sem_available, cast(i32)pool->thread_count); + + gb_yield(); + + for (isize i = 0; i < pool->thread_count; i++) { + gbThread *t = &pool->threads[i]; + gb_thread_join(t); + } +} + + +void thread_pool_destroy(ThreadPool *pool) { + thread_pool_join(pool); + + gb_semaphore_destroy(&pool->sem_available); + gb_mutex_destroy(&pool->mutex); + gb_free(pool->allocator, pool->threads); + pool->thread_count = 0; + gb_free(pool->allocator, pool->tasks); + pool->task_head = 0; + pool->task_tail = 0; + pool->task_capacity = 0; +} + + +void thread_pool_add_task(ThreadPool *pool, WorkerTaskProc *proc, void *data) { + gb_mutex_lock(&pool->mutex); + + if (pool->task_tail == pool->task_capacity) { + isize new_cap = 2*pool->task_capacity + 8; + WorkerTask *new_tasks = gb_alloc_array(pool->allocator, WorkerTask, new_cap); + gb_memmove(new_tasks, pool->tasks, (pool->task_tail)*gb_size_of(WorkerTask)); + pool->tasks = new_tasks; + pool->task_capacity = new_cap; + } + WorkerTask task = {}; + task.do_work = proc; + task.data = data; + + pool->tasks[pool->task_tail++] = task; + gb_semaphore_post(&pool->sem_available, 1); + gb_mutex_unlock(&pool->mutex); +} + +bool thread_pool_try_and_pop_task(ThreadPool *pool, WorkerTask *task) { + bool got_task = false; + if (gb_mutex_try_lock(&pool->mutex)) { + if (pool->task_tail > pool->task_head) { + gb_atomic32_fetch_add(&pool->processing_work_count, +1); + *task = pool->tasks[pool->task_head++]; + got_task = true; + } + gb_mutex_unlock(&pool->mutex); + } + return got_task; +} +void thread_pool_do_work(ThreadPool *pool, WorkerTask *task) { + task->result = task->do_work(task->data); + gb_atomic32_fetch_add(&pool->processing_work_count, -1); +} + +void thread_pool_wait_to_process(ThreadPool *pool) { + while (pool->task_tail > pool->task_head || gb_atomic32_load(&pool->processing_work_count) != 0) { + WorkerTask task = {}; + if (thread_pool_try_and_pop_task(pool, &task)) { + thread_pool_do_work(pool, &task); + } + + // Safety-kick + if (pool->task_tail > pool->task_head && gb_atomic32_load(&pool->processing_work_count) == 0) { + gb_mutex_lock(&pool->mutex); + gb_semaphore_post(&pool->sem_available, cast(i32)(pool->task_tail-pool->task_head)); + gb_mutex_unlock(&pool->mutex); + } + + gb_yield(); + } + + thread_pool_join(pool); +} + + +GB_THREAD_PROC(worker_thread_internal) { + ThreadPool *pool = cast(ThreadPool *)thread->user_data; + while (pool->is_running) { + gb_semaphore_wait(&pool->sem_available); + + WorkerTask task = {}; + if (thread_pool_try_and_pop_task(pool, &task)) { + thread_pool_do_work(pool, &task); + } + } + // Cascade + gb_semaphore_release(&pool->sem_available); + + return 0; +} + diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a551f0545..4b0db7ac4 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -86,6 +86,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \ TOKEN_KIND(Token_package, "package"), \ TOKEN_KIND(Token_typeid, "typeid"), \ TOKEN_KIND(Token_when, "when"), \ + TOKEN_KIND(Token_where, "where"), \ TOKEN_KIND(Token_if, "if"), \ TOKEN_KIND(Token_else, "else"), \ TOKEN_KIND(Token_for, "for"), \ @@ -400,6 +401,15 @@ void syntax_error(Token token, char *fmt, ...) { va_end(va); } +void syntax_error(TokenPos pos, char *fmt, ...) { + va_list va; + va_start(va, fmt); + Token token = {}; + token.pos = pos; + syntax_error_va(token, fmt, va); + va_end(va); +} + void syntax_warning(Token token, char *fmt, ...) { va_list va; va_start(va, fmt); @@ -745,9 +755,11 @@ 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); + break; } end: diff --git a/src/types.cpp b/src/types.cpp index dc7ecb946..bef69ee30 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, @@ -107,21 +112,23 @@ struct BasicType { struct TypeStruct { Array<Entity *> fields; - Ast *node; - Scope * scope; - - Array<i64> offsets; - bool are_offsets_set; - bool are_offsets_being_processed; - bool is_packed; - bool is_raw_union; - bool is_polymorphic; - bool is_poly_specialized; + Array<String> tags; + Array<i64> offsets; + Ast * node; + Scope * scope; + Type * polymorphic_params; // Type_Tuple Type * polymorphic_parent; - i64 custom_align; // NOTE(bill): Only used in structs at the moment + i64 custom_align; Entity * names; + + bool are_offsets_set; + bool are_offsets_being_processed; + bool is_packed; + bool is_raw_union; + bool is_polymorphic; + bool is_poly_specialized; }; struct TypeUnion { @@ -131,12 +138,11 @@ struct TypeUnion { i64 variant_block_size; i64 custom_align; i64 tag_size; + Type * polymorphic_params; // Type_Tuple + Type * polymorphic_parent; bool no_nil; - - bool is_polymorphic; - bool is_poly_specialized; - Type * polymorphic_params; // Type_Tuple - Type * polymorphic_parent; + bool is_polymorphic; + bool is_poly_specialized; }; #define TYPE_KINDS \ @@ -184,7 +190,9 @@ struct TypeUnion { TYPE_KIND(Tuple, struct { \ Array<Entity *> variables; /* Entity_Variable */ \ Array<i64> offsets; \ + bool are_offsets_being_processed; \ bool are_offsets_set; \ + bool is_packed; \ }) \ TYPE_KIND(Proc, struct { \ Ast *node; \ @@ -195,9 +203,9 @@ struct TypeUnion { i32 result_count; \ Array<Type *> abi_compat_params; \ Type * abi_compat_result_type; \ - bool return_by_pointer; \ - bool variadic; \ i32 variadic_index; \ + bool variadic; \ + bool abi_types_set; \ bool require_results; \ bool c_vararg; \ bool is_polymorphic; \ @@ -205,6 +213,7 @@ struct TypeUnion { bool has_proc_default_values; \ bool has_named_results; \ bool diverging; /* no return */ \ + bool return_by_pointer; \ u64 tags; \ isize specialization_count; \ ProcCallingConvention calling_convention; \ @@ -341,6 +350,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")}}, @@ -376,6 +388,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")}}, @@ -411,6 +424,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]; @@ -445,6 +461,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]; @@ -471,6 +488,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; @@ -495,6 +513,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; @@ -535,8 +554,27 @@ i64 type_offset_of (Type *t, i32 index); gbString type_to_string (Type *type); void init_map_internal_types(Type *type); Type * bit_set_to_int(Type *t); +bool are_types_identical(Type *x, Type *y); +bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) { + if (ptr_set_exists(s, t)) { + return true; + } + + // TODO(bill, 2019-10-05): This is very slow and it's probably a lot + // faster to cache types correctly + for_array(i, s->entries) { + Type *f = s->entries[i].ptr; + if (are_types_identical(t, f)) { + ptr_set_add(s, t); + return true; + } + } + + return false; +} + Type *base_type(Type *t) { for (;;) { if (t == nullptr) { @@ -923,6 +961,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) { @@ -1037,14 +1082,40 @@ Type *core_array_type(Type *t) { return t; } +// NOTE(bill): type can be easily compared using memcmp +bool is_type_simple_compare(Type *t) { + t = core_type(t); + switch (t->kind) { + case Type_Array: + return is_type_simple_compare(t->Array.elem); + + case Type_Basic: + if (t->Basic.flags & (BasicFlag_Integer|BasicFlag_Float|BasicFlag_Complex|BasicFlag_Rune|BasicFlag_Pointer)) { + return true; + } + return false; + + case Type_Pointer: + case Type_Proc: + case Type_BitSet: + case Type_BitField: + return true; + } + + return false; +} + 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"); @@ -1099,12 +1170,11 @@ bool is_type_integer_endian_big(Type *t) { return is_type_integer_endian_big(bit_set_to_int(t)); } else if (t->kind == Type_Pointer) { return is_type_integer_endian_big(&basic_types[Basic_uintptr]); - } else { - GB_PANIC("Unsupported type: %s", type_to_string(t)); } return build_context.endian_kind == TargetEndian_Big; } + bool is_type_integer_endian_little(Type *t) { t = core_type(t); if (t->kind == Type_Basic) { @@ -1118,11 +1188,24 @@ bool is_type_integer_endian_little(Type *t) { return is_type_integer_endian_little(bit_set_to_int(t)); } else if (t->kind == Type_Pointer) { return is_type_integer_endian_little(&basic_types[Basic_uintptr]); - } else { - GB_PANIC("Unsupported type: %s", type_to_string(t)); } return build_context.endian_kind == TargetEndian_Little; } +bool is_type_endian_big(Type *t) { + return is_type_integer_endian_big(t); +} +bool is_type_endian_little(Type *t) { + return is_type_integer_endian_little(t); +} + +bool is_type_dereferenceable(Type *t) { + if (is_type_rawptr(t)) { + return false; + } + return is_type_pointer(t); +} + + bool is_type_different_to_arch_endianness(Type *t) { switch (build_context.endian_kind) { @@ -1284,6 +1367,19 @@ bool is_type_indexable(Type *t) { return false; } +bool is_type_sliceable(Type *t) { + Type *bt = base_type(t); + switch (bt->kind) { + case Type_Basic: + return bt->Basic.kind == Basic_string; + case Type_Array: + case Type_Slice: + case Type_DynamicArray: + return true; + } + return false; +} + bool is_type_polymorphic_record(Type *t) { t = base_type(t); @@ -1663,6 +1759,12 @@ bool are_types_identical(Type *x, Type *y) { if (xf_is_using ^ yf_is_using) { return false; } + if (x->Struct.tags.count != y->Struct.tags.count) { + return false; + } + if (x->Struct.tags.count > 0 && x->Struct.tags[i] != y->Struct.tags[i]) { + return false; + } } return true; } @@ -1683,7 +1785,8 @@ bool are_types_identical(Type *x, Type *y) { case Type_Tuple: if (y->kind == Type_Tuple) { - if (x->Tuple.variables.count == y->Tuple.variables.count) { + if (x->Tuple.variables.count == y->Tuple.variables.count && + x->Tuple.is_packed == y->Tuple.is_packed) { for_array(i, x->Tuple.variables) { Entity *xe = x->Tuple.variables[i]; Entity *ye = y->Tuple.variables[i]; @@ -1763,6 +1866,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; } @@ -2131,19 +2235,22 @@ Selection lookup_field_with_selection(Type *type_, String field_name, bool is_ty if (type->Array.count <= 4) { // HACK(bill): Memory leak switch (type->Array.count) { - #define _ARRAY_FIELD_CASE(_length, _name) \ - case (_length): \ - if (field_name == _name) { \ + #define _ARRAY_FIELD_CASE_IF(_length, _name) \ + if (field_name == (_name)) { \ selection_add_index(&sel, (_length)-1); \ sel.entity = alloc_entity_array_elem(nullptr, make_token_ident(str_lit(_name)), type->Array.elem, (_length)-1); \ return sel; \ - } \ + } + #define _ARRAY_FIELD_CASE(_length, _name0, _name1) \ + case (_length): \ + _ARRAY_FIELD_CASE_IF(_length, _name0); \ + _ARRAY_FIELD_CASE_IF(_length, _name1); \ /*fallthrough*/ - _ARRAY_FIELD_CASE(4, "w"); - _ARRAY_FIELD_CASE(3, "z"); - _ARRAY_FIELD_CASE(2, "y"); - _ARRAY_FIELD_CASE(1, "x"); + _ARRAY_FIELD_CASE(4, "w", "a"); + _ARRAY_FIELD_CASE(3, "z", "b"); + _ARRAY_FIELD_CASE(2, "y", "g"); + _ARRAY_FIELD_CASE(1, "x", "r"); default: break; #undef _ARRAY_FIELD_CASE @@ -2254,7 +2361,9 @@ i64 type_size_of(Type *t) { return 0; } // NOTE(bill): Always calculate the size when it is a Type_Basic - if (t->kind != Type_Basic && t->cached_size >= 0) { + if (t->kind == Type_Named && t->cached_size >= 0) { + + } else if (t->kind != Type_Basic && t->cached_size >= 0) { return t->cached_size; } TypePath path = {0}; @@ -2269,7 +2378,9 @@ i64 type_align_of(Type *t) { return 1; } // NOTE(bill): Always calculate the size when it is a Type_Basic - if (t->kind != Type_Basic && t->cached_align > 0) { + if (t->kind == Type_Named && t->cached_align >= 0) { + + } if (t->kind != Type_Basic && t->cached_align > 0) { return t->cached_align; } @@ -2303,6 +2414,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; @@ -2488,9 +2601,9 @@ bool type_set_offsets(Type *t) { } } else if (is_type_tuple(t)) { if (!t->Tuple.are_offsets_set) { - t->Struct.are_offsets_being_processed = true; - t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, false, false); - t->Struct.are_offsets_being_processed = false; + t->Tuple.are_offsets_being_processed = true; + t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false); + t->Tuple.are_offsets_being_processed = false; t->Tuple.are_offsets_set = true; return true; } @@ -2913,35 +3026,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"); } } } diff --git a/src/unicode.cpp b/src/unicode.cpp index 0ad658806..679d56365 100644 --- a/src/unicode.cpp +++ b/src/unicode.cpp @@ -2,7 +2,6 @@ #pragma warning(disable: 4245) extern "C" { -#include "utf8proc/utf8proc.h" #include "utf8proc/utf8proc.c" } #pragma warning(pop) diff --git a/src/utf8proc/utf8proc.c b/src/utf8proc/utf8proc.c index e821889c6..29fc250fc 100644 --- a/src/utf8proc/utf8proc.c +++ b/src/utf8proc/utf8proc.c @@ -40,7 +40,6 @@ * Implementation of libutf8proc. */ - #include "utf8proc.h" #include "utf8proc_data.c" |