mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
132 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7057034b75 | |||
| 1430ca30a3 | |||
| e63393e394 | |||
| 784f3ecf7e | |||
| 54ea70df98 | |||
| c7575164cc | |||
| 99125dc743 | |||
| b78e970698 | |||
| 5b8be25938 | |||
| 29efdc5fc1 | |||
| a80872b60d | |||
| 822bb51b55 | |||
| c2fa79012e | |||
| 3fd37c6dc5 | |||
| 0ea815db49 | |||
| 91ed51ff5c | |||
| 4d0afc55c3 | |||
| 9a1566d665 | |||
| a713e33007 | |||
| c5411a25a9 | |||
| 95692fda52 | |||
| 813a028ed0 | |||
| 0c22081e5f | |||
| 6d9fadf351 | |||
| a213061f33 | |||
| d1a0a46141 | |||
| 187b186112 | |||
| 5041a35b95 | |||
| 92d4fcedee | |||
| c69df7cd3a | |||
| 67d8f48553 | |||
| b4a339f2e3 | |||
| 0d7bf58b60 | |||
| abb9930725 | |||
| 169310a9f6 | |||
| 23a0a6de4b | |||
| 0d2dbee84e | |||
| d8d22e34dd | |||
| 627ee002e8 | |||
| 8e73d1ce1f | |||
| b53d16d1d5 | |||
| f5819eafa9 | |||
| 5916e71d4f | |||
| 913b9b6447 | |||
| 8e55bb2a6c | |||
| 98d493504b | |||
| 3a3202fbc6 | |||
| aaf355e750 | |||
| 0683d2b4f4 | |||
| d7fdd3d7b8 | |||
| 83ebb24015 | |||
| 70f9cacdce | |||
| 6b33b254e9 | |||
| c0019cc305 | |||
| c067a1f0ec | |||
| 63345cd0d8 | |||
| e41d6261c2 | |||
| 3e80411d37 | |||
| f952c7c747 | |||
| 642256f9ba | |||
| c9c82da1f3 | |||
| 382a5ca6a2 | |||
| 96e8bb5b6f | |||
| 22afac2b90 | |||
| 01da0d1377 | |||
| 8ce58573df | |||
| ce0d874efd | |||
| 2c8b99337b | |||
| 5008e2c88b | |||
| 90fc9abeae | |||
| dc303cde21 | |||
| 24b33374b7 | |||
| 3315dc7f25 | |||
| 77b3295de5 | |||
| 1349aa6f2c | |||
| a75ccb6fbc | |||
| 7a28827602 | |||
| c61015b1fe | |||
| e935f8e2ff | |||
| 690c682847 | |||
| f541dd40db | |||
| c7bb861d3c | |||
| 188b290dd5 | |||
| c6ff961088 | |||
| c26990c22d | |||
| c34d839f9f | |||
| 5562364a98 | |||
| 32150e401e | |||
| aaec8bf423 | |||
| 0fcbda951a | |||
| e2734a2dc6 | |||
| 5adfbec847 | |||
| 4ef4605d6d | |||
| 2aa402f462 | |||
| 00f6bee454 | |||
| d890731716 | |||
| 231ea8b026 | |||
| 5bbdb3a3a3 | |||
| 27aa07307b | |||
| 20b9f1ff59 | |||
| 561c583b3f | |||
| 8d5896ab7e | |||
| 802b1a70f8 | |||
| aaa4dd5c36 | |||
| 9d19ee7e4c | |||
| 8df3175f10 | |||
| ebb10e5597 | |||
| 047f883078 | |||
| 320c22e08a | |||
| a9398bf30f | |||
| 7829421085 | |||
| c50aabd916 | |||
| 3f3122bccc | |||
| fc1a006de1 | |||
| 754b368140 | |||
| a49e888ce6 | |||
| 99c663d9f3 | |||
| afac95e092 | |||
| 05486f9fa3 | |||
| cad46ae51c | |||
| 3424b2badd | |||
| 3445a28c4a | |||
| 7f6b83d50c | |||
| 72d4bfb32a | |||
| 37f7630a9e | |||
| 73c5c5d5d3 | |||
| 584869730a | |||
| 90ab448bca | |||
| 8becbdc1b2 | |||
| eeeb90c441 | |||
| 6efd400c98 | |||
| 5cfa4ba580 |
+9
-4
@@ -251,8 +251,13 @@ paket-files/
|
||||
|
||||
|
||||
# Project Specific
|
||||
|
||||
# - Windows
|
||||
*.sln
|
||||
!misc/llvm-bim/lli.exe
|
||||
!misc/llvm-bim/opt.exe
|
||||
builds
|
||||
bin
|
||||
builds/
|
||||
bin/
|
||||
*.exe
|
||||
|
||||
# - Linux/MacOS
|
||||
odin
|
||||
odin.dSYM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<img src="logo-slim.png" alt="Odin logo" height="74">
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" height="74">
|
||||
|
||||
# The Odin Programming Language
|
||||
|
||||
@@ -10,6 +10,8 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
|
||||
* metaprogramming
|
||||
* designed for good programmers
|
||||
|
||||
Website: [https://odin.handmade.network/](https://odin.handmade.network/)
|
||||
|
||||
## Demonstrations:
|
||||
* First Talk & Demo
|
||||
- [Talk](https://youtu.be/TMCkT-uASaE?t=338)
|
||||
@@ -19,14 +21,28 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
|
||||
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
|
||||
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
|
||||
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
|
||||
|
||||
## Requirements to build and run
|
||||
|
||||
* Windows
|
||||
* x86-64
|
||||
* MSVC 2015 installed (C99 support)
|
||||
* Requires MSVC's link.exe as the linker
|
||||
- run `vcvarsall.bat` to setup the path
|
||||
- Windows
|
||||
* x86-64
|
||||
* MSVC 2015 installed (C99 support)
|
||||
* [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe`
|
||||
* Requires MSVC's link.exe as the linker
|
||||
* run `vcvarsall.bat` to setup the path
|
||||
|
||||
- MacOS
|
||||
* x86-64
|
||||
* LLVM explicitly installed (`brew install llvm`)
|
||||
* XCode installed (for the linker)
|
||||
|
||||
- GNU/Linux
|
||||
* x86-64
|
||||
* Build tools (ld)
|
||||
* LLVM installed
|
||||
* Clang installed (temporary - this is calling the linker for now)
|
||||
|
||||
## Warnings
|
||||
|
||||
|
||||
@@ -45,15 +45,11 @@ del *.ilk > NUL 2> NUL
|
||||
cl %compiler_settings% "src\main.c" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
&& odin run code/demo.odin
|
||||
rem && odin build code/Jaze/src/main.odin
|
||||
rem && odin build_dll code/example.odin ^
|
||||
rem odin run code/demo.odin
|
||||
rem && odin build code/metagen.odin ^
|
||||
rem && call "code\metagen.exe" "src\ast_nodes.metagen"
|
||||
rem && odin run code/Jaze/src/main.odin
|
||||
|
||||
rem pushd src\asm
|
||||
rem nasm hellope.asm -fwin64 -o hellope.obj ^
|
||||
rem && cl /nologo hellope.obj /link kernel32.lib /entry:main ^
|
||||
rem && hellope.exe
|
||||
rem popd
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
release_mode=0
|
||||
|
||||
warnings_to_disable="-std=c11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
|
||||
libraries="-pthread -ldl -lm"
|
||||
other_args=""
|
||||
compiler="clang"
|
||||
|
||||
if [ "$release_mode" -eq "0" ]; then
|
||||
other_args="${other_args} -g -fno-inline-functions"
|
||||
fi
|
||||
if [[ "$(uname)" == "Darwin" ]]; then
|
||||
|
||||
# Set compiler to clang on MacOS
|
||||
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
|
||||
compiler="clang"
|
||||
|
||||
other_args="${other_args} -liconv"
|
||||
fi
|
||||
|
||||
${compiler} src/main.c ${warnings_to_disable} ${libraries} ${other_args} -o odin
|
||||
|
||||
./odin run code/demo.odin
|
||||
+10
-159
@@ -1,167 +1,18 @@
|
||||
#import "fmt.odin";
|
||||
#import "atomic.odin";
|
||||
#import "hash.odin";
|
||||
#import "math.odin";
|
||||
#import "mem.odin";
|
||||
#import "opengl.odin";
|
||||
#import "os.odin";
|
||||
#import "strconv.odin";
|
||||
#import "sync.odin";
|
||||
|
||||
main :: proc() {
|
||||
// buf: [64]byte;
|
||||
// // len := strconv.generic_ftoa(buf[..], 123.5431, 'f', 4, 64);
|
||||
// x := 624.123;
|
||||
// s := strconv.format_float(buf[..], x, 'f', 6, 64);
|
||||
// fmt.println(s);
|
||||
// fmt.printf("%3d\n", 102);
|
||||
immutable program := "+ + * - /";
|
||||
accumulator := 0;
|
||||
|
||||
s := new_slice(int, 0, 10);
|
||||
append(s, 1, 2, 6, 3, 6, 5, 5, 5, 5, 1, 2);
|
||||
fmt.println(s);
|
||||
|
||||
|
||||
when false {
|
||||
/*
|
||||
Version 0.1.1
|
||||
|
||||
Added:
|
||||
* Dynamic Arrays `[dynamic]Type`
|
||||
* Dynamic Maps `map[Key]Value`
|
||||
* Dynamic array and map literals
|
||||
* Custom struct alignemnt `struct #align 8 { bar: i8 }`
|
||||
* Allow `_` in numbers
|
||||
* Variadic `append`
|
||||
* fmt.sprint*
|
||||
* Entities prefixes with an underscore do not get exported on imports
|
||||
* Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps
|
||||
* enum types have an implict `names` field, a []string of all the names in that enum
|
||||
* immutable variables are "completely immutable" - rules need a full explanation
|
||||
* `slice_to_bytes` - convert any slice to a slice of bytes
|
||||
* `union_cast` allows for optional ok check
|
||||
* Record type field `names` (struct/raw_union/enum)
|
||||
* ?: ternary operator
|
||||
* Unions with variants and common fields
|
||||
* New built-in procedures
|
||||
- `delete` to delete map entries `delete(m, key)`
|
||||
- `clear` to clear dynamic maps and arrays `clear(map_or_array)`
|
||||
- `reserve` to reserve space for the dynamic maps and arrays `reserve(map_or_array)`
|
||||
* Unexported entities and fields using an underscore prefix
|
||||
|
||||
Removed:
|
||||
* Maybe/option types
|
||||
* Remove `type` keyword and other "reserved" keywords
|
||||
* `compile_assert` and `assert`return the value of the condition for semantic reasons
|
||||
|
||||
Changed:
|
||||
* thread_local -> #thread_local
|
||||
* #include -> #load
|
||||
* Files only get checked if they are actually used
|
||||
* match x in y {} // For type match statements
|
||||
* Version numbering now starts from 0.1.0 and uses the convention:
|
||||
- major.minor.patch
|
||||
* Core library additions to Windows specific stuff
|
||||
|
||||
Fixes:
|
||||
* Many fmt.* fixes
|
||||
* Overloading bug due to comparison of named types
|
||||
* Overloading bug due to `#import .` collision
|
||||
* disallow a `cast` from pointers of unions
|
||||
* Minor bugs in generated IR code for slices
|
||||
|
||||
To come very Soon™:
|
||||
* Linux and OS X builds (unofficial ones do exist already)
|
||||
*/
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
{
|
||||
Fruit :: enum {
|
||||
APPLE,
|
||||
BANANA,
|
||||
COCONUT,
|
||||
for token in program {
|
||||
match token {
|
||||
case '+': accumulator += 1;
|
||||
case '-': accumulator -= 1;
|
||||
case '*': accumulator *= 2;
|
||||
case '/': accumulator /= 2;
|
||||
default: // Ignore everything else
|
||||
}
|
||||
fmt.println(Fruit.names);
|
||||
}
|
||||
|
||||
{
|
||||
m: map[f32]int;
|
||||
reserve(m, 16);
|
||||
defer free(m);
|
||||
|
||||
m[1.0] = 1278;
|
||||
m[2.0] = 7643;
|
||||
m[3.0] = 564;
|
||||
_, ok := m[3.0];
|
||||
c := m[3.0];
|
||||
assert(ok && c == 564);
|
||||
|
||||
fmt.print("map[");
|
||||
i := 0;
|
||||
for val, key in m {
|
||||
if i > 0 {
|
||||
fmt.print(", ");
|
||||
}
|
||||
fmt.printf("%v=%v", key, val);
|
||||
i += 1;
|
||||
}
|
||||
fmt.println("]");
|
||||
}
|
||||
{
|
||||
m := map[string]u32{
|
||||
"a" = 56,
|
||||
"b" = 13453,
|
||||
"c" = 7654,
|
||||
};
|
||||
defer free(m);
|
||||
|
||||
c := m["c"];
|
||||
_, ok := m["c"];
|
||||
assert(ok && c == 7654);
|
||||
|
||||
fmt.println(m);
|
||||
}
|
||||
|
||||
{
|
||||
x: [dynamic]f64;
|
||||
reserve(x, 16);
|
||||
defer free(x);
|
||||
append(x, 2_000_000.500_000, 3, 5, 7);
|
||||
|
||||
for p, i in x {
|
||||
if i > 0 { fmt.print(", "); }
|
||||
fmt.print(p);
|
||||
}
|
||||
fmt.println();
|
||||
}
|
||||
|
||||
{
|
||||
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
|
||||
defer free(x);
|
||||
fmt.println(x);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
Vec3 :: [vector 3]f32;
|
||||
|
||||
x := Vec3{1, 2, 3};
|
||||
y := Vec3{4, 5, 6};
|
||||
fmt.println(x < y);
|
||||
fmt.println(x + y);
|
||||
fmt.println(x - y);
|
||||
fmt.println(x * y);
|
||||
fmt.println(x / y);
|
||||
|
||||
for i in x {
|
||||
fmt.println(i);
|
||||
}
|
||||
|
||||
compile_assert(size_of([vector 7]bool) == size_of([7]bool));
|
||||
compile_assert(size_of([vector 7]i32) == size_of([7]i32));
|
||||
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
|
||||
}
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n", program, accumulator);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+46
-39
@@ -1,4 +1,5 @@
|
||||
#import "win32.odin" when ODIN_OS == "windows";
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#import wgl "sys/wgl.odin" when ODIN_OS == "windows";
|
||||
#import "fmt.odin";
|
||||
#import "math.odin";
|
||||
#import "os.odin";
|
||||
@@ -12,11 +13,11 @@ time_now :: proc() -> f64 {
|
||||
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := counter as f64 / win32_perf_count_freq as f64;
|
||||
result := cast(f64)counter / cast(f64)win32_perf_count_freq;
|
||||
return result;
|
||||
}
|
||||
win32_print_last_error :: proc() {
|
||||
err_code := win32.GetLastError() as int;
|
||||
err_code := cast(int)win32.GetLastError();
|
||||
if err_code != 0 {
|
||||
fmt.println("GetLastError: %", err_code);
|
||||
}
|
||||
@@ -24,42 +25,42 @@ win32_print_last_error :: proc() {
|
||||
|
||||
// Yuk!
|
||||
to_c_string :: proc(s: string) -> []u8 {
|
||||
c_str := new_slice(u8, s.count+1);
|
||||
copy(c_str, s as []byte);
|
||||
c_str[s.count] = 0;
|
||||
c_str := make([]u8, len(s)+1);
|
||||
copy(c_str, cast([]byte)s);
|
||||
c_str[len(s)] = 0;
|
||||
return c_str;
|
||||
}
|
||||
|
||||
|
||||
Window :: struct {
|
||||
width, height: int;
|
||||
wc: win32.WNDCLASSEXA;
|
||||
dc: win32.HDC;
|
||||
hwnd: win32.HWND;
|
||||
opengl_context, rc: win32.HGLRC;
|
||||
c_title: []u8;
|
||||
width, height: int,
|
||||
wc: win32.WndClassExA,
|
||||
dc: win32.Hdc,
|
||||
hwnd: win32.Hwnd,
|
||||
opengl_context, rc: wgl.Hglrc,
|
||||
c_title: []u8,
|
||||
}
|
||||
|
||||
make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC) -> (Window, bool) {
|
||||
make_window :: proc(title: string, msg, height: int, window_proc: win32.Wnd_Proc) -> (Window, bool) {
|
||||
using win32;
|
||||
|
||||
w: Window;
|
||||
w.width, w.height = msg, height;
|
||||
|
||||
class_name := "Win32-Odin-Window\x00";
|
||||
c_class_name := class_name.data;
|
||||
if title[title.count-1] != 0 {
|
||||
c_class_name := ^class_name[0];
|
||||
if title[len(title)-1] != 0 {
|
||||
w.c_title = to_c_string(title);
|
||||
} else {
|
||||
w.c_title = title as []u8;
|
||||
w.c_title = cast([]u8)title;
|
||||
}
|
||||
|
||||
instance := GetModuleHandleA(nil);
|
||||
|
||||
w.wc = WNDCLASSEXA{
|
||||
size = size_of(WNDCLASSEXA) as u32,
|
||||
w.wc = WndClassExA{
|
||||
size = size_of(WndClassExA),
|
||||
style = CS_VREDRAW | CS_HREDRAW,
|
||||
instance = instance as HINSTANCE,
|
||||
instance = cast(Hinstance)instance,
|
||||
class_name = c_class_name,
|
||||
wnd_proc = window_proc,
|
||||
};
|
||||
@@ -70,10 +71,10 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
|
||||
}
|
||||
|
||||
w.hwnd = CreateWindowExA(0,
|
||||
c_class_name, w.c_title.data,
|
||||
c_class_name, ^w.c_title[0],
|
||||
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
w.width as i32, w.height as i32,
|
||||
cast(i32)w.width, cast(i32)w.height,
|
||||
nil, nil, instance, nil);
|
||||
|
||||
if w.hwnd == nil {
|
||||
@@ -85,7 +86,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
|
||||
|
||||
{
|
||||
pfd := PIXELFORMATDESCRIPTOR{
|
||||
size = size_of(PIXELFORMATDESCRIPTOR) as u32,
|
||||
size = size_of(PIXELFORMATDESCRIPTOR),
|
||||
version = 1,
|
||||
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
||||
pixel_type = PFD_TYPE_RGBA,
|
||||
@@ -97,19 +98,20 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
|
||||
};
|
||||
|
||||
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil);
|
||||
w.opengl_context = wglCreateContext(w.dc);
|
||||
wglMakeCurrent(w.dc, w.opengl_context);
|
||||
w.opengl_context = wgl.CreateContext(w.dc);
|
||||
wgl.MakeCurrent(w.dc, w.opengl_context);
|
||||
|
||||
attribs := [8]i32{
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
|
||||
wgl.CONTEXT_MINOR_VERSION_ARB, 1,
|
||||
wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
0, // NOTE(bill): tells the proc that this is the end of attribs
|
||||
};
|
||||
|
||||
wglCreateContextAttribsARB := wglGetProcAddress(("wglCreateContextAttribsARB\x00" as string).data) as wglCreateContextAttribsARBType;
|
||||
w.rc = wglCreateContextAttribsARB(w.dc, 0, ^attribs[0]);
|
||||
wglMakeCurrent(w.dc, w.rc);
|
||||
wgl_str := "wglCreateContextAttribsARB\x00";
|
||||
wglCreateContextAttribsARB := cast(wgl.Create_Context_Attribs_ARB_Type)wgl.GetProcAddress(^wgl_str[0]);
|
||||
w.rc = wglCreateContextAttribsARB(w.dc, nil, ^attribs[0]);
|
||||
wgl.MakeCurrent(w.dc, w.rc);
|
||||
SwapBuffers(w.dc);
|
||||
}
|
||||
|
||||
@@ -117,7 +119,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
|
||||
}
|
||||
|
||||
destroy_window :: proc(w: ^Window) {
|
||||
free(w.c_title.data);
|
||||
free(w.c_title);
|
||||
}
|
||||
|
||||
display_window :: proc(w: ^Window) {
|
||||
@@ -129,7 +131,7 @@ run :: proc() {
|
||||
using win32;
|
||||
using math;
|
||||
|
||||
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline {
|
||||
win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
|
||||
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
|
||||
os.exit(0);
|
||||
return 0;
|
||||
@@ -137,7 +139,7 @@ run :: proc() {
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc);
|
||||
window, window_success := make_window("Odin Language Demo", 854, 480, cast(Wnd_Proc)win32_proc);
|
||||
if !window_success {
|
||||
return;
|
||||
}
|
||||
@@ -153,10 +155,10 @@ run :: proc() {
|
||||
|
||||
for running {
|
||||
curr_time := time_now();
|
||||
dt := (curr_time - prev_time) as f32;
|
||||
dt := cast(f32)(curr_time - prev_time);
|
||||
prev_time = curr_time;
|
||||
|
||||
msg: MSG;
|
||||
msg: Msg;
|
||||
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
|
||||
if msg.message == WM_QUIT {
|
||||
running = false;
|
||||
@@ -178,7 +180,7 @@ run :: proc() {
|
||||
if is_key_down(Key_Code.UP) { v[1] += 1; }
|
||||
if is_key_down(Key_Code.DOWN) { v[1] -= 1; }
|
||||
|
||||
v = vec2_norm0(v);
|
||||
v = norm(v);
|
||||
|
||||
pos += v * Vec2{SPEED * dt};
|
||||
}
|
||||
@@ -188,8 +190,8 @@ run :: proc() {
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.LoadIdentity();
|
||||
gl.Ortho(0, window.width as f64,
|
||||
0, window.height as f64, 0, 1);
|
||||
gl.Ortho(0, cast(f64)window.width,
|
||||
0, cast(f64)window.height, 0, 1);
|
||||
|
||||
draw_rect :: proc(x, y, w, h: f32) {
|
||||
gl.Begin(gl.TRIANGLES);
|
||||
@@ -207,9 +209,14 @@ run :: proc() {
|
||||
draw_rect(pos.x, pos.y, 50, 50);
|
||||
|
||||
display_window(^window);
|
||||
ms_to_sleep := (16 - 1000*dt) as i32;
|
||||
ms_to_sleep := cast(i32)(16 - 1000*dt);
|
||||
if ms_to_sleep > 0 {
|
||||
win32.Sleep(ms_to_sleep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
run();
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ bounds_checking :: proc() {
|
||||
|
||||
{
|
||||
base: [10]int;
|
||||
s := base[2:6];
|
||||
s := base[2..6];
|
||||
a, b := -1, 6;
|
||||
|
||||
#no_bounds_check {
|
||||
@@ -164,7 +164,7 @@ type_introspection :: proc() {
|
||||
info = type_info_of_val(x); // by value
|
||||
// See: runtime.odin
|
||||
|
||||
match type i in info {
|
||||
match i in info {
|
||||
case Type_Info.Integer:
|
||||
fmt.println("integer!");
|
||||
case Type_Info.Float:
|
||||
@@ -174,7 +174,7 @@ type_introspection :: proc() {
|
||||
}
|
||||
|
||||
// Unsafe cast
|
||||
integer_info := cast(^Type_Info.Integer)info;
|
||||
integer_info := cast(^Type_Info.Integer)cast(rawptr)info;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -263,12 +263,12 @@ crazy_introspection :: proc() {
|
||||
}
|
||||
|
||||
fruit_ti := type_info(Fruit);
|
||||
name := (cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
|
||||
info := cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
|
||||
name := (union_cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
|
||||
info, _ := union_cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
|
||||
|
||||
fmt.printf("% :: enum % {\n", name, info.base);
|
||||
for i := 0; i < info.values.count; i += 1 {
|
||||
fmt.printf("\t%\t= %,\n", info.names[i], info.values[i]);
|
||||
fmt.printf("%s :: enum %T {\n", name, info.base);
|
||||
for i := 0; i < len(info.values); i++ {
|
||||
fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
|
||||
}
|
||||
fmt.printf("}\n");
|
||||
|
||||
|
||||
+361
-369
File diff suppressed because it is too large
Load Diff
+17
-17
@@ -1,14 +1,14 @@
|
||||
#import "fmt.odin"
|
||||
#import "utf8.odin"
|
||||
#import "hash.odin"
|
||||
#import "mem.odin"
|
||||
#import "fmt.odin";
|
||||
#import "utf8.odin";
|
||||
#import "hash.odin";
|
||||
#import "mem.odin";
|
||||
|
||||
main :: proc() {
|
||||
{ // New Standard Library stuff
|
||||
s := "Hello"
|
||||
s := "Hello";
|
||||
fmt.println(s,
|
||||
utf8.valid_string(s),
|
||||
hash.murmur64(s.data, s.count))
|
||||
hash.murmur64(cast([]byte)s));
|
||||
|
||||
// utf8.odin
|
||||
// hash.odin
|
||||
@@ -19,15 +19,15 @@ main :: proc() {
|
||||
}
|
||||
|
||||
{
|
||||
arena: mem.Arena
|
||||
mem.init_arena_from_context(^arena, mem.megabytes(16)) // Uses default allocator
|
||||
defer mem.free_arena(^arena)
|
||||
arena: mem.Arena;
|
||||
mem.init_arena_from_context(^arena, mem.megabytes(16)); // Uses default allocator
|
||||
defer mem.free_arena(^arena);
|
||||
|
||||
push_allocator mem.arena_allocator(^arena) {
|
||||
x := new(int)
|
||||
x^ = 1337
|
||||
x := new(int);
|
||||
x^ = 1337;
|
||||
|
||||
fmt.println(x^)
|
||||
fmt.println(x^);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -48,14 +48,14 @@ main :: proc() {
|
||||
|
||||
// You can also "push" a context
|
||||
|
||||
c := current_context() // Create copy of the allocator
|
||||
c.allocator = mem.arena_allocator(^arena)
|
||||
c := context; // Create copy of the allocator
|
||||
c.allocator = mem.arena_allocator(^arena);
|
||||
|
||||
push_context c {
|
||||
x := new(int)
|
||||
x^ = 365
|
||||
x := new(int);
|
||||
x^ = 365;
|
||||
|
||||
fmt.println(x^)
|
||||
fmt.println(x^);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-13
@@ -42,12 +42,12 @@ syntax :: proc() {
|
||||
};
|
||||
Thing2 :: struct {x: f32, y: int, z: ^[]int};
|
||||
|
||||
// Slice interals are now just a `ptr+count`
|
||||
slice: []int; compile_assert(size_of_val(slice) == 2*size_of(int));
|
||||
// Slice interals are now just a `ptr+len+cap`
|
||||
slice: []int; compile_assert(size_of_val(slice) == 3*size_of(int));
|
||||
|
||||
// Helper type - Help the reader understand what it is quicker
|
||||
My_Int :: type int;
|
||||
My_Proc :: type proc(int) -> f32;
|
||||
My_Int :: #type int;
|
||||
My_Proc :: #type proc(int) -> f32;
|
||||
|
||||
|
||||
// All declarations with : are either variable or constant
|
||||
@@ -59,6 +59,7 @@ syntax :: proc() {
|
||||
c_proc :: proc() { /* code here */ };
|
||||
|
||||
|
||||
/*
|
||||
x += 1;
|
||||
x -= 1;
|
||||
// ++ and -- have been removed
|
||||
@@ -67,7 +68,7 @@ syntax :: proc() {
|
||||
// Question: Should they be added again?
|
||||
// They were removed as they are redundant and statements, not expressions
|
||||
// like in C/C++
|
||||
|
||||
*/
|
||||
|
||||
// You can now build files as a `.dll`
|
||||
// `odin build_dll demo.odin`
|
||||
@@ -85,7 +86,7 @@ syntax :: proc() {
|
||||
|
||||
Prefix_Type :: struct {x: int, y: f32, z: rawptr};
|
||||
|
||||
thread_local my_tls: Prefix_Type;
|
||||
#thread_local my_tls: Prefix_Type;
|
||||
|
||||
prefixes :: proc() {
|
||||
using var: Prefix_Type;
|
||||
@@ -98,7 +99,7 @@ prefixes :: proc() {
|
||||
|
||||
foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
|
||||
// int_ptr = nil; // Not valid
|
||||
int_ptr^ = 123; // Is valid
|
||||
// int_ptr^ = 123; // Not valid
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +155,7 @@ foreign_procedures :: proc() {
|
||||
}
|
||||
|
||||
special_expressions :: proc() {
|
||||
/*
|
||||
// Block expression
|
||||
x := {
|
||||
a: f32 = 123;
|
||||
@@ -168,7 +170,7 @@ special_expressions :: proc() {
|
||||
// TODO: Type cohesion is not yet finished
|
||||
give 123;
|
||||
}; // semicolon is required as it's an expression
|
||||
|
||||
*/
|
||||
|
||||
// This is allows for inline blocks of code and will be a useful feature to have when
|
||||
// macros will be implemented into the language
|
||||
@@ -191,17 +193,17 @@ loops :: proc() {
|
||||
break;
|
||||
}
|
||||
|
||||
for i in 0..<123 { // 123 exclusive
|
||||
for i in 0..123 { // 123 exclusive
|
||||
}
|
||||
|
||||
for i in 0...122 { // 122 inclusive
|
||||
for i in 0..123-1 { // 122 inclusive
|
||||
}
|
||||
|
||||
for val, idx in 12..<16 {
|
||||
for val, idx in 12..16 {
|
||||
fmt.println(val, idx);
|
||||
}
|
||||
|
||||
primes := [...]int{2, 3, 5, 7, 11, 13, 17, 19};
|
||||
primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19};
|
||||
|
||||
for p in primes {
|
||||
fmt.println(p);
|
||||
@@ -224,7 +226,7 @@ loops :: proc() {
|
||||
when false {
|
||||
for i, size := 0; i < name.count; i += size {
|
||||
r: rune;
|
||||
r, size = utf8.decode_rune(name[i:]);
|
||||
r, size = utf8.decode_rune(name[i..]);
|
||||
fmt.printf("%r\n", r);
|
||||
}
|
||||
}
|
||||
|
||||
+211
-124
@@ -2,9 +2,8 @@
|
||||
|
||||
#import "os.odin";
|
||||
#import "fmt.odin";
|
||||
#import "mem.odin";
|
||||
#import "utf8.odin";
|
||||
#import "hash.odin";
|
||||
#import "raw.odin";
|
||||
|
||||
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
|
||||
// #shared_global_scope due to the internals of the compiler.
|
||||
@@ -29,24 +28,29 @@ Calling_Convention :: enum {
|
||||
Type_Info_Record :: struct #ordered {
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
size: int, // in bytes
|
||||
align: int, // in bytes
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
usings: []bool, // usings may not be used in tuples
|
||||
packed: bool,
|
||||
ordered: bool,
|
||||
custom_align: bool,
|
||||
}
|
||||
|
||||
Type_Info :: union {
|
||||
size: int,
|
||||
align: int,
|
||||
|
||||
Named{name: string, base: ^Type_Info},
|
||||
Integer{size: int, signed: bool},
|
||||
Float{size: int},
|
||||
Integer{signed: bool},
|
||||
Float{},
|
||||
Complex{},
|
||||
Quaternion{},
|
||||
String{},
|
||||
Boolean{},
|
||||
Any{},
|
||||
Pointer{
|
||||
elem: ^Type_Info, // nil -> rawptr
|
||||
},
|
||||
Atomic{elem: ^Type_Info},
|
||||
Procedure{
|
||||
params: ^Type_Info, // Type_Info.Tuple
|
||||
results: ^Type_Info, // Type_Info.Tuple
|
||||
@@ -60,20 +64,18 @@ Type_Info :: union {
|
||||
},
|
||||
Dynamic_Array{elem: ^Type_Info, elem_size: int},
|
||||
Slice {elem: ^Type_Info, elem_size: int},
|
||||
Vector {elem: ^Type_Info, elem_size, count, align: int},
|
||||
Vector {elem: ^Type_Info, elem_size, count: int},
|
||||
Tuple {using record: Type_Info_Record}, // Only really used for procedures
|
||||
Struct {using record: Type_Info_Record},
|
||||
Raw_Union {using record: Type_Info_Record},
|
||||
Union{
|
||||
common_fields: struct {
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
types: []^Type_Info,
|
||||
names: []string,
|
||||
offsets: []int, // offsets may not be used in tuples
|
||||
},
|
||||
variant_names: []string,
|
||||
variant_types: []^Type_Info,
|
||||
size: int,
|
||||
align: int,
|
||||
},
|
||||
Enum{
|
||||
base: ^Type_Info,
|
||||
@@ -93,6 +95,9 @@ Type_Info :: union {
|
||||
// This will be set by the compiler
|
||||
__type_table: []Type_Info;
|
||||
|
||||
__argv__: ^^byte;
|
||||
__argc__: i32;
|
||||
|
||||
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil;
|
||||
@@ -128,11 +133,6 @@ __debug_trap :: proc() #foreign __llvm_core "llvm.debugtrap";
|
||||
__trap :: proc() #foreign __llvm_core "llvm.trap";
|
||||
read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter";
|
||||
|
||||
__cpuid :: proc(level: u32, sig: ^u32) -> i32 #foreign __llvm_core "__get_cpuid";
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
|
||||
Allocator_Mode :: enum u8 {
|
||||
@@ -149,6 +149,7 @@ Allocator :: struct #ordered {
|
||||
data: rawptr,
|
||||
}
|
||||
|
||||
|
||||
Context :: struct #ordered {
|
||||
thread_id: int,
|
||||
|
||||
@@ -165,7 +166,7 @@ DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
|
||||
|
||||
|
||||
__check_context :: proc() {
|
||||
c := ^__context;
|
||||
c := &__context;
|
||||
|
||||
if c.allocator.procedure == nil {
|
||||
c.allocator = default_allocator();
|
||||
@@ -233,7 +234,7 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
|
||||
return nil;
|
||||
}
|
||||
|
||||
mem.copy(new_memory, old_memory, min(old_size, new_size));;
|
||||
__mem_copy(new_memory, old_memory, min(old_size, new_size));;
|
||||
free(old_memory);
|
||||
return new_memory;
|
||||
}
|
||||
@@ -279,20 +280,21 @@ default_allocator :: proc() -> Allocator {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
__string_eq :: proc(a, b: string) -> bool {
|
||||
if a.count != b.count {
|
||||
if len(a) != len(b) {
|
||||
return false;
|
||||
}
|
||||
if a.data == b.data {
|
||||
if len(a) == 0 {
|
||||
return true;
|
||||
}
|
||||
if &a[0] == &b[0] {
|
||||
return true;
|
||||
}
|
||||
return __string_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
__string_cmp :: proc(a, b: string) -> int {
|
||||
return mem.compare(cast([]byte)a, cast([]byte)b);
|
||||
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
|
||||
__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); }
|
||||
@@ -302,6 +304,26 @@ __string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <=
|
||||
__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; }
|
||||
|
||||
|
||||
__complex64_eq :: proc(a, b: complex64) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
__complex64_ne :: proc(a, b: complex64) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
__complex128_eq :: proc(a, b: complex128) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
|
||||
__complex128_ne :: proc(a, b: complex128) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
|
||||
|
||||
|
||||
__quaternion128_eq :: proc(a, b: quaternion128) -> bool #inline {
|
||||
return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
|
||||
}
|
||||
__quaternion128_ne :: proc(a, b: quaternion128) -> bool #inline {
|
||||
return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
|
||||
}
|
||||
__quaternion256_eq :: proc(a, b: quaternion256) -> bool #inline {
|
||||
return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
|
||||
}
|
||||
__quaternion256_ne :: proc(a, b: quaternion256) -> bool #inline {
|
||||
return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
|
||||
}
|
||||
|
||||
__assert :: proc(file: string, line, column: int, msg: string) #inline {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n",
|
||||
file, line, column, msg);
|
||||
@@ -316,7 +338,7 @@ __bounds_check_error :: proc(file: string, line, column: int, index, count: int)
|
||||
if 0 <= index && index < count {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..%d\n",
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..<%d\n",
|
||||
file, line, column, index, count);
|
||||
__debug_trap();
|
||||
}
|
||||
@@ -325,21 +347,22 @@ __slice_expr_error :: proc(file: string, line, column: int, low, high, max: int)
|
||||
if 0 <= low && low <= high && high <= max {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..%d..%d]\n",
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..<%d..<%d]\n",
|
||||
file, line, column, low, high, max);
|
||||
__debug_trap();
|
||||
}
|
||||
|
||||
__substring_expr_error :: proc(file: string, line, column: int, low, high: int) {
|
||||
if 0 <= low && low <= high {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d..%d]\n",
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d..<%d]\n",
|
||||
file, line, column, low, high);
|
||||
__debug_trap();
|
||||
}
|
||||
__union_cast_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) {
|
||||
__type_assertion_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) {
|
||||
if !ok {
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid `union_cast` from %T to %T\n",
|
||||
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid type_assertion from %T to %T\n",
|
||||
file, line, column, from, to);
|
||||
__debug_trap();
|
||||
}
|
||||
@@ -350,40 +373,77 @@ __string_decode_rune :: proc(s: string) -> (rune, int) #inline {
|
||||
}
|
||||
|
||||
|
||||
Raw_Any :: struct #ordered {
|
||||
type_info: ^Type_Info,
|
||||
data: rawptr,
|
||||
__mem_set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
|
||||
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
|
||||
llvm_memset_64bit(data, byte(value), len, 1, false);
|
||||
return data;
|
||||
}
|
||||
__mem_zero :: proc(data: rawptr, len: int) -> rawptr {
|
||||
return __mem_set(data, 0, len);
|
||||
}
|
||||
__mem_copy :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
|
||||
llvm_memmove_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
__mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
|
||||
llvm_memcpy_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
|
||||
Raw_String :: struct #ordered {
|
||||
data: ^byte,
|
||||
count: int,
|
||||
};
|
||||
__mem_compare :: proc(a, b: ^byte, n: int) -> int {
|
||||
for i in 0..<n {
|
||||
match {
|
||||
case (a+i)^ < (b+i)^:
|
||||
return -1;
|
||||
case (a+i)^ > (b+i)^:
|
||||
return +1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Raw_Slice :: struct #ordered {
|
||||
data: rawptr,
|
||||
count: int,
|
||||
capacity: int,
|
||||
};
|
||||
|
||||
Raw_Dynamic_Array :: struct #ordered {
|
||||
data: rawptr,
|
||||
count: int,
|
||||
capacity: int,
|
||||
allocator: Allocator,
|
||||
};
|
||||
|
||||
Raw_Dynamic_Map :: struct #ordered {
|
||||
hashes: [dynamic]int,
|
||||
entries: Raw_Dynamic_Array,
|
||||
};
|
||||
__sqrt_f32 :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
|
||||
__sqrt_f64 :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
|
||||
__abs_complex64 :: proc(x: complex64) -> f32 #inline {
|
||||
r, i := real(x), imag(x);
|
||||
return __sqrt_f32(r*r + i*i);
|
||||
}
|
||||
__abs_complex128 :: proc(x: complex128) -> f64 #inline {
|
||||
r, i := real(x), imag(x);
|
||||
return __sqrt_f64(r*r + i*i);
|
||||
}
|
||||
__abs_quaternion128 :: proc(x: quaternion128) -> f32 #inline {
|
||||
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 :: proc(x: quaternion256) -> f64 #inline {
|
||||
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
|
||||
return __sqrt_f64(r*r + i*i + j*j + k*k);
|
||||
}
|
||||
|
||||
|
||||
|
||||
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capacity: int) -> bool {
|
||||
array := cast(^Raw_Dynamic_Array)array_;
|
||||
|
||||
if capacity <= array.capacity {
|
||||
__dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int) {
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
__check_context();
|
||||
array.allocator = context.allocator;
|
||||
assert(array.allocator.procedure != nil);
|
||||
|
||||
if cap > 0 {
|
||||
__dynamic_array_reserve(array_, elem_size, elem_align, cap);
|
||||
array.len = len;
|
||||
}
|
||||
}
|
||||
|
||||
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int) -> bool {
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
|
||||
if cap <= array.cap {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -393,8 +453,8 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capa
|
||||
}
|
||||
assert(array.allocator.procedure != nil);
|
||||
|
||||
old_size := array.capacity * elem_size;
|
||||
new_size := capacity * elem_size;
|
||||
old_size := array.cap * elem_size;
|
||||
new_size := cap * elem_size;
|
||||
allocator := array.allocator;
|
||||
|
||||
new_data := allocator.procedure(allocator.data, Allocator_Mode.RESIZE, new_size, elem_align, array.data, old_size, 0);
|
||||
@@ -403,83 +463,102 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capa
|
||||
}
|
||||
|
||||
array.data = new_data;
|
||||
array.capacity = capacity;
|
||||
array.cap = cap;
|
||||
return true;
|
||||
}
|
||||
|
||||
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int) -> bool {
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
|
||||
ok := __dynamic_array_reserve(array_, elem_size, elem_align, len);
|
||||
if ok {
|
||||
array.len = len;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
|
||||
items: rawptr, item_count: int) -> int {
|
||||
array := cast(^Raw_Dynamic_Array)array_;
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
|
||||
if item_count <= 0 || items == nil {
|
||||
return array.count;
|
||||
return array.len;
|
||||
}
|
||||
|
||||
|
||||
ok := true;
|
||||
if array.capacity <= array.count+item_count {
|
||||
capacity := 2 * array.capacity + max(8, item_count);
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity);
|
||||
if array.cap <= array.len+item_count {
|
||||
cap := 2 * array.cap + max(8, item_count);
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap);
|
||||
}
|
||||
if !ok {
|
||||
// TODO(bill): Better error handling for failed reservation
|
||||
return array.count;
|
||||
return array.len;
|
||||
}
|
||||
data := cast(^byte)array.data;
|
||||
data := ^byte(array.data);
|
||||
assert(data != nil);
|
||||
mem.copy(data + (elem_size*array.count), items, elem_size * item_count);
|
||||
array.count += item_count;
|
||||
return array.count;
|
||||
__mem_copy(data + (elem_size*array.len), items, elem_size * item_count);
|
||||
array.len += item_count;
|
||||
return array.len;
|
||||
}
|
||||
|
||||
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int) -> int {
|
||||
array := cast(^Raw_Dynamic_Array)array_;
|
||||
array := ^raw.Dynamic_Array(array_);
|
||||
|
||||
ok := true;
|
||||
if array.capacity <= array.count+1 {
|
||||
capacity := 2 * array.capacity + max(8, 1);
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity);
|
||||
if array.cap <= array.len+1 {
|
||||
cap := 2 * array.cap + max(8, 1);
|
||||
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap);
|
||||
}
|
||||
if !ok {
|
||||
// TODO(bill): Better error handling for failed reservation
|
||||
return array.count;
|
||||
return array.len;
|
||||
}
|
||||
data := cast(^byte)array.data;
|
||||
data := ^byte(array.data);
|
||||
assert(data != nil);
|
||||
mem.zero(data + (elem_size*array.count), elem_size);
|
||||
array.count++;
|
||||
return array.count;
|
||||
__mem_zero(data + (elem_size*array.len), elem_size);
|
||||
array.len++;
|
||||
return array.len;
|
||||
}
|
||||
|
||||
__slice_append :: proc(slice_: rawptr, elem_size, elem_align: int,
|
||||
items: rawptr, item_count: int) -> int {
|
||||
slice := cast(^Raw_Slice)slice_;
|
||||
slice := ^raw.Slice(slice_);
|
||||
|
||||
if item_count <= 0 || items == nil {
|
||||
return slice.count;
|
||||
return slice.len;
|
||||
}
|
||||
|
||||
item_count = min(slice.capacity-slice.count, item_count);
|
||||
item_count = min(slice.cap-slice.len, item_count);
|
||||
if item_count > 0 {
|
||||
data := cast(^byte)slice.data;
|
||||
data := ^byte(slice.data);
|
||||
assert(data != nil);
|
||||
mem.copy(data + (elem_size*slice.count), items, elem_size * item_count);
|
||||
slice.count += item_count;
|
||||
__mem_copy(data + (elem_size*slice.len), items, elem_size * item_count);
|
||||
slice.len += item_count;
|
||||
}
|
||||
return slice.count;
|
||||
return slice.len;
|
||||
}
|
||||
|
||||
|
||||
// Map stuff
|
||||
|
||||
__default_hash :: proc(data: []byte) -> u64 {
|
||||
return hash.fnv64a(data);
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
return fnv64a(data);
|
||||
}
|
||||
__default_hash_string :: proc(s: string) -> u64 {
|
||||
return __default_hash(cast([]byte)s);
|
||||
return __default_hash([]byte(s));
|
||||
}
|
||||
|
||||
__INITIAL_MAP_CAP :: 16;
|
||||
|
||||
__Map_Key :: struct #ordered {
|
||||
hash: u64,
|
||||
str: string,
|
||||
@@ -500,39 +579,41 @@ __Map_Entry_Header :: struct #ordered {
|
||||
}
|
||||
|
||||
__Map_Header :: struct #ordered {
|
||||
m: ^Raw_Dynamic_Map,
|
||||
m: ^raw.Dynamic_Map,
|
||||
is_key_string: bool,
|
||||
entry_size: int,
|
||||
entry_align: int,
|
||||
value_offset: int,
|
||||
value_size: int,
|
||||
}
|
||||
|
||||
__dynamic_map_reserve :: proc(using header: __Map_Header, capacity: int) -> bool {
|
||||
h := __dynamic_array_reserve(^m.hashes, size_of(int), align_of(int), capacity);
|
||||
e := __dynamic_array_reserve(^m.entries, entry_size, entry_align, capacity);
|
||||
return h && e;
|
||||
__dynamic_map_reserve :: proc(using header: __Map_Header, cap: int) {
|
||||
__dynamic_array_reserve(&m.hashes, size_of(int), align_of(int), cap);
|
||||
__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap);
|
||||
}
|
||||
|
||||
__dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
|
||||
new_header := header;
|
||||
nm: Raw_Dynamic_Map;
|
||||
new_header.m = ^nm;
|
||||
new_header: __Map_Header = header;
|
||||
nm: raw.Dynamic_Map;
|
||||
new_header.m = &nm;
|
||||
|
||||
reserve(nm.hashes, new_count);
|
||||
nm.hashes.count = nm.hashes.capacity;
|
||||
__dynamic_array_reserve(^nm.entries, entry_size, entry_align, m.entries.count);
|
||||
for _, i in nm.hashes {
|
||||
header_hashes := ^raw.Dynamic_Array(&header.m.hashes);
|
||||
nm_hashes := ^raw.Dynamic_Array(&nm.hashes);
|
||||
|
||||
__dynamic_array_resize(nm_hashes, size_of(int), align_of(int), new_count);
|
||||
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len);
|
||||
for i in 0..<new_count {
|
||||
nm.hashes[i] = -1;
|
||||
}
|
||||
|
||||
for i := 0; i < nm.entries.count; i++ {
|
||||
entry_header := __dynamic_map_get_entry(new_header, i);
|
||||
data := cast(^byte)entry_header;
|
||||
|
||||
if nm.hashes.count == 0 {
|
||||
for i := 0; i < m.entries.len; i++ {
|
||||
if len(nm.hashes) == 0 {
|
||||
__dynamic_map_grow(new_header);
|
||||
}
|
||||
|
||||
entry_header := __dynamic_map_get_entry(header, i);
|
||||
data := ^byte(entry_header);
|
||||
|
||||
fr := __dynamic_map_find(new_header, entry_header.key);
|
||||
j := __dynamic_map_add_entry(new_header, entry_header.key);
|
||||
if fr.entry_prev < 0 {
|
||||
@@ -544,13 +625,14 @@ __dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
|
||||
|
||||
e := __dynamic_map_get_entry(new_header, j);
|
||||
e.next = fr.entry_index;
|
||||
ndata := cast(^byte)e;
|
||||
mem.copy(ndata+value_offset, data+value_offset, entry_size-value_offset);
|
||||
ndata := ^byte(e);
|
||||
__mem_copy(ndata+value_offset, data+value_offset, value_size);
|
||||
|
||||
if __dynamic_map_full(new_header) {
|
||||
__dynamic_map_grow(new_header);
|
||||
}
|
||||
}
|
||||
free_ptr_with_allocator(header.m.hashes.allocator, header.m.hashes.data);
|
||||
free_ptr_with_allocator(header_hashes.allocator, header_hashes.data);
|
||||
free_ptr_with_allocator(header.m.entries.allocator, header.m.entries.data);
|
||||
header.m^ = nm;
|
||||
}
|
||||
@@ -558,7 +640,7 @@ __dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
|
||||
__dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr {
|
||||
index := __dynamic_map_find(h, key).entry_index;
|
||||
if index >= 0 {
|
||||
data := cast(^byte)__dynamic_map_get_entry(h, index);
|
||||
data := ^byte(__dynamic_map_get_entry(h, index));
|
||||
val := data + h.value_offset;
|
||||
return val;
|
||||
}
|
||||
@@ -567,10 +649,14 @@ __dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr {
|
||||
|
||||
__dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr) {
|
||||
index: int;
|
||||
assert(value != nil);
|
||||
|
||||
if m.hashes.count == 0 {
|
||||
|
||||
if len(m.hashes) == 0 {
|
||||
__dynamic_map_reserve(h, __INITIAL_MAP_CAP);
|
||||
__dynamic_map_grow(h);
|
||||
}
|
||||
|
||||
fr := __dynamic_map_find(h, key);
|
||||
if fr.entry_index >= 0 {
|
||||
index = fr.entry_index;
|
||||
@@ -584,9 +670,10 @@ __dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr)
|
||||
}
|
||||
}
|
||||
{
|
||||
data := cast(^byte)__dynamic_map_get_entry(h, index);
|
||||
val := data+value_offset;
|
||||
mem.copy(val, value, entry_size-value_offset);
|
||||
e := __dynamic_map_get_entry(h, index);
|
||||
e.key = key;
|
||||
val := ^byte(e) + value_offset;
|
||||
__mem_copy(val, value, value_size);
|
||||
}
|
||||
|
||||
if __dynamic_map_full(h) {
|
||||
@@ -596,12 +683,12 @@ __dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr)
|
||||
|
||||
|
||||
__dynamic_map_grow :: proc(using h: __Map_Header) {
|
||||
new_count := 2*m.entries.count + 8;
|
||||
new_count := max(2*m.entries.cap + 8, __INITIAL_MAP_CAP);
|
||||
__dynamic_map_rehash(h, new_count);
|
||||
}
|
||||
|
||||
__dynamic_map_full :: proc(using h: __Map_Header) -> bool {
|
||||
return cast(int)(0.75 * cast(f64)m.hashes.count) <= m.entries.count;
|
||||
return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
|
||||
}
|
||||
|
||||
|
||||
@@ -617,8 +704,8 @@ __dynamic_map_hash_equal :: proc(h: __Map_Header, a, b: __Map_Key) -> bool {
|
||||
|
||||
__dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_Result {
|
||||
fr := __Map_Find_Result{-1, -1, -1};
|
||||
if m.hashes.count > 0 {
|
||||
fr.hash_index = cast(int)(key.hash % cast(u64)m.hashes.count);
|
||||
if len(m.hashes) > 0 {
|
||||
fr.hash_index = int(key.hash % u64(len(m.hashes)));
|
||||
fr.entry_index = m.hashes[fr.hash_index];
|
||||
for fr.entry_index >= 0 {
|
||||
entry := __dynamic_map_get_entry(h, fr.entry_index);
|
||||
@@ -633,8 +720,8 @@ __dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_
|
||||
}
|
||||
|
||||
__dynamic_map_add_entry :: proc(using h: __Map_Header, key: __Map_Key) -> int {
|
||||
prev := m.entries.count;
|
||||
c := __dynamic_array_append_nothing(^m.entries, entry_size, entry_align);
|
||||
prev := m.entries.len;
|
||||
c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align);
|
||||
if c != prev {
|
||||
end := __dynamic_map_get_entry(h, c-1);
|
||||
end.key = key;
|
||||
@@ -652,8 +739,8 @@ __dynamic_map_delete :: proc(using h: __Map_Header, key: __Map_Key) {
|
||||
}
|
||||
|
||||
__dynamic_map_get_entry :: proc(using h: __Map_Header, index: int) -> ^__Map_Entry_Header {
|
||||
data := cast(^byte)m.entries.data + index*entry_size;
|
||||
return cast(^__Map_Entry_Header)data;
|
||||
data := ^byte(m.entries.data) + index*entry_size;
|
||||
return ^__Map_Entry_Header(data);
|
||||
}
|
||||
|
||||
__dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
|
||||
@@ -663,10 +750,10 @@ __dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
|
||||
__dynamic_map_get_entry(h, fr.entry_prev).next = __dynamic_map_get_entry(h, fr.entry_index).next;
|
||||
}
|
||||
|
||||
if fr.entry_index == m.entries.count-1 {
|
||||
m.entries.count--;
|
||||
if fr.entry_index == m.entries.len-1 {
|
||||
m.entries.len--;
|
||||
}
|
||||
mem.copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.count-1), entry_size);
|
||||
__mem_copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.len-1), entry_size);
|
||||
last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key);
|
||||
if last.entry_prev >= 0 {
|
||||
__dynamic_map_get_entry(h, last.entry_prev).next = fr.entry_index;
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
// TODO(bill): Use assembly instead here to implement atomics
|
||||
// Inline vs external file?
|
||||
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
_ := compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
|
||||
|
||||
|
||||
yield_thread :: proc() { win32.mm_pause(); }
|
||||
mfence :: proc() { win32.ReadWriteBarrier(); }
|
||||
sfence :: proc() { win32.WriteBarrier(); }
|
||||
lfence :: proc() { win32.ReadBarrier(); }
|
||||
|
||||
|
||||
load :: proc(a: ^i32) -> i32 {
|
||||
return a^;
|
||||
}
|
||||
store :: proc(a: ^i32, value: i32) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange :: proc(a: ^i32, expected, desired: i32) -> i32 {
|
||||
return win32.InterlockedCompareExchange(a, desired, expected);
|
||||
}
|
||||
exchanged :: proc(a: ^i32, desired: i32) -> i32 {
|
||||
return win32.InterlockedExchange(a, desired);
|
||||
}
|
||||
fetch_add :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedExchangeAdd(a, operand);
|
||||
|
||||
}
|
||||
fetch_and :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedAnd(a, operand);
|
||||
}
|
||||
fetch_or :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedOr(a, operand);
|
||||
}
|
||||
spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
counter := 0;
|
||||
for old_value != 0 && (time_out < 0 || counter < time_out) {
|
||||
counter++;
|
||||
yield_thread();
|
||||
old_value = compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
}
|
||||
return old_value == 0;
|
||||
}
|
||||
spin_unlock :: proc(a: ^i32) {
|
||||
store(a, 0);
|
||||
mfence();
|
||||
}
|
||||
try_acquire_lock :: proc(a: ^i32) -> bool {
|
||||
yield_thread();
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
return old_value == 0;
|
||||
}
|
||||
|
||||
|
||||
load :: proc(a: ^i64) -> i64 {
|
||||
return a^;
|
||||
}
|
||||
store :: proc(a: ^i64, value: i64) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange :: proc(a: ^i64, expected, desired: i64) -> i64 {
|
||||
return win32.InterlockedCompareExchange64(a, desired, expected);
|
||||
}
|
||||
exchanged :: proc(a: ^i64, desired: i64) -> i64 {
|
||||
return win32.InterlockedExchange64(a, desired);
|
||||
}
|
||||
fetch_add :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedExchangeAdd64(a, operand);
|
||||
}
|
||||
fetch_and :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedAnd64(a, operand);
|
||||
}
|
||||
fetch_or :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedOr64(a, operand);
|
||||
}
|
||||
spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
counter := 0;
|
||||
for old_value != 0 && (time_out < 0 || counter < time_out) {
|
||||
counter++;
|
||||
yield_thread();
|
||||
old_value = compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
}
|
||||
return old_value == 0;
|
||||
}
|
||||
spin_unlock :: proc(a: ^i64) {
|
||||
store(a, 0);
|
||||
mfence();
|
||||
}
|
||||
try_acquire_lock :: proc(a: ^i64) -> bool {
|
||||
yield_thread();
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
mfence();
|
||||
return old_value == 0;
|
||||
}
|
||||
+27
-27
@@ -14,37 +14,37 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
|
||||
for _, i in buf {
|
||||
buf[i] = '0';
|
||||
}
|
||||
return buf.count;
|
||||
return len(buf);
|
||||
}
|
||||
|
||||
|
||||
n := 10 + a.count + abs(a.decimal_point);
|
||||
|
||||
// TODO(bill): make this work with a buffer that's not big enough
|
||||
assert(buf.count >= n);
|
||||
buf = buf[..n];
|
||||
assert(len(buf) >= n);
|
||||
buf = buf[0..<n];
|
||||
|
||||
if a.count == 0 {
|
||||
buf[0] = '0';
|
||||
return cast(string)buf[0..1];
|
||||
return string(buf[0..<1]);
|
||||
}
|
||||
|
||||
w := 0;
|
||||
if a.decimal_point <= 0 {
|
||||
buf[w] = '0'; w++;
|
||||
buf[w] = '.'; w++;
|
||||
w += digit_zero(buf[w .. w-a.decimal_point]);
|
||||
w += copy(buf[w..], a.digits[0..a.count]);
|
||||
w += digit_zero(buf[w ..< w-a.decimal_point]);
|
||||
w += copy(buf[w..], a.digits[0..<a.count]);
|
||||
} else if a.decimal_point < a.count {
|
||||
w += copy(buf[w..], a.digits[0..a.decimal_point]);
|
||||
w += copy(buf[w..], a.digits[0..<a.decimal_point]);
|
||||
buf[w] = '.'; w++;
|
||||
w += copy(buf[w..], a.digits[a.decimal_point .. a.count]);
|
||||
w += copy(buf[w..], a.digits[a.decimal_point ..< a.count]);
|
||||
} else {
|
||||
w += copy(buf[w..], a.digits[0..a.count]);
|
||||
w += digit_zero(buf[w .. w+a.decimal_point-a.count]);
|
||||
w += copy(buf[w..], a.digits[0..<a.count]);
|
||||
w += digit_zero(buf[w ..< w+a.decimal_point-a.count]);
|
||||
}
|
||||
|
||||
return cast(string)buf[0..w];
|
||||
return string(buf[0..<w]);
|
||||
}
|
||||
|
||||
// trim trailing zeros
|
||||
@@ -64,7 +64,7 @@ assign :: proc(a: ^Decimal, i: u64) {
|
||||
for i > 0 {
|
||||
j := i/10;
|
||||
i -= 10*j;
|
||||
buf[n] = cast(byte)('0'+i);
|
||||
buf[n] = byte('0'+i);
|
||||
n++;
|
||||
i = j;
|
||||
}
|
||||
@@ -99,7 +99,7 @@ shift_right :: proc(a: ^Decimal, k: uint) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
c := cast(uint)a.digits[r];
|
||||
c := uint(a.digits[r]);
|
||||
n = n*10 + c - '0';
|
||||
}
|
||||
a.decimal_point -= r-1;
|
||||
@@ -107,10 +107,10 @@ shift_right :: proc(a: ^Decimal, k: uint) {
|
||||
mask: uint = (1<<k) - 1;
|
||||
|
||||
for ; r < a.count; r++ {
|
||||
c := cast(uint)a.digits[r];
|
||||
c := uint(a.digits[r]);
|
||||
dig := n>>k;
|
||||
n &= mask;
|
||||
a.digits[w] = cast(byte)('0' + dig);
|
||||
a.digits[w] = byte('0' + dig);
|
||||
w++;
|
||||
n = n*10 + c - '0';
|
||||
}
|
||||
@@ -118,8 +118,8 @@ shift_right :: proc(a: ^Decimal, k: uint) {
|
||||
for n > 0 {
|
||||
dig := n>>k;
|
||||
n &= mask;
|
||||
if w < a.digits.count {
|
||||
a.digits[w] = cast(byte)('0' + dig);
|
||||
if w < len(a.digits) {
|
||||
a.digits[w] = byte('0' + dig);
|
||||
w++;
|
||||
} else if dig > 0 {
|
||||
a.trunc = true;
|
||||
@@ -133,19 +133,19 @@ shift_right :: proc(a: ^Decimal, k: uint) {
|
||||
}
|
||||
|
||||
shift_left :: proc(a: ^Decimal, k: uint) {
|
||||
delta := cast(int)(k/4);
|
||||
delta := int(k/4);
|
||||
|
||||
r := a.count; // read index
|
||||
w := a.count+delta; // write index
|
||||
|
||||
n: uint;
|
||||
for r--; r >= 0; r-- {
|
||||
n += (cast(uint)a.digits[r] - '0') << k;
|
||||
n += (uint(a.digits[r]) - '0') << k;
|
||||
quo := n/10;
|
||||
rem := n - 10*quo;
|
||||
w--;
|
||||
if w < a.digits.count {
|
||||
a.digits[w] = cast(byte)('0' + rem);
|
||||
if w < len(a.digits) {
|
||||
a.digits[w] = byte('0' + rem);
|
||||
} else if rem != 0 {
|
||||
a.trunc = true;
|
||||
}
|
||||
@@ -156,8 +156,8 @@ shift_left :: proc(a: ^Decimal, k: uint) {
|
||||
quo := n/10;
|
||||
rem := n - 10*quo;
|
||||
w--;
|
||||
if w < a.digits.count {
|
||||
a.digits[w] = cast(byte)('0' + rem);
|
||||
if 0 <= w && w < len(a.digits) {
|
||||
a.digits[w] = byte('0' + rem);
|
||||
} else if rem != 0 {
|
||||
a.trunc = true;
|
||||
}
|
||||
@@ -165,7 +165,7 @@ shift_left :: proc(a: ^Decimal, k: uint) {
|
||||
}
|
||||
|
||||
a.count += delta;
|
||||
a.count = min(a.count, a.digits.count);
|
||||
a.count = min(a.count, len(a.digits));
|
||||
a.decimal_point += delta;
|
||||
trim(a);
|
||||
}
|
||||
@@ -179,7 +179,7 @@ shift :: proc(a: ^Decimal, k: int) {
|
||||
shift_left(a, max_shift);
|
||||
k -= max_shift;
|
||||
}
|
||||
shift_left(a, cast(uint)k);
|
||||
shift_left(a, uint(k));
|
||||
|
||||
|
||||
case k < 0:
|
||||
@@ -187,7 +187,7 @@ shift :: proc(a: ^Decimal, k: int) {
|
||||
shift_right(a, max_shift);
|
||||
k += max_shift;
|
||||
}
|
||||
shift_right(a, cast(uint)-k);
|
||||
shift_right(a, uint(-k));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ rounded_integer :: proc(a: ^Decimal) -> u64 {
|
||||
n: u64 = 0;
|
||||
m := min(a.decimal_point, a.count);
|
||||
for i = 0; i < m; i++ {
|
||||
n = n*10 + cast(u64)(a.digits[i]-'0');
|
||||
n = n*10 + u64(a.digits[i]-'0');
|
||||
}
|
||||
for ; i < a.decimal_point; i++ {
|
||||
n *= 10;
|
||||
|
||||
+405
-227
File diff suppressed because it is too large
Load Diff
+36
-35
@@ -1,14 +1,14 @@
|
||||
crc32 :: proc(data: []byte) -> u32 {
|
||||
result := ~cast(u32)0;
|
||||
result := ~u32(0);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ cast(u32)b) & 0xff];
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
crc64 :: proc(data: []byte) -> u64 {
|
||||
result := ~cast(u64)0;
|
||||
result := ~u64(0);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc64_table[(result ~ cast(u64)b) & 0xff];
|
||||
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
@@ -16,7 +16,7 @@ crc64 :: proc(data: []byte) -> u64 {
|
||||
fnv32 :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ cast(u32)b;
|
||||
h = (h * 0x01000193) ~ u32(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
@@ -24,7 +24,7 @@ fnv32 :: proc(data: []byte) -> u32 {
|
||||
fnv64 :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ cast(u64)b;
|
||||
h = (h * 0x100000001b3) ~ u64(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
@@ -32,7 +32,7 @@ fnv64 :: proc(data: []byte) -> u64 {
|
||||
fnv32a :: proc(data: []byte) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for b in data {
|
||||
h = (h ~ cast(u32)b) * 0x01000193;
|
||||
h = (h ~ u32(b)) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
@@ -40,7 +40,7 @@ fnv32a :: proc(data: []byte) -> u32 {
|
||||
fnv64a :: proc(data: []byte) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h ~ cast(u64)b) * 0x100000001b3;
|
||||
h = (h ~ u64(b)) * 0x100000001b3;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
@@ -50,12 +50,12 @@ murmur32 :: proc(data: []byte) -> u32 {
|
||||
c2_32: u32 : 0x1b873593;
|
||||
|
||||
h1: u32 = 0;
|
||||
nblocks := data.count/4;
|
||||
p := data.data;
|
||||
nblocks := len(data)/4;
|
||||
p := &data[0];
|
||||
p1 := p + 4*nblocks;
|
||||
|
||||
for ; p < p1; p += 4 {
|
||||
k1 := (cast(^u32)p)^;
|
||||
k1 := ^u32(p)^;
|
||||
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17);
|
||||
@@ -69,22 +69,22 @@ murmur32 :: proc(data: []byte) -> u32 {
|
||||
tail := data[nblocks*4 ..];
|
||||
|
||||
k1: u32;
|
||||
match tail.count&3 {
|
||||
match len(tail)&3 {
|
||||
case 3:
|
||||
k1 ~= cast(u32)tail[2] << 16;
|
||||
k1 ~= u32(tail[2]) << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
k1 ~= cast(u32)tail[2] << 8;
|
||||
k1 ~= u32(tail[2]) << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
k1 ~= cast(u32)tail[0];
|
||||
k1 ~= u32(tail[0]);
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17) ;
|
||||
k1 *= c2_32;
|
||||
h1 ~= k1;
|
||||
}
|
||||
|
||||
h1 ~= cast(u32)data.count;
|
||||
h1 ~= u32(len(data));
|
||||
|
||||
h1 ~= h1 >> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
@@ -98,12 +98,12 @@ murmur32 :: proc(data: []byte) -> u32 {
|
||||
murmur64 :: proc(data: []byte) -> u64 {
|
||||
SEED :: 0x9747b28c;
|
||||
|
||||
when false && size_of(int) == 8 {
|
||||
when size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995;
|
||||
r :: 47;
|
||||
|
||||
h: u64 = SEED ~ (cast(u64)data.count * m);
|
||||
data64 := slice_ptr(cast(^u64)^data[0], data.count/size_of(u64));
|
||||
h: u64 = SEED ~ (u64(len(data)) * m);
|
||||
data64 := slice_ptr(^u64(&data[0]), len(data)/size_of(u64));
|
||||
|
||||
for _, i in data64 {
|
||||
k := data64[i];
|
||||
@@ -116,15 +116,15 @@ murmur64 :: proc(data: []byte) -> u64 {
|
||||
h *= m;
|
||||
}
|
||||
|
||||
match data.count&7 {
|
||||
case 7: h ~= cast(u64)data[6] << 48; fallthrough;
|
||||
case 6: h ~= cast(u64)data[5] << 40; fallthrough;
|
||||
case 5: h ~= cast(u64)data[4] << 32; fallthrough;
|
||||
case 4: h ~= cast(u64)data[3] << 24; fallthrough;
|
||||
case 3: h ~= cast(u64)data[2] << 16; fallthrough;
|
||||
case 2: h ~= cast(u64)data[1] << 8; fallthrough;
|
||||
match len(data)&7 {
|
||||
case 7: h ~= u64(data[6]) << 48; fallthrough;
|
||||
case 6: h ~= u64(data[5]) << 40; fallthrough;
|
||||
case 5: h ~= u64(data[4]) << 32; fallthrough;
|
||||
case 4: h ~= u64(data[3]) << 24; fallthrough;
|
||||
case 3: h ~= u64(data[2]) << 16; fallthrough;
|
||||
case 2: h ~= u64(data[1]) << 8; fallthrough;
|
||||
case 1:
|
||||
h ~= cast(u64)data[0];
|
||||
h ~= u64(data[0]);
|
||||
h *= m;
|
||||
}
|
||||
|
||||
@@ -137,11 +137,11 @@ murmur64 :: proc(data: []byte) -> u64 {
|
||||
m :: 0x5bd1e995;
|
||||
r :: 24;
|
||||
|
||||
h1: u32 = cast(u32)SEED ~ cast(u32)data.count;
|
||||
h2: u32 = SEED >> 32;
|
||||
h1 := u32(SEED) ~ u32(len(data));
|
||||
h2 := u32(SEED) >> 32;
|
||||
|
||||
data32 := slice_ptr(cast(^u32)^data[0], data.count/size_of(u32));
|
||||
len := data.count;
|
||||
data32 := slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
|
||||
len := len(data);
|
||||
|
||||
i := 0;
|
||||
for len >= 8 {
|
||||
@@ -174,16 +174,17 @@ murmur64 :: proc(data: []byte) -> u64 {
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
data8 := slice_to_bytes(data32[i..])[..3];
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := slice_to_bytes(data32[i..])[0..<3];
|
||||
match len {
|
||||
case 3:
|
||||
h2 ~= cast(u32)data8[2] << 16;
|
||||
h2 ~= u32(data8[2]) << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
h2 ~= cast(u32)data8[1] << 8;
|
||||
h2 ~= u32(data8[1]) << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
h2 ~= cast(u32)data8[0];
|
||||
h2 ~= u32(data8[0]);
|
||||
h2 *= m;
|
||||
}
|
||||
|
||||
|
||||
+49
-43
@@ -27,20 +27,24 @@ Mat4 :: [4]Vec4;
|
||||
sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
|
||||
sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
|
||||
|
||||
sin :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
|
||||
sin :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
|
||||
sin :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
|
||||
sin :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
|
||||
|
||||
cos :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
|
||||
cos :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
|
||||
cos :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
|
||||
cos :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
|
||||
|
||||
tan :: proc(θ: f32) -> f32 #inline { return sin(θ)/cos(θ); }
|
||||
tan :: proc(θ: f64) -> f64 #inline { return sin(θ)/cos(θ); }
|
||||
|
||||
pow :: proc(x, power: f32) -> f32 #foreign __llvm_core "llvm.pow.f32";
|
||||
pow :: proc(x, power: f64) -> f64 #foreign __llvm_core "llvm.pow.f64";
|
||||
|
||||
tan :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); }
|
||||
tan :: proc(x: f64) -> f64 #inline { return sin(x)/cos(x); }
|
||||
|
||||
lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
|
||||
lerp :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; }
|
||||
|
||||
sign :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
|
||||
sign :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; }
|
||||
sign :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; }
|
||||
sign :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; }
|
||||
|
||||
bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16";
|
||||
bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32";
|
||||
@@ -51,38 +55,44 @@ fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
|
||||
|
||||
|
||||
copy_sign :: proc(x, y: f32) -> f32 {
|
||||
ix := transmute(u32)x;
|
||||
iy := transmute(u32)y;
|
||||
ix &= 0x7fffffff;
|
||||
ix |= iy & 0x80000000;
|
||||
return transmute(f32)ix;
|
||||
}
|
||||
round :: proc(x: f32) -> f32 {
|
||||
if x >= 0 {
|
||||
return floor(x + 0.5);
|
||||
}
|
||||
return ceil(x - 0.5);
|
||||
}
|
||||
floor :: proc(x: f32) -> f32 {
|
||||
if x >= 0 {
|
||||
return cast(f32)cast(int)x;
|
||||
}
|
||||
return cast(f32)cast(int)(x-0.5);
|
||||
}
|
||||
ceil :: proc(x: f32) -> f32 {
|
||||
if x < 0 {
|
||||
return cast(f32)cast(int)x;
|
||||
}
|
||||
return cast(f32)cast(int)(x+1);
|
||||
ix := transmute(u32, x);
|
||||
iy := transmute(u32, y);
|
||||
ix &= 0x7fff_ffff;
|
||||
ix |= iy & 0x8000_0000;
|
||||
return transmute(f32, ix);
|
||||
}
|
||||
|
||||
remainder32 :: proc(x, y: f32) -> f32 {
|
||||
return x - round(x/y) * y;
|
||||
copy_sign :: proc(x, y: f64) -> f64 {
|
||||
ix := transmute(u64, x);
|
||||
iy := transmute(u64, y);
|
||||
ix &= 0x7fff_ffff_ffff_ff;
|
||||
ix |= iy & 0x8000_0000_0000_0000;
|
||||
return transmute(f64, ix);
|
||||
}
|
||||
|
||||
fmod32 :: proc(x, y: f32) -> f32 {
|
||||
round :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
|
||||
round :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
|
||||
|
||||
floor :: proc(x: f32) -> f32 { return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
floor :: proc(x: f64) -> f64 { return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
|
||||
ceil :: proc(x: f32) -> f32 { return x < 0 ? f32(i64(x)) : f32(i64(x+1)); } // TODO: Get accurate versions
|
||||
ceil :: proc(x: f64) -> f64 { return x < 0 ? f64(i64(x)) : f64(i64(x+1)); } // TODO: Get accurate versions
|
||||
|
||||
remainder :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
|
||||
remainder :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
|
||||
|
||||
mod :: proc(x, y: f32) -> f32 {
|
||||
y = abs(y);
|
||||
result := remainder32(abs(x), y);
|
||||
result := remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
return copy_sign(result, x);
|
||||
}
|
||||
mod :: proc(x, y: f64) -> f64 {
|
||||
y = abs(y);
|
||||
result := remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
@@ -95,7 +105,6 @@ to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
|
||||
|
||||
|
||||
|
||||
|
||||
dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; }
|
||||
dot :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z; }
|
||||
dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }
|
||||
@@ -151,8 +160,8 @@ mat4_identity :: proc() -> Mat4 {
|
||||
}
|
||||
|
||||
mat4_transpose :: proc(m: Mat4) -> Mat4 {
|
||||
for j in 0..4 {
|
||||
for i in 0..4 {
|
||||
for j in 0..<4 {
|
||||
for i in 0..<4 {
|
||||
m[i][j], m[j][i] = m[j][i], m[i][j];
|
||||
}
|
||||
}
|
||||
@@ -161,8 +170,8 @@ mat4_transpose :: proc(m: Mat4) -> Mat4 {
|
||||
|
||||
mul :: proc(a, b: Mat4) -> Mat4 {
|
||||
c: Mat4;
|
||||
for j in 0..4 {
|
||||
for i in 0..4 {
|
||||
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] +
|
||||
@@ -367,6 +376,3 @@ F64_MIN_10_EXP :: -307; // min decimal exponent
|
||||
F64_MIN_EXP :: -1021; // min binary exponent
|
||||
F64_RADIX :: 2; // exponent radix
|
||||
F64_ROUNDS :: 1; // addition rounding: near
|
||||
|
||||
|
||||
|
||||
|
||||
+26
-47
@@ -6,41 +6,20 @@ swap :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bswap.i32";
|
||||
swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64";
|
||||
|
||||
|
||||
set :: proc(data: rawptr, value: i32, len: int) -> rawptr #link_name "__mem_set" {
|
||||
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
|
||||
llvm_memset_64bit(data, cast(byte)value, len, 1, false);
|
||||
return data;
|
||||
set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
|
||||
return __mem_set(data, value, len);
|
||||
}
|
||||
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr #link_name "__mem_zero" {
|
||||
return set(data, 0, len);
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr {
|
||||
return __mem_zero(data, len);
|
||||
}
|
||||
|
||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy" {
|
||||
// NOTE(bill): This _must_ be implemented like C's memmove
|
||||
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
|
||||
llvm_memmove_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return __mem_copy(dst, src, len);
|
||||
}
|
||||
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy_non_overlapping" {
|
||||
// NOTE(bill): This _must_ be implemented like C's memcpy
|
||||
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
|
||||
llvm_memcpy_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
|
||||
return __mem_copy_non_overlapping(dst, src, len);
|
||||
}
|
||||
|
||||
compare :: proc(a, b: []byte) -> int #link_name "__mem_compare" {
|
||||
n := min(a.count, b.count);
|
||||
for i in 0..n {
|
||||
match {
|
||||
case a[i] < b[i]:
|
||||
return -1;
|
||||
case a[i] > b[i]:
|
||||
return +1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
compare :: proc(a, b: []byte) -> int {
|
||||
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
|
||||
|
||||
@@ -60,13 +39,13 @@ is_power_of_two :: proc(x: int) -> bool {
|
||||
align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
a := cast(uint)align;
|
||||
p := cast(uint)ptr;
|
||||
a := uint(align);
|
||||
p := uint(ptr);
|
||||
modulo := p & (a-1);
|
||||
if modulo != 0 {
|
||||
p += a - modulo;
|
||||
}
|
||||
return cast(rawptr)p;
|
||||
return rawptr(p);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,9 +56,9 @@ Allocation_Header :: struct {
|
||||
|
||||
allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: int) {
|
||||
header.size = size;
|
||||
ptr := cast(^int)(header+1);
|
||||
ptr := ^int(header+1);
|
||||
|
||||
for i := 0; cast(rawptr)ptr < data; i++ {
|
||||
for i := 0; rawptr(ptr) < data; i++ {
|
||||
(ptr+i)^ = -1;
|
||||
}
|
||||
}
|
||||
@@ -87,11 +66,11 @@ allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
|
||||
if data == nil {
|
||||
return nil;
|
||||
}
|
||||
p := cast(^int)data;
|
||||
p := ^int(data);
|
||||
for (p-1)^ == -1 {
|
||||
p = (p-1);
|
||||
}
|
||||
return cast(^Allocation_Header)p-1;
|
||||
return ^Allocation_Header(p-1);
|
||||
}
|
||||
|
||||
|
||||
@@ -117,13 +96,13 @@ Arena_Temp_Memory :: struct {
|
||||
|
||||
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
|
||||
backing = Allocator{};
|
||||
memory = data[..0];
|
||||
memory = data[0..<0];
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
init_arena_from_context :: proc(using a: ^Arena, size: int) {
|
||||
backing = context.allocator;
|
||||
memory = new_slice(byte, size);
|
||||
memory = make([]byte, size);
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
@@ -148,18 +127,18 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
using Allocator_Mode;
|
||||
arena := cast(^Arena)allocator_data;
|
||||
arena := ^Arena(allocator_data);
|
||||
|
||||
match mode {
|
||||
case ALLOC:
|
||||
total_size := size + alignment;
|
||||
|
||||
if arena.offset + total_size > arena.memory.count {
|
||||
if arena.offset + total_size > len(arena.memory) {
|
||||
fmt.fprintln(os.stderr, "Arena out of memory");
|
||||
return nil;
|
||||
}
|
||||
|
||||
#no_bounds_check end := ^arena.memory[arena.offset];
|
||||
#no_bounds_check end := &arena.memory[arena.offset];
|
||||
|
||||
ptr := align_forward(end, alignment);
|
||||
arena.offset += total_size;
|
||||
@@ -182,15 +161,15 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
tmp: Arena_Temp_Memory;
|
||||
tmp.arena = a;
|
||||
tmp.original_count = a.memory.count;
|
||||
tmp.original_count = len(a.memory);
|
||||
a.temp_count++;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
assert(arena.memory.count >= original_count);
|
||||
assert(len(arena.memory) >= original_count);
|
||||
assert(arena.temp_count > 0);
|
||||
arena.memory.count = original_count;
|
||||
arena.memory = arena.memory[0..<original_count];
|
||||
arena.temp_count--;
|
||||
}
|
||||
|
||||
@@ -242,7 +221,7 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||
return WORD_SIZE;
|
||||
case Vector:
|
||||
size := size_of_type_info(info.elem);
|
||||
count := cast(int)max(prev_pow2(cast(i64)info.count), 1);
|
||||
count := int(max(prev_pow2(i64(info.count)), 1));
|
||||
total := size * count;
|
||||
return clamp(total, 1, MAX_ALIGN);
|
||||
case Tuple:
|
||||
|
||||
+55
-45
@@ -1,4 +1,5 @@
|
||||
#foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library lib "gl" when ODIN_OS == "linux";
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#import "sys/wgl.odin" when ODIN_OS == "windows";
|
||||
#load "opengl_constants.odin";
|
||||
@@ -30,15 +31,19 @@ TexImage2D :: proc(target, level, internal_format,
|
||||
format, type: i32, pixels: rawptr) #foreign lib "glTexImage2D";
|
||||
|
||||
|
||||
string_data :: proc(s: string) -> ^u8 #inline { return ^s[0]; }
|
||||
_string_data :: proc(s: string) -> ^u8 #inline { return &s[0]; }
|
||||
|
||||
_libgl := win32.LoadLibraryA(string_data("opengl32.dll\x00"));
|
||||
_libgl := win32.LoadLibraryA(_string_data("opengl32.dll\x00"));
|
||||
|
||||
GetProcAddress :: proc(name: string) -> proc() #cc_c {
|
||||
assert(name[name.count-1] == 0);
|
||||
res := wgl.GetProcAddress(name.data);
|
||||
if name[len(name)-1] == 0 {
|
||||
name = name[0..<len(name)-1];
|
||||
}
|
||||
// NOTE(bill): null terminated
|
||||
assert((&name[0] + len(name))^ == 0);
|
||||
res := wgl.GetProcAddress(&name[0]);
|
||||
if res == nil {
|
||||
res = win32.GetProcAddress(_libgl, name.data);
|
||||
res = win32.GetProcAddress(_libgl, &name[0]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@@ -46,6 +51,7 @@ GetProcAddress :: proc(name: string) -> proc() #cc_c {
|
||||
GenBuffers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenSamplers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
DeleteBuffers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
BindBuffer: proc(target: i32, buffer: u32) #cc_c;
|
||||
BindVertexArray: proc(buffer: u32) #cc_c;
|
||||
BindSampler: proc(position: i32, sampler: u32) #cc_c;
|
||||
@@ -102,55 +108,59 @@ UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32
|
||||
GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c;
|
||||
|
||||
init :: proc() {
|
||||
set_proc_address :: proc(p: rawptr, name: string) #inline { (cast(^(proc() #cc_c))p)^ = GetProcAddress(name); }
|
||||
set_proc_address :: proc(p: rawptr, name: string) #inline {
|
||||
x := ^(proc() #cc_c)(p);
|
||||
x^ = GetProcAddress(name);
|
||||
}
|
||||
|
||||
set_proc_address(^GenBuffers, "glGenBuffers\x00");
|
||||
set_proc_address(^GenVertexArrays, "glGenVertexArrays\x00");
|
||||
set_proc_address(^GenSamplers, "glGenSamplers\x00");
|
||||
set_proc_address(^BindBuffer, "glBindBuffer\x00");
|
||||
set_proc_address(^BindSampler, "glBindSampler\x00");
|
||||
set_proc_address(^BindVertexArray, "glBindVertexArray\x00");
|
||||
set_proc_address(^BufferData, "glBufferData\x00");
|
||||
set_proc_address(^BufferSubData, "glBufferSubData\x00");
|
||||
set_proc_address(&GenBuffers, "glGenBuffers\x00");
|
||||
set_proc_address(&GenVertexArrays, "glGenVertexArrays\x00");
|
||||
set_proc_address(&GenSamplers, "glGenSamplers\x00");
|
||||
set_proc_address(&DeleteBuffers, "glDeleteBuffers\x00");
|
||||
set_proc_address(&BindBuffer, "glBindBuffer\x00");
|
||||
set_proc_address(&BindSampler, "glBindSampler\x00");
|
||||
set_proc_address(&BindVertexArray, "glBindVertexArray\x00");
|
||||
set_proc_address(&BufferData, "glBufferData\x00");
|
||||
set_proc_address(&BufferSubData, "glBufferSubData\x00");
|
||||
|
||||
set_proc_address(^DrawArrays, "glDrawArrays\x00");
|
||||
set_proc_address(^DrawElements, "glDrawElements\x00");
|
||||
set_proc_address(&DrawArrays, "glDrawArrays\x00");
|
||||
set_proc_address(&DrawElements, "glDrawElements\x00");
|
||||
|
||||
set_proc_address(^MapBuffer, "glMapBuffer\x00");
|
||||
set_proc_address(^UnmapBuffer, "glUnmapBuffer\x00");
|
||||
set_proc_address(&MapBuffer, "glMapBuffer\x00");
|
||||
set_proc_address(&UnmapBuffer, "glUnmapBuffer\x00");
|
||||
|
||||
set_proc_address(^VertexAttribPointer, "glVertexAttribPointer\x00");
|
||||
set_proc_address(^EnableVertexAttribArray, "glEnableVertexAttribArray\x00");
|
||||
set_proc_address(&VertexAttribPointer, "glVertexAttribPointer\x00");
|
||||
set_proc_address(&EnableVertexAttribArray, "glEnableVertexAttribArray\x00");
|
||||
|
||||
set_proc_address(^CreateShader, "glCreateShader\x00");
|
||||
set_proc_address(^ShaderSource, "glShaderSource\x00");
|
||||
set_proc_address(^CompileShader, "glCompileShader\x00");
|
||||
set_proc_address(^CreateProgram, "glCreateProgram\x00");
|
||||
set_proc_address(^AttachShader, "glAttachShader\x00");
|
||||
set_proc_address(^DetachShader, "glDetachShader\x00");
|
||||
set_proc_address(^DeleteShader, "glDeleteShader\x00");
|
||||
set_proc_address(^LinkProgram, "glLinkProgram\x00");
|
||||
set_proc_address(^UseProgram, "glUseProgram\x00");
|
||||
set_proc_address(^DeleteProgram, "glDeleteProgram\x00");
|
||||
set_proc_address(&CreateShader, "glCreateShader\x00");
|
||||
set_proc_address(&ShaderSource, "glShaderSource\x00");
|
||||
set_proc_address(&CompileShader, "glCompileShader\x00");
|
||||
set_proc_address(&CreateProgram, "glCreateProgram\x00");
|
||||
set_proc_address(&AttachShader, "glAttachShader\x00");
|
||||
set_proc_address(&DetachShader, "glDetachShader\x00");
|
||||
set_proc_address(&DeleteShader, "glDeleteShader\x00");
|
||||
set_proc_address(&LinkProgram, "glLinkProgram\x00");
|
||||
set_proc_address(&UseProgram, "glUseProgram\x00");
|
||||
set_proc_address(&DeleteProgram, "glDeleteProgram\x00");
|
||||
|
||||
set_proc_address(^GetShaderiv, "glGetShaderiv\x00");
|
||||
set_proc_address(^GetProgramiv, "glGetProgramiv\x00");
|
||||
set_proc_address(^GetShaderInfoLog, "glGetShaderInfoLog\x00");
|
||||
set_proc_address(^GetProgramInfoLog, "glGetProgramInfoLog\x00");
|
||||
set_proc_address(&GetShaderiv, "glGetShaderiv\x00");
|
||||
set_proc_address(&GetProgramiv, "glGetProgramiv\x00");
|
||||
set_proc_address(&GetShaderInfoLog, "glGetShaderInfoLog\x00");
|
||||
set_proc_address(&GetProgramInfoLog, "glGetProgramInfoLog\x00");
|
||||
|
||||
set_proc_address(^ActiveTexture, "glActiveTexture\x00");
|
||||
set_proc_address(^GenerateMipmap, "glGenerateMipmap\x00");
|
||||
set_proc_address(&ActiveTexture, "glActiveTexture\x00");
|
||||
set_proc_address(&GenerateMipmap, "glGenerateMipmap\x00");
|
||||
|
||||
set_proc_address(^Uniform1i, "glUniform1i\x00");
|
||||
set_proc_address(^UniformMatrix4fv, "glUniformMatrix4fv\x00");
|
||||
set_proc_address(&Uniform1i, "glUniform1i\x00");
|
||||
set_proc_address(&UniformMatrix4fv, "glUniformMatrix4fv\x00");
|
||||
|
||||
set_proc_address(^GetUniformLocation, "glGetUniformLocation\x00");
|
||||
set_proc_address(&GetUniformLocation, "glGetUniformLocation\x00");
|
||||
|
||||
set_proc_address(^SamplerParameteri, "glSamplerParameteri\x00");
|
||||
set_proc_address(^SamplerParameterf, "glSamplerParameterf\x00");
|
||||
set_proc_address(^SamplerParameteriv, "glSamplerParameteriv\x00");
|
||||
set_proc_address(^SamplerParameterfv, "glSamplerParameterfv\x00");
|
||||
set_proc_address(^SamplerParameterIiv, "glSamplerParameterIiv\x00");
|
||||
set_proc_address(^SamplerParameterIuiv, "glSamplerParameterIuiv\x00");
|
||||
set_proc_address(&SamplerParameteri, "glSamplerParameteri\x00");
|
||||
set_proc_address(&SamplerParameterf, "glSamplerParameterf\x00");
|
||||
set_proc_address(&SamplerParameteriv, "glSamplerParameteriv\x00");
|
||||
set_proc_address(&SamplerParameterfv, "glSamplerParameterfv\x00");
|
||||
set_proc_address(&SamplerParameterIiv, "glSamplerParameterIiv\x00");
|
||||
set_proc_address(&SamplerParameterIuiv, "glSamplerParameterIuiv\x00");
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
#load "os_windows.odin" when ODIN_OS == "windows";
|
||||
#load "os_x.odin" when ODIN_OS == "osx";
|
||||
|
||||
#load "os_linux.odin" when ODIN_OS == "linux";
|
||||
@@ -0,0 +1,303 @@
|
||||
#import "fmt.odin";
|
||||
#import "strings.odin";
|
||||
|
||||
Handle :: i32;
|
||||
File_Time :: u64;
|
||||
Errno :: i32;
|
||||
|
||||
// INVALID_HANDLE: Handle : -1;
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
O_CREAT :: 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;
|
||||
SEEK_SET :: 0;
|
||||
SEEK_CUR :: 1;
|
||||
SEEK_END :: 2;
|
||||
SEEK_DATA :: 3;
|
||||
SEEK_HOLE :: 4;
|
||||
SEEK_MAX :: SEEK_HOLE;
|
||||
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x001;
|
||||
RTLD_NOW :: 0x002;
|
||||
RTLD_BINDING_MASK :: 0x3;
|
||||
RTLD_GLOBAL :: 0x100;
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
immutable args := _alloc_command_line_arguments();
|
||||
|
||||
_File_Time :: struct #ordered {
|
||||
seconds: i64,
|
||||
nanoseconds: i32,
|
||||
reserved: i32,
|
||||
}
|
||||
|
||||
// Translated from
|
||||
// https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/+/jb-dev/sysroot/usr/include/bits/stat.h
|
||||
// Validity is not guaranteed.
|
||||
|
||||
Stat :: struct #ordered {
|
||||
device_id: u64, // ID of device containing file
|
||||
serial: u64, // File serial number
|
||||
nlink: u32, // Number of hard links
|
||||
mode: u32, // Mode of the file
|
||||
uid: u32, // User ID of the file's owner
|
||||
gid: u32, // Group ID of the file's group
|
||||
_padding: i32, // 32 bits of padding
|
||||
rdev: u64, // Device ID, if device
|
||||
size: i64, // Size of the file, in bytes
|
||||
block_size: i64, // Optimal bllocksize for I/O
|
||||
blocks: i64, // Number of 512-byte blocks allocated
|
||||
|
||||
last_access: _File_Time, // Time of last access
|
||||
modified: _File_Time, // Time of last modification
|
||||
status_change: _File_Time, // Time of last status change
|
||||
|
||||
_reserve1,
|
||||
_reserve2,
|
||||
_reserve3: i64,
|
||||
serial_numbe: u64, // File serial number...? Maybe.
|
||||
_reserve4: i64,
|
||||
};
|
||||
|
||||
// File type
|
||||
|
||||
S_IFMT :: 0170000; // Type of file mask
|
||||
S_IFIFO :: 0010000; // Named pipe (fifo)
|
||||
S_IFCHR :: 0020000; // Character special
|
||||
S_IFDIR :: 0040000; // Directory
|
||||
S_IFBLK :: 0060000; // Block special
|
||||
S_IFREG :: 0100000; // Regular
|
||||
S_IFLNK :: 0120000; // Symbolic link
|
||||
S_IFSOCK :: 0140000; // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
|
||||
S_IRWXU :: 0000700; // RWX mask for owner
|
||||
S_IRUSR :: 0000400; // R for owner
|
||||
S_IWUSR :: 0000200; // W for owner
|
||||
S_IXUSR :: 0000100; // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
|
||||
S_IRWXG :: 0000070; // RWX mask for group
|
||||
S_IRGRP :: 0000040; // R for group
|
||||
S_IWGRP :: 0000020; // W for group
|
||||
S_IXGRP :: 0000010; // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
|
||||
S_IRWXO :: 0000007; // RWX mask for other
|
||||
S_IROTH :: 0000004; // R for other
|
||||
S_IWOTH :: 0000002; // W for other
|
||||
S_IXOTH :: 0000001; // X for other
|
||||
|
||||
S_ISUID :: 0004000; // Set user id on execution
|
||||
S_ISGID :: 0002000; // Set group id on execution
|
||||
S_ISVTX :: 0001000; // Directory restrcted delete
|
||||
|
||||
S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; }
|
||||
S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; }
|
||||
S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; }
|
||||
S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; }
|
||||
S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; }
|
||||
S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; }
|
||||
S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;}
|
||||
|
||||
R_OK :: 4; // Test for read permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
X_OK :: 1; // Test for execute permission
|
||||
F_OK :: 0; // Test for file existance
|
||||
|
||||
#foreign_system_library dl "dl";
|
||||
#foreign_system_library libc "c";
|
||||
|
||||
_unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open";
|
||||
_unix_close :: proc(fd: Handle) -> i32 #foreign libc "close";
|
||||
_unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int #foreign libc "read";
|
||||
_unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int #foreign libc "write";
|
||||
_unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 #foreign libc "lseek64";
|
||||
_unix_gettid :: proc() -> u64 #foreign libc "gettid";
|
||||
_unix_stat :: proc(path: ^u8, stat: ^Stat) -> i32 #foreign libc "stat";
|
||||
_unix_access :: proc(path: ^u8, mask: int) -> i32 #foreign libc "access";
|
||||
|
||||
_unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
|
||||
_unix_free :: proc(ptr: rawptr) #foreign libc "free";
|
||||
_unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
|
||||
_unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv";
|
||||
|
||||
_unix_exit :: proc(status: int) #foreign libc "exit";
|
||||
|
||||
_unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen";
|
||||
_unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym";
|
||||
_unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose";
|
||||
_unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror";
|
||||
|
||||
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
|
||||
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
|
||||
|
||||
cstr := strings.new_c_string(path);
|
||||
handle := _unix_open(cstr, mode);
|
||||
free(cstr);
|
||||
if(handle == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return handle, 0;
|
||||
}
|
||||
// NOTE(zangent): This is here for compatability reasons. Should this be here?
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
_unix_close(fd);
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
sz := _unix_read(fd, &data[0], len(data));
|
||||
return sz, 0;
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
sz := _unix_write(fd, &data[0], len(data));
|
||||
return sz, 0;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
res := _unix_seek(fd, offset, i32(whence));
|
||||
return res, 0;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin: Handle = 0;
|
||||
stdout: Handle = 1;
|
||||
stderr: Handle = 2;
|
||||
|
||||
/* TODO(zangent): Implement these!
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {}
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {}
|
||||
*/
|
||||
|
||||
stat :: proc(path: string) -> (Stat, int) #inline {
|
||||
s: Stat;
|
||||
cstr := strings.new_c_string(path);
|
||||
defer free(cstr);
|
||||
ret_int := _unix_stat(cstr, &s);
|
||||
return s, int(ret_int);
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> bool #inline {
|
||||
cstr := strings.new_c_string(path);
|
||||
defer free(cstr);
|
||||
return _unix_access(cstr, mask) == 0;
|
||||
}
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
fd: Handle;
|
||||
err: Errno;
|
||||
size: i64;
|
||||
|
||||
fd, err = open_simple(name, O_RDONLY);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to open file.");
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
// We have a file
|
||||
size, err = seek(fd, 0, SEEK_END);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to seek to end of file.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
_, err = seek(fd, 0, SEEK_SET);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to seek to beginning of file.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
// We have a file size!
|
||||
|
||||
data := make([]u8, size+1);
|
||||
if data == nil {
|
||||
fmt.println("Failed to allocate file buffer.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
read(fd, data);
|
||||
data[size] = 0;
|
||||
|
||||
return data, true;
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size > 0);
|
||||
return _unix_malloc(size);
|
||||
}
|
||||
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return _unix_realloc(ptr, new_size);
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.new_c_string(name);
|
||||
cstr: ^u8 = _unix_getenv(path_str);
|
||||
free(path_str);
|
||||
if(cstr == nil) {
|
||||
return "", false;
|
||||
}
|
||||
return strings.to_odin_string(cstr), true;
|
||||
}
|
||||
|
||||
exit :: proc(code: int) {
|
||||
_unix_exit(code);
|
||||
}
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
// return int(_unix_gettid());
|
||||
return 0;
|
||||
}
|
||||
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
|
||||
cstr := strings.new_c_string(filename);
|
||||
handle := _unix_dlopen(cstr, flags);
|
||||
free(cstr);
|
||||
return handle;
|
||||
}
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
|
||||
assert(handle != nil);
|
||||
cstr := strings.new_c_string(symbol);
|
||||
proc_handle := _unix_dlsym(handle, cstr);
|
||||
free(cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: proc(handle: rawptr) -> bool #inline {
|
||||
assert(handle != nil);
|
||||
return _unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return strings.to_odin_string(_unix_dlerror());
|
||||
}
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
// TODO(bill):
|
||||
return nil;
|
||||
}
|
||||
+140
-70
@@ -1,6 +1,5 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
#import "fmt.odin";
|
||||
|
||||
#import fmt "fmt.odin";
|
||||
|
||||
Handle :: int;
|
||||
File_Time :: u64;
|
||||
@@ -50,105 +49,114 @@ WSAECONNRESET: Errno : 10054;
|
||||
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
|
||||
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
immutable args := _alloc_command_line_arguments();
|
||||
|
||||
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
using win32;
|
||||
if path.count == 0 {
|
||||
if len(path) == 0 {
|
||||
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
access: u32;
|
||||
match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = FILE_GENERIC_READ;
|
||||
case O_WRONLY: access = FILE_GENERIC_WRITE;
|
||||
case O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
||||
case O_RDONLY: access = win32.FILE_GENERIC_READ;
|
||||
case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
|
||||
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
|
||||
if mode&O_CREAT != 0 {
|
||||
access |= FILE_GENERIC_WRITE;
|
||||
access |= win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
if mode&O_APPEND != 0 {
|
||||
access &~= FILE_GENERIC_WRITE;
|
||||
access |= FILE_APPEND_DATA;
|
||||
access &~= win32.FILE_GENERIC_WRITE;
|
||||
access |= win32.FILE_APPEND_DATA;
|
||||
}
|
||||
|
||||
share_mode := cast(u32)(FILE_SHARE_READ|FILE_SHARE_WRITE);
|
||||
sa: ^SECURITY_ATTRIBUTES = nil;
|
||||
sa_inherit := SECURITY_ATTRIBUTES{length = size_of(SECURITY_ATTRIBUTES), inherit_handle = 1};
|
||||
share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
|
||||
sa: ^win32.Security_Attributes = nil;
|
||||
sa_inherit := win32.Security_Attributes{length = size_of(win32.Security_Attributes), inherit_handle = 1};
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = ^sa_inherit;
|
||||
sa = &sa_inherit;
|
||||
}
|
||||
|
||||
create_mode: u32;
|
||||
match {
|
||||
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||
create_mode = CREATE_NEW;
|
||||
create_mode = win32.CREATE_NEW;
|
||||
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
|
||||
create_mode = CREATE_ALWAYS;
|
||||
create_mode = win32.CREATE_ALWAYS;
|
||||
case mode&O_CREAT == O_CREAT:
|
||||
create_mode = OPEN_ALWAYS;
|
||||
create_mode = win32.OPEN_ALWAYS;
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = TRUNCATE_EXISTING;
|
||||
create_mode = win32.TRUNCATE_EXISTING;
|
||||
default:
|
||||
create_mode = OPEN_EXISTING;
|
||||
create_mode = win32.OPEN_EXISTING;
|
||||
}
|
||||
|
||||
buf: [300]byte;
|
||||
copy(buf[..], cast([]byte)path);
|
||||
copy(buf[..], []byte(path));
|
||||
|
||||
handle := cast(Handle)CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil);
|
||||
handle := Handle(win32.CreateFileA(&buf[0], access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
|
||||
if handle != INVALID_HANDLE {
|
||||
return handle, ERROR_NONE;
|
||||
}
|
||||
err := GetLastError();
|
||||
return INVALID_HANDLE, cast(Errno)err;
|
||||
err := win32.GetLastError();
|
||||
return INVALID_HANDLE, Errno(err);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
win32.CloseHandle(cast(win32.HANDLE)fd);
|
||||
win32.CloseHandle(win32.Handle(fd));
|
||||
}
|
||||
|
||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||
return write(fd, []byte(str));
|
||||
}
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
bytes_written: i32;
|
||||
e := win32.WriteFile(cast(win32.HANDLE)fd, data.data, cast(i32)data.count, ^bytes_written, nil);
|
||||
e := win32.WriteFile(win32.Handle(fd), &data[0], i32(len(data)), &bytes_written, nil);
|
||||
if e == win32.FALSE {
|
||||
err := win32.GetLastError();
|
||||
return 0, cast(Errno)err;
|
||||
return 0, Errno(err);
|
||||
}
|
||||
return cast(int)bytes_written, ERROR_NONE;
|
||||
return int(bytes_written), ERROR_NONE;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE;
|
||||
}
|
||||
bytes_read: i32;
|
||||
e := win32.ReadFile(cast(win32.HANDLE)fd, data.data, cast(u32)data.count, ^bytes_read, nil);
|
||||
e := win32.ReadFile(win32.Handle(fd), &data[0], u32(len(data)), &bytes_read, nil);
|
||||
if e == win32.FALSE {
|
||||
err := win32.GetLastError();
|
||||
return 0, cast(Errno)err;
|
||||
return 0, Errno(err);
|
||||
}
|
||||
return cast(int)bytes_read, ERROR_NONE;
|
||||
return int(bytes_read), ERROR_NONE;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
using win32;
|
||||
w: u32;
|
||||
match whence {
|
||||
case 0: w = FILE_BEGIN;
|
||||
case 1: w = FILE_CURRENT;
|
||||
case 2: w = FILE_END;
|
||||
case 0: w = win32.FILE_BEGIN;
|
||||
case 1: w = win32.FILE_CURRENT;
|
||||
case 2: w = win32.FILE_END;
|
||||
}
|
||||
hi := cast(i32)(offset>>32);
|
||||
lo := cast(i32)(offset);
|
||||
ft := GetFileType(cast(HANDLE)fd);
|
||||
if ft == FILE_TYPE_PIPE {
|
||||
hi := i32(offset>>32);
|
||||
lo := i32(offset);
|
||||
ft := win32.GetFileType(win32.Handle(fd));
|
||||
if ft == win32.FILE_TYPE_PIPE {
|
||||
return 0, ERROR_FILE_IS_PIPE;
|
||||
}
|
||||
dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w);
|
||||
if dw_ptr == INVALID_SET_FILE_POINTER {
|
||||
err := GetLastError();
|
||||
return 0, cast(Errno)err;
|
||||
dw_ptr := win32.SetFilePointer(win32.Handle(fd), lo, &hi, w);
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := win32.GetLastError();
|
||||
return 0, Errno(err);
|
||||
}
|
||||
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
|
||||
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -159,9 +167,9 @@ stderr := get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
|
||||
get_std_handle :: proc(h: int) -> Handle {
|
||||
fd := win32.GetStdHandle(cast(i32)h);
|
||||
fd := win32.GetStdHandle(i32(h));
|
||||
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
return cast(Handle)fd;
|
||||
return Handle(fd);
|
||||
}
|
||||
|
||||
|
||||
@@ -170,38 +178,35 @@ get_std_handle :: proc(h: int) -> Handle {
|
||||
|
||||
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {
|
||||
file_info: win32.BY_HANDLE_FILE_INFORMATION;
|
||||
win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info);
|
||||
lo := cast(File_Time)file_info.last_write_time.lo;
|
||||
hi := cast(File_Time)file_info.last_write_time.hi;
|
||||
file_info: win32.By_Handle_File_Information;
|
||||
win32.GetFileInformationByHandle(win32.Handle(fd), &file_info);
|
||||
lo := File_Time(file_info.last_write_time.lo);
|
||||
hi := File_Time(file_info.last_write_time.hi);
|
||||
return lo | hi << 32;
|
||||
}
|
||||
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {
|
||||
last_write_time: win32.FILETIME;
|
||||
data: win32.FILE_ATTRIBUTE_DATA;
|
||||
last_write_time: win32.Filetime;
|
||||
data: win32.File_Attribute_Data;
|
||||
buf: [1024]byte;
|
||||
|
||||
assert(buf.count > name.count);
|
||||
assert(len(buf) > len(name));
|
||||
|
||||
copy(buf[..], cast([]byte)name);
|
||||
copy(buf[..], []byte(name));
|
||||
|
||||
if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
|
||||
if win32.GetFileAttributesExA(&buf[0], win32.GetFileExInfoStandard, &data) != 0 {
|
||||
last_write_time = data.last_write_time;
|
||||
}
|
||||
|
||||
l := cast(File_Time)last_write_time.lo;
|
||||
h := cast(File_Time)last_write_time.hi;
|
||||
l := File_Time(last_write_time.lo);
|
||||
h := File_Time(last_write_time.hi);
|
||||
return l | h << 32;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
buf: [300]byte;
|
||||
copy(buf[..], cast([]byte)name);
|
||||
copy(buf[..], []byte(name));
|
||||
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != ERROR_NONE {
|
||||
@@ -210,13 +215,16 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
defer close(fd);
|
||||
|
||||
length: i64;
|
||||
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
|
||||
if !file_size_ok {
|
||||
if ok := win32.GetFileSizeEx(win32.Handle(fd), &length) != 0; !ok {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
data := new_slice(u8, length);
|
||||
if data.data == nil {
|
||||
if length == 0 {
|
||||
return nil, true;
|
||||
}
|
||||
|
||||
data := make([]byte, length);
|
||||
if data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
@@ -228,18 +236,18 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
to_read: u32;
|
||||
MAX :: 1<<32-1;
|
||||
if remaining <= MAX {
|
||||
to_read = cast(u32)remaining;
|
||||
to_read = u32(remaining);
|
||||
} else {
|
||||
to_read = MAX;
|
||||
}
|
||||
|
||||
win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil);
|
||||
win32.ReadFile(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil);
|
||||
if single_read_length <= 0 {
|
||||
free(data);
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
total_read += cast(i64)single_read_length;
|
||||
total_read += i64(single_read_length);
|
||||
}
|
||||
|
||||
return data, true;
|
||||
@@ -248,7 +256,6 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
assert(size > 0);
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
@@ -270,14 +277,77 @@ heap_free :: proc(ptr: rawptr) {
|
||||
|
||||
|
||||
exit :: proc(code: int) {
|
||||
win32.ExitProcess(cast(u32)code);
|
||||
win32.ExitProcess(u32(code));
|
||||
}
|
||||
|
||||
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
return cast(int)win32.GetCurrentThreadId();
|
||||
return int(win32.GetCurrentThreadId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string {
|
||||
wstr_len := 0;
|
||||
for (wstr+wstr_len)^ != 0 {
|
||||
wstr_len++;
|
||||
}
|
||||
len := 2*wstr_len-1;
|
||||
buf := make([]byte, len+1);
|
||||
str := slice_ptr(wstr, wstr_len+1);
|
||||
|
||||
i, j := 0, 0;
|
||||
for str[j] != 0 {
|
||||
match {
|
||||
case str[j] < 0x80:
|
||||
if i+1 > len {
|
||||
return "";
|
||||
}
|
||||
buf[i] = byte(str[j]); i++;
|
||||
j++;
|
||||
case str[j] < 0x800:
|
||||
if i+2 > len {
|
||||
return "";
|
||||
}
|
||||
buf[i] = byte(0xc0 + (str[j]>>6)); i++;
|
||||
buf[i] = byte(0x80 + (str[j]&0x3f)); i++;
|
||||
j++;
|
||||
case 0xd800 <= str[j] && str[j] < 0xdc00:
|
||||
if i+4 > len {
|
||||
return "";
|
||||
}
|
||||
c := rune((str[j] - 0xd800) << 10) + rune((str[j+1]) - 0xdc00) + 0x10000;
|
||||
buf[i] = byte(0xf0 + (c >> 18)); i++;
|
||||
buf[i] = byte(0x80 + ((c >> 12) & 0x3f)); i++;
|
||||
buf[i] = byte(0x80 + ((c >> 6) & 0x3f)); i++;
|
||||
buf[i] = byte(0x80 + ((c ) & 0x3f)); i++;
|
||||
j += 2;
|
||||
case 0xdc00 <= str[j] && str[j] < 0xe000:
|
||||
return "";
|
||||
default:
|
||||
if i+3 > len {
|
||||
return "";
|
||||
}
|
||||
buf[i] = 0xe0 + byte (str[j] >> 12); i++;
|
||||
buf[i] = 0x80 + byte((str[j] >> 6) & 0x3f); i++;
|
||||
buf[i] = 0x80 + byte((str[j] ) & 0x3f); i++;
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf[0..<i]);
|
||||
}
|
||||
|
||||
arg_count: i32;
|
||||
arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count);
|
||||
arg_list := make([]string, arg_count);
|
||||
for _, i in arg_list {
|
||||
arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^);
|
||||
}
|
||||
return arg_list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+221
-139
@@ -1,11 +1,14 @@
|
||||
#import "fmt.odin";
|
||||
#import "strings.odin";
|
||||
|
||||
Handle :: i32;
|
||||
File_Time :: u64;
|
||||
Errno :: int;
|
||||
|
||||
// INVALID_HANDLE: Handle : -1;
|
||||
// TODO(zangent): Find out how to make this work on x64 and x32.
|
||||
AddressSize :: i64;
|
||||
|
||||
// INVALID_HANDLE: Handle : -1;
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
@@ -19,91 +22,178 @@ O_APPEND :: 0x00400;
|
||||
O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
SEEK_SET :: 0;
|
||||
SEEK_CUR :: 1;
|
||||
SEEK_END :: 2;
|
||||
SEEK_DATA :: 3;
|
||||
SEEK_HOLE :: 4;
|
||||
SEEK_MAX :: SEEK_HOLE;
|
||||
|
||||
// ERROR_NONE: Errno : 0;
|
||||
// ERROR_FILE_NOT_FOUND: Errno : 2;
|
||||
// ERROR_PATH_NOT_FOUND: Errno : 3;
|
||||
// ERROR_ACCESS_DENIED: Errno : 5;
|
||||
// ERROR_NO_MORE_FILES: Errno : 18;
|
||||
// ERROR_HANDLE_EOF: Errno : 38;
|
||||
// ERROR_NETNAME_DELETED: Errno : 64;
|
||||
// ERROR_FILE_EXISTS: Errno : 80;
|
||||
// ERROR_BROKEN_PIPE: Errno : 109;
|
||||
// ERROR_BUFFER_OVERFLOW: Errno : 111;
|
||||
// ERROR_INSUFFICIENT_BUFFER: Errno : 122;
|
||||
// ERROR_MOD_NOT_FOUND: Errno : 126;
|
||||
// ERROR_PROC_NOT_FOUND: Errno : 127;
|
||||
// ERROR_DIR_NOT_EMPTY: Errno : 145;
|
||||
// ERROR_ALREADY_EXISTS: Errno : 183;
|
||||
// ERROR_ENVVAR_NOT_FOUND: Errno : 203;
|
||||
// ERROR_MORE_DATA: Errno : 234;
|
||||
// ERROR_OPERATION_ABORTED: Errno : 995;
|
||||
// ERROR_IO_PENDING: Errno : 997;
|
||||
// ERROR_NOT_FOUND: Errno : 1168;
|
||||
// ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
|
||||
// WSAEACCES: Errno : 10013;
|
||||
// WSAECONNRESET: Errno : 10054;
|
||||
// NOTE(zangent): These are OS specific!
|
||||
// Do not mix these up!
|
||||
RTLD_LAZY :: 0x1;
|
||||
RTLD_NOW :: 0x2;
|
||||
RTLD_LOCAL :: 0x4;
|
||||
RTLD_GLOBAL :: 0x8;
|
||||
RTLD_NODELETE :: 0x80;
|
||||
RTLD_NOLOAD :: 0x10;
|
||||
RTLD_FIRST :: 0x100;
|
||||
|
||||
// Windows reserves errors >= 1<<29 for application use
|
||||
// ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
|
||||
args: [dynamic]string;
|
||||
|
||||
FileTime :: struct #ordered {
|
||||
seconds: i64,
|
||||
nanoseconds: i64
|
||||
}
|
||||
|
||||
Stat :: struct #ordered {
|
||||
device_id : i32, // ID of device containing file
|
||||
mode : u16, // Mode of the file
|
||||
nlink : u16, // Number of hard links
|
||||
serial : u64, // File serial number
|
||||
uid : u32, // User ID of the file's owner
|
||||
gid : u32, // Group ID of the file's group
|
||||
rdev : i32, // Device ID, if device
|
||||
|
||||
last_access : FileTime, // Time of last access
|
||||
modified : FileTime, // Time of last modification
|
||||
status_change : FileTime, // Time of last status change
|
||||
created : FileTime, // Time of creation
|
||||
|
||||
size : i64, // Size of the file, in bytes
|
||||
blocks : i64, // Number of blocks allocated for the file
|
||||
block_size: i32, // Optimal blocksize for I/O
|
||||
flags : u32, // User-defined flags for the file
|
||||
gen_num : u32, // File generation number ...?
|
||||
_spare : i32, // RESERVED
|
||||
_reserve1,
|
||||
_reserve2 : i64, // RESERVED
|
||||
};
|
||||
|
||||
// File type
|
||||
|
||||
S_IFMT :: 0170000; // Type of file mask
|
||||
S_IFIFO :: 0010000; // Named pipe (fifo)
|
||||
S_IFCHR :: 0020000; // Character special
|
||||
S_IFDIR :: 0040000; // Directory
|
||||
S_IFBLK :: 0060000; // Block special
|
||||
S_IFREG :: 0100000; // Regular
|
||||
S_IFLNK :: 0120000; // Symbolic link
|
||||
S_IFSOCK :: 0140000; // Socket
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
|
||||
S_IRWXU :: 0000700; // RWX mask for owner
|
||||
S_IRUSR :: 0000400; // R for owner
|
||||
S_IWUSR :: 0000200; // W for owner
|
||||
S_IXUSR :: 0000100; // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
|
||||
S_IRWXG :: 0000070; // RWX mask for group
|
||||
S_IRGRP :: 0000040; // R for group
|
||||
S_IWGRP :: 0000020; // W for group
|
||||
S_IXGRP :: 0000010; // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
|
||||
S_IRWXO :: 0000007; // RWX mask for other
|
||||
S_IROTH :: 0000004; // R for other
|
||||
S_IWOTH :: 0000002; // W for other
|
||||
S_IXOTH :: 0000001; // X for other
|
||||
|
||||
S_ISUID :: 0004000; // Set user id on execution
|
||||
S_ISGID :: 0002000; // Set group id on execution
|
||||
S_ISVTX :: 0001000; // Directory restrcted delete
|
||||
|
||||
S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; }
|
||||
S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; }
|
||||
S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; }
|
||||
S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; }
|
||||
S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; }
|
||||
S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; }
|
||||
S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;}
|
||||
|
||||
R_OK :: 4; // Test for read permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
X_OK :: 1; // Test for execute permission
|
||||
F_OK :: 0; // Test for file existance
|
||||
|
||||
#foreign_system_library dl "dl";
|
||||
#foreign_system_library libc "c";
|
||||
|
||||
unix_open :: proc(path: ^u8, mode: int, perm: u32) -> Handle #foreign libc "open";
|
||||
unix_close :: proc(handle: Handle) #foreign libc "close";
|
||||
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "read";
|
||||
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "write";
|
||||
unix_gettid :: proc() -> u64 #foreign libc "gettid";
|
||||
unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open";
|
||||
unix_close :: proc(handle: Handle) #foreign libc "close";
|
||||
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "read";
|
||||
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "write";
|
||||
unix_lseek :: proc(fs: Handle, offset: AddressSize, whence: int) -> AddressSize #foreign libc "lseek";
|
||||
unix_gettid :: proc() -> u64 #foreign libc "gettid";
|
||||
unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #foreign libc "stat";
|
||||
unix_access :: proc(path: ^u8, mask: int) -> int #foreign libc "access";
|
||||
|
||||
unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
|
||||
unix_free :: proc(ptr: rawptr) #foreign libc "free";
|
||||
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
|
||||
unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
|
||||
unix_free :: proc(ptr: rawptr) #foreign libc "free";
|
||||
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
|
||||
unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv";
|
||||
|
||||
unix_exit :: proc(status: int) #foreign libc "exit";
|
||||
unix_exit :: proc(status: int) #foreign libc "exit";
|
||||
|
||||
unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen";
|
||||
unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym";
|
||||
unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose";
|
||||
unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror";
|
||||
|
||||
|
||||
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
|
||||
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
|
||||
|
||||
cstr := strings.new_c_string(path);
|
||||
handle := unix_open(cstr, mode);
|
||||
free(cstr);
|
||||
if(handle == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return handle, 0;
|
||||
}
|
||||
|
||||
// NOTE(zangent): This is here for compatability reasons. Should this be here?
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
return unix_open(path.data, mode, perm), 0;
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
unix_close(fd);
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return unix_write(fd, data.data, data.count), 0;
|
||||
write :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
bytes_written := unix_write(fd, &data[0], len(data));
|
||||
if(bytes_written == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_written, 0;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return unix_read(fd, data.data, data.count), 0;
|
||||
read :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
bytes_read := unix_read(fd, &data[0], len(data));
|
||||
if(bytes_read == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return bytes_read, 0;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
/*
|
||||
using win32;
|
||||
w: u32;
|
||||
match whence {
|
||||
case 0: w = FILE_BEGIN;
|
||||
case 1: w = FILE_CURRENT;
|
||||
case 2: w = FILE_END;
|
||||
}
|
||||
hi := cast(i32)(offset>>32);
|
||||
lo := cast(i32)(offset);
|
||||
ft := GetFileType(cast(HANDLE)fd);
|
||||
if ft == FILE_TYPE_PIPE {
|
||||
return 0, ERROR_FILE_IS_PIPE;
|
||||
}
|
||||
dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w);
|
||||
if dw_ptr == INVALID_SET_FILE_POINTER {
|
||||
err := GetLastError();
|
||||
return 0, cast(Errno)err;
|
||||
}
|
||||
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
|
||||
seek :: proc(fd: Handle, offset: AddressSize, whence: int) -> (AddressSize, Errno) {
|
||||
assert(fd != -1);
|
||||
|
||||
*/
|
||||
return 0, 0;
|
||||
final_offset := unix_lseek(fd, offset, whence);
|
||||
if(final_offset == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return final_offset, 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -112,111 +202,85 @@ stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE);
|
||||
stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
/* TODO(zangent): Implement these!
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {}
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {}
|
||||
*/
|
||||
|
||||
/*
|
||||
get_std_handle :: proc(h: int) -> Handle {
|
||||
fd := win32.GetStdHandle(cast(i32)h);
|
||||
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
return cast(Handle)fd;
|
||||
stat :: proc(path: string) -> (Stat, bool) #inline {
|
||||
s: Stat;
|
||||
cstr := strings.new_c_string(path);
|
||||
defer free(cstr);
|
||||
ret_int := unix_stat(cstr, &s);
|
||||
return s, ret_int==0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {
|
||||
file_info: win32.BY_HANDLE_FILE_INFORMATION;
|
||||
win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info);
|
||||
lo := cast(File_Time)file_info.last_write_time.lo;
|
||||
hi := cast(File_Time)file_info.last_write_time.hi;
|
||||
return lo | hi << 32;
|
||||
access :: proc(path: string, mask: int) -> bool #inline {
|
||||
cstr := strings.new_c_string(path);
|
||||
defer free(cstr);
|
||||
return unix_access(cstr, mask) == 0;
|
||||
}
|
||||
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {
|
||||
last_write_time: win32.FILETIME;
|
||||
data: win32.FILE_ATTRIBUTE_DATA;
|
||||
buf: [1024]byte;
|
||||
|
||||
assert(buf.count > name.count);
|
||||
|
||||
copy(buf[:], cast([]byte)name);
|
||||
|
||||
if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
|
||||
last_write_time = data.last_write_time;
|
||||
}
|
||||
|
||||
l := cast(File_Time)last_write_time.lo;
|
||||
h := cast(File_Time)last_write_time.hi;
|
||||
return l | h << 32;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
buf: [300]byte;
|
||||
copy(buf[:], cast([]byte)name);
|
||||
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != ERROR_NONE {
|
||||
handle, err := open_simple(name, O_RDONLY);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to open file.");
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
defer(close(handle));
|
||||
|
||||
length: i64;
|
||||
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
|
||||
if !file_size_ok {
|
||||
// We have a file!
|
||||
|
||||
size: AddressSize;
|
||||
size, err = seek(handle, 0, SEEK_END);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to seek to end of file.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
data := new_slice(u8, length);
|
||||
if data.data == nil {
|
||||
_, err = seek(handle, 0, SEEK_SET);
|
||||
if(err != 0) {
|
||||
fmt.println("Failed to seek to beginning of file.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
single_read_length: i32;
|
||||
total_read: i64;
|
||||
// We have a file size!
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read;
|
||||
to_read: u32;
|
||||
MAX :: 1<<32-1;
|
||||
if remaining <= MAX {
|
||||
to_read = cast(u32)remaining;
|
||||
} else {
|
||||
to_read = MAX;
|
||||
}
|
||||
|
||||
win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil);
|
||||
if single_read_length <= 0 {
|
||||
free(data);
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
total_read += cast(i64)single_read_length;
|
||||
data := make([]u8, size+1);
|
||||
if data == nil {
|
||||
fmt.println("Failed to allocate file buffer.");
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
read(handle, data);
|
||||
data[size] = 0;
|
||||
|
||||
return data, true;
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
heap_alloc :: proc(size: int) -> rawptr #inline {
|
||||
assert(size > 0);
|
||||
return unix_malloc(size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr #inline {
|
||||
return unix_realloc(ptr, new_size);
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
heap_free :: proc(ptr: rawptr) #inline {
|
||||
unix_free(ptr);
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.new_c_string(name);
|
||||
cstr: ^u8 = unix_getenv(path_str);
|
||||
free(path_str);
|
||||
if(cstr == nil) {
|
||||
return "", false;
|
||||
}
|
||||
return strings.to_odin_string(cstr), true;
|
||||
}
|
||||
|
||||
exit :: proc(code: int) {
|
||||
exit :: proc(code: int) #inline {
|
||||
unix_exit(code);
|
||||
}
|
||||
|
||||
@@ -226,5 +290,23 @@ current_thread_id :: proc() -> int {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
|
||||
cstr := strings.new_c_string(filename);
|
||||
handle := unix_dlopen(cstr, flags);
|
||||
free(cstr);
|
||||
return handle;
|
||||
}
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
|
||||
assert(handle != nil);
|
||||
cstr := strings.new_c_string(symbol);
|
||||
proc_handle := unix_dlsym(handle, cstr);
|
||||
free(cstr);
|
||||
return proc_handle;
|
||||
}
|
||||
dlclose :: proc(handle: rawptr) -> bool #inline {
|
||||
assert(handle != nil);
|
||||
return unix_dlclose(handle) == 0;
|
||||
}
|
||||
dlerror :: proc() -> string {
|
||||
return strings.to_odin_string(unix_dlerror());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
Any :: struct #ordered {
|
||||
data: rawptr,
|
||||
type_info: ^Type_Info,
|
||||
}
|
||||
|
||||
String :: struct #ordered {
|
||||
data: ^byte,
|
||||
len: int,
|
||||
};
|
||||
|
||||
Slice :: struct #ordered {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
cap: int,
|
||||
};
|
||||
|
||||
Dynamic_Array :: struct #ordered {
|
||||
data: rawptr,
|
||||
len: int,
|
||||
cap: int,
|
||||
allocator: Allocator,
|
||||
};
|
||||
|
||||
Dynamic_Map :: struct #ordered {
|
||||
hashes: [dynamic]int,
|
||||
entries: Dynamic_Array,
|
||||
};
|
||||
+119
-75
@@ -18,24 +18,65 @@ parse_bool :: proc(s: string) -> (result: bool, ok: bool) {
|
||||
return false, false;
|
||||
}
|
||||
|
||||
_digit_value :: proc(r: rune) -> (int) {
|
||||
ri := int(r);
|
||||
v: int = 16;
|
||||
match r {
|
||||
case '0'..'9': v = ri-'0';
|
||||
case 'a'..'z': v = ri-'a'+10;
|
||||
case 'A'..'Z': v = ri-'A'+10;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
parse_i64 :: proc(s: string, base: int) -> i64 {
|
||||
result: i64;
|
||||
for r in s {
|
||||
v := _digit_value(r);
|
||||
if v >= base {
|
||||
break;
|
||||
}
|
||||
result *= i64(base);
|
||||
result += i64(v);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
parse_u64 :: proc(s: string, base: int) -> u64 {
|
||||
result: u64;
|
||||
for r in s {
|
||||
v := _digit_value(r);
|
||||
if v >= base {
|
||||
break;
|
||||
}
|
||||
result *= u64(base);
|
||||
result += u64(v);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
parse_int :: proc(s: string, base: int) -> int {
|
||||
return int(parse_i64(s, base));
|
||||
}
|
||||
parse_uint :: proc(s: string, base: int) -> uint {
|
||||
return uint(parse_u64(s, base));
|
||||
}
|
||||
|
||||
|
||||
append_bool :: proc(buf: []byte, b: bool) -> string {
|
||||
s := b ? "true" : "false";
|
||||
append(buf, ..cast([]byte)s);
|
||||
return cast(string)buf;
|
||||
append(buf, ..[]byte(s));
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
|
||||
return append_bits(buf, u, base, false, digits, 0);
|
||||
return append_bits(buf, u, base, false, 8*size_of(uint), digits, 0);
|
||||
}
|
||||
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
|
||||
return append_bits(buf, cast(u64)i, base, i < 0, digits, 0);
|
||||
}
|
||||
itoa :: proc(buf: []byte, i: int) -> string {
|
||||
return append_int(buf, cast(i64)i, 10);
|
||||
return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, 0);
|
||||
}
|
||||
itoa :: proc(buf: []byte, i: int) -> string { return append_int(buf, i64(i), 10); }
|
||||
|
||||
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
|
||||
return cast(string)generic_ftoa(buf, f, fmt, prec, bit_size);
|
||||
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
|
||||
}
|
||||
|
||||
|
||||
@@ -63,18 +104,18 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
|
||||
flt: ^Float_Info;
|
||||
match bit_size {
|
||||
case 32:
|
||||
bits = cast(u64)transmute(u32)cast(f32)val;
|
||||
flt = ^f32_info;
|
||||
bits = u64(transmute(u32, f32(val)));
|
||||
flt = &f32_info;
|
||||
case 64:
|
||||
bits = transmute(u64)val;
|
||||
flt = ^f64_info;
|
||||
bits = transmute(u64, val);
|
||||
flt = &f64_info;
|
||||
default:
|
||||
panic("strconv: invalid bit_size");
|
||||
}
|
||||
|
||||
neg := bits>>(flt.expbits+flt.mantbits) != 0;
|
||||
exp := cast(int)(bits>>flt.mantbits) & (1<<flt.expbits - 1);
|
||||
mant := bits & (cast(u64)1 << flt.mantbits - 1);
|
||||
exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1);
|
||||
mant := bits & (u64(1) << flt.mantbits - 1);
|
||||
|
||||
match exp {
|
||||
case 1<<flt.expbits - 1:
|
||||
@@ -86,22 +127,22 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
|
||||
} else {
|
||||
s = "+Inf";
|
||||
}
|
||||
append(buf, ..cast([]byte)s);
|
||||
append(buf, ..[]byte(s));
|
||||
return buf;
|
||||
|
||||
case 0: // denormalized
|
||||
exp++;
|
||||
|
||||
default:
|
||||
mant |= cast(u64)1 << flt.mantbits;
|
||||
mant |= u64(1) << flt.mantbits;
|
||||
}
|
||||
|
||||
exp += flt.bias;
|
||||
|
||||
d_: Decimal;
|
||||
d := ^d_;
|
||||
d := &d_;
|
||||
assign(d, mant);
|
||||
shift(d, exp - cast(int)flt.mantbits);
|
||||
shift(d, exp - int(flt.mantbits));
|
||||
digs: Decimal_Slice;
|
||||
shortest := prec < 0;
|
||||
if shortest {
|
||||
@@ -133,50 +174,33 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
|
||||
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []byte {
|
||||
match fmt {
|
||||
case 'f', 'F':
|
||||
add_bytes :: proc(dst: ^[]byte, w: ^int, bytes: ..byte) {
|
||||
for b in bytes {
|
||||
if dst.capacity <= w^ {
|
||||
break;
|
||||
}
|
||||
dst.count++;
|
||||
dst[w^] = b;
|
||||
w^++;
|
||||
}
|
||||
}
|
||||
|
||||
dst := buf[..];
|
||||
w := 0;
|
||||
if neg {
|
||||
add_bytes(^dst, ^w, '-');
|
||||
} else {
|
||||
add_bytes(^dst, ^w, '+');
|
||||
}
|
||||
append(buf, neg ? '-' : '+');
|
||||
|
||||
// integer, padded with zeros when needed
|
||||
if digs.decimal_point > 0 {
|
||||
m := min(digs.count, digs.decimal_point);
|
||||
add_bytes(^dst, ^w, ..digs.digits[..m]);
|
||||
append(buf, ..digs.digits[0..<m]);
|
||||
for ; m < digs.decimal_point; m++ {
|
||||
add_bytes(^dst, ^w, '0');
|
||||
append(buf, '0');
|
||||
}
|
||||
} else {
|
||||
add_bytes(^dst, ^w, '0');
|
||||
append(buf, '0');
|
||||
}
|
||||
|
||||
|
||||
// fractional part
|
||||
if prec > 0 {
|
||||
add_bytes(^dst, ^w, '.');
|
||||
append(buf, '.');
|
||||
for i in 0..prec {
|
||||
c: byte = '0';
|
||||
if j := digs.decimal_point + i; 0 <= j && j < digs.count {
|
||||
c = digs.digits[j];
|
||||
}
|
||||
add_bytes(^dst, ^w, c);
|
||||
append(buf, c);
|
||||
}
|
||||
}
|
||||
|
||||
return buf[..w];
|
||||
return buf;
|
||||
|
||||
case 'e', 'E':
|
||||
panic("strconv: e/E float printing is not yet supported");
|
||||
@@ -207,14 +231,14 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
|
||||
332*(dp-nd) >= 100*(exp-mantbits)
|
||||
*/
|
||||
minexp := flt.bias+1;
|
||||
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - cast(int)flt.mantbits) {
|
||||
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
|
||||
// Number is already its shortest
|
||||
return;
|
||||
}
|
||||
|
||||
upper_: Decimal; upper: = ^upper_;
|
||||
upper_: Decimal; upper: = &upper_;
|
||||
assign(upper, 2*mant - 1);
|
||||
shift(upper, exp - cast(int)flt.mantbits - 1);
|
||||
shift(upper, exp - int(flt.mantbits) - 1);
|
||||
|
||||
mantlo: u64;
|
||||
explo: int;
|
||||
@@ -225,13 +249,13 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
|
||||
mantlo = 2*mant - 1;
|
||||
explo = exp-1;
|
||||
}
|
||||
lower_: Decimal; lower: = ^lower_;
|
||||
lower_: Decimal; lower: = &lower_;
|
||||
assign(lower, 2*mantlo + 1);
|
||||
shift(lower, explo - cast(int)flt.mantbits - 1);
|
||||
shift(lower, explo - int(flt.mantbits) - 1);
|
||||
|
||||
inclusive := mant%2 == 0;
|
||||
|
||||
for i in 0..d.count {
|
||||
for i in 0..<d.count {
|
||||
l: byte = '0'; // lower digit
|
||||
if i < lower.count {
|
||||
l = lower.digits[i];
|
||||
@@ -265,7 +289,39 @@ MAX_BASE :: 32;
|
||||
immutable digits := "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
|
||||
append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, flags: Int_Flag) -> string {
|
||||
is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
|
||||
neg := false;
|
||||
if is_signed {
|
||||
match bit_size {
|
||||
case 8:
|
||||
i := i8(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u64(i);
|
||||
case 16:
|
||||
i := i16(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u64(i);
|
||||
case 32:
|
||||
i := i32(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u64(i);
|
||||
case 64:
|
||||
i := i64(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u64(i);
|
||||
default:
|
||||
panic("is_integer_negative: Unknown integer size");
|
||||
}
|
||||
}
|
||||
return u, neg;
|
||||
}
|
||||
|
||||
|
||||
append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flag) -> string {
|
||||
is_pow2 :: proc(x: i64) -> bool {
|
||||
if (x <= 0) {
|
||||
return false;
|
||||
@@ -278,33 +334,20 @@ append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, f
|
||||
}
|
||||
|
||||
a: [65]byte;
|
||||
i := a.count;
|
||||
if neg {
|
||||
u = -u;
|
||||
i := len(a);
|
||||
|
||||
neg: bool;
|
||||
u, neg = is_integer_negative(u, is_signed, bit_size);
|
||||
|
||||
for b := u64(base); u >= b; {
|
||||
i--;
|
||||
q := u / b;
|
||||
a[i] = digits[uint(u-q*b)];
|
||||
u = q;
|
||||
}
|
||||
|
||||
if is_pow2(cast(i64)base) {
|
||||
b := cast(u64)base;
|
||||
m := cast(uint)b - 1;
|
||||
for u >= b {
|
||||
i--;
|
||||
a[i] = digits[cast(uint)u & m];
|
||||
u >>= b;
|
||||
}
|
||||
i--;
|
||||
a[i] = digits[cast(uint)u];
|
||||
} else {
|
||||
b := cast(u64)base;
|
||||
for u >= b {
|
||||
i--;
|
||||
q := u / b;
|
||||
a[i] = digits[cast(uint)(u-q*b)];
|
||||
u = q;
|
||||
}
|
||||
|
||||
i--;
|
||||
a[i] = digits[cast(uint)u];
|
||||
}
|
||||
i--;
|
||||
a[i] = digits[uint(u)];
|
||||
|
||||
if flags&Int_Flag.PREFIX != 0 {
|
||||
ok := true;
|
||||
@@ -312,6 +355,7 @@ append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, f
|
||||
case 2: i--; a[i] = 'b';
|
||||
case 8: i--; a[i] = 'o';
|
||||
case 10: i--; a[i] = 'd';
|
||||
case 12: i--; a[i] = 'z';
|
||||
case 16: i--; a[i] = 'x';
|
||||
default: ok = false;
|
||||
}
|
||||
@@ -331,6 +375,6 @@ append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, f
|
||||
|
||||
|
||||
append(buf, ..a[i..]);
|
||||
return cast(string)buf;
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
|
||||
+8
-9
@@ -1,15 +1,14 @@
|
||||
new_c_string :: proc(s: string) -> ^byte {
|
||||
c := new_slice(byte, s.count+1);
|
||||
copy(c, cast([]byte)s);
|
||||
c[s.count] = 0;
|
||||
return c.data;
|
||||
c := make([]byte, len(s)+1);
|
||||
copy(c, []byte(s));
|
||||
c[len(s)] = 0;
|
||||
return &c[0];
|
||||
}
|
||||
|
||||
to_odin_string :: proc(c: ^byte) -> string {
|
||||
s: string;
|
||||
s.data = c;
|
||||
for (c+s.count)^ != 0 {
|
||||
s.count++;
|
||||
len := 0;
|
||||
for (c+len)^ != 0 {
|
||||
len++;
|
||||
}
|
||||
return s;
|
||||
return string(slice_ptr(c, len));
|
||||
}
|
||||
|
||||
+2
-91
@@ -1,91 +1,2 @@
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#import "atomic.odin";
|
||||
|
||||
Semaphore :: struct {
|
||||
_handle: win32.HANDLE,
|
||||
}
|
||||
|
||||
Mutex :: struct {
|
||||
_semaphore: Semaphore,
|
||||
_counter: i32,
|
||||
_owner: i32,
|
||||
_recursion: i32,
|
||||
}
|
||||
|
||||
current_thread_id :: proc() -> i32 {
|
||||
return cast(i32)win32.GetCurrentThreadId();
|
||||
}
|
||||
|
||||
semaphore_init :: proc(s: ^Semaphore) {
|
||||
s._handle = win32.CreateSemaphoreA(nil, 0, 1<<31-1, nil);
|
||||
}
|
||||
|
||||
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||
win32.CloseHandle(s._handle);
|
||||
}
|
||||
|
||||
semaphore_post :: proc(s: ^Semaphore, count: int) {
|
||||
win32.ReleaseSemaphore(s._handle, cast(i32)count, nil);
|
||||
}
|
||||
|
||||
semaphore_release :: proc(s: ^Semaphore) #inline { semaphore_post(s, 1); }
|
||||
|
||||
semaphore_wait :: proc(s: ^Semaphore) {
|
||||
win32.WaitForSingleObject(s._handle, win32.INFINITE);
|
||||
}
|
||||
|
||||
|
||||
mutex_init :: proc(m: ^Mutex) {
|
||||
atomic.store(^m._counter, 0);
|
||||
atomic.store(^m._owner, current_thread_id());
|
||||
semaphore_init(^m._semaphore);
|
||||
m._recursion = 0;
|
||||
}
|
||||
mutex_destroy :: proc(m: ^Mutex) {
|
||||
semaphore_destroy(^m._semaphore);
|
||||
}
|
||||
mutex_lock :: proc(m: ^Mutex) {
|
||||
thread_id := current_thread_id();
|
||||
if atomic.fetch_add(^m._counter, 1) > 0 {
|
||||
if thread_id != atomic.load(^m._owner) {
|
||||
semaphore_wait(^m._semaphore);
|
||||
}
|
||||
}
|
||||
atomic.store(^m._owner, thread_id);
|
||||
m._recursion++;
|
||||
}
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
thread_id := current_thread_id();
|
||||
if atomic.load(^m._owner) == thread_id {
|
||||
atomic.fetch_add(^m._counter, 1);
|
||||
} else {
|
||||
expected: i32 = 0;
|
||||
if atomic.load(^m._counter) != 0 {
|
||||
return false;
|
||||
}
|
||||
if atomic.compare_exchange(^m._counter, expected, 1) == 0 {
|
||||
return false;
|
||||
}
|
||||
atomic.store(^m._owner, thread_id);
|
||||
}
|
||||
m._recursion++;
|
||||
return true;
|
||||
}
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
recursion: i32;
|
||||
thread_id := current_thread_id();
|
||||
assert(thread_id == atomic.load(^m._owner));
|
||||
|
||||
m._recursion--;
|
||||
recursion = m._recursion;
|
||||
if recursion == 0 {
|
||||
atomic.store(^m._owner, thread_id);
|
||||
}
|
||||
|
||||
if atomic.fetch_add(^m._counter, -1) > 1 {
|
||||
if recursion == 0 {
|
||||
semaphore_release(^m._semaphore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#load "sync_windows.odin" when ODIN_OS == "windows";
|
||||
#load "sync_linux.odin" when ODIN_OS == "linux";
|
||||
|
||||
+35
-25
@@ -7,11 +7,12 @@ CONTEXT_FLAGS_ARB :: 0x2094;
|
||||
CONTEXT_PROFILE_MASK_ARB :: 0x9126;
|
||||
CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002;
|
||||
CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001;
|
||||
CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x00000002;
|
||||
|
||||
HGLRC :: HANDLE;
|
||||
COLORREF :: u32;
|
||||
Hglrc :: Handle;
|
||||
Color_Ref :: u32;
|
||||
|
||||
LAYERPLANEDESCRIPTOR :: struct #ordered {
|
||||
Layer_Plane_Descriptor :: struct #ordered {
|
||||
size: u16,
|
||||
version: u16,
|
||||
flags: u32,
|
||||
@@ -35,38 +36,47 @@ LAYERPLANEDESCRIPTOR :: struct #ordered {
|
||||
aux_buffers: byte,
|
||||
layer_type: byte,
|
||||
reserved: byte,
|
||||
transparent: COLORREF,
|
||||
transparent: Color_Ref,
|
||||
}
|
||||
|
||||
POINTFLOAT :: struct #ordered {
|
||||
Point_Float :: struct #ordered {
|
||||
x, y: f32,
|
||||
}
|
||||
|
||||
GLYPHMETRICSFLOAT :: struct #ordered {
|
||||
Glyph_Metrics_Float :: struct #ordered {
|
||||
black_box_x: f32,
|
||||
black_box_y: f32,
|
||||
glyph_origin: POINTFLOAT,
|
||||
glyph_origin: Point_Float,
|
||||
cell_inc_x: f32,
|
||||
cell_inc_y: f32,
|
||||
}
|
||||
|
||||
CreateContextAttribsARBType :: #type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC;
|
||||
ChoosePixelFormatARBType :: #type proc(hdc: HDC, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> BOOL #cc_c;
|
||||
CreateContextAttribsARB_Type :: #type proc(hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc;
|
||||
ChoosePixelFormatARB_Type :: #type proc(hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool #cc_c;
|
||||
SwapIntervalEXT_Type :: #type proc(interval : i32) -> bool #cc_c;
|
||||
GetExtensionsStringARB_Type :: #type proc(Hdc) -> ^byte #cc_c;
|
||||
|
||||
|
||||
CreateContext :: proc(hdc: HDC) -> HGLRC #foreign opengl32 "wglCreateContext";
|
||||
MakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign opengl32 "wglMakeCurrent";
|
||||
GetProcAddress :: proc(c_str: ^u8) -> PROC #foreign opengl32 "wglGetProcAddress";
|
||||
DeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign opengl32 "wglDeleteContext";
|
||||
CopyContext :: proc(src, dst: HGLRC, mask: u32) -> BOOL #foreign opengl32 "wglCopyContext";
|
||||
CreateLayerContext :: proc(hdc: HDC, layer_plane: i32) -> HGLRC #foreign opengl32 "wglCreateLayerContext";
|
||||
DescribeLayerPlane :: proc(hdc: HDC, pixel_format, layer_plane: i32, bytes: u32, pd: ^LAYERPLANEDESCRIPTOR) -> BOOL #foreign opengl32 "wglDescribeLayerPlane";
|
||||
GetCurrentContext :: proc() -> HGLRC #foreign opengl32 "wglGetCurrentContext";
|
||||
GetCurrentDC :: proc() -> HDC #foreign opengl32 "wglGetCurrentDC";
|
||||
GetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32 #foreign opengl32 "wglGetLayerPaletteEntries";
|
||||
RealizeLayerPalette :: proc(hdc: HDC, layer_plane: i32, realize: BOOL) -> BOOL #foreign opengl32 "wglRealizeLayerPalette";
|
||||
SetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32 #foreign opengl32 "wglSetLayerPaletteEntries";
|
||||
ShareLists :: proc(hglrc1, hglrc2: HGLRC) -> BOOL #foreign opengl32 "wglShareLists";
|
||||
SwapLayerBuffers :: proc(hdc: HDC, planes: u32) -> BOOL #foreign opengl32 "wglSwapLayerBuffers";
|
||||
UseFontBitmaps :: proc(hdc: HDC, first, count, list_base: u32) -> BOOL #foreign opengl32 "wglUseFontBitmaps";
|
||||
UseFontOutlines :: proc(hdc: HDC, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^GLYPHMETRICSFLOAT) -> BOOL #foreign opengl32 "wglUseFontOutlines";
|
||||
CreateContextAttribsARB: CreateContextAttribsARB_Type;
|
||||
ChoosePixelFormatARB: ChoosePixelFormatARB_Type;
|
||||
SwapIntervalEXT: SwapIntervalEXT_Type;
|
||||
GetExtensionsStringARB: GetExtensionsStringARB_Type;
|
||||
|
||||
|
||||
|
||||
CreateContext :: proc(hdc: Hdc) -> Hglrc #foreign opengl32 "wglCreateContext";
|
||||
MakeCurrent :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool #foreign opengl32 "wglMakeCurrent";
|
||||
GetProcAddress :: proc(c_str: ^u8) -> Proc #foreign opengl32 "wglGetProcAddress";
|
||||
DeleteContext :: proc(hglrc: Hglrc) -> Bool #foreign opengl32 "wglDeleteContext";
|
||||
CopyContext :: proc(src, dst: Hglrc, mask: u32) -> Bool #foreign opengl32 "wglCopyContext";
|
||||
CreateLayerContext :: proc(hdc: Hdc, layer_plane: i32) -> Hglrc #foreign opengl32 "wglCreateLayerContext";
|
||||
DescribeLayerPlane :: proc(hdc: Hdc, pixel_format, layer_plane: i32, bytes: u32, pd: ^Layer_Plane_Descriptor) -> Bool #foreign opengl32 "wglDescribeLayerPlane";
|
||||
GetCurrentContext :: proc() -> Hglrc #foreign opengl32 "wglGetCurrentContext";
|
||||
GetCurrentDC :: proc() -> Hdc #foreign opengl32 "wglGetCurrentDC";
|
||||
GetLayerPaletteEntries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 #foreign opengl32 "wglGetLayerPaletteEntries";
|
||||
RealizeLayerPalette :: proc(hdc: Hdc, layer_plane: i32, realize: Bool) -> Bool #foreign opengl32 "wglRealizeLayerPalette";
|
||||
SetLayerPaletteEntries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 #foreign opengl32 "wglSetLayerPaletteEntries";
|
||||
ShareLists :: proc(hglrc1, hglrc2: Hglrc) -> Bool #foreign opengl32 "wglShareLists";
|
||||
SwapLayerBuffers :: proc(hdc: Hdc, planes: u32) -> Bool #foreign opengl32 "wglSwapLayerBuffers";
|
||||
UseFontBitmaps :: proc(hdc: Hdc, first, count, list_base: u32) -> Bool #foreign opengl32 "wglUseFontBitmaps";
|
||||
UseFontOutlines :: proc(hdc: Hdc, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^Glyph_Metrics_Float) -> Bool #foreign opengl32 "wglUseFontOutlines";
|
||||
|
||||
+135
-133
@@ -2,29 +2,29 @@
|
||||
#foreign_system_library "user32.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library "gdi32.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library "winmm.lib" when ODIN_OS == "windows";
|
||||
#foreign_system_library "shell32.lib" when ODIN_OS == "windows";
|
||||
|
||||
HANDLE :: rawptr;
|
||||
HWND :: HANDLE;
|
||||
HDC :: HANDLE;
|
||||
HINSTANCE :: HANDLE;
|
||||
HICON :: HANDLE;
|
||||
HCURSOR :: HANDLE;
|
||||
HMENU :: HANDLE;
|
||||
HBRUSH :: HANDLE;
|
||||
HGDIOBJ :: HANDLE;
|
||||
HMODULE :: HANDLE;
|
||||
WPARAM :: uint;
|
||||
LPARAM :: int;
|
||||
LRESULT :: int;
|
||||
ATOM :: i16;
|
||||
BOOL :: i32;
|
||||
WNDPROC :: #type proc(HWND, u32, WPARAM, LPARAM) -> LRESULT #cc_c;
|
||||
Handle :: rawptr;
|
||||
Hwnd :: Handle;
|
||||
Hdc :: Handle;
|
||||
Hinstance :: Handle;
|
||||
Hicon :: Handle;
|
||||
Hcursor :: Handle;
|
||||
Hmenu :: Handle;
|
||||
Hbrush :: Handle;
|
||||
Hgdiobj :: Handle;
|
||||
Hmodule :: Handle;
|
||||
Wparam :: uint;
|
||||
Lparam :: int;
|
||||
Lresult :: int;
|
||||
Bool :: i32;
|
||||
Wnd_Proc :: #type proc(Hwnd, u32, Wparam, Lparam) -> Lresult #cc_c;
|
||||
|
||||
|
||||
INVALID_HANDLE_VALUE :: cast(HANDLE)~cast(int)0;
|
||||
INVALID_HANDLE :: Handle(~int(0));
|
||||
|
||||
FALSE: BOOL : 0;
|
||||
TRUE: BOOL : 1;
|
||||
FALSE: Bool : 0;
|
||||
TRUE: Bool : 1;
|
||||
|
||||
CS_VREDRAW :: 0x0001;
|
||||
CS_HREDRAW :: 0x0002;
|
||||
@@ -56,7 +56,7 @@ WM_CHAR :: 0x0102;
|
||||
|
||||
PM_REMOVE :: 1;
|
||||
|
||||
COLOR_BACKGROUND :: cast(HBRUSH)(cast(int)1);
|
||||
COLOR_BACKGROUND :: Hbrush(int(1));
|
||||
BLACK_BRUSH :: 4;
|
||||
|
||||
SM_CXSCREEN :: 0;
|
||||
@@ -65,53 +65,53 @@ SM_CYSCREEN :: 1;
|
||||
SW_SHOW :: 5;
|
||||
|
||||
|
||||
POINT :: struct #ordered {
|
||||
Point :: struct #ordered {
|
||||
x, y: i32,
|
||||
}
|
||||
|
||||
WNDCLASSEXA :: struct #ordered {
|
||||
WndClassExA :: struct #ordered {
|
||||
size, style: u32,
|
||||
wnd_proc: WNDPROC,
|
||||
wnd_proc: Wnd_Proc,
|
||||
cls_extra, wnd_extra: i32,
|
||||
instance: HINSTANCE,
|
||||
icon: HICON,
|
||||
cursor: HCURSOR,
|
||||
background: HBRUSH,
|
||||
instance: Hinstance,
|
||||
icon: Hicon,
|
||||
cursor: Hcursor,
|
||||
background: Hbrush,
|
||||
menu_name, class_name: ^u8,
|
||||
sm: HICON,
|
||||
sm: Hicon,
|
||||
}
|
||||
|
||||
MSG :: struct #ordered {
|
||||
hwnd: HWND,
|
||||
Msg :: struct #ordered {
|
||||
hwnd: Hwnd,
|
||||
message: u32,
|
||||
wparam: WPARAM,
|
||||
lparam: LPARAM,
|
||||
wparam: Wparam,
|
||||
lparam: Lparam,
|
||||
time: u32,
|
||||
pt: POINT,
|
||||
pt: Point,
|
||||
}
|
||||
|
||||
RECT :: struct #ordered {
|
||||
Rect :: struct #ordered {
|
||||
left: i32,
|
||||
top: i32,
|
||||
right: i32,
|
||||
bottom: i32,
|
||||
}
|
||||
|
||||
FILETIME :: struct #ordered {
|
||||
Filetime :: struct #ordered {
|
||||
lo, hi: u32,
|
||||
}
|
||||
|
||||
SYSTEMTIME :: struct #ordered {
|
||||
Systemtime :: struct #ordered {
|
||||
year, month: u16,
|
||||
day_of_week, day: u16,
|
||||
hour, minute, second, millisecond: u16,
|
||||
}
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION :: struct #ordered {
|
||||
By_Handle_File_Information :: struct #ordered {
|
||||
file_attributes: u32,
|
||||
creation_time,
|
||||
last_access_time,
|
||||
last_write_time: FILETIME,
|
||||
last_write_time: Filetime,
|
||||
volume_serial_number,
|
||||
file_size_high,
|
||||
file_size_low,
|
||||
@@ -120,11 +120,11 @@ BY_HANDLE_FILE_INFORMATION :: struct #ordered {
|
||||
file_index_low: u32,
|
||||
}
|
||||
|
||||
FILE_ATTRIBUTE_DATA :: struct #ordered {
|
||||
File_Attribute_Data :: struct #ordered {
|
||||
file_attributes: u32,
|
||||
creation_time,
|
||||
last_access_time,
|
||||
last_write_time: FILETIME,
|
||||
last_write_time: Filetime,
|
||||
file_size_high,
|
||||
file_size_low: u32,
|
||||
}
|
||||
@@ -136,13 +136,13 @@ GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1;
|
||||
|
||||
GetLastError :: proc() -> i32 #foreign kernel32;
|
||||
ExitProcess :: proc(exit_code: u32) #foreign kernel32;
|
||||
GetDesktopWindow :: proc() -> HWND #foreign user32;
|
||||
GetCursorPos :: proc(p: ^POINT) -> i32 #foreign user32;
|
||||
ScreenToClient :: proc(h: HWND, p: ^POINT) -> i32 #foreign user32;
|
||||
GetModuleHandleA :: proc(module_name: ^u8) -> HINSTANCE #foreign kernel32;
|
||||
GetStockObject :: proc(fn_object: i32) -> HGDIOBJ #foreign gdi32;
|
||||
GetDesktopWindow :: proc() -> Hwnd #foreign user32;
|
||||
GetCursorPos :: proc(p: ^Point) -> i32 #foreign user32;
|
||||
ScreenToClient :: proc(h: Hwnd, p: ^Point) -> i32 #foreign user32;
|
||||
GetModuleHandleA :: proc(module_name: ^u8) -> Hinstance #foreign kernel32;
|
||||
GetStockObject :: proc(fn_object: i32) -> Hgdiobj #foreign gdi32;
|
||||
PostQuitMessage :: proc(exit_code: i32) #foreign user32;
|
||||
SetWindowTextA :: proc(hwnd: HWND, c_string: ^u8) -> BOOL #foreign user32;
|
||||
SetWindowTextA :: proc(hwnd: Hwnd, c_string: ^u8) -> Bool #foreign user32;
|
||||
|
||||
QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign kernel32;
|
||||
QueryPerformanceCounter :: proc(result: ^i64) -> i32 #foreign kernel32;
|
||||
@@ -152,64 +152,66 @@ Sleep :: proc(ms: i32) -> i32 #foreign kernel32;
|
||||
OutputDebugStringA :: proc(c_str: ^u8) #foreign kernel32;
|
||||
|
||||
|
||||
RegisterClassExA :: proc(wc: ^WNDCLASSEXA) -> ATOM #foreign user32;
|
||||
RegisterClassExA :: proc(wc: ^WndClassExA) -> i16 #foreign user32;
|
||||
CreateWindowExA :: proc(ex_style: u32,
|
||||
class_name, title: ^u8,
|
||||
style: u32,
|
||||
x, y, w, h: i32,
|
||||
parent: HWND, menu: HMENU, instance: HINSTANCE,
|
||||
param: rawptr) -> HWND #foreign user32;
|
||||
parent: Hwnd, menu: Hmenu, instance: Hinstance,
|
||||
param: rawptr) -> Hwnd #foreign user32;
|
||||
|
||||
ShowWindow :: proc(hwnd: HWND, cmd_show: i32) -> BOOL #foreign user32;
|
||||
TranslateMessage :: proc(msg: ^MSG) -> BOOL #foreign user32;
|
||||
DispatchMessageA :: proc(msg: ^MSG) -> LRESULT #foreign user32;
|
||||
UpdateWindow :: proc(hwnd: HWND) -> BOOL #foreign user32;
|
||||
PeekMessageA :: proc(msg: ^MSG, hwnd: HWND,
|
||||
msg_filter_min, msg_filter_max, remove_msg: u32) -> BOOL #foreign user32;
|
||||
ShowWindow :: proc(hwnd: Hwnd, cmd_show: i32) -> Bool #foreign user32;
|
||||
TranslateMessage :: proc(msg: ^Msg) -> Bool #foreign user32;
|
||||
DispatchMessageA :: proc(msg: ^Msg) -> Lresult #foreign user32;
|
||||
UpdateWindow :: proc(hwnd: Hwnd) -> Bool #foreign user32;
|
||||
PeekMessageA :: proc(msg: ^Msg, hwnd: Hwnd,
|
||||
msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool #foreign user32;
|
||||
|
||||
DefWindowProcA :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #foreign user32;
|
||||
DefWindowProcA :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult #foreign user32;
|
||||
|
||||
AdjustWindowRect :: proc(rect: ^RECT, style: u32, menu: BOOL) -> BOOL #foreign user32;
|
||||
GetActiveWindow :: proc() -> HWND #foreign user32;
|
||||
AdjustWindowRect :: proc(rect: ^Rect, style: u32, menu: Bool) -> Bool #foreign user32;
|
||||
GetActiveWindow :: proc() -> Hwnd #foreign user32;
|
||||
|
||||
DestroyWindow :: proc(wnd: HWND) -> BOOL #foreign user32;
|
||||
DescribePixelFormat :: proc(dc: HDC, pixel_format: i32, bytes : u32, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign user32;
|
||||
DestroyWindow :: proc(wnd: Hwnd) -> Bool #foreign user32;
|
||||
DescribePixelFormat :: proc(dc: Hdc, pixel_format: i32, bytes : u32, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign user32;
|
||||
|
||||
|
||||
GetQueryPerformanceFrequency :: proc() -> i64 {
|
||||
r: i64;
|
||||
QueryPerformanceFrequency(^r);
|
||||
QueryPerformanceFrequency(&r);
|
||||
return r;
|
||||
}
|
||||
|
||||
GetCommandLineA :: proc() -> ^u8 #foreign kernel32;
|
||||
GetSystemMetrics :: proc(index: i32) -> i32 #foreign kernel32;
|
||||
GetCurrentThreadId :: proc() -> u32 #foreign kernel32;
|
||||
GetCommandLineA :: proc() -> ^u8 #foreign kernel32;
|
||||
GetCommandLineW :: proc() -> ^u16 #foreign kernel32;
|
||||
GetSystemMetrics :: proc(index: i32) -> i32 #foreign kernel32;
|
||||
GetCurrentThreadId :: proc() -> u32 #foreign kernel32;
|
||||
CommandLineToArgvW :: proc(cmd_list: ^u16, num_args: ^i32) -> ^^u16 #foreign shell32;
|
||||
|
||||
timeGetTime :: proc() -> u32 #foreign winmm;
|
||||
GetSystemTimeAsFileTime :: proc(system_time_as_file_time: ^FILETIME) #foreign kernel32;
|
||||
FileTimeToLocalFileTime :: proc(file_time: ^FILETIME, local_file_time: ^FILETIME) -> BOOL #foreign kernel32;
|
||||
FileTimeToSystemTime :: proc(file_time: ^FILETIME, system_time: ^SYSTEMTIME) -> BOOL #foreign kernel32;
|
||||
SystemTimeToFileTime :: proc(system_time: ^SYSTEMTIME, file_time: ^FILETIME) -> BOOL #foreign kernel32;
|
||||
timeGetTime :: proc() -> u32 #foreign winmm;
|
||||
GetSystemTimeAsFileTime :: proc(system_time_as_file_time: ^Filetime) #foreign kernel32;
|
||||
FileTimeToLocalFileTime :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool #foreign kernel32;
|
||||
FileTimeToSystemTime :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool #foreign kernel32;
|
||||
SystemTimeToFileTime :: proc(system_time: ^Systemtime, file_time: ^Filetime) -> Bool #foreign kernel32;
|
||||
|
||||
// File Stuff
|
||||
|
||||
CloseHandle :: proc(h: HANDLE) -> i32 #foreign kernel32;
|
||||
GetStdHandle :: proc(h: i32) -> HANDLE #foreign kernel32;
|
||||
CloseHandle :: proc(h: Handle) -> i32 #foreign kernel32;
|
||||
GetStdHandle :: proc(h: i32) -> Handle #foreign kernel32;
|
||||
CreateFileA :: proc(filename: ^u8, desired_access, share_mode: u32,
|
||||
security: rawptr,
|
||||
creation, flags_and_attribs: u32, template_file: HANDLE) -> HANDLE #foreign kernel32;
|
||||
ReadFile :: proc(h: HANDLE, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> BOOL #foreign kernel32;
|
||||
WriteFile :: proc(h: HANDLE, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> BOOL #foreign kernel32;
|
||||
creation, flags_and_attribs: u32, template_file: Handle) -> Handle #foreign kernel32;
|
||||
ReadFile :: proc(h: Handle, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> Bool #foreign kernel32;
|
||||
WriteFile :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool #foreign kernel32;
|
||||
|
||||
GetFileSizeEx :: proc(file_handle: HANDLE, file_size: ^i64) -> BOOL #foreign kernel32;
|
||||
GetFileAttributesExA :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> BOOL #foreign kernel32;
|
||||
GetFileInformationByHandle :: proc(file_handle: HANDLE, file_info: ^BY_HANDLE_FILE_INFORMATION) -> BOOL #foreign kernel32;
|
||||
GetFileSizeEx :: proc(file_handle: Handle, file_size: ^i64) -> Bool #foreign kernel32;
|
||||
GetFileAttributesExA :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> Bool #foreign kernel32;
|
||||
GetFileInformationByHandle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool #foreign kernel32;
|
||||
|
||||
GetFileType :: proc(file_handle: HANDLE) -> u32 #foreign kernel32;
|
||||
SetFilePointer :: proc(file_handle: HANDLE, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign kernel32;
|
||||
GetFileType :: proc(file_handle: Handle) -> u32 #foreign kernel32;
|
||||
SetFilePointer :: proc(file_handle: Handle, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign kernel32;
|
||||
|
||||
SetHandleInformation :: proc(obj: HANDLE, mask, flags: u32) -> BOOL #foreign kernel32;
|
||||
SetHandleInformation :: proc(obj: Handle, mask, flags: u32) -> Bool #foreign kernel32;
|
||||
|
||||
HANDLE_FLAG_INHERIT :: 1;
|
||||
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2;
|
||||
@@ -242,13 +244,13 @@ TRUNCATE_EXISTING :: 5;
|
||||
FILE_ATTRIBUTE_READONLY :: 0x00000001;
|
||||
FILE_ATTRIBUTE_HIDDEN :: 0x00000002;
|
||||
FILE_ATTRIBUTE_SYSTEM :: 0x00000004;
|
||||
FILE_ATTRIBUTE_DIRECTORY :: 0x00000010;
|
||||
FILE_ATTRIBUTE_DIRectORY :: 0x00000010;
|
||||
FILE_ATTRIBUTE_ARCHIVE :: 0x00000020;
|
||||
FILE_ATTRIBUTE_DEVICE :: 0x00000040;
|
||||
FILE_ATTRIBUTE_NORMAL :: 0x00000080;
|
||||
FILE_ATTRIBUTE_TEMPORARY :: 0x00000100;
|
||||
FILE_ATTRIBUTE_SPARSE_FILE :: 0x00000200;
|
||||
FILE_ATTRIBUTE_REPARSE_POINT :: 0x00000400;
|
||||
FILE_ATTRIBUTE_REPARSE_Point :: 0x00000400;
|
||||
FILE_ATTRIBUTE_COMPRESSED :: 0x00000800;
|
||||
FILE_ATTRIBUTE_OFFLINE :: 0x00001000;
|
||||
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED :: 0x00002000;
|
||||
@@ -258,32 +260,32 @@ FILE_TYPE_DISK :: 0x0001;
|
||||
FILE_TYPE_CHAR :: 0x0002;
|
||||
FILE_TYPE_PIPE :: 0x0003;
|
||||
|
||||
INVALID_SET_FILE_POINTER :: ~cast(u32)0;
|
||||
INVALID_SET_FILE_POINTER :: ~u32(0);
|
||||
|
||||
|
||||
|
||||
|
||||
HeapAlloc :: proc (h: HANDLE, flags: u32, bytes: int) -> rawptr #foreign kernel32;
|
||||
HeapReAlloc :: proc (h: HANDLE, flags: u32, memory: rawptr, bytes: int) -> rawptr #foreign kernel32;
|
||||
HeapFree :: proc (h: HANDLE, flags: u32, memory: rawptr) -> BOOL #foreign kernel32;
|
||||
GetProcessHeap :: proc () -> HANDLE #foreign kernel32;
|
||||
HeapAlloc :: proc (h: Handle, flags: u32, bytes: int) -> rawptr #foreign kernel32;
|
||||
HeapReAlloc :: proc (h: Handle, flags: u32, memory: rawptr, bytes: int) -> rawptr #foreign kernel32;
|
||||
HeapFree :: proc (h: Handle, flags: u32, memory: rawptr) -> Bool #foreign kernel32;
|
||||
GetProcessHeap :: proc () -> Handle #foreign kernel32;
|
||||
|
||||
|
||||
HEAP_ZERO_MEMORY :: 0x00000008;
|
||||
|
||||
// Synchronization
|
||||
|
||||
SECURITY_ATTRIBUTES :: struct #ordered {
|
||||
Security_Attributes :: struct #ordered {
|
||||
length: u32,
|
||||
security_descriptor: rawptr,
|
||||
inherit_handle: BOOL,
|
||||
inherit_handle: Bool,
|
||||
}
|
||||
|
||||
INFINITE :: 0xffffffff;
|
||||
|
||||
CreateSemaphoreA :: proc(attributes: ^SECURITY_ATTRIBUTES, initial_count, maximum_count: i32, name: ^byte) -> HANDLE #foreign kernel32;
|
||||
ReleaseSemaphore :: proc(semaphore: HANDLE, release_count: i32, previous_count: ^i32) -> BOOL #foreign kernel32;
|
||||
WaitForSingleObject :: proc(handle: HANDLE, milliseconds: u32) -> u32 #foreign kernel32;
|
||||
CreateSemaphoreA :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: ^byte) -> Handle #foreign kernel32;
|
||||
ReleaseSemaphore :: proc(semaphore: Handle, release_count: i32, previous_count: ^i32) -> Bool #foreign kernel32;
|
||||
WaitForSingleObject :: proc(handle: Handle, milliseconds: u32) -> u32 #foreign kernel32;
|
||||
|
||||
|
||||
InterlockedCompareExchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 #foreign kernel32;
|
||||
@@ -307,11 +309,11 @@ ReadBarrier :: proc() #foreign kernel32;
|
||||
|
||||
|
||||
|
||||
HMONITOR :: HANDLE;
|
||||
Hmonitor :: Handle;
|
||||
|
||||
GWL_STYLE :: -16;
|
||||
|
||||
HWND_TOP :: cast(HWND)cast(uint)0;
|
||||
Hwnd_TOP :: Hwnd(uint(0));
|
||||
|
||||
MONITOR_DEFAULTTONULL :: 0x00000000;
|
||||
MONITOR_DEFAULTTOPRIMARY :: 0x00000001;
|
||||
@@ -324,39 +326,40 @@ SWP_NOSIZE :: 0x0001;
|
||||
SWP_NOMOVE :: 0x0002;
|
||||
|
||||
|
||||
MONITORINFO :: struct #ordered {
|
||||
Monitor_Info :: struct #ordered {
|
||||
size: u32,
|
||||
monitor: RECT,
|
||||
work: RECT,
|
||||
monitor: Rect,
|
||||
work: Rect,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
WINDOWPLACEMENT :: struct #ordered {
|
||||
Window_Placement :: struct #ordered {
|
||||
length: u32,
|
||||
flags: u32,
|
||||
show_cmd: u32,
|
||||
min_pos: POINT,
|
||||
max_pos: POINT,
|
||||
normal_pos: RECT,
|
||||
min_pos: Point,
|
||||
max_pos: Point,
|
||||
normal_pos: Rect,
|
||||
}
|
||||
|
||||
GetMonitorInfoA :: proc(monitor: HMONITOR, mi: ^MONITORINFO) -> BOOL #foreign user32;
|
||||
MonitorFromWindow :: proc(wnd: HWND, flags : u32) -> HMONITOR #foreign user32;
|
||||
GetMonitorInfoA :: proc(monitor: Hmonitor, mi: ^Monitor_Info) -> Bool #foreign user32;
|
||||
MonitorFromWindow :: proc(wnd: Hwnd, flags : u32) -> Hmonitor #foreign user32;
|
||||
|
||||
SetWindowPos :: proc(wnd: HWND, wndInsertAfter: HWND, x, y, width, height: i32, flags: u32) #foreign user32 "SetWindowPos";
|
||||
SetWindowPos :: proc(wnd: Hwnd, wndInsertAfter: Hwnd, x, y, width, height: i32, flags: u32) #foreign user32 "SetWindowPos";
|
||||
|
||||
GetWindowPlacement :: proc(wnd: HWND, wndpl: ^WINDOWPLACEMENT) -> BOOL #foreign user32;
|
||||
SetWindowPlacement :: proc(wnd: HWND, wndpl: ^WINDOWPLACEMENT) -> BOOL #foreign user32;
|
||||
GetWindowPlacement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #foreign user32;
|
||||
SetWindowPlacement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #foreign user32;
|
||||
GetWindowRect :: proc(wnd: Hwnd, rect: ^Rect) -> Bool #foreign user32;
|
||||
|
||||
GetWindowLongPtrA :: proc(wnd: HWND, index: i32) -> i64 #foreign user32;
|
||||
SetWindowLongPtrA :: proc(wnd: HWND, index: i32, new: i64) -> i64 #foreign user32;
|
||||
GetWindowLongPtrA :: proc(wnd: Hwnd, index: i32) -> i64 #foreign user32;
|
||||
SetWindowLongPtrA :: proc(wnd: Hwnd, index: i32, new: i64) -> i64 #foreign user32;
|
||||
|
||||
GetWindowText :: proc(wnd: HWND, str: ^byte, maxCount: i32) -> i32 #foreign user32;
|
||||
GetWindowText :: proc(wnd: Hwnd, str: ^byte, maxCount: i32) -> i32 #foreign user32;
|
||||
|
||||
HIWORD :: proc(wParam: WPARAM) -> u16 { return cast(u16)((cast(u32)wParam >> 16) & 0xffff); }
|
||||
HIWORD :: proc(lParam: LPARAM) -> u16 { return cast(u16)((cast(u32)lParam >> 16) & 0xffff); }
|
||||
LOWORD :: proc(wParam: WPARAM) -> u16 { return cast(u16)wParam; }
|
||||
LOWORD :: proc(lParam: LPARAM) -> u16 { return cast(u16)lParam; }
|
||||
HIWORD :: proc(wParam: Wparam) -> u16 { return u16((u32(wParam) >> 16) & 0xffff); }
|
||||
HIWORD :: proc(lParam: Lparam) -> u16 { return u16((u32(lParam) >> 16) & 0xffff); }
|
||||
LOWORD :: proc(wParam: Wparam) -> u16 { return u16(wParam); }
|
||||
LOWORD :: proc(lParam: Lparam) -> u16 { return u16(lParam); }
|
||||
|
||||
|
||||
|
||||
@@ -367,7 +370,7 @@ LOWORD :: proc(lParam: LPARAM) -> u16 { return cast(u16)lParam; }
|
||||
|
||||
|
||||
|
||||
BITMAPINFOHEADER :: struct #ordered {
|
||||
Bitmap_Info_Header :: struct #ordered {
|
||||
size: u32,
|
||||
width, height: i32,
|
||||
planes, bit_count: i16,
|
||||
@@ -378,33 +381,33 @@ BITMAPINFOHEADER :: struct #ordered {
|
||||
clr_used: u32,
|
||||
clr_important: u32,
|
||||
}
|
||||
BITMAPINFO :: struct #ordered {
|
||||
using header: BITMAPINFOHEADER,
|
||||
colors: [1]RGBQUAD,
|
||||
Bitmap_Info :: struct #ordered {
|
||||
using header: Bitmap_Info_Header,
|
||||
colors: [1]Rgb_Quad,
|
||||
}
|
||||
|
||||
|
||||
RGBQUAD :: struct #ordered { blue, green, red, reserved: byte }
|
||||
Rgb_Quad :: struct #ordered { blue, green, red, reserved: byte }
|
||||
|
||||
BI_RGB :: 0;
|
||||
DIB_RGB_COLORS :: 0x00;
|
||||
SRCCOPY: u32 : 0x00cc0020;
|
||||
|
||||
|
||||
StretchDIBits :: proc (hdc: HDC,
|
||||
StretchDIBits :: proc (hdc: Hdc,
|
||||
x_dst, y_dst, width_dst, height_dst: i32,
|
||||
x_src, y_src, width_src, header_src: i32,
|
||||
bits: rawptr, bits_info: ^BITMAPINFO,
|
||||
bits: rawptr, bits_info: ^Bitmap_Info,
|
||||
usage: u32,
|
||||
rop: u32) -> i32 #foreign gdi32;
|
||||
|
||||
|
||||
|
||||
LoadLibraryA :: proc (c_str: ^u8) -> HMODULE #foreign kernel32;
|
||||
FreeLibrary :: proc (h: HMODULE) #foreign kernel32;
|
||||
GetProcAddress :: proc (h: HMODULE, c_str: ^u8) -> PROC #foreign kernel32;
|
||||
LoadLibraryA :: proc (c_str: ^u8) -> Hmodule #foreign kernel32;
|
||||
FreeLibrary :: proc (h: Hmodule) #foreign kernel32;
|
||||
GetProcAddress :: proc (h: Hmodule, c_str: ^u8) -> Proc #foreign kernel32;
|
||||
|
||||
GetClientRect :: proc(hwnd: HWND, rect: ^RECT) -> BOOL #foreign user32;
|
||||
GetClientRect :: proc(hwnd: Hwnd, rect: ^Rect) -> Bool #foreign user32;
|
||||
|
||||
// Windows OpenGL
|
||||
PFD_TYPE_RGBA :: 0;
|
||||
@@ -461,20 +464,20 @@ PIXELFORMATDESCRIPTOR :: struct #ordered {
|
||||
damage_mask: u32,
|
||||
}
|
||||
|
||||
GetDC :: proc(h: HWND) -> HDC #foreign user32;
|
||||
SetPixelFormat :: proc(hdc: HDC, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR) -> BOOL #foreign gdi32;
|
||||
ChoosePixelFormat :: proc(hdc: HDC, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign gdi32;
|
||||
SwapBuffers :: proc(hdc: HDC) -> BOOL #foreign gdi32;
|
||||
ReleaseDC :: proc(wnd: HWND, hdc: HDC) -> i32 #foreign user32;
|
||||
GetDC :: proc(h: Hwnd) -> Hdc #foreign user32;
|
||||
SetPixelFormat :: proc(hdc: Hdc, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR) -> Bool #foreign gdi32;
|
||||
ChoosePixelFormat :: proc(hdc: Hdc, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign gdi32;
|
||||
SwapBuffers :: proc(hdc: Hdc) -> Bool #foreign gdi32;
|
||||
ReleaseDC :: proc(wnd: Hwnd, hdc: Hdc) -> i32 #foreign user32;
|
||||
|
||||
|
||||
PROC :: #type proc() #cc_c;
|
||||
Proc :: #type proc() #cc_c;
|
||||
|
||||
|
||||
GetKeyState :: proc(v_key: i32) -> i16 #foreign user32;
|
||||
GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign user32;
|
||||
|
||||
is_key_down :: proc(key: Key_Code) -> bool #inline { return GetAsyncKeyState(cast(i32)key) < 0; }
|
||||
is_key_down :: proc(key: Key_Code) -> bool #inline { return GetAsyncKeyState(i32(key)) < 0; }
|
||||
|
||||
Key_Code :: enum i32 {
|
||||
LBUTTON = 0x01,
|
||||
@@ -611,7 +614,7 @@ Key_Code :: enum i32 {
|
||||
RCONTROL = 0xA3,
|
||||
LMENU = 0xA4,
|
||||
RMENU = 0xA5,
|
||||
PROCESSKEY = 0xE5,
|
||||
ProcESSKEY = 0xE5,
|
||||
ATTN = 0xF6,
|
||||
CRSEL = 0xF7,
|
||||
EXSEL = 0xF8,
|
||||
@@ -622,4 +625,3 @@ Key_Code :: enum i32 {
|
||||
PA1 = 0xFD,
|
||||
OEM_CLEAR = 0xFE,
|
||||
}
|
||||
|
||||
|
||||
+43
-91
@@ -1,146 +1,98 @@
|
||||
is_signed :: proc(info: ^Type_Info) -> bool {
|
||||
if is_integer(info) {
|
||||
i := union_cast(^Type_Info.Integer)info;
|
||||
return i.signed;
|
||||
}
|
||||
if is_float(info) {
|
||||
return true;
|
||||
if info == nil { return false; }
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Integer: return i.signed;
|
||||
case Type_Info.Float: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
is_integer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Integer: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Integer);
|
||||
return ok;
|
||||
}
|
||||
is_float :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Float: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Float);
|
||||
return ok;
|
||||
}
|
||||
is_complex :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
_, ok := type_info_base(info).(^Type_Info.Complex);
|
||||
return ok;
|
||||
}
|
||||
is_any :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Any: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Any);
|
||||
return ok;
|
||||
}
|
||||
is_string :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.String: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.String);
|
||||
return ok;
|
||||
}
|
||||
is_boolean :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Boolean: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Boolean);
|
||||
return ok;
|
||||
}
|
||||
is_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Pointer: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Pointer);
|
||||
return ok;
|
||||
}
|
||||
is_procedure :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Procedure: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Procedure);
|
||||
return ok;
|
||||
}
|
||||
is_array :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Array: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Array);
|
||||
return ok;
|
||||
}
|
||||
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Dynamic_Array: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Dynamic_Array);
|
||||
return ok;
|
||||
}
|
||||
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Map: return i.count == 0;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Map);
|
||||
return ok;
|
||||
}
|
||||
is_slice :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Slice: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Slice);
|
||||
return ok;
|
||||
}
|
||||
is_vector :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Vector: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Vector);
|
||||
return ok;
|
||||
}
|
||||
is_tuple :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Tuple: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Tuple);
|
||||
return ok;
|
||||
}
|
||||
is_struct :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Struct: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Struct);
|
||||
return ok;
|
||||
}
|
||||
is_union :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Union: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Union);
|
||||
return ok;
|
||||
}
|
||||
is_raw_union :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Raw_Union: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Raw_Union);
|
||||
return ok;
|
||||
}
|
||||
is_enum :: proc(info: ^Type_Info) -> bool {
|
||||
if info == nil { return false; }
|
||||
|
||||
match i in type_info_base(info) {
|
||||
case Type_Info.Enum: return true;
|
||||
}
|
||||
return false;
|
||||
_, ok := type_info_base(info).(^Type_Info.Enum);
|
||||
return ok;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
REPLACEMENT_CHAR :: '\uFFFD';
|
||||
MAX_RUNE :: '\U0010FFFF';
|
||||
|
||||
_surr1 :: 0xd800;
|
||||
_surr2 :: 0xdc00;
|
||||
_surr3 :: 0xe000;
|
||||
_surr_self :: 0x10000;
|
||||
|
||||
|
||||
is_surrogate :: proc(r: rune) -> bool {
|
||||
return _surr1 <= r && r < _surr3;
|
||||
}
|
||||
|
||||
decode_surrogate_pair :: proc(r1, r2: rune) -> rune {
|
||||
if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 {
|
||||
return (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self;
|
||||
}
|
||||
return REPLACEMENT_CHAR;
|
||||
}
|
||||
|
||||
|
||||
encode_surrogate_pair :: proc(r: rune) -> (r1, r2: rune) {
|
||||
if r < _surr_self || r > MAX_RUNE {
|
||||
return REPLACEMENT_CHAR, REPLACEMENT_CHAR;
|
||||
}
|
||||
r -= _surr_self;
|
||||
return _surr1 + (r>>10)&0x3ff, _surr2 + r&0x3ff;
|
||||
}
|
||||
|
||||
encode :: proc(d: []u16, s: []rune) {
|
||||
n := len(s);
|
||||
for r in s {
|
||||
if r >= _surr_self {
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
max_n := min(len(d), n);
|
||||
n = 0;
|
||||
|
||||
for r in s {
|
||||
match r {
|
||||
case 0..<_surr1, _surr3..<_surr_self:
|
||||
d[n] = u16(r);
|
||||
n++;
|
||||
|
||||
case _surr_self..MAX_RUNE:
|
||||
r1, r2 := encode_surrogate_pair(r);
|
||||
d[n] = u16(r1);
|
||||
d[n+1] = u16(r2);
|
||||
n += 2;
|
||||
|
||||
default:
|
||||
d[n] = u16(REPLACEMENT_CHAR);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
}
|
||||
+29
-29
@@ -1,7 +1,7 @@
|
||||
RUNE_ERROR :: '\ufffd';
|
||||
RUNE_SELF :: 0x80;
|
||||
RUNE_BOM :: 0xfeff;
|
||||
RUNE_EOF :: ~cast(rune)0;
|
||||
RUNE_EOF :: ~rune(0);
|
||||
MAX_RUNE :: '\U0010ffff';
|
||||
UTF_MAX :: 4;
|
||||
|
||||
@@ -60,15 +60,15 @@ immutable accept_sizes := [256]byte{
|
||||
|
||||
encode_rune :: proc(r: rune) -> ([4]byte, int) {
|
||||
buf: [4]byte;
|
||||
i := cast(u32)r;
|
||||
i := u32(r);
|
||||
mask: byte : 0x3f;
|
||||
if i <= 1<<7-1 {
|
||||
buf[0] = cast(byte)r;
|
||||
buf[0] = byte(r);
|
||||
return buf, 1;
|
||||
}
|
||||
if i <= 1<<11-1 {
|
||||
buf[0] = 0xc0 | cast(byte)(r>>6);
|
||||
buf[1] = 0x80 | cast(byte)r & mask;
|
||||
buf[0] = 0xc0 | byte(r>>6);
|
||||
buf[1] = 0x80 | byte(r) & mask;
|
||||
return buf, 2;
|
||||
}
|
||||
|
||||
@@ -79,34 +79,34 @@ encode_rune :: proc(r: rune) -> ([4]byte, int) {
|
||||
}
|
||||
|
||||
if i <= 1<<16-1 {
|
||||
buf[0] = 0xe0 | cast(byte)(r>>12);
|
||||
buf[1] = 0x80 | cast(byte)(r>>6) & mask;
|
||||
buf[2] = 0x80 | cast(byte)r & mask;
|
||||
buf[0] = 0xe0 | byte(r>>12);
|
||||
buf[1] = 0x80 | byte(r>>6) & mask;
|
||||
buf[2] = 0x80 | byte(r) & mask;
|
||||
return buf, 3;
|
||||
}
|
||||
|
||||
buf[0] = 0xf0 | cast(byte)(r>>18);
|
||||
buf[1] = 0x80 | cast(byte)(r>>12) & mask;
|
||||
buf[2] = 0x80 | cast(byte)(r>>6) & mask;
|
||||
buf[3] = 0x80 | cast(byte)r & mask;
|
||||
buf[0] = 0xf0 | byte(r>>18);
|
||||
buf[1] = 0x80 | byte(r>>12) & mask;
|
||||
buf[2] = 0x80 | byte(r>>6) & mask;
|
||||
buf[3] = 0x80 | byte(r) & mask;
|
||||
return buf, 4;
|
||||
}
|
||||
|
||||
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune(cast([]byte)s); }
|
||||
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune([]byte(s)); }
|
||||
decode_rune :: proc(s: []byte) -> (rune, int) {
|
||||
n := s.count;
|
||||
n := len(s);
|
||||
if n < 1 {
|
||||
return RUNE_ERROR, 0;
|
||||
}
|
||||
s0 := s[0];
|
||||
x := accept_sizes[s0];
|
||||
if x >= 0xF0 {
|
||||
mask := cast(rune)(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
|
||||
return cast(rune)(s[0])&~mask | RUNE_ERROR&mask, 1;
|
||||
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 < cast(int)sz {
|
||||
if n < int(sz) {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
b1 := s[1];
|
||||
@@ -114,36 +114,36 @@ decode_rune :: proc(s: []byte) -> (rune, int) {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
if sz == 2 {
|
||||
return cast(rune)(s0&MASK2)<<6 | cast(rune)(b1&MASKX), 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 cast(rune)(s0&MASK3)<<12 | cast(rune)(b1&MASKX)<<6 | cast(rune)(b2&MASKX), 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 cast(rune)(s0&MASK4)<<18 | cast(rune)(b1&MASKX)<<12 | cast(rune)(b2&MASKX)<<6 | cast(rune)(b3&MASKX), 4;
|
||||
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
|
||||
}
|
||||
|
||||
|
||||
|
||||
decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune(cast([]byte)s); }
|
||||
decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune([]byte(s)); }
|
||||
decode_last_rune :: proc(s: []byte) -> (rune, int) {
|
||||
r: rune;
|
||||
size: int;
|
||||
start, end, limit: int;
|
||||
|
||||
end = s.count;
|
||||
end = len(s);
|
||||
if end == 0 {
|
||||
return RUNE_ERROR, 0;
|
||||
}
|
||||
start = end-1;
|
||||
r = cast(rune)s[start];
|
||||
r = rune(s[start]);
|
||||
if r < RUNE_SELF {
|
||||
return r, 1;
|
||||
}
|
||||
@@ -160,7 +160,7 @@ decode_last_rune :: proc(s: []byte) -> (rune, int) {
|
||||
}
|
||||
|
||||
start = max(start, 0);
|
||||
r, size = decode_rune(s[start..end]);
|
||||
r, size = decode_rune(s[start..<end]);
|
||||
if start+size != end {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
@@ -183,7 +183,7 @@ valid_rune :: proc(r: rune) -> bool {
|
||||
}
|
||||
|
||||
valid_string :: proc(s: string) -> bool {
|
||||
n := s.count;
|
||||
n := len(s);
|
||||
for i := 0; i < n; {
|
||||
si := s[i];
|
||||
if si < RUNE_SELF { // ascii
|
||||
@@ -194,7 +194,7 @@ valid_string :: proc(s: string) -> bool {
|
||||
if x == 0xf1 {
|
||||
return false;
|
||||
}
|
||||
size := cast(int)(x & 7);
|
||||
size := int(x & 7);
|
||||
if i+size > n {
|
||||
return false;
|
||||
}
|
||||
@@ -217,10 +217,10 @@ valid_string :: proc(s: string) -> bool {
|
||||
|
||||
rune_start :: proc(b: byte) -> bool #inline { return b&0xc0 != 0x80; }
|
||||
|
||||
rune_count :: proc(s: string) -> int #inline { return rune_count(cast([]byte)s); }
|
||||
rune_count :: proc(s: string) -> int #inline { return rune_count([]byte(s)); }
|
||||
rune_count :: proc(s: []byte) -> int {
|
||||
count := 0;
|
||||
n := s.count;
|
||||
n := len(s);
|
||||
|
||||
for i := 0; i < n; {
|
||||
defer count++;
|
||||
@@ -234,7 +234,7 @@ rune_count :: proc(s: []byte) -> int {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
size := cast(int)(x & 7);
|
||||
size := int(x & 7);
|
||||
if i+size > n {
|
||||
i++;
|
||||
continue;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
set file_input=%1
|
||||
set name=%1
|
||||
FOR %%f IN (name) do (
|
||||
FOR %%g in (!%%f!) do set "%%f=%%~ng"
|
||||
)
|
||||
|
||||
call clang -O2 -c %file_input% -o %name%.o ^
|
||||
&& call ar %name%.o -rcs %name%.lib
|
||||
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 246 KiB |
+1
-2
@@ -1,8 +1,7 @@
|
||||
@echo off
|
||||
|
||||
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 10.0\VC\vcvarsall.bat" x86 1> NUL
|
||||
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL
|
||||
set _NO_DEBUG_HEAP=1
|
||||
|
||||
set path=w:\Odin\misc;%path%
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
@echo off
|
||||
|
||||
|
||||
rem call clang -c -emit-llvm -DGB_IMPLEMENTATION -DGB_DEF=GB_DLL_EXPORT ..\src\gb\gb.h
|
||||
+2
-8
@@ -87,14 +87,8 @@ void array__set_capacity(void *ptr, isize capacity, isize element_size) {
|
||||
x->count = capacity;
|
||||
}
|
||||
|
||||
{
|
||||
// TODO(bill): Resize rather than copy and delete
|
||||
void *new_data = gb_alloc(x->allocator, element_size*capacity);
|
||||
gb_memmove(new_data, x->e, element_size*x->count);
|
||||
gb_free(x->allocator, x->e);
|
||||
x->capacity = capacity;
|
||||
x->e = new_data;
|
||||
}
|
||||
x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
|
||||
x->capacity = capacity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+99
-9
@@ -135,7 +135,62 @@ String odin_root_dir(void) {
|
||||
return path;
|
||||
}
|
||||
#else
|
||||
#error Implement system
|
||||
|
||||
// NOTE: Linux / Unix is unfinished and not tested very well.
|
||||
#include <sys/stat.h>
|
||||
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
Array(char) path_buf;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
u8 *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
array_init_count(&path_buf, heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
// This is not a 100% reliable system, but for the purposes
|
||||
// of this compiler, it should be _good enough_.
|
||||
// That said, there's no solid 100% method on Linux to get the program's
|
||||
// path without checking this link. Sorry.
|
||||
len = readlink("/proc/self/exe", &path_buf.e[0], path_buf.count);
|
||||
if(len == 0) {
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
if (len < path_buf.count) {
|
||||
break;
|
||||
}
|
||||
array_resize(&path_buf, 2*path_buf.count + 300);
|
||||
}
|
||||
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
|
||||
gb_memmove(text, &path_buf.e[0], len);
|
||||
|
||||
path = make_string(text, len);
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path.text[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
|
||||
array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -157,11 +212,10 @@ String path_to_fullpath(gbAllocator a, String s) {
|
||||
}
|
||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
char* p = realpath(s.text, 0);
|
||||
// GB_ASSERT(p && "file does not exist");
|
||||
if(!p) return make_string_c("");
|
||||
char *p = realpath(cast(char *)s.text, 0);
|
||||
if(p == NULL) return make_string_c("");
|
||||
|
||||
return make_string(p, strlen(p));
|
||||
return make_string_c(p);
|
||||
}
|
||||
#else
|
||||
#error Implement system
|
||||
@@ -209,7 +263,7 @@ String get_fullpath_core(gbAllocator a, String path) {
|
||||
void init_build_context(void) {
|
||||
BuildContext *bc = &build_context;
|
||||
bc->ODIN_VENDOR = str_lit("odin");
|
||||
bc->ODIN_VERSION = str_lit("0.1.1");
|
||||
bc->ODIN_VERSION = str_lit("0.2.0");
|
||||
bc->ODIN_ROOT = odin_root_dir();
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
@@ -221,18 +275,54 @@ void init_build_context(void) {
|
||||
bc->ODIN_ARCH = str_lit("amd64");
|
||||
bc->ODIN_ENDIAN = str_lit("little");
|
||||
#else
|
||||
#error Implement system
|
||||
bc->ODIN_OS = str_lit("linux");
|
||||
bc->ODIN_ARCH = str_lit("amd64");
|
||||
bc->ODIN_ENDIAN = str_lit("little");
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// NOTE(zangent): The linker flags to set the build architecture are different
|
||||
// across OSs. It doesn't make sense to allocate extra data on the heap
|
||||
// here, so I just #defined the linker flags to keep things concise.
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
|
||||
#define LINK_FLAG_X64 "/machine:x64"
|
||||
#define LINK_FLAG_X86 "/machine:x86"
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
|
||||
// NOTE(zangent): MacOS systems are x64 only, so ld doesn't have
|
||||
// an architecture option. All compilation done on MacOS must be x64.
|
||||
GB_ASSERT(str_eq(bc->ODIN_ARCH, str_lit("amd64")));
|
||||
|
||||
#define LINK_FLAG_X64 ""
|
||||
#define LINK_FLAG_X86 ""
|
||||
#else
|
||||
// Linux, but also BSDs and the like.
|
||||
// NOTE(zangent): When clang is swapped out with ld as the linker,
|
||||
// the commented flags here should be used. Until then, we'll have
|
||||
// to use alternative build flags made for clang.
|
||||
/*
|
||||
#define LINK_FLAG_X64 "-m elf_x86_64"
|
||||
#define LINK_FLAG_X86 "-m elf_i386"
|
||||
*/
|
||||
#define LINK_FLAG_X64 "-arch x86-64"
|
||||
#define LINK_FLAG_X86 "-arch x86"
|
||||
#endif
|
||||
|
||||
if (str_eq(bc->ODIN_ARCH, str_lit("amd64"))) {
|
||||
bc->word_size = 8;
|
||||
bc->max_align = 16;
|
||||
bc->llc_flags = str_lit("-march=x86-64 ");
|
||||
bc->link_flags = str_lit("/machine:x64 ");
|
||||
bc->link_flags = str_lit(LINK_FLAG_X64 " ");
|
||||
} else if (str_eq(bc->ODIN_ARCH, str_lit("x86"))) {
|
||||
bc->word_size = 4;
|
||||
bc->max_align = 8;
|
||||
bc->llc_flags = str_lit("-march=x86 ");
|
||||
bc->link_flags = str_lit("/machine:x86 ");
|
||||
bc->link_flags = str_lit(LINK_FLAG_X86 " ");
|
||||
}
|
||||
|
||||
#undef LINK_FLAG_X64
|
||||
#undef LINK_FLAG_X86
|
||||
}
|
||||
|
||||
+108
-32
@@ -1,21 +1,21 @@
|
||||
bool check_is_terminating(AstNode *node);
|
||||
void check_stmt (Checker *c, AstNode *node, u32 flags);
|
||||
void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
|
||||
|
||||
// NOTE(bill): `content_name` is for debugging and error messages
|
||||
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
operand->type == t_invalid ||
|
||||
e->type == t_invalid) {
|
||||
operand->type == t_invalid ||
|
||||
e->type == t_invalid) {
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
gbString expr_str = expr_to_string(operand->expr);
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
|
||||
error_node(operand->expr,
|
||||
"Cannot assign builtin procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
"Cannot assign builtin procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
|
||||
@@ -85,8 +85,8 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
|
||||
|
||||
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
operand->type == t_invalid ||
|
||||
e->type == t_invalid) {
|
||||
operand->type == t_invalid ||
|
||||
e->type == t_invalid) {
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
@@ -181,7 +181,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
|
||||
check_init_constant(c, e, &operand);
|
||||
|
||||
if (operand.mode == Addressing_Invalid ||
|
||||
base_type(operand.type) == t_invalid) {
|
||||
base_type(operand.type) == t_invalid) {
|
||||
error(e->token, "Invalid declaration type");
|
||||
}
|
||||
}
|
||||
@@ -241,14 +241,15 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
|
||||
check_open_scope(c, pd->type);
|
||||
check_procedure_type(c, proc_type, pd->type);
|
||||
|
||||
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
|
||||
bool is_link_name = (pd->tags & ProcTag_link_name) != 0;
|
||||
bool is_export = (pd->tags & ProcTag_export) != 0;
|
||||
bool is_inline = (pd->tags & ProcTag_inline) != 0;
|
||||
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
|
||||
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
|
||||
bool is_link_name = (pd->tags & ProcTag_link_name) != 0;
|
||||
bool is_export = (pd->tags & ProcTag_export) != 0;
|
||||
bool is_inline = (pd->tags & ProcTag_inline) != 0;
|
||||
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
|
||||
bool is_require_results = (pd->tags & ProcTag_require_results) != 0;
|
||||
|
||||
if ((d->scope->is_file || d->scope->is_global) &&
|
||||
str_eq(e->token.string, str_lit("main"))) {
|
||||
|
||||
if (d->scope->is_file && str_eq(e->token.string, str_lit("main"))) {
|
||||
if (proc_type != NULL) {
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
if (pt->param_count != 0 ||
|
||||
@@ -276,18 +277,22 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
|
||||
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
|
||||
}
|
||||
|
||||
// TODO(bill): Is this the best option? What about passing to external shit?!
|
||||
// if (proc_type->Proc.calling_convention != ProcCC_Odin) {
|
||||
// error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention");
|
||||
// proc_type->Proc.calling_convention = ProcCC_Odin;
|
||||
// }
|
||||
|
||||
d->scope = c->context.scope;
|
||||
|
||||
GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
|
||||
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
|
||||
}
|
||||
|
||||
|
||||
if (proc_type != NULL && is_type_proc(proc_type)) {
|
||||
TypeProc *tp = &proc_type->Proc;
|
||||
if (tp->result_count == 0 && is_require_results) {
|
||||
error_node(pd->type, "`#require_results` is not needed on a procedure with no results");
|
||||
} else {
|
||||
tp->require_results = is_require_results;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_foreign) {
|
||||
MapEntity *fp = &c->info.foreigns;
|
||||
String name = e->token.string;
|
||||
@@ -330,9 +335,9 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
|
||||
Type *other_type = base_type(f->type);
|
||||
if (!are_signatures_similar_enough(this_type, other_type)) {
|
||||
error_node(d->proc_lit,
|
||||
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
}
|
||||
} else {
|
||||
map_entity_set(fp, key, e);
|
||||
@@ -355,9 +360,9 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
|
||||
TokenPos pos = f->token.pos;
|
||||
// TODO(bill): Better error message?
|
||||
error_node(d->proc_lit,
|
||||
"Non unique linking name for procedure `%.*s`\n"
|
||||
"\tother at %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
"Non unique linking name for procedure `%.*s`\n"
|
||||
"\tother at %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
map_entity_set(fp, key, e);
|
||||
}
|
||||
@@ -408,6 +413,67 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
|
||||
}
|
||||
|
||||
|
||||
void check_alias_decl(Checker *c, Entity *e, AstNode *expr, Type *named_type) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
GB_ASSERT(e->kind == Entity_Alias);
|
||||
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
e->type = t_invalid;
|
||||
|
||||
expr = unparen_expr(expr);
|
||||
|
||||
if (expr->kind == AstNode_Alias) {
|
||||
error_node(expr, "#alias of an #alias is not allowed");
|
||||
return;
|
||||
}
|
||||
|
||||
Operand operand = {0};
|
||||
check_expr_or_type(c, &operand, expr);
|
||||
if (operand.mode != Addressing_Type) {
|
||||
error_node(expr, "#alias declarations only allow types");
|
||||
return;
|
||||
}
|
||||
e->kind = Entity_TypeName;
|
||||
e->TypeName.is_type_alias = true;
|
||||
e->type = NULL;
|
||||
|
||||
DeclInfo *d = c->context.decl;
|
||||
d->type_expr = expr;
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
|
||||
|
||||
// Operand o = {0};
|
||||
// Entity *f = NULL;
|
||||
// if (expr->kind == AstNode_Ident) {
|
||||
// f = check_ident(c, &o, expr, NULL, NULL, true);
|
||||
// } else if (expr->kind == AstNode_SelectorExpr) {
|
||||
// f = check_selector(c, &o, expr, NULL);
|
||||
// } else {
|
||||
// check_expr_or_type(c, &o, expr);
|
||||
// }
|
||||
// if (o.mode == Addressing_Invalid) {
|
||||
// return;
|
||||
// }
|
||||
// switch (o.mode) {
|
||||
// case Addressing_Type:
|
||||
// e->type = o.type;
|
||||
// // e->kind = Entity_TypeName;
|
||||
// // e->TypeName.is_type_alias = true;
|
||||
// e->Alias.kind = EntityAlias_Type;
|
||||
// e->Alias.original = f;
|
||||
// break;
|
||||
// default:
|
||||
// error_node(expr, "#alias declarations only allow types");
|
||||
// e->kind = Entity_Invalid;
|
||||
// e->type = t_invalid;
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
|
||||
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
if (e->type != NULL) {
|
||||
return;
|
||||
@@ -440,6 +506,9 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
case Entity_TypeName:
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
break;
|
||||
case Entity_Alias:
|
||||
check_alias_decl(c, e, d->init_expr, named_type);
|
||||
break;
|
||||
case Entity_Procedure:
|
||||
check_proc_lit(c, e, d);
|
||||
break;
|
||||
@@ -466,14 +535,13 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
||||
c->context.decl = decl;
|
||||
c->context.proc_name = proc_name;
|
||||
|
||||
|
||||
GB_ASSERT(type->kind == Type_Proc);
|
||||
if (type->Proc.param_count > 0) {
|
||||
TypeTuple *params = &type->Proc.params->Tuple;
|
||||
for (isize i = 0; i < params->variable_count; i++) {
|
||||
Entity *e = params->variables[i];
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
if (!(e->flags & EntityFlag_Anonymous)) {
|
||||
if (!(e->flags & EntityFlag_Using)) {
|
||||
continue;
|
||||
}
|
||||
bool is_immutable = e->Variable.is_immutable;
|
||||
@@ -504,7 +572,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
||||
push_procedure(c, type);
|
||||
{
|
||||
ast_node(bs, BlockStmt, body);
|
||||
check_stmt_list(c, bs->stmts, 0);
|
||||
check_stmt_list(c, bs->stmts, Stmt_CheckScopeDecls);
|
||||
if (type->Proc.result_count > 0) {
|
||||
if (!check_is_terminating(body)) {
|
||||
if (token.kind == Token_Ident) {
|
||||
@@ -519,8 +587,16 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
||||
|
||||
|
||||
check_scope_usage(c, c->context.scope);
|
||||
|
||||
c->context = old_context;
|
||||
|
||||
if (decl->parent != NULL) {
|
||||
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
|
||||
for_array(i, decl->deps.entries) {
|
||||
HashKey key = decl->deps.entries.e[i].key;
|
||||
Entity *e = cast(Entity *)key.ptr;
|
||||
map_bool_set(&decl->parent->deps, key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+1201
-576
File diff suppressed because it is too large
Load Diff
+422
-257
@@ -3,7 +3,9 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
||||
return;
|
||||
}
|
||||
|
||||
check_scope_decls(c, stmts, 1.2*stmts.count);
|
||||
if (flags&Stmt_CheckScopeDecls) {
|
||||
check_scope_decls(c, stmts, 1.2*stmts.count);
|
||||
}
|
||||
|
||||
bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
|
||||
flags &= ~Stmt_FallthroughAllowed;
|
||||
@@ -123,15 +125,13 @@ bool check_is_terminating(AstNode *node) {
|
||||
case_end;
|
||||
|
||||
case_ast_node(fs, ForStmt, node);
|
||||
if (!check_has_break(fs->body, true)) {
|
||||
if (fs->cond == NULL && !check_has_break(fs->body, true)) {
|
||||
return check_is_terminating(fs->body);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(rs, RangeStmt, node);
|
||||
if (!check_has_break(rs->body, true)) {
|
||||
return check_is_terminating(rs->body);
|
||||
}
|
||||
return false;
|
||||
case_end;
|
||||
|
||||
case_ast_node(ms, MatchStmt, node);
|
||||
@@ -307,16 +307,21 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) {
|
||||
return rhs->type;
|
||||
}
|
||||
|
||||
bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) {
|
||||
if (is_type_pointer(type)) {
|
||||
*is_union_ptr = is_type_union(type_deref(type));
|
||||
return *is_union_ptr;
|
||||
typedef enum MatchTypeKind {
|
||||
MatchType_Invalid,
|
||||
MatchType_Union,
|
||||
MatchType_Any,
|
||||
} MatchTypeKind;
|
||||
|
||||
MatchTypeKind check_valid_type_match_type(Type *type) {
|
||||
type = type_deref(type);
|
||||
if (is_type_union(type)) {
|
||||
return MatchType_Union;
|
||||
}
|
||||
if (is_type_any(type)) {
|
||||
*is_any = true;
|
||||
return *is_any;
|
||||
return MatchType_Any;
|
||||
}
|
||||
return false;
|
||||
return MatchType_Invalid;
|
||||
}
|
||||
|
||||
void check_stmt_internal(Checker *c, AstNode *node, u32 flags);
|
||||
@@ -357,6 +362,7 @@ typedef struct TypeAndToken {
|
||||
#include "map.c"
|
||||
|
||||
void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
|
||||
flags &= ~Stmt_CheckScopeDecls;
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
|
||||
@@ -385,6 +391,175 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
|
||||
}
|
||||
}
|
||||
|
||||
void check_label(Checker *c, AstNode *label) {
|
||||
if (label == NULL) {
|
||||
return;
|
||||
}
|
||||
ast_node(l, Label, label);
|
||||
if (l->name->kind != AstNode_Ident) {
|
||||
error_node(l->name, "A label's name must be an identifier");
|
||||
return;
|
||||
}
|
||||
String name = l->name->Ident.string;
|
||||
if (str_eq(name, str_lit("_"))) {
|
||||
error_node(l->name, "A label's name cannot be a blank identifier");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (c->proc_stack.count == 0) {
|
||||
error_node(l->name, "A label is only allowed within a procedure");
|
||||
return;
|
||||
}
|
||||
GB_ASSERT(c->context.decl != NULL);
|
||||
|
||||
bool ok = true;
|
||||
for_array(i, c->context.decl->labels) {
|
||||
BlockLabel bl = c->context.decl->labels.e[i];
|
||||
if (str_eq(bl.name, name)) {
|
||||
error_node(label, "Duplicate label with the name `%.*s`", LIT(name));
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Entity *e = make_entity_label(c->allocator, c->context.scope, l->name->Ident, t_invalid, label);
|
||||
add_entity(c, c->context.scope, l->name, e);
|
||||
|
||||
if (ok) {
|
||||
BlockLabel bl = {name, label};
|
||||
array_add(&c->context.decl->labels, bl);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns `true` for `continue`, `false` for `return`
|
||||
bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bool is_selector, Entity *e) {
|
||||
if (e == NULL) {
|
||||
error(us->token, "`using` applied to an unknown entity");
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_Alias: {
|
||||
if (e->Alias.original != NULL) {
|
||||
check_using_stmt_entity(c, us, expr, is_selector, e->Alias.original);
|
||||
} else {
|
||||
error(us->token, "`using` cannot be applied to the alias `%.*s`", LIT(e->token.string));
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Entity_TypeName: {
|
||||
Type *t = base_type(e->type);
|
||||
if (is_type_union(t)) {
|
||||
TokenPos pos = ast_node_token(expr).pos;
|
||||
for (isize i = 1; i < t->Record.variant_count; i++) {
|
||||
Entity *f = t->Record.variants[i];
|
||||
// gb_printf_err("%s\n", type_to_string(f->type));
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
gb_string_free(expr_str);
|
||||
return false;
|
||||
}
|
||||
f->using_parent = e;
|
||||
}
|
||||
} else if (is_type_enum(t)) {
|
||||
for (isize i = 0; i < t->Record.field_count; i++) {
|
||||
Entity *f = t->Record.fields[i];
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
gb_string_free(expr_str);
|
||||
return false;
|
||||
}
|
||||
f->using_parent = e;
|
||||
}
|
||||
|
||||
} else {
|
||||
error(us->token, "`using` can be only applied to `union` or `enum` type entities");
|
||||
}
|
||||
} break;
|
||||
|
||||
case Entity_ImportName: {
|
||||
Scope *scope = e->ImportName.scope;
|
||||
for_array(i, scope->elements.entries) {
|
||||
Entity *decl = scope->elements.entries.e[i].value;
|
||||
Entity *found = scope_insert_entity(c->context.scope, decl);
|
||||
if (found != NULL) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
error(us->token,
|
||||
"Namespace collision while `using` `%s` of: %.*s\n"
|
||||
"\tat %.*s(%td:%td)\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
expr_str, LIT(found->token.string),
|
||||
LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column,
|
||||
LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column
|
||||
);
|
||||
gb_string_free(expr_str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case Entity_Variable: {
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||
// TODO(bill): Make it work for unions too
|
||||
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
|
||||
GB_ASSERT(found != NULL);
|
||||
for_array(i, (*found)->elements.entries) {
|
||||
Entity *f = (*found)->elements.entries.e[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
if (is_selector) {
|
||||
uvar->using_expr = expr;
|
||||
}
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != NULL) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
|
||||
gb_string_free(expr_str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Entity_Constant:
|
||||
error(us->token, "`using` cannot be applied to a constant");
|
||||
break;
|
||||
|
||||
case Entity_Procedure:
|
||||
case Entity_Builtin:
|
||||
error(us->token, "`using` cannot be applied to a procedure");
|
||||
break;
|
||||
|
||||
case Entity_Nil:
|
||||
error(us->token, "`using` cannot be applied to `nil`");
|
||||
break;
|
||||
|
||||
case Entity_Label:
|
||||
error(us->token, "`using` cannot be applied to a label");
|
||||
break;
|
||||
|
||||
case Entity_Invalid:
|
||||
error(us->token, "`using` cannot be applied to an invalid entity");
|
||||
break;
|
||||
|
||||
default:
|
||||
GB_PANIC("TODO(bill): `using` other expressions?");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
|
||||
switch (node->kind) {
|
||||
@@ -396,9 +571,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
ExprKind kind = check_expr_base(c, &operand, es->expr, NULL);
|
||||
switch (operand.mode) {
|
||||
case Addressing_Type:
|
||||
error_node(node, "Is not an expression");
|
||||
break;
|
||||
case Addressing_Type: {
|
||||
gbString str = type_to_string(operand.type);
|
||||
error_node(node, "`%s` is not an expression", str);
|
||||
gb_string_free(str);
|
||||
} break;
|
||||
case Addressing_NoValue:
|
||||
return;
|
||||
default: {
|
||||
@@ -406,6 +583,15 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
return;
|
||||
}
|
||||
if (operand.expr->kind == AstNode_CallExpr) {
|
||||
AstNodeCallExpr *ce = &operand.expr->CallExpr;
|
||||
Type *t = type_of_expr(&c->info, ce->proc);
|
||||
if (is_type_proc(t)) {
|
||||
if (t->Proc.require_results) {
|
||||
gbString expr_str = expr_to_string(ce->proc);
|
||||
error_node(node, "`%s` requires that its results must be handled", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
gbString expr_str = expr_to_string(operand.expr);
|
||||
@@ -475,9 +661,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(bill): This is a very similar to check_init_variables, should I merge the two some how or just
|
||||
// leave it?
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
|
||||
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
||||
@@ -515,7 +698,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
|
||||
return;
|
||||
}
|
||||
// TODO(bill): Check if valid assignment operator
|
||||
Operand operand = {Addressing_Invalid};
|
||||
AstNode binary_expr = {AstNode_BinaryExpr};
|
||||
ast_node(be, BinaryExpr, &binary_expr);
|
||||
@@ -580,7 +762,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
if (c->context.in_defer) {
|
||||
error(rs->token, "You cannot `return` within a defer statement");
|
||||
// TODO(bill): Should I break here?
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -619,7 +800,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
case_ast_node(fs, ForStmt, node);
|
||||
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||
|
||||
check_open_scope(c, node);
|
||||
check_label(c, fs->label); // TODO(bill): What should the label's "scope" be?
|
||||
|
||||
if (fs->init != NULL) {
|
||||
check_stmt(c, fs->init, 0);
|
||||
@@ -646,16 +829,20 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
case_ast_node(rs, RangeStmt, node);
|
||||
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||
|
||||
check_open_scope(c, node);
|
||||
check_label(c, rs->label);
|
||||
|
||||
Type *val = NULL;
|
||||
Type *idx = NULL;
|
||||
Entity *entities[2] = {0};
|
||||
isize entity_count = 0;
|
||||
|
||||
AstNode *expr = unparen_expr(rs->expr);
|
||||
|
||||
if (rs->expr != NULL && rs->expr->kind == AstNode_IntervalExpr) {
|
||||
ast_node(ie, IntervalExpr, rs->expr);
|
||||
|
||||
if (is_ast_node_a_range(expr)) {
|
||||
ast_node(ie, BinaryExpr, expr);
|
||||
Operand x = {Addressing_Invalid};
|
||||
Operand y = {Addressing_Invalid};
|
||||
|
||||
@@ -715,10 +902,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
TokenKind op = Token_Lt;
|
||||
switch (ie->op.kind) {
|
||||
case Token_Ellipsis: op = Token_Lt; break;
|
||||
case Token_Ellipsis: op = Token_LtEq; break;
|
||||
case Token_HalfClosed: op = Token_Lt; break;
|
||||
default: error(ie->op, "Invalid range operator"); break;
|
||||
}
|
||||
bool ok = compare_exact_values(Token_Lt, a, b);
|
||||
bool ok = compare_exact_values(op, a, b);
|
||||
if (!ok) {
|
||||
// TODO(bill): Better error message
|
||||
error(ie->op, "Invalid interval range");
|
||||
@@ -840,6 +1028,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
mod_flags |= Stmt_BreakAllowed;
|
||||
check_open_scope(c, node);
|
||||
check_label(c, ms->label); // TODO(bill): What should the label's "scope" be?
|
||||
|
||||
if (ms->init != NULL) {
|
||||
check_stmt(c, ms->init, 0);
|
||||
@@ -850,13 +1039,20 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
} else {
|
||||
x.mode = Addressing_Constant;
|
||||
x.type = t_bool;
|
||||
x.value = make_exact_value_bool(true);
|
||||
x.value = exact_value_bool(true);
|
||||
|
||||
Token token = {0};
|
||||
token.pos = ast_node_token(ms->body).pos;
|
||||
Token token = {0};
|
||||
token.pos = ast_node_token(ms->body).pos;
|
||||
token.string = str_lit("true");
|
||||
x.expr = ast_ident(c->curr_ast_file, token);
|
||||
}
|
||||
if (is_type_vector(x.type)) {
|
||||
gbString str = type_to_string(x.type);
|
||||
error_node(x.expr, "Invalid match expression type: %s", str);
|
||||
gb_string_free(str);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Check for multiple defaults
|
||||
AstNode *first_default = NULL;
|
||||
@@ -897,68 +1093,121 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
ast_node(cc, CaseClause, stmt);
|
||||
|
||||
|
||||
for_array(j, cc->list) {
|
||||
AstNode *expr = cc->list.e[j];
|
||||
Operand y = {0};
|
||||
AstNode *expr = unparen_expr(cc->list.e[j]);
|
||||
|
||||
check_expr(c, &y, expr);
|
||||
if (x.mode == Addressing_Invalid ||
|
||||
y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
if (is_ast_node_a_range(expr)) {
|
||||
ast_node(ie, BinaryExpr, expr);
|
||||
Operand lhs = {0};
|
||||
Operand rhs = {0};
|
||||
check_expr(c, &lhs, ie->left);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
if (lhs.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
check_expr(c, &rhs, ie->right);
|
||||
if (rhs.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
convert_to_typed(c, &y, x.type, 0);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE(bill): the ordering here matters
|
||||
Operand z = y;
|
||||
check_comparison(c, &z, &x, Token_CmpEq);
|
||||
if (z.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
if (y.mode != Addressing_Constant) {
|
||||
continue;
|
||||
}
|
||||
if (!is_type_ordered(x.type)) {
|
||||
gbString str = type_to_string(x.type);
|
||||
error_node(x.expr, "Unordered type `%s`, is invalid for an interval expression", str);
|
||||
gb_string_free(str);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (y.value.kind != ExactValue_Invalid) {
|
||||
HashKey key = hash_exact_value(y.value);
|
||||
TypeAndToken *found = map_type_and_token_get(&seen, key);
|
||||
if (found != NULL) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
isize count = map_type_and_token_multi_count(&seen, key);
|
||||
TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
|
||||
TokenKind op = {0};
|
||||
|
||||
map_type_and_token_multi_get_all(&seen, key, taps);
|
||||
bool continue_outer = false;
|
||||
Operand a = lhs;
|
||||
Operand b = rhs;
|
||||
check_comparison(c, &a, &x, Token_LtEq);
|
||||
if (a.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
switch (ie->op.kind) {
|
||||
case Token_Ellipsis: op = Token_GtEq; break;
|
||||
case Token_HalfClosed: op = Token_Gt; break;
|
||||
default: error(ie->op, "Invalid interval operator"); continue;
|
||||
}
|
||||
|
||||
for (isize i = 0; i < count; i++) {
|
||||
TypeAndToken tap = taps[i];
|
||||
if (are_types_identical(y.type, tap.type)) {
|
||||
TokenPos pos = tap.token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error_node(y.expr,
|
||||
"Duplicate case `%s`\n"
|
||||
"\tprevious case at %.*s(%td:%td)",
|
||||
expr_str,
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
gb_string_free(expr_str);
|
||||
continue_outer = true;
|
||||
break;
|
||||
check_comparison(c, &b, &x, op);
|
||||
if (b.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (ie->op.kind) {
|
||||
case Token_Ellipsis: op = Token_LtEq; break;
|
||||
case Token_HalfClosed: op = Token_Lt; break;
|
||||
default: error(ie->op, "Invalid interval operator"); continue;
|
||||
}
|
||||
|
||||
Operand a1 = lhs;
|
||||
Operand b1 = rhs;
|
||||
check_comparison(c, &a1, &b1, op);
|
||||
} else {
|
||||
Operand y = {0};
|
||||
check_expr(c, &y, expr);
|
||||
if (x.mode == Addressing_Invalid ||
|
||||
y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
convert_to_typed(c, &y, x.type, 0);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// NOTE(bill): the ordering here matters
|
||||
Operand z = y;
|
||||
check_comparison(c, &z, &x, Token_CmpEq);
|
||||
if (z.mode == Addressing_Invalid) {
|
||||
continue;
|
||||
}
|
||||
if (y.mode != Addressing_Constant) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (y.value.kind != ExactValue_Invalid) {
|
||||
HashKey key = hash_exact_value(y.value);
|
||||
TypeAndToken *found = map_type_and_token_get(&seen, key);
|
||||
if (found != NULL) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
isize count = map_type_and_token_multi_count(&seen, key);
|
||||
TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
|
||||
|
||||
map_type_and_token_multi_get_all(&seen, key, taps);
|
||||
bool continue_outer = false;
|
||||
|
||||
for (isize i = 0; i < count; i++) {
|
||||
TypeAndToken tap = taps[i];
|
||||
if (are_types_identical(y.type, tap.type)) {
|
||||
TokenPos pos = tap.token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error_node(y.expr,
|
||||
"Duplicate case `%s`\n"
|
||||
"\tprevious case at %.*s(%td:%td)",
|
||||
expr_str,
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
gb_string_free(expr_str);
|
||||
continue_outer = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
|
||||
if (continue_outer) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
|
||||
if (continue_outer) {
|
||||
continue;
|
||||
}
|
||||
TypeAndToken tap = {y.type, ast_node_token(y.expr)};
|
||||
map_type_and_token_multi_insert(&seen, key, tap);
|
||||
}
|
||||
TypeAndToken tap = {y.type, ast_node_token(y.expr)};
|
||||
map_type_and_token_multi_insert(&seen, key, tap);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,9 +1230,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
mod_flags |= Stmt_BreakAllowed;
|
||||
check_open_scope(c, node);
|
||||
check_label(c, ms->label); // TODO(bill): What should the label's "scope" be?
|
||||
|
||||
bool is_union_ptr = false;
|
||||
bool is_any = false;
|
||||
MatchTypeKind match_type_kind = MatchType_Invalid;
|
||||
|
||||
if (ms->tag->kind != AstNode_AssignStmt) {
|
||||
error_node(ms->tag, "Expected an `in` assignment for this type match statement");
|
||||
@@ -1005,14 +1254,15 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
check_expr(c, &x, rhs);
|
||||
check_assignment(c, &x, NULL, str_lit("type match expression"));
|
||||
if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
|
||||
match_type_kind = check_valid_type_match_type(x.type);
|
||||
if (check_valid_type_match_type(x.type) == MatchType_Invalid) {
|
||||
gbString str = type_to_string(x.type);
|
||||
error_node(x.expr,
|
||||
"Invalid type for this type match expression, got `%s`", str);
|
||||
error_node(x.expr, "Invalid type for this type match expression, got `%s`", str);
|
||||
gb_string_free(str);
|
||||
break;
|
||||
}
|
||||
|
||||
bool is_ptr = is_type_pointer(x.type);
|
||||
|
||||
// NOTE(bill): Check for multiple defaults
|
||||
AstNode *first_default = NULL;
|
||||
@@ -1048,7 +1298,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
|
||||
|
||||
MapBool seen = {0};
|
||||
MapBool seen = {0}; // Multimap
|
||||
map_bool_init(&seen, heap_allocator());
|
||||
|
||||
for_array(i, bs->stmts) {
|
||||
@@ -1062,74 +1312,76 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
// TODO(bill): Make robust
|
||||
Type *bt = base_type(type_deref(x.type));
|
||||
|
||||
|
||||
AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
|
||||
Type *case_type = NULL;
|
||||
if (type_expr != NULL) { // Otherwise it's a default expression
|
||||
Operand y = {0};
|
||||
check_expr_or_type(c, &y, type_expr);
|
||||
for_array(type_index, cc->list) {
|
||||
AstNode *type_expr = cc->list.e[type_index];
|
||||
if (type_expr != NULL) { // Otherwise it's a default expression
|
||||
Operand y = {0};
|
||||
check_expr_or_type(c, &y, type_expr);
|
||||
|
||||
if (is_union_ptr) {
|
||||
GB_ASSERT(is_type_union(bt));
|
||||
bool tag_type_found = false;
|
||||
for (isize i = 0; i < bt->Record.variant_count; i++) {
|
||||
Entity *f = bt->Record.variants[i];
|
||||
if (are_types_identical(f->type, y.type)) {
|
||||
tag_type_found = true;
|
||||
break;
|
||||
if (match_type_kind == MatchType_Union) {
|
||||
GB_ASSERT(is_type_union(bt));
|
||||
bool tag_type_found = false;
|
||||
for (isize i = 0; i < bt->Record.variant_count; i++) {
|
||||
Entity *f = bt->Record.variants[i];
|
||||
if (are_types_identical(f->type, y.type)) {
|
||||
tag_type_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!tag_type_found) {
|
||||
gbString type_str = type_to_string(y.type);
|
||||
error_node(y.expr, "Unknown tag type, got `%s`", type_str);
|
||||
gb_string_free(type_str);
|
||||
continue;
|
||||
}
|
||||
case_type = y.type;
|
||||
} else if (match_type_kind == MatchType_Any) {
|
||||
case_type = y.type;
|
||||
} else {
|
||||
GB_PANIC("Unknown type to type match statement");
|
||||
}
|
||||
if (!tag_type_found) {
|
||||
gbString type_str = type_to_string(y.type);
|
||||
error_node(y.expr, "Unknown tag type, got `%s`", type_str);
|
||||
gb_string_free(type_str);
|
||||
continue;
|
||||
}
|
||||
case_type = y.type;
|
||||
} else if (is_any) {
|
||||
case_type = y.type;
|
||||
} else {
|
||||
GB_PANIC("Unknown type to type match statement");
|
||||
}
|
||||
|
||||
HashKey key = hash_pointer(y.type);
|
||||
bool *found = map_bool_get(&seen, key);
|
||||
if (found) {
|
||||
TokenPos pos = cc->token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error_node(y.expr,
|
||||
"Duplicate type case `%s`\n"
|
||||
"\tprevious type case at %.*s(%td:%td)",
|
||||
expr_str,
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
gb_string_free(expr_str);
|
||||
break;
|
||||
HashKey key = hash_pointer(y.type);
|
||||
bool *found = map_bool_get(&seen, key);
|
||||
if (found) {
|
||||
TokenPos pos = cc->token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error_node(y.expr,
|
||||
"Duplicate type case `%s`\n"
|
||||
"\tprevious type case at %.*s(%td:%td)",
|
||||
expr_str,
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
gb_string_free(expr_str);
|
||||
break;
|
||||
}
|
||||
map_bool_set(&seen, key, cast(bool)true);
|
||||
}
|
||||
map_bool_set(&seen, key, cast(bool)true);
|
||||
}
|
||||
|
||||
check_open_scope(c, stmt);
|
||||
if (is_ptr &&
|
||||
!is_type_any(type_deref(x.type)) &&
|
||||
cc->list.count == 1 &&
|
||||
case_type != NULL) {
|
||||
case_type = make_type_pointer(c->allocator, case_type);
|
||||
}
|
||||
|
||||
if (cc->list.count > 1) {
|
||||
case_type = NULL;
|
||||
}
|
||||
if (case_type == NULL) {
|
||||
if (is_union_ptr) {
|
||||
case_type = type_deref(x.type);
|
||||
} else {
|
||||
case_type = x.type;
|
||||
}
|
||||
case_type = x.type;
|
||||
}
|
||||
|
||||
add_type_info_type(c, case_type);
|
||||
|
||||
check_open_scope(c, stmt);
|
||||
{
|
||||
// NOTE(bill): Dummy type
|
||||
Type *tt = case_type;
|
||||
if (is_union_ptr) {
|
||||
tt = make_type_pointer(c->allocator, case_type);
|
||||
add_type_info_type(c, tt);
|
||||
}
|
||||
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, tt, true);
|
||||
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, case_type, false);
|
||||
tag_var->flags |= EntityFlag_Used;
|
||||
tag_var->flags |= EntityFlag_Value;
|
||||
add_entity(c, c->context.scope, lhs, tag_var);
|
||||
add_entity_use(c, lhs, tag_var);
|
||||
add_implicit_entity(c, stmt, tag_var);
|
||||
}
|
||||
check_stmt_list(c, cc->stmts, mod_flags);
|
||||
check_close_scope(c);
|
||||
@@ -1173,20 +1425,38 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
|
||||
break;
|
||||
}
|
||||
|
||||
if (bs->label != NULL) {
|
||||
if (bs->label->kind != AstNode_Ident) {
|
||||
error_node(bs->label, "A branch statement's label name must be an identifier");
|
||||
return;
|
||||
}
|
||||
AstNode *ident = bs->label;
|
||||
String name = ident->Ident.string;
|
||||
Entity *e = scope_lookup_entity(c->context.scope, name);
|
||||
if (e == NULL) {
|
||||
error_node(ident, "Undeclared label name: %.*s", LIT(name));
|
||||
return;
|
||||
}
|
||||
add_entity_use(c, ident, e);
|
||||
if (e->kind != Entity_Label) {
|
||||
error_node(ident, "`%.*s` is not a label", LIT(name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
case_end;
|
||||
|
||||
case_ast_node(us, UsingStmt, node);
|
||||
switch (us->node->kind) {
|
||||
default:
|
||||
// TODO(bill): Better error message for invalid using statement
|
||||
error(us->token, "Invalid `using` statement");
|
||||
break;
|
||||
case_ast_node(es, ExprStmt, us->node);
|
||||
// TODO(bill): Allow for just a LHS expression list rather than this silly code
|
||||
if (us->list.count == 0) {
|
||||
error(us->token, "Empty `using` list");
|
||||
return;
|
||||
}
|
||||
for_array(i, us->list) {
|
||||
AstNode *expr = unparen_expr(us->list.e[0]);
|
||||
Entity *e = NULL;
|
||||
|
||||
bool is_selector = false;
|
||||
AstNode *expr = unparen_expr(es->expr);
|
||||
if (expr->kind == AstNode_Ident) {
|
||||
String name = expr->Ident.string;
|
||||
e = scope_lookup_entity(c->context.scope, name);
|
||||
@@ -1194,122 +1464,18 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
Operand o = {0};
|
||||
e = check_selector(c, &o, expr, NULL);
|
||||
is_selector = true;
|
||||
} else if (expr->kind == AstNode_Implicit) {
|
||||
error(us->token, "`using` applied to an implicit value");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e == NULL) {
|
||||
error(us->token, "`using` applied to an unknown entity");
|
||||
if (!check_using_stmt_entity(c, us, expr, is_selector, e)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_TypeName: {
|
||||
Type *t = base_type(e->type);
|
||||
if (is_type_union(t)) {
|
||||
for (isize i = 0; i < t->Record.variant_count; i++) {
|
||||
Entity *f = t->Record.variants[i];
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
gb_string_free(expr_str);
|
||||
return;
|
||||
}
|
||||
f->using_parent = e;
|
||||
}
|
||||
} else if (is_type_enum(t)) {
|
||||
for (isize i = 0; i < t->Record.field_count; i++) {
|
||||
Entity *f = t->Record.fields[i];
|
||||
Entity *found = scope_insert_entity(c->context.scope, f);
|
||||
if (found != NULL) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
|
||||
gb_string_free(expr_str);
|
||||
return;
|
||||
}
|
||||
f->using_parent = e;
|
||||
}
|
||||
|
||||
} else {
|
||||
error(us->token, "`using` can be only applied to `union` or `enum` type entities");
|
||||
}
|
||||
} break;
|
||||
|
||||
case Entity_ImportName: {
|
||||
Scope *scope = e->ImportName.scope;
|
||||
for_array(i, scope->elements.entries) {
|
||||
Entity *decl = scope->elements.entries.e[i].value;
|
||||
Entity *found = scope_insert_entity(c->context.scope, decl);
|
||||
if (found != NULL) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
error(us->token,
|
||||
"Namespace collision while `using` `%s` of: %.*s\n"
|
||||
"\tat %.*s(%td:%td)\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
expr_str, LIT(found->token.string),
|
||||
LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column,
|
||||
LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column
|
||||
);
|
||||
gb_string_free(expr_str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case Entity_Variable: {
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||
// TODO(bill): Make it work for unions too
|
||||
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
|
||||
GB_ASSERT(found != NULL);
|
||||
for_array(i, (*found)->elements.entries) {
|
||||
Entity *f = (*found)->elements.entries.e[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
if (is_selector) {
|
||||
uvar->using_expr = expr;
|
||||
}
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != NULL) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
|
||||
gb_string_free(expr_str);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
return;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Entity_Constant:
|
||||
error(us->token, "`using` cannot be applied to a constant");
|
||||
break;
|
||||
|
||||
case Entity_Procedure:
|
||||
case Entity_Builtin:
|
||||
error(us->token, "`using` cannot be applied to a procedure");
|
||||
break;
|
||||
|
||||
case Entity_Nil:
|
||||
error(us->token, "`using` cannot be applied to `nil`");
|
||||
break;
|
||||
|
||||
case Entity_Invalid:
|
||||
error(us->token, "`using` cannot be applied to an invalid entity");
|
||||
break;
|
||||
|
||||
default:
|
||||
GB_PANIC("TODO(bill): `using` other expressions?");
|
||||
}
|
||||
case_end;
|
||||
|
||||
}
|
||||
case_end;
|
||||
|
||||
|
||||
|
||||
case_ast_node(pa, PushAllocator, node);
|
||||
Operand op = {0};
|
||||
check_expr(c, &op, pa->expr);
|
||||
@@ -1328,7 +1494,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
case_ast_node(vd, ValueDecl, node);
|
||||
GB_ASSERT(!c->context.scope->is_file);
|
||||
if (vd->is_var) {
|
||||
if (!vd->is_var) {
|
||||
// NOTE(bill): Handled elsewhere
|
||||
} else {
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
|
||||
isize entity_count = 0;
|
||||
|
||||
@@ -1438,9 +1606,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// NOTE(bill): Handled elsewhere
|
||||
}
|
||||
case_end;
|
||||
}
|
||||
|
||||
+188
-86
@@ -11,7 +11,8 @@ typedef enum StmtFlag {
|
||||
Stmt_BreakAllowed = 1<<0,
|
||||
Stmt_ContinueAllowed = 1<<1,
|
||||
Stmt_FallthroughAllowed = 1<<2,
|
||||
Stmt_GiveAllowed = 1<<3,
|
||||
|
||||
Stmt_CheckScopeDecls = 1<<5,
|
||||
} StmtFlag;
|
||||
|
||||
typedef struct BuiltinProc {
|
||||
@@ -23,9 +24,12 @@ typedef struct BuiltinProc {
|
||||
typedef enum BuiltinProcId {
|
||||
BuiltinProc_Invalid,
|
||||
|
||||
BuiltinProc_len,
|
||||
BuiltinProc_cap,
|
||||
|
||||
BuiltinProc_new,
|
||||
BuiltinProc_new_slice,
|
||||
BuiltinProc_free,
|
||||
BuiltinProc_make,
|
||||
|
||||
BuiltinProc_reserve,
|
||||
BuiltinProc_clear,
|
||||
@@ -48,12 +52,17 @@ typedef enum BuiltinProcId {
|
||||
BuiltinProc_panic,
|
||||
|
||||
BuiltinProc_copy,
|
||||
// BuiltinProc_append,
|
||||
|
||||
BuiltinProc_swizzle,
|
||||
|
||||
// BuiltinProc_ptr_offset,
|
||||
// BuiltinProc_ptr_sub,
|
||||
BuiltinProc_complex,
|
||||
BuiltinProc_quaternion,
|
||||
BuiltinProc_real,
|
||||
BuiltinProc_imag,
|
||||
BuiltinProc_jmag,
|
||||
BuiltinProc_kmag,
|
||||
BuiltinProc_conj,
|
||||
|
||||
BuiltinProc_slice_ptr,
|
||||
BuiltinProc_slice_to_bytes,
|
||||
|
||||
@@ -62,14 +71,19 @@ typedef enum BuiltinProcId {
|
||||
BuiltinProc_abs,
|
||||
BuiltinProc_clamp,
|
||||
|
||||
BuiltinProc_transmute,
|
||||
|
||||
BuiltinProc_Count,
|
||||
} BuiltinProcId;
|
||||
gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
|
||||
{STR_LIT(""), 0, false, Expr_Stmt},
|
||||
|
||||
{STR_LIT("len"), 1, false, Expr_Expr},
|
||||
{STR_LIT("cap"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("new"), 1, false, Expr_Expr},
|
||||
{STR_LIT("new_slice"), 2, true, Expr_Expr},
|
||||
{STR_LIT("free"), 1, false, Expr_Stmt},
|
||||
{STR_LIT("make"), 1, true, Expr_Expr},
|
||||
|
||||
{STR_LIT("reserve"), 2, false, Expr_Stmt},
|
||||
{STR_LIT("clear"), 1, false, Expr_Stmt},
|
||||
@@ -87,24 +101,31 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
|
||||
{STR_LIT("type_info"), 1, false, Expr_Expr},
|
||||
{STR_LIT("type_info_of_val"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("compile_assert"), 1, false, Expr_Stmt},
|
||||
{STR_LIT("assert"), 1, false, Expr_Stmt},
|
||||
{STR_LIT("compile_assert"), 1, false, Expr_Expr},
|
||||
{STR_LIT("assert"), 1, false, Expr_Expr},
|
||||
{STR_LIT("panic"), 1, false, Expr_Stmt},
|
||||
|
||||
{STR_LIT("copy"), 2, false, Expr_Expr},
|
||||
// {STR_LIT("append"), 2, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("swizzle"), 1, true, Expr_Expr},
|
||||
|
||||
// {STR_LIT("ptr_offset"), 2, false, Expr_Expr},
|
||||
// {STR_LIT("ptr_sub"), 2, false, Expr_Expr},
|
||||
{STR_LIT("slice_ptr"), 2, false, Expr_Expr},
|
||||
{STR_LIT("complex"), 2, false, Expr_Expr},
|
||||
{STR_LIT("quaternion"), 4, false, Expr_Expr},
|
||||
{STR_LIT("real"), 1, false, Expr_Expr},
|
||||
{STR_LIT("imag"), 1, false, Expr_Expr},
|
||||
{STR_LIT("jmag"), 1, false, Expr_Expr},
|
||||
{STR_LIT("kmag"), 1, false, Expr_Expr},
|
||||
{STR_LIT("conj"), 1, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("slice_ptr"), 2, true, Expr_Expr},
|
||||
{STR_LIT("slice_to_bytes"), 1, false, Expr_Stmt},
|
||||
|
||||
{STR_LIT("min"), 2, false, Expr_Expr},
|
||||
{STR_LIT("max"), 2, false, Expr_Expr},
|
||||
{STR_LIT("abs"), 1, false, Expr_Expr},
|
||||
{STR_LIT("clamp"), 3, false, Expr_Expr},
|
||||
|
||||
{STR_LIT("transmute"), 2, false, Expr_Expr},
|
||||
};
|
||||
|
||||
|
||||
@@ -163,28 +184,38 @@ bool is_operand_nil(Operand o) {
|
||||
}
|
||||
|
||||
|
||||
typedef struct BlockLabel {
|
||||
String name;
|
||||
AstNode *label; // AstNode_Label;
|
||||
} BlockLabel;
|
||||
|
||||
// DeclInfo is used to store information of certain declarations to allow for "any order" usage
|
||||
typedef struct DeclInfo {
|
||||
Scope *scope;
|
||||
typedef struct DeclInfo DeclInfo;
|
||||
struct DeclInfo {
|
||||
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
|
||||
Scope * scope;
|
||||
|
||||
Entity **entities;
|
||||
isize entity_count;
|
||||
Entity ** entities;
|
||||
isize entity_count;
|
||||
|
||||
AstNode *type_expr;
|
||||
AstNode *init_expr;
|
||||
AstNode *proc_lit; // AstNode_ProcLit
|
||||
AstNode * type_expr;
|
||||
AstNode * init_expr;
|
||||
AstNode * proc_lit; // AstNode_ProcLit
|
||||
|
||||
MapBool deps; // Key: Entity *
|
||||
} DeclInfo;
|
||||
MapBool deps; // Key: Entity *
|
||||
Array(BlockLabel) labels;
|
||||
};
|
||||
|
||||
// ProcedureInfo stores the information needed for checking a procedure
|
||||
|
||||
|
||||
typedef struct ProcedureInfo {
|
||||
AstFile * file;
|
||||
Token token;
|
||||
DeclInfo *decl;
|
||||
Type * type; // Type_Procedure
|
||||
AstNode * body; // AstNode_BlockStmt
|
||||
u32 tags;
|
||||
AstFile * file;
|
||||
Token token;
|
||||
DeclInfo * decl;
|
||||
Type * type; // Type_Procedure
|
||||
AstNode * body; // AstNode_BlockStmt
|
||||
u32 tags;
|
||||
} ProcedureInfo;
|
||||
|
||||
// ExprInfo stores information used for "untyped" expressions
|
||||
@@ -208,21 +239,21 @@ ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue
|
||||
#include "map.c"
|
||||
|
||||
typedef struct Scope {
|
||||
Scope * parent;
|
||||
Scope * prev, *next;
|
||||
Scope * first_child;
|
||||
Scope * last_child;
|
||||
MapEntity elements; // Key: String
|
||||
MapBool implicit; // Key: Entity *
|
||||
Scope * parent;
|
||||
Scope * prev, *next;
|
||||
Scope * first_child;
|
||||
Scope * last_child;
|
||||
MapEntity elements; // Key: String
|
||||
MapBool implicit; // Key: Entity *
|
||||
|
||||
Array(Scope *) shared;
|
||||
Array(Scope *) imported;
|
||||
bool is_proc;
|
||||
bool is_global;
|
||||
bool is_file;
|
||||
bool is_init;
|
||||
bool has_been_imported; // This is only applicable to file scopes
|
||||
AstFile * file;
|
||||
Array(Scope *) shared;
|
||||
Array(Scope *) imported;
|
||||
bool is_proc;
|
||||
bool is_global;
|
||||
bool is_file;
|
||||
bool is_init;
|
||||
bool has_been_imported; // This is only applicable to file scopes
|
||||
AstFile * file;
|
||||
} Scope;
|
||||
gb_global Scope *universal_scope = NULL;
|
||||
|
||||
@@ -278,6 +309,7 @@ typedef struct CheckerInfo {
|
||||
MapScope scopes; // Key: AstNode * | Node -> Scope
|
||||
MapExprInfo untyped; // Key: AstNode * | Expression -> ExprInfo
|
||||
MapDeclInfo entities; // Key: Entity *
|
||||
MapEntity implicits; // Key: AstNode *
|
||||
MapEntity foreigns; // Key: String
|
||||
MapAstFile files; // Key: String (full path)
|
||||
MapIsize type_info_map; // Key: Type *
|
||||
@@ -317,14 +349,16 @@ typedef Array(DelayedEntity) DelayedEntities;
|
||||
|
||||
|
||||
|
||||
void init_declaration_info(DeclInfo *d, Scope *scope) {
|
||||
d->scope = scope;
|
||||
void init_declaration_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
|
||||
d->parent = parent;
|
||||
d->scope = scope;
|
||||
map_bool_init(&d->deps, heap_allocator());
|
||||
array_init(&d->labels, heap_allocator());
|
||||
}
|
||||
|
||||
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) {
|
||||
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope, DeclInfo *parent) {
|
||||
DeclInfo *d = gb_alloc_item(a, DeclInfo);
|
||||
init_declaration_info(d, scope);
|
||||
init_declaration_info(d, scope, parent);
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -357,9 +391,9 @@ Scope *make_scope(Scope *parent, gbAllocator allocator) {
|
||||
Scope *s = gb_alloc_item(allocator, Scope);
|
||||
s->parent = parent;
|
||||
map_entity_init(&s->elements, heap_allocator());
|
||||
map_bool_init(&s->implicit, heap_allocator());
|
||||
array_init(&s->shared, heap_allocator());
|
||||
array_init(&s->imported, heap_allocator());
|
||||
map_bool_init(&s->implicit, heap_allocator());
|
||||
array_init(&s->shared, heap_allocator());
|
||||
array_init(&s->imported, heap_allocator());
|
||||
|
||||
if (parent != NULL && parent != universal_scope) {
|
||||
DLIST_APPEND(parent->first_child, parent->last_child, s);
|
||||
@@ -455,6 +489,9 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit
|
||||
if (found) {
|
||||
Entity *e = *found;
|
||||
if (gone_thru_proc) {
|
||||
if (e->kind == Entity_Label) {
|
||||
continue;
|
||||
}
|
||||
if (e->kind == Entity_Variable &&
|
||||
!e->scope->is_file &&
|
||||
!e->scope->is_global) {
|
||||
@@ -520,6 +557,10 @@ Entity *scope_lookup_entity(Scope *s, String name) {
|
||||
|
||||
Entity *scope_insert_entity(Scope *s, Entity *entity) {
|
||||
String name = entity->token.string;
|
||||
if (str_eq(name, str_lit("output"))) {
|
||||
gb_printf_err("Here! %.*s\n", LIT(name));
|
||||
}
|
||||
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(&s->elements, key);
|
||||
|
||||
@@ -596,7 +637,7 @@ void add_global_constant(gbAllocator a, String name, Type *type, ExactValue valu
|
||||
|
||||
|
||||
void add_global_string_constant(gbAllocator a, String name, String value) {
|
||||
add_global_constant(a, name, t_untyped_string, make_exact_value_string(value));
|
||||
add_global_constant(a, name, t_untyped_string, exact_value_string(value));
|
||||
|
||||
}
|
||||
|
||||
@@ -616,8 +657,8 @@ void init_universal_scope(void) {
|
||||
}
|
||||
|
||||
// Constants
|
||||
add_global_constant(a, str_lit("true"), t_untyped_bool, make_exact_value_bool(true));
|
||||
add_global_constant(a, str_lit("false"), t_untyped_bool, make_exact_value_bool(false));
|
||||
add_global_constant(a, str_lit("true"), t_untyped_bool, exact_value_bool(true));
|
||||
add_global_constant(a, str_lit("false"), t_untyped_bool, exact_value_bool(false));
|
||||
|
||||
add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil));
|
||||
add_global_entity(make_entity_library_name(a, universal_scope,
|
||||
@@ -662,6 +703,7 @@ void init_checker_info(CheckerInfo *i) {
|
||||
map_decl_info_init(&i->entities, a);
|
||||
map_expr_info_init(&i->untyped, a);
|
||||
map_entity_init(&i->foreigns, a);
|
||||
map_entity_init(&i->implicits, a);
|
||||
map_isize_init(&i->type_info_map, a);
|
||||
map_ast_file_init(&i->files, a);
|
||||
i->type_info_count = 0;
|
||||
@@ -676,12 +718,17 @@ void destroy_checker_info(CheckerInfo *i) {
|
||||
map_decl_info_destroy(&i->entities);
|
||||
map_expr_info_destroy(&i->untyped);
|
||||
map_entity_destroy(&i->foreigns);
|
||||
map_entity_destroy(&i->implicits);
|
||||
map_isize_destroy(&i->type_info_map);
|
||||
map_ast_file_destroy(&i->files);
|
||||
}
|
||||
|
||||
|
||||
void init_checker(Checker *c, Parser *parser, BuildContext *bc) {
|
||||
if (global_error_collector.count > 0) {
|
||||
gb_exit(1);
|
||||
}
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
c->parser = parser;
|
||||
@@ -816,7 +863,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
|
||||
return false;
|
||||
}
|
||||
error(entity->token,
|
||||
"Redeclararation of `%.*s` in this scope through `using`\n"
|
||||
"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);
|
||||
@@ -827,7 +874,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
|
||||
return false;
|
||||
}
|
||||
error(entity->token,
|
||||
"Redeclararation of `%.*s` in this scope\n"
|
||||
"Redeclaration of `%.*s` in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name),
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
@@ -861,6 +908,12 @@ void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclIn
|
||||
}
|
||||
|
||||
|
||||
void add_implicit_entity(Checker *c, AstNode *node, Entity *e) {
|
||||
GB_ASSERT(node != NULL);
|
||||
GB_ASSERT(e != NULL);
|
||||
map_entity_set(&c->info.implicits, hash_pointer(node), e);
|
||||
}
|
||||
|
||||
|
||||
void add_type_info_type(Checker *c, Type *t) {
|
||||
if (t == NULL) {
|
||||
@@ -919,6 +972,24 @@ void add_type_info_type(Checker *c, Type *t) {
|
||||
add_type_info_type(c, t_type_info_ptr);
|
||||
add_type_info_type(c, t_rawptr);
|
||||
break;
|
||||
|
||||
case Basic_complex64:
|
||||
add_type_info_type(c, t_type_info_float);
|
||||
add_type_info_type(c, t_f32);
|
||||
break;
|
||||
case Basic_complex128:
|
||||
add_type_info_type(c, t_type_info_float);
|
||||
add_type_info_type(c, t_f64);
|
||||
break;
|
||||
|
||||
case Basic_quaternion128:
|
||||
add_type_info_type(c, t_type_info_float);
|
||||
add_type_info_type(c, t_f32);
|
||||
break;
|
||||
case Basic_quaternion256:
|
||||
add_type_info_type(c, t_type_info_float);
|
||||
add_type_info_type(c, t_f64);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -926,6 +997,10 @@ void add_type_info_type(Checker *c, Type *t) {
|
||||
add_type_info_type(c, bt->Pointer.elem);
|
||||
break;
|
||||
|
||||
case Type_Atomic:
|
||||
add_type_info_type(c, bt->Atomic.elem);
|
||||
break;
|
||||
|
||||
case Type_Array:
|
||||
add_type_info_type(c, bt->Array.elem);
|
||||
add_type_info_type(c, make_type_pointer(c->allocator, bt->Array.elem));
|
||||
@@ -1107,35 +1182,41 @@ void init_preload(Checker *c) {
|
||||
|
||||
|
||||
|
||||
if (record->variant_count != 19) {
|
||||
if (record->variant_count != 22) {
|
||||
compiler_error("Invalid `Type_Info` layout");
|
||||
}
|
||||
t_type_info_named = record->variants[ 1]->type;
|
||||
t_type_info_integer = record->variants[ 2]->type;
|
||||
t_type_info_float = record->variants[ 3]->type;
|
||||
t_type_info_string = record->variants[ 4]->type;
|
||||
t_type_info_boolean = record->variants[ 5]->type;
|
||||
t_type_info_any = record->variants[ 6]->type;
|
||||
t_type_info_pointer = record->variants[ 7]->type;
|
||||
t_type_info_procedure = record->variants[ 8]->type;
|
||||
t_type_info_array = record->variants[ 9]->type;
|
||||
t_type_info_dynamic_array = record->variants[10]->type;
|
||||
t_type_info_slice = record->variants[11]->type;
|
||||
t_type_info_vector = record->variants[12]->type;
|
||||
t_type_info_tuple = record->variants[13]->type;
|
||||
t_type_info_struct = record->variants[14]->type;
|
||||
t_type_info_raw_union = record->variants[15]->type;
|
||||
t_type_info_union = record->variants[16]->type;
|
||||
t_type_info_enum = record->variants[17]->type;
|
||||
t_type_info_map = record->variants[18]->type;
|
||||
t_type_info_complex = record->variants[ 4]->type;
|
||||
t_type_info_quaternion = record->variants[ 5]->type;
|
||||
t_type_info_string = record->variants[ 6]->type;
|
||||
t_type_info_boolean = record->variants[ 7]->type;
|
||||
t_type_info_any = record->variants[ 8]->type;
|
||||
t_type_info_pointer = record->variants[ 9]->type;
|
||||
t_type_info_atomic = record->variants[10]->type;
|
||||
t_type_info_procedure = record->variants[11]->type;
|
||||
t_type_info_array = record->variants[12]->type;
|
||||
t_type_info_dynamic_array = record->variants[13]->type;
|
||||
t_type_info_slice = record->variants[14]->type;
|
||||
t_type_info_vector = record->variants[15]->type;
|
||||
t_type_info_tuple = record->variants[16]->type;
|
||||
t_type_info_struct = record->variants[17]->type;
|
||||
t_type_info_raw_union = record->variants[18]->type;
|
||||
t_type_info_union = record->variants[19]->type;
|
||||
t_type_info_enum = record->variants[20]->type;
|
||||
t_type_info_map = record->variants[21]->type;
|
||||
|
||||
t_type_info_named_ptr = make_type_pointer(c->allocator, t_type_info_named);
|
||||
t_type_info_integer_ptr = make_type_pointer(c->allocator, t_type_info_integer);
|
||||
t_type_info_float_ptr = make_type_pointer(c->allocator, t_type_info_float);
|
||||
t_type_info_complex_ptr = make_type_pointer(c->allocator, t_type_info_complex);
|
||||
t_type_info_quaternion_ptr = make_type_pointer(c->allocator, t_type_info_quaternion);
|
||||
t_type_info_string_ptr = make_type_pointer(c->allocator, t_type_info_string);
|
||||
t_type_info_boolean_ptr = make_type_pointer(c->allocator, t_type_info_boolean);
|
||||
t_type_info_any_ptr = make_type_pointer(c->allocator, t_type_info_any);
|
||||
t_type_info_pointer_ptr = make_type_pointer(c->allocator, t_type_info_pointer);
|
||||
t_type_info_atomic_ptr = make_type_pointer(c->allocator, t_type_info_atomic);
|
||||
t_type_info_procedure_ptr = make_type_pointer(c->allocator, t_type_info_procedure);
|
||||
t_type_info_array_ptr = make_type_pointer(c->allocator, t_type_info_array);
|
||||
t_type_info_dynamic_array_ptr = make_type_pointer(c->allocator, t_type_info_dynamic_array);
|
||||
@@ -1162,12 +1243,6 @@ void init_preload(Checker *c) {
|
||||
t_context_ptr = make_type_pointer(c->allocator, t_context);
|
||||
}
|
||||
|
||||
if (t_raw_dynamic_array == NULL) {
|
||||
Entity *e = find_core_entity(c, str_lit("Raw_Dynamic_Array"));
|
||||
t_raw_dynamic_array = e->type;
|
||||
t_raw_dynamic_array = make_type_pointer(c->allocator, t_raw_dynamic_array);
|
||||
}
|
||||
|
||||
if (t_map_key == NULL) {
|
||||
Entity *e = find_core_entity(c, str_lit("__Map_Key"));
|
||||
t_map_key = e->type;
|
||||
@@ -1234,7 +1309,6 @@ void check_procedure_overloading(Checker *c, Entity *e) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
String name = p->token.string;
|
||||
|
||||
GB_ASSERT(p->kind == Entity_Procedure);
|
||||
@@ -1397,7 +1471,7 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_cap);
|
||||
DeclInfo *di = NULL;
|
||||
if (vd->values.count > 0) {
|
||||
di = make_declaration_info(heap_allocator(), c->context.scope);
|
||||
di = make_declaration_info(heap_allocator(), c->context.scope, c->context.decl);
|
||||
di->entities = entities;
|
||||
di->type_expr = vd->type;
|
||||
di->init_expr = vd->values.e[0];
|
||||
@@ -1432,7 +1506,7 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
|
||||
DeclInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(heap_allocator(), e->scope);
|
||||
d = make_declaration_info(heap_allocator(), e->scope, c->context.decl);
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = init_expr;
|
||||
}
|
||||
@@ -1458,15 +1532,25 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
|
||||
init = vd->values.e[i];
|
||||
}
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope);
|
||||
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);
|
||||
Entity *e = NULL;
|
||||
|
||||
AstNode *up_init = unparen_expr(init);
|
||||
if (up_init != NULL && is_ast_node_type(up_init)) {
|
||||
AstNode *type = up_init;
|
||||
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
|
||||
// TODO(bill): What if vd->type != NULL??? How to handle this case?
|
||||
d->type_expr = init;
|
||||
d->init_expr = init;
|
||||
d->type_expr = type;
|
||||
d->init_expr = type;
|
||||
} else if (up_init != NULL && up_init->kind == AstNode_Alias) {
|
||||
#if 1
|
||||
error_node(up_init, "#alias declarations are not yet supported");
|
||||
continue;
|
||||
#else
|
||||
e = make_entity_alias(c->allocator, d->scope, name->Ident, NULL, EntityAlias_Invalid, NULL);
|
||||
d->type_expr = vd->type;
|
||||
d->init_expr = up_init->Alias.expr;
|
||||
#endif
|
||||
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
|
||||
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
|
||||
d->proc_lit = up_init;
|
||||
@@ -1512,6 +1596,19 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fl->cond != NULL) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, fl->cond);
|
||||
if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
|
||||
error_node(fl->cond, "Non-constant boolean `when` condition");
|
||||
continue;
|
||||
}
|
||||
if (operand.value.kind == ExactValue_Bool &&
|
||||
!operand.value.value_bool) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
DelayedDecl di = {c->context.scope, decl};
|
||||
array_add(&c->delayed_foreign_libraries, di);
|
||||
case_end;
|
||||
@@ -1826,7 +1923,7 @@ void check_parsed_files(Checker *c) {
|
||||
}
|
||||
|
||||
f->scope = scope;
|
||||
f->decl_info = make_declaration_info(c->allocator, f->scope);
|
||||
f->decl_info = make_declaration_info(c->allocator, f->scope, c->context.decl);
|
||||
HashKey key = hash_string(f->tokenizer.fullpath);
|
||||
map_scope_set(&file_scopes, key, scope);
|
||||
map_ast_file_set(&c->info.files, key, f);
|
||||
@@ -1903,17 +2000,25 @@ void check_parsed_files(Checker *c) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Check for illegal cyclic type declarations
|
||||
for_array(i, c->info.definitions.entries) {
|
||||
Entity *e = c->info.definitions.entries.e[i].value;
|
||||
if (e->kind == Entity_TypeName) {
|
||||
if (e->kind == Entity_TypeName ||
|
||||
(e->kind == Entity_Alias && e->Alias.kind == EntityAlias_Type)) {
|
||||
if (e->type != NULL) {
|
||||
// i64 size = type_size_of(c->sizes, c->allocator, e->type);
|
||||
i64 align = type_align_of(c->allocator, e->type);
|
||||
if (align > 0) {
|
||||
// add_type_info_type(c, e->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// gb_printf_err("Count: %td\n", c->info.type_info_count++);
|
||||
|
||||
for_array(i, file_scopes.entries) {
|
||||
Scope *s = file_scopes.entries.e[i].value;
|
||||
if (s->is_init) {
|
||||
@@ -1938,6 +2043,3 @@ void check_parsed_files(Checker *c) {
|
||||
map_scope_destroy(&file_scopes);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
#if defined(GB_SYSTEM_UNIX)
|
||||
// Required for intrinsics on GCC
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#define GB_NO_DEFER
|
||||
#define GB_IMPLEMENTATION
|
||||
#include "gb/gb.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
gbAllocator heap_allocator(void) {
|
||||
return gb_heap_allocator();
|
||||
}
|
||||
@@ -24,6 +31,94 @@ gbAllocator scratch_allocator(void) {
|
||||
return gb_scratch_allocator(&scratch_memory);
|
||||
}
|
||||
|
||||
typedef struct DynamicArenaBlock DynamicArenaBlock;
|
||||
typedef struct DynamicArena DynamicArena;
|
||||
|
||||
struct DynamicArenaBlock {
|
||||
DynamicArenaBlock *prev;
|
||||
DynamicArenaBlock *next;
|
||||
u8 * start;
|
||||
isize count;
|
||||
isize capacity;
|
||||
|
||||
gbVirtualMemory vm;
|
||||
};
|
||||
|
||||
struct DynamicArena {
|
||||
DynamicArenaBlock *start_block;
|
||||
DynamicArenaBlock *current_block;
|
||||
isize block_size;
|
||||
};
|
||||
|
||||
DynamicArenaBlock *add_dynamic_arena_block(DynamicArena *a) {
|
||||
GB_ASSERT(a != NULL);
|
||||
GB_ASSERT(a->block_size > 0);
|
||||
|
||||
gbVirtualMemory vm = gb_vm_alloc(NULL, a->block_size);
|
||||
DynamicArenaBlock *block = cast(DynamicArenaBlock *)vm.data;
|
||||
|
||||
u8 *start = cast(u8 *)gb_align_forward(cast(u8 *)(block + 1), GB_DEFAULT_MEMORY_ALIGNMENT);
|
||||
u8 *end = cast(u8 *)vm.data + vm.size;
|
||||
|
||||
block->vm = vm;
|
||||
block->start = start;
|
||||
block->count = 0;
|
||||
block->capacity = end-start;
|
||||
|
||||
if (a->current_block != NULL) {
|
||||
a->current_block->next = block;
|
||||
block->prev = a->current_block;
|
||||
}
|
||||
a->current_block = block;
|
||||
return block;
|
||||
}
|
||||
|
||||
void init_dynamic_arena(DynamicArena *a, isize block_size) {
|
||||
isize size = gb_size_of(DynamicArenaBlock) + block_size;
|
||||
size = cast(isize)gb_align_forward(cast(void *)cast(uintptr)size, GB_DEFAULT_MEMORY_ALIGNMENT);
|
||||
a->block_size = size;
|
||||
a->start_block = add_dynamic_arena_block(a);
|
||||
}
|
||||
|
||||
void destroy_dynamic_arena(DynamicArena *a) {
|
||||
DynamicArenaBlock *b = a->current_block;
|
||||
while (b != NULL) {
|
||||
gbVirtualMemory vm = b->vm;
|
||||
b = b->prev;
|
||||
gb_vm_free(b->vm);
|
||||
}
|
||||
}
|
||||
|
||||
GB_ALLOCATOR_PROC(dynamic_arena_allocator_proc) {
|
||||
DynamicArena *a = cast(DynamicArena *)allocator_data;
|
||||
void *ptr = NULL;
|
||||
|
||||
switch (type) {
|
||||
case gbAllocation_Alloc: {
|
||||
|
||||
} break;
|
||||
|
||||
case gbAllocation_Free: {
|
||||
} break;
|
||||
|
||||
case gbAllocation_Resize: {
|
||||
} break;
|
||||
|
||||
case gbAllocation_FreeAll:
|
||||
GB_PANIC("free_all is not supported by this allocator");
|
||||
break;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
gbAllocator dynamic_arena_allocator(DynamicArena *a) {
|
||||
gbAllocator allocator = {dynamic_arena_allocator_proc, a};
|
||||
return allocator;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
i64 next_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
@@ -104,6 +199,10 @@ i16 f32_to_f16(f32 value) {
|
||||
}
|
||||
}
|
||||
|
||||
f64 gb_sqrt(f64 x) {
|
||||
return sqrt(x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
|
||||
|
||||
+47
-8
@@ -13,13 +13,15 @@ typedef struct Type Type;
|
||||
ENTITY_KIND(Builtin) \
|
||||
ENTITY_KIND(ImportName) \
|
||||
ENTITY_KIND(LibraryName) \
|
||||
ENTITY_KIND(Alias) \
|
||||
ENTITY_KIND(Nil) \
|
||||
ENTITY_KIND(Count)
|
||||
ENTITY_KIND(Label)
|
||||
|
||||
typedef enum EntityKind {
|
||||
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
|
||||
ENTITY_KINDS
|
||||
#undef ENTITY_KIND
|
||||
Entity_Count,
|
||||
} EntityKind;
|
||||
|
||||
String const entity_strings[] = {
|
||||
@@ -31,13 +33,14 @@ String const entity_strings[] = {
|
||||
typedef enum EntityFlag {
|
||||
EntityFlag_Visited = 1<<0,
|
||||
EntityFlag_Used = 1<<1,
|
||||
EntityFlag_Anonymous = 1<<2,
|
||||
EntityFlag_Using = 1<<2,
|
||||
EntityFlag_Field = 1<<3,
|
||||
EntityFlag_Param = 1<<4,
|
||||
EntityFlag_VectorElem = 1<<5,
|
||||
EntityFlag_Ellipsis = 1<<6,
|
||||
EntityFlag_NoAlias = 1<<7,
|
||||
EntityFlag_TypeField = 1<<8,
|
||||
EntityFlag_Value = 1<<9,
|
||||
} EntityFlag;
|
||||
|
||||
// Zero value means the overloading process is not yet done
|
||||
@@ -47,11 +50,18 @@ typedef enum OverloadKind {
|
||||
Overload_Yes,
|
||||
} OverloadKind;
|
||||
|
||||
typedef enum EntityAliasKind {
|
||||
EntityAlias_Invalid,
|
||||
EntityAlias_Type,
|
||||
EntityAlias_Entity,
|
||||
} EntityAliasKind;
|
||||
|
||||
|
||||
// An Entity is a named "thing" in the language
|
||||
typedef struct Entity Entity;
|
||||
struct Entity {
|
||||
EntityKind kind;
|
||||
u64 id;
|
||||
u32 flags;
|
||||
Token token;
|
||||
Scope * scope;
|
||||
@@ -72,7 +82,9 @@ struct Entity {
|
||||
bool is_immutable;
|
||||
bool is_thread_local;
|
||||
} Variable;
|
||||
i32 TypeName;
|
||||
struct {
|
||||
bool is_type_alias;
|
||||
} TypeName;
|
||||
struct {
|
||||
bool is_foreign;
|
||||
String foreign_name;
|
||||
@@ -95,7 +107,15 @@ struct Entity {
|
||||
String name;
|
||||
bool used;
|
||||
} LibraryName;
|
||||
struct {
|
||||
EntityAliasKind kind;
|
||||
Entity * original;
|
||||
} Alias;
|
||||
i32 Nil;
|
||||
struct {
|
||||
String name;
|
||||
AstNode *node;
|
||||
} Label;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -126,6 +146,7 @@ bool is_entity_exported(Entity *e) {
|
||||
return name.text[0] != '_';
|
||||
}
|
||||
|
||||
gb_global u64 global_entity_id = 0;
|
||||
|
||||
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
|
||||
Entity *entity = gb_alloc_item(a, Entity);
|
||||
@@ -133,6 +154,7 @@ Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token,
|
||||
entity->scope = scope;
|
||||
entity->token = token;
|
||||
entity->type = type;
|
||||
entity->id = ++global_entity_id;
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -147,7 +169,7 @@ Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, T
|
||||
token.pos = parent->token.pos;
|
||||
Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
|
||||
entity->using_parent = parent;
|
||||
entity->flags |= EntityFlag_Anonymous;
|
||||
entity->flags |= EntityFlag_Using;
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -163,20 +185,20 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, bool is_immutable) {
|
||||
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, bool is_immutable) {
|
||||
Entity *entity = make_entity_variable(a, scope, token, type, is_immutable);
|
||||
entity->flags |= EntityFlag_Used;
|
||||
if (anonymous) entity->flags |= EntityFlag_Anonymous;
|
||||
if (is_using) entity->flags |= EntityFlag_Using;
|
||||
entity->flags |= EntityFlag_Param;
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) {
|
||||
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index) {
|
||||
Entity *entity = make_entity_variable(a, scope, token, type, false);
|
||||
entity->Variable.field_src_index = field_src_index;
|
||||
entity->Variable.field_index = field_src_index;
|
||||
if (is_using) entity->flags |= EntityFlag_Using;
|
||||
entity->flags |= EntityFlag_Field;
|
||||
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -218,12 +240,29 @@ Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type,
|
||||
EntityAliasKind kind, Entity *original) {
|
||||
Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
|
||||
entity->Alias.kind = kind;
|
||||
entity->Alias.original = original;
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
|
||||
Token token = make_token_ident(name);
|
||||
Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type,
|
||||
AstNode *node) {
|
||||
Entity *entity = alloc_entity(a, Entity_Label, scope, token, type);
|
||||
entity->Label.node = node;
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
|
||||
token.string = str_lit("_");
|
||||
return make_entity_variable(a, scope, token, NULL, false);
|
||||
|
||||
+368
-58
@@ -5,6 +5,14 @@
|
||||
|
||||
typedef struct AstNode AstNode;
|
||||
|
||||
typedef struct Complex128 {
|
||||
f64 real, imag;
|
||||
} Complex128;
|
||||
|
||||
typedef struct Quaternion256 {
|
||||
f64 real, imag, jmag, kmag;
|
||||
} Quaternion256;
|
||||
|
||||
typedef enum ExactValueKind {
|
||||
ExactValue_Invalid,
|
||||
|
||||
@@ -12,6 +20,8 @@ typedef enum ExactValueKind {
|
||||
ExactValue_String,
|
||||
ExactValue_Integer,
|
||||
ExactValue_Float,
|
||||
ExactValue_Complex,
|
||||
ExactValue_Quaternion,
|
||||
ExactValue_Pointer,
|
||||
ExactValue_Compound, // TODO(bill): Is this good enough?
|
||||
|
||||
@@ -21,12 +31,14 @@ typedef enum ExactValueKind {
|
||||
typedef struct ExactValue {
|
||||
ExactValueKind kind;
|
||||
union {
|
||||
bool value_bool;
|
||||
String value_string;
|
||||
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
|
||||
f64 value_float;
|
||||
i64 value_pointer;
|
||||
AstNode *value_compound;
|
||||
bool value_bool;
|
||||
String value_string;
|
||||
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
|
||||
f64 value_float;
|
||||
i64 value_pointer;
|
||||
Complex128 value_complex;
|
||||
Quaternion256 value_quaternion;
|
||||
AstNode * value_compound;
|
||||
};
|
||||
} ExactValue;
|
||||
|
||||
@@ -35,45 +47,62 @@ HashKey hash_exact_value(ExactValue v) {
|
||||
}
|
||||
|
||||
|
||||
ExactValue make_exact_value_compound(AstNode *node) {
|
||||
ExactValue exact_value_compound(AstNode *node) {
|
||||
ExactValue result = {ExactValue_Compound};
|
||||
result.value_compound = node;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_bool(bool b) {
|
||||
ExactValue exact_value_bool(bool b) {
|
||||
ExactValue result = {ExactValue_Bool};
|
||||
result.value_bool = (b != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_string(String string) {
|
||||
ExactValue exact_value_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
ExactValue result = {ExactValue_String};
|
||||
result.value_string = string;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_integer(i64 i) {
|
||||
ExactValue exact_value_integer(i64 i) {
|
||||
ExactValue result = {ExactValue_Integer};
|
||||
result.value_integer = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_float(f64 f) {
|
||||
ExactValue exact_value_float(f64 f) {
|
||||
ExactValue result = {ExactValue_Float};
|
||||
result.value_float = f;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_pointer(i64 ptr) {
|
||||
ExactValue exact_value_complex(f64 real, f64 imag) {
|
||||
ExactValue result = {ExactValue_Complex};
|
||||
result.value_complex.real = real;
|
||||
result.value_complex.imag = imag;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue exact_value_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;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ExactValue make_exact_value_integer_from_string(String string) {
|
||||
ExactValue exact_value_integer_from_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
i32 base = 10;
|
||||
bool has_prefix = false;
|
||||
@@ -82,6 +111,7 @@ ExactValue make_exact_value_integer_from_string(String string) {
|
||||
case 'b': base = 2; has_prefix = true; break;
|
||||
case 'o': base = 8; has_prefix = true; break;
|
||||
case 'd': base = 10; has_prefix = true; break;
|
||||
case 'z': base = 12; has_prefix = true; break;
|
||||
case 'x': base = 16; has_prefix = true; break;
|
||||
}
|
||||
}
|
||||
@@ -100,25 +130,19 @@ ExactValue make_exact_value_integer_from_string(String string) {
|
||||
continue;
|
||||
}
|
||||
i64 v = 0;
|
||||
if (gb_char_is_digit(r)) {
|
||||
v = r - '0';
|
||||
} else if (gb_char_is_hex_digit(r)) {
|
||||
v = gb_hex_digit_to_int(r);
|
||||
} else {
|
||||
v = digit_value(r);
|
||||
if (v >= base) {
|
||||
break;
|
||||
}
|
||||
|
||||
result *= base;
|
||||
result += v;
|
||||
}
|
||||
|
||||
|
||||
return make_exact_value_integer(result);
|
||||
return exact_value_integer(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ExactValue make_exact_value_float_from_string(String string) {
|
||||
f64 float_from_string(String string) {
|
||||
isize i = 0;
|
||||
u8 *str = string.text;
|
||||
isize len = string.len;
|
||||
@@ -137,10 +161,10 @@ ExactValue make_exact_value_float_from_string(String string) {
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
if (!gb_char_is_digit(r)) {
|
||||
i64 v = digit_value(r);
|
||||
if (v >= 10) {
|
||||
break;
|
||||
}
|
||||
i64 v = r - '0';
|
||||
value *= 10.0;
|
||||
value += v;
|
||||
}
|
||||
@@ -153,29 +177,38 @@ ExactValue make_exact_value_float_from_string(String string) {
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
if (!gb_char_is_digit(r)) {
|
||||
i64 v = digit_value(r);
|
||||
if (v >= 10) {
|
||||
break;
|
||||
}
|
||||
value += (r-'0')/pow10;
|
||||
value += v/pow10;
|
||||
pow10 *= 10.0;
|
||||
}
|
||||
}
|
||||
|
||||
f64 frac = 0;
|
||||
bool frac = false;
|
||||
f64 scale = 1.0;
|
||||
if ((str[i] == 'e') || (str[i] == 'E')) {
|
||||
i++;
|
||||
|
||||
if (str[i] == '-') {
|
||||
frac = 1;
|
||||
frac = true;
|
||||
i++;
|
||||
} else if (str[i] == '+') {
|
||||
i++;
|
||||
}
|
||||
|
||||
u32 exp;
|
||||
for (exp = 0; gb_char_is_digit(str[i]); i++) {
|
||||
exp = exp * 10 + (str[i]-'0');
|
||||
u32 exp = 0;
|
||||
for (; i < len; i++) {
|
||||
Rune r = cast(Rune)str[i];
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
u32 d = cast(u32)digit_value(r);
|
||||
if (d >= 10) {
|
||||
break;
|
||||
}
|
||||
exp = exp * 10 + d;
|
||||
}
|
||||
if (exp > 308) exp = 308;
|
||||
|
||||
@@ -184,21 +217,36 @@ ExactValue make_exact_value_float_from_string(String string) {
|
||||
while (exp > 0) { scale *= 10.0; exp -= 1; }
|
||||
}
|
||||
|
||||
f64 result = sign * (frac ? (value / scale) : (value * scale));
|
||||
return make_exact_value_float(result);
|
||||
return sign * (frac ? (value / scale) : (value * scale));
|
||||
}
|
||||
|
||||
ExactValue exact_value_float_from_string(String string) {
|
||||
return exact_value_float(float_from_string(string));
|
||||
}
|
||||
|
||||
|
||||
ExactValue make_exact_value_from_basic_literal(Token token) {
|
||||
ExactValue exact_value_from_basic_literal(Token token) {
|
||||
switch (token.kind) {
|
||||
case Token_String: return make_exact_value_string(token.string);
|
||||
case Token_Integer: return make_exact_value_integer_from_string(token.string);
|
||||
case Token_Float: return make_exact_value_float_from_string(token.string);
|
||||
case Token_String: return exact_value_string(token.string);
|
||||
case Token_Integer: return exact_value_integer_from_string(token.string);
|
||||
case Token_Float: return exact_value_float_from_string(token.string);
|
||||
case Token_Imag: {
|
||||
String str = token.string;
|
||||
Rune last_rune = cast(Rune)str.text[str.len-1];
|
||||
str.len--; // Ignore the `i|j|k`
|
||||
f64 imag = float_from_string(str);
|
||||
|
||||
switch (last_rune) {
|
||||
case 'i': return exact_value_complex(0, imag);
|
||||
case 'j': return exact_value_quaternion(0, 0, imag, 0);
|
||||
case 'k': return exact_value_quaternion(0, 0, 0, imag);
|
||||
}
|
||||
}
|
||||
case Token_Rune: {
|
||||
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 make_exact_value_integer(r);
|
||||
return exact_value_integer(r);
|
||||
}
|
||||
default:
|
||||
GB_PANIC("Invalid token for basic literal");
|
||||
@@ -217,12 +265,12 @@ ExactValue exact_value_to_integer(ExactValue v) {
|
||||
i64 i = cast(i64)v.value_float;
|
||||
f64 f = cast(f64)i;
|
||||
if (f == v.value_float) {
|
||||
return make_exact_value_integer(i);
|
||||
return exact_value_integer(i);
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Pointer:
|
||||
return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
|
||||
return exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
@@ -231,7 +279,7 @@ ExactValue exact_value_to_integer(ExactValue v) {
|
||||
ExactValue exact_value_to_float(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
return make_exact_value_float(cast(i64)v.value_integer);
|
||||
return exact_value_float(cast(i64)v.value_integer);
|
||||
case ExactValue_Float:
|
||||
return v;
|
||||
}
|
||||
@@ -239,6 +287,128 @@ ExactValue exact_value_to_float(ExactValue v) {
|
||||
return r;
|
||||
}
|
||||
|
||||
ExactValue exact_value_to_complex(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
return exact_value_complex(cast(i64)v.value_integer, 0);
|
||||
case ExactValue_Float:
|
||||
return exact_value_complex(v.value_float, 0);
|
||||
case ExactValue_Complex:
|
||||
return v;
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
ExactValue exact_value_to_quaternion(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
return exact_value_quaternion(cast(i64)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) {
|
||||
case ExactValue_Integer:
|
||||
case ExactValue_Float:
|
||||
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;
|
||||
}
|
||||
|
||||
ExactValue exact_value_imag(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
case ExactValue_Float:
|
||||
return exact_value_integer(0);
|
||||
case ExactValue_Complex:
|
||||
return exact_value_float(v.value_complex.imag);
|
||||
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_integer(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_integer(0);
|
||||
case ExactValue_Quaternion:
|
||||
return exact_value_float(v.value_quaternion.kmag);
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
ExactValue exact_value_make_imag(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
return exact_value_complex(0, exact_value_to_float(v).value_float);
|
||||
case ExactValue_Float:
|
||||
return exact_value_complex(0, v.value_float);
|
||||
default:
|
||||
GB_PANIC("Expected an integer or float type for `exact_value_make_imag`");
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
ExactValue exact_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_jmag`");
|
||||
}
|
||||
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_kmag`");
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
|
||||
switch (op) {
|
||||
@@ -247,6 +417,8 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
|
||||
case ExactValue_Invalid:
|
||||
case ExactValue_Integer:
|
||||
case ExactValue_Float:
|
||||
case ExactValue_Complex:
|
||||
case ExactValue_Quaternion:
|
||||
return v;
|
||||
}
|
||||
} break;
|
||||
@@ -265,6 +437,18 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
|
||||
i.value_float = -i.value_float;
|
||||
return i;
|
||||
}
|
||||
case ExactValue_Complex: {
|
||||
f64 real = v.value_complex.real;
|
||||
f64 imag = v.value_complex.imag;
|
||||
return exact_value_complex(-real, -imag);
|
||||
}
|
||||
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;
|
||||
|
||||
@@ -287,14 +471,14 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
|
||||
i &= ~((~0ll)<<precision);
|
||||
}
|
||||
|
||||
return make_exact_value_integer(i);
|
||||
return exact_value_integer(i);
|
||||
} break;
|
||||
|
||||
case Token_Not: {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid: return v;
|
||||
case ExactValue_Bool:
|
||||
return make_exact_value_bool(!v.value_bool);
|
||||
return exact_value_bool(!v.value_bool);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@@ -318,8 +502,12 @@ i32 exact_value_order(ExactValue v) {
|
||||
return 2;
|
||||
case ExactValue_Float:
|
||||
return 3;
|
||||
case ExactValue_Pointer:
|
||||
case ExactValue_Complex:
|
||||
return 4;
|
||||
case ExactValue_Quaternion:
|
||||
return 5;
|
||||
case ExactValue_Pointer:
|
||||
return 6;
|
||||
|
||||
default:
|
||||
GB_PANIC("How'd you get here? Invalid Value.kind");
|
||||
@@ -340,6 +528,8 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
case ExactValue_Complex:
|
||||
case ExactValue_Quaternion:
|
||||
return;
|
||||
|
||||
case ExactValue_Integer:
|
||||
@@ -348,18 +538,32 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||
return;
|
||||
case ExactValue_Float:
|
||||
// TODO(bill): Is this good enough?
|
||||
*x = make_exact_value_float(cast(f64)x->value_integer);
|
||||
*x = exact_value_float(cast(f64)x->value_integer);
|
||||
return;
|
||||
case ExactValue_Complex:
|
||||
*x = exact_value_complex(cast(f64)x->value_integer, 0);
|
||||
return;
|
||||
case ExactValue_Quaternion:
|
||||
*x = exact_value_quaternion(cast(f64)x->value_integer, 0, 0, 0);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Float:
|
||||
if (y->kind == ExactValue_Float)
|
||||
switch (y->kind) {
|
||||
case ExactValue_Float:
|
||||
return;
|
||||
case ExactValue_Complex:
|
||||
*x = exact_value_to_complex(*x);
|
||||
return;
|
||||
case ExactValue_Quaternion:
|
||||
*x = exact_value_to_quaternion(*x);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
compiler_error("How'd you get here? Invalid ExactValueKind");
|
||||
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
|
||||
}
|
||||
|
||||
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
|
||||
@@ -372,10 +576,10 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
|
||||
|
||||
case ExactValue_Bool:
|
||||
switch (op) {
|
||||
case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool);
|
||||
case Token_CmpOr: return make_exact_value_bool(x.value_bool || y.value_bool);
|
||||
case Token_And: return make_exact_value_bool(x.value_bool & y.value_bool);
|
||||
case Token_Or: return make_exact_value_bool(x.value_bool | y.value_bool);
|
||||
case Token_CmpAnd: return exact_value_bool(x.value_bool && y.value_bool);
|
||||
case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool);
|
||||
case Token_And: return exact_value_bool(x.value_bool & y.value_bool);
|
||||
case Token_Or: return exact_value_bool(x.value_bool | y.value_bool);
|
||||
default: goto error;
|
||||
}
|
||||
break;
|
||||
@@ -388,7 +592,7 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
|
||||
case Token_Add: c = a + b; break;
|
||||
case Token_Sub: c = a - b; break;
|
||||
case Token_Mul: c = a * b; break;
|
||||
case Token_Quo: return make_exact_value_float(fmod(cast(f64)a, cast(f64)b));
|
||||
case Token_Quo: return exact_value_float(fmod(cast(f64)a, cast(f64)b));
|
||||
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
|
||||
case Token_Mod: c = a % b; break;
|
||||
case Token_And: c = a & b; break;
|
||||
@@ -400,20 +604,98 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
|
||||
default: goto error;
|
||||
}
|
||||
|
||||
return make_exact_value_integer(c);
|
||||
return exact_value_integer(c);
|
||||
} break;
|
||||
|
||||
case ExactValue_Float: {
|
||||
f64 a = x.value_float;
|
||||
f64 b = y.value_float;
|
||||
switch (op) {
|
||||
case Token_Add: return make_exact_value_float(a + b);
|
||||
case Token_Sub: return make_exact_value_float(a - b);
|
||||
case Token_Mul: return make_exact_value_float(a * b);
|
||||
case Token_Quo: return make_exact_value_float(a / b);
|
||||
case Token_Add: return exact_value_float(a + b);
|
||||
case Token_Sub: return exact_value_float(a - b);
|
||||
case Token_Mul: return exact_value_float(a * b);
|
||||
case Token_Quo: return exact_value_float(a / b);
|
||||
default: goto error;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Complex: {
|
||||
y = exact_value_to_complex(y);
|
||||
f64 a = x.value_complex.real;
|
||||
f64 b = x.value_complex.imag;
|
||||
f64 c = y.value_complex.real;
|
||||
f64 d = y.value_complex.imag;
|
||||
f64 real = 0;
|
||||
f64 imag = 0;
|
||||
switch (op) {
|
||||
case Token_Add:
|
||||
real = a + c;
|
||||
imag = b + d;
|
||||
break;
|
||||
case Token_Sub:
|
||||
real = a - c;
|
||||
imag = b - d;
|
||||
break;
|
||||
case Token_Mul:
|
||||
real = (a*c - b*d);
|
||||
imag = (b*c + a*d);
|
||||
break;
|
||||
case Token_Quo: {
|
||||
f64 s = c*c + d*d;
|
||||
real = (a*c + b*d)/s;
|
||||
imag = (b*c - a*d)/s;
|
||||
} break;
|
||||
default: goto error;
|
||||
}
|
||||
return exact_value_complex(real, imag);
|
||||
} break;
|
||||
|
||||
case ExactValue_Quaternion: {
|
||||
y = exact_value_to_quaternion(y);
|
||||
f64 a = x.value_quaternion.real;
|
||||
f64 b = x.value_quaternion.imag;
|
||||
f64 c = x.value_quaternion.jmag;
|
||||
f64 d = x.value_quaternion.kmag;
|
||||
|
||||
f64 e = x.value_quaternion.real;
|
||||
f64 f = x.value_quaternion.imag;
|
||||
f64 g = x.value_quaternion.jmag;
|
||||
f64 h = x.value_quaternion.kmag;
|
||||
|
||||
f64 real = 0;
|
||||
f64 imag = 0;
|
||||
f64 jmag = 0;
|
||||
f64 kmag = 0;
|
||||
switch (op) {
|
||||
case Token_Add:
|
||||
real = a + e;
|
||||
imag = b + f;
|
||||
jmag = c + g;
|
||||
kmag = d + h;
|
||||
break;
|
||||
case Token_Sub:
|
||||
real = a - e;
|
||||
imag = b - f;
|
||||
jmag = c - g;
|
||||
kmag = d - h;
|
||||
break;
|
||||
case Token_Mul:
|
||||
real = a*f + b*e + c*h - d*g;
|
||||
imag = a*g - b*h + c*e + d*f;
|
||||
jmag = a*h + b*g - c*f + d*e;
|
||||
kmag = a*e - b*f - c*g - d*h;
|
||||
break;
|
||||
case Token_Quo: {
|
||||
f64 s = e*e + f*f + g*g + h*h;
|
||||
real = (+a*e + b*f + c*g + d*h)/s;
|
||||
imag = (-a*f + b*e - c*h + d*h)/s;
|
||||
jmag = (-a*g + b*h + c*e - d*f)/s;
|
||||
kmag = (-a*h - b*g + c*f + d*e)/s;
|
||||
} break;
|
||||
default: goto error;
|
||||
}
|
||||
return exact_value_quaternion(real, imag, jmag, kmag);
|
||||
} break;
|
||||
}
|
||||
|
||||
error:
|
||||
@@ -474,6 +756,34 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Complex: {
|
||||
f64 a = x.value_complex.real;
|
||||
f64 b = x.value_complex.imag;
|
||||
f64 c = y.value_complex.real;
|
||||
f64 d = y.value_complex.imag;
|
||||
switch (op) {
|
||||
case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
|
||||
case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Quaternion: {
|
||||
f64 a = x.value_quaternion.real;
|
||||
f64 b = x.value_quaternion.imag;
|
||||
f64 c = x.value_quaternion.jmag;
|
||||
f64 d = x.value_quaternion.kmag;
|
||||
|
||||
f64 e = y.value_quaternion.real;
|
||||
f64 f = y.value_quaternion.imag;
|
||||
f64 g = y.value_quaternion.jmag;
|
||||
f64 h = y.value_quaternion.kmag;
|
||||
|
||||
switch (op) {
|
||||
case Token_CmpEq: return cmp_f64(a, e) == 0 && cmp_f64(b, f) == 0 && cmp_f64(c, g) == 0 && cmp_f64(d, h) == 0;
|
||||
case Token_NotEq: return cmp_f64(a, e) != 0 || cmp_f64(b, f) != 0 || cmp_f64(c, g) != 0 || cmp_f64(d, h) != 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_String: {
|
||||
String a = x.value_string;
|
||||
String b = y.value_string;
|
||||
|
||||
+429
-155
File diff suppressed because it is too large
Load Diff
+281
-85
@@ -135,6 +135,28 @@ void ir_print_encoded_global(irFileBuffer *f, String name, bool remove_prefix) {
|
||||
ir_print_escape_string(f, name, true);
|
||||
}
|
||||
|
||||
void ir_print_type(irFileBuffer *f, irModule *m, Type *t);
|
||||
|
||||
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
|
||||
GB_ASSERT(is_type_proc(t));
|
||||
t = base_type(t);
|
||||
isize result_count = t->Proc.result_count;
|
||||
if (result_count == 0) {
|
||||
ir_fprintf(f, "void");
|
||||
} else if (result_count == 1) {
|
||||
ir_print_type(f, m, t->Proc.abi_compat_results[0]);
|
||||
} else {
|
||||
ir_fprintf(f, "{");
|
||||
for (isize i = 0; i < result_count; i++) {
|
||||
if (i > 0) {
|
||||
ir_fprintf(f, ", ");
|
||||
}
|
||||
ir_print_type(f, m, t->Proc.abi_compat_results[i]);
|
||||
}
|
||||
ir_fprintf(f, "}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
|
||||
i64 word_bits = 8*build_context.word_size;
|
||||
@@ -145,32 +167,39 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_bool: ir_fprintf(f, "i1"); return;
|
||||
case Basic_i8: ir_fprintf(f, "i8"); return;
|
||||
case Basic_u8: ir_fprintf(f, "i8"); return;
|
||||
case Basic_i16: ir_fprintf(f, "i16"); return;
|
||||
case Basic_u16: ir_fprintf(f, "i16"); return;
|
||||
case Basic_i32: ir_fprintf(f, "i32"); return;
|
||||
case Basic_u32: ir_fprintf(f, "i32"); return;
|
||||
case Basic_i64: ir_fprintf(f, "i64"); return;
|
||||
case Basic_u64: ir_fprintf(f, "i64"); return;
|
||||
// case Basic_i128: ir_fprintf(f, "i128"); return;
|
||||
// case Basic_u128: ir_fprintf(f, "i128"); return;
|
||||
// case Basic_f16: ir_fprintf(f, "half"); return;
|
||||
case Basic_f32: ir_fprintf(f, "float"); return;
|
||||
case Basic_f64: ir_fprintf(f, "double"); return;
|
||||
// case Basic_f128: ir_fprintf(f, "fp128"); return;
|
||||
case Basic_rawptr: ir_fprintf(f, "%%..rawptr"); return;
|
||||
case Basic_string: ir_fprintf(f, "%%..string"); return;
|
||||
case Basic_uint: ir_fprintf(f, "i%lld", word_bits); return;
|
||||
case Basic_int: ir_fprintf(f, "i%lld", word_bits); return;
|
||||
case Basic_any: ir_fprintf(f, "%%..any"); return;
|
||||
case Basic_bool: ir_fprintf(f, "i1"); return;
|
||||
case Basic_i8: ir_fprintf(f, "i8"); return;
|
||||
case Basic_u8: ir_fprintf(f, "i8"); return;
|
||||
case Basic_i16: ir_fprintf(f, "i16"); return;
|
||||
case Basic_u16: ir_fprintf(f, "i16"); return;
|
||||
case Basic_i32: ir_fprintf(f, "i32"); return;
|
||||
case Basic_u32: ir_fprintf(f, "i32"); return;
|
||||
case Basic_i64: ir_fprintf(f, "i64"); return;
|
||||
case Basic_u64: ir_fprintf(f, "i64"); return;
|
||||
|
||||
case Basic_f32: ir_fprintf(f, "float"); return;
|
||||
case Basic_f64: ir_fprintf(f, "double"); return;
|
||||
|
||||
case Basic_complex64: ir_fprintf(f, "%%..complex64"); return;
|
||||
case Basic_complex128: ir_fprintf(f, "%%..complex128"); return;
|
||||
|
||||
case Basic_quaternion128: ir_fprintf(f, "%%..quaternion128"); return;
|
||||
case Basic_quaternion256: ir_fprintf(f, "%%..quaternion256"); return;
|
||||
|
||||
case Basic_rawptr: ir_fprintf(f, "%%..rawptr"); return;
|
||||
case Basic_string: ir_fprintf(f, "%%..string"); return;
|
||||
case Basic_uint: ir_fprintf(f, "i%lld", word_bits); return;
|
||||
case Basic_int: ir_fprintf(f, "i%lld", word_bits); return;
|
||||
case Basic_any: ir_fprintf(f, "%%..any"); return;
|
||||
}
|
||||
break;
|
||||
case Type_Pointer:
|
||||
ir_print_type(f, m, t->Pointer.elem);
|
||||
ir_fprintf(f, "*");
|
||||
return;
|
||||
case Type_Atomic:
|
||||
ir_print_type(f, m, t->Atomic.elem);
|
||||
return;
|
||||
case Type_Array:
|
||||
ir_fprintf(f, "[%lld x ", t->Array.count);
|
||||
ir_print_type(f, m, t->Array.elem);
|
||||
@@ -227,9 +256,30 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
|
||||
case TypeRecord_Union: {
|
||||
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
|
||||
// LLVM takes the first element's alignment as the entire alignment (like C)
|
||||
i64 size_of_union = type_size_of(heap_allocator(), t) - build_context.word_size;
|
||||
i64 align_of_union = type_align_of(heap_allocator(), t);
|
||||
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align_of_union, size_of_union, word_bits);
|
||||
i64 align = type_align_of(heap_allocator(), t);
|
||||
i64 total_size = type_size_of(heap_allocator(), t);
|
||||
#if 1
|
||||
i64 fields_size = 0;
|
||||
if (t->Record.field_count > 0) {
|
||||
type_set_offsets(m->allocator, t);
|
||||
isize end_index = t->Record.field_count-1;
|
||||
i64 end_offset = t->Record.offsets[end_index];
|
||||
isize end_size = type_size_of(m->allocator, t->Record.fields[end_index]->type);
|
||||
fields_size = align_formula(end_offset + end_size, build_context.word_size);
|
||||
}
|
||||
i64 block_size = total_size - fields_size - build_context.word_size;
|
||||
|
||||
ir_fprintf(f, "{[0 x <%lld x i8>], ", align);
|
||||
for (isize i = 0; i < t->Record.field_count; i++) {
|
||||
ir_print_type(f, m, t->Record.fields[i]->type);
|
||||
ir_fprintf(f, ", ");
|
||||
}
|
||||
ir_fprintf(f, "[%lld x i8], ", block_size);
|
||||
ir_fprintf(f, "i%lld}", word_bits);
|
||||
#else
|
||||
i64 block_size = total_size - build_context.word_size;
|
||||
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align, block_size, word_bits);
|
||||
#endif
|
||||
} return;
|
||||
case TypeRecord_RawUnion: {
|
||||
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
|
||||
@@ -247,7 +297,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
|
||||
|
||||
case Type_Named:
|
||||
if (is_type_struct(t) || is_type_union(t)) {
|
||||
String *name = map_string_get(&m->type_names, hash_pointer(t));
|
||||
String *name = map_string_get(&m->entity_names, hash_pointer(t->Named.type_name));
|
||||
GB_ASSERT_MSG(name != NULL, "%.*s", LIT(t->Named.name));
|
||||
ir_print_encoded_local(f, *name);
|
||||
} else {
|
||||
@@ -269,18 +319,15 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
|
||||
}
|
||||
return;
|
||||
case Type_Proc: {
|
||||
if (t->Proc.result_count == 0) {
|
||||
ir_fprintf(f, "void");
|
||||
} else {
|
||||
ir_print_type(f, m, t->Proc.results);
|
||||
}
|
||||
isize param_count = t->Proc.param_count;
|
||||
isize result_count = t->Proc.result_count;
|
||||
ir_print_proc_results(f, m, t);
|
||||
ir_fprintf(f, " (");
|
||||
TypeTuple *params = &t->Proc.params->Tuple;
|
||||
for (isize i = 0; i < t->Proc.param_count; i++) {
|
||||
for (isize i = 0; i < param_count; i++) {
|
||||
if (i > 0) {
|
||||
ir_fprintf(f, ", ");
|
||||
}
|
||||
ir_print_type(f, m, params->variables[i]->type);
|
||||
ir_print_type(f, m, t->Proc.abi_compat_params[i]);
|
||||
}
|
||||
ir_fprintf(f, ")*");
|
||||
} return;
|
||||
@@ -306,7 +353,7 @@ void ir_print_compound_element(irFileBuffer *f, irModule *m, ExactValue v, Type
|
||||
}
|
||||
|
||||
void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type) {
|
||||
type = base_type(base_enum_type(type));
|
||||
type = core_type(type);
|
||||
if (is_type_float(type)) {
|
||||
value = exact_value_to_float(value);
|
||||
} else if (is_type_integer(type)) {
|
||||
@@ -334,7 +381,6 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
||||
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
|
||||
// of the .ll file
|
||||
irValue *str_array = ir_add_global_string_array(m, str);
|
||||
|
||||
ir_fprintf(f, "{i8* getelementptr inbounds (");
|
||||
ir_print_type(f, m, str_array->Global.entity->type);
|
||||
ir_fprintf(f, ", ");
|
||||
@@ -364,8 +410,8 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
||||
}
|
||||
} break;
|
||||
case ExactValue_Float: {
|
||||
GB_ASSERT(is_type_float(type));
|
||||
type = base_type(type);
|
||||
GB_ASSERT_MSG(is_type_float(type), "%s", type_to_string(type));
|
||||
type = core_type(type);
|
||||
u64 u = *cast(u64*)&value.value_float;
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_f32:
|
||||
@@ -382,28 +428,52 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
||||
|
||||
switch (type->Basic.kind) {
|
||||
case 0: break;
|
||||
#if 0
|
||||
case Basic_f16:
|
||||
ir_fprintf(f, "bitcast (");
|
||||
ir_print_type(f, m, t_u16);
|
||||
ir_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float));
|
||||
ir_print_type(f, m, t_f16);
|
||||
ir_fprintf(f, ")");
|
||||
break;
|
||||
case Basic_f128:
|
||||
ir_fprintf(f, "bitcast (");
|
||||
ir_fprintf(f, "i128");
|
||||
// TODO(bill): Actually support f128
|
||||
ir_fprintf(f, " %llu to ", u);
|
||||
ir_print_type(f, m, t_f128);
|
||||
ir_fprintf(f, ")");
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ir_fprintf(f, "0x%016llx", u);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Complex: {
|
||||
type = core_type(type);
|
||||
if (is_type_quaternion(type)) {
|
||||
Type *ft = base_quaternion_elem_type(type);
|
||||
ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
|
||||
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
|
||||
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(0), ft);
|
||||
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(0), ft);
|
||||
ir_fprintf(f, "}");
|
||||
|
||||
} else {
|
||||
GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type));
|
||||
Type *ft = base_complex_elem_type(type);
|
||||
ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
|
||||
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
|
||||
ir_fprintf(f, "}");
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Quaternion: {
|
||||
GB_ASSERT_MSG(is_type_quaternion(type), "%s", type_to_string(type));
|
||||
type = core_type(type);
|
||||
Type *ft = base_quaternion_elem_type(type);
|
||||
ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft);
|
||||
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft);
|
||||
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft);
|
||||
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft);
|
||||
ir_fprintf(f, "}");
|
||||
} break;
|
||||
|
||||
case ExactValue_Pointer:
|
||||
if (value.value_pointer == 0) {
|
||||
ir_fprintf(f, "null");
|
||||
@@ -515,12 +585,15 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
|
||||
}
|
||||
} else {
|
||||
for (isize i = 0; i < value_count; i++) {
|
||||
TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]);
|
||||
GB_ASSERT(tav != NULL);
|
||||
|
||||
Entity *f = type->Record.fields_in_src_order[i];
|
||||
|
||||
values[f->Variable.field_index] = tav->value;
|
||||
if (str_eq(f->token.string, str_lit("_"))) {
|
||||
values[f->Variable.field_index] = (ExactValue){0};
|
||||
} else {
|
||||
TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]);
|
||||
GB_ASSERT(tav != NULL);
|
||||
values[f->Variable.field_index] = tav->value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -616,6 +689,8 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " 0, i32 0), ");
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " %lld, ", cs->count);
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " %lld}", cs->count);
|
||||
}
|
||||
} break;
|
||||
@@ -631,9 +706,10 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
|
||||
Scope *scope = value->Global.entity->scope;
|
||||
bool in_global_scope = false;
|
||||
if (scope != NULL) {
|
||||
// TODO(bill): Fix this rule. What should it be?
|
||||
in_global_scope = scope->is_global || scope->is_init;
|
||||
}
|
||||
ir_print_encoded_global(f, value->Global.entity->token.string, in_global_scope);
|
||||
ir_print_encoded_global(f, ir_get_global_name(m, value), in_global_scope);
|
||||
} break;
|
||||
case irValue_Param:
|
||||
ir_print_encoded_local(f, value->Param.entity->token.string);
|
||||
@@ -681,9 +757,13 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
|
||||
case irInstr_Local: {
|
||||
Type *type = instr->Local.entity->type;
|
||||
i64 align = instr->Local.alignment;
|
||||
if (align <= 0) {
|
||||
align = type_align_of(m->allocator, type);
|
||||
}
|
||||
ir_fprintf(f, "%%%d = alloca ", value->index);
|
||||
ir_print_type(f, m, type);
|
||||
ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
|
||||
ir_fprintf(f, ", align %lld\n", align);
|
||||
} break;
|
||||
|
||||
case irInstr_ZeroInit: {
|
||||
@@ -698,6 +778,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
case irInstr_Store: {
|
||||
Type *type = type_deref(ir_type(instr->Store.address));
|
||||
ir_fprintf(f, "store ");
|
||||
if (instr->Store.atomic) {
|
||||
ir_fprintf(f, "atomic ");
|
||||
}
|
||||
ir_print_type(f, m, type);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_value(f, m, instr->Store.value, type);
|
||||
@@ -705,17 +788,29 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
ir_print_type(f, m, type);
|
||||
ir_fprintf(f, "* ");
|
||||
ir_print_value(f, m, instr->Store.address, type);
|
||||
if (is_type_atomic(type)) {
|
||||
// TODO(bill): Do ordering
|
||||
ir_fprintf(f, " unordered");
|
||||
ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
|
||||
}
|
||||
ir_fprintf(f, "\n");
|
||||
} break;
|
||||
|
||||
case irInstr_Load: {
|
||||
Type *type = instr->Load.type;
|
||||
ir_fprintf(f, "%%%d = load ", value->index);
|
||||
if (is_type_atomic(type)) {
|
||||
ir_fprintf(f, "atomic ");
|
||||
}
|
||||
ir_print_type(f, m, type);
|
||||
ir_fprintf(f, ", ");
|
||||
ir_print_type(f, m, type);
|
||||
ir_fprintf(f, "* ");
|
||||
ir_print_value(f, m, instr->Load.address, type);
|
||||
if (is_type_atomic(type)) {
|
||||
// TODO(bill): Do ordering
|
||||
ir_fprintf(f, " unordered");
|
||||
}
|
||||
ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
|
||||
} break;
|
||||
|
||||
@@ -753,6 +848,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
if (st->Record.custom_align > 0) {
|
||||
index += 1;
|
||||
}
|
||||
} else if (is_type_union(st)) {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
ir_print_type(f, m, type_deref(et));
|
||||
@@ -821,6 +918,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
if (st->Record.custom_align > 0) {
|
||||
index += 1;
|
||||
}
|
||||
} else if (is_type_union(st)) {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -833,6 +932,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
case irInstr_UnionTagPtr: {
|
||||
Type *et = ir_type(instr->UnionTagPtr.address);
|
||||
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
|
||||
Type *t = base_type(type_deref(et));
|
||||
GB_ASSERT(is_type_union(t));
|
||||
|
||||
ir_print_type(f, m, type_deref(et));
|
||||
ir_fprintf(f, ", ");
|
||||
@@ -843,7 +944,11 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " 0, ");
|
||||
ir_print_type(f, m, t_i32);
|
||||
#if 1
|
||||
ir_fprintf(f, " %d", 2 + t->Record.field_count);
|
||||
#else
|
||||
ir_fprintf(f, " %d", 2);
|
||||
#endif
|
||||
ir_fprintf(f, " ; UnionTagPtr");
|
||||
ir_fprintf(f, "\n");
|
||||
} break;
|
||||
@@ -851,11 +956,19 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
case irInstr_UnionTagValue: {
|
||||
Type *et = ir_type(instr->UnionTagValue.address);
|
||||
ir_fprintf(f, "%%%d = extractvalue ", value->index);
|
||||
Type *t = base_type(et);
|
||||
GB_ASSERT(is_type_union(t));
|
||||
|
||||
ir_print_type(f, m, et);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_value(f, m, instr->UnionTagValue.address, et);
|
||||
ir_fprintf(f, ", %d", 2);
|
||||
ir_fprintf(f, ",");
|
||||
#if 1
|
||||
ir_fprintf(f, " %d", 2 + t->Record.field_count);
|
||||
#else
|
||||
ir_fprintf(f, " %d", 2);
|
||||
#endif
|
||||
ir_fprintf(f, ", %d", 2 + t->Record.field_count);
|
||||
ir_fprintf(f, " ; UnionTagValue");
|
||||
ir_fprintf(f, "\n");
|
||||
} break;
|
||||
@@ -942,7 +1055,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
switch (uo->op) {
|
||||
case Token_Sub:
|
||||
if (is_type_float(elem_type)) {
|
||||
ir_print_exact_value(f, m, make_exact_value_float(0), type);
|
||||
ir_print_exact_value(f, m, exact_value_float(0), elem_type);
|
||||
} else {
|
||||
ir_fprintf(f, "0");
|
||||
}
|
||||
@@ -1007,6 +1120,72 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
case Token_LtEq: ir_fprintf(f, "ole"); break;
|
||||
case Token_GtEq: ir_fprintf(f, "oge"); break;
|
||||
}
|
||||
} else if (is_type_complex(elem_type)) {
|
||||
ir_fprintf(f, "call ");
|
||||
ir_print_calling_convention(f, m, ProcCC_Odin);
|
||||
ir_print_type(f, m, t_bool);
|
||||
char *runtime_proc = "";
|
||||
i64 sz = 8*type_size_of(m->allocator, elem_type);
|
||||
switch (sz) {
|
||||
case 64:
|
||||
switch (bo->op) {
|
||||
case Token_CmpEq: runtime_proc = "__complex64_eq"; break;
|
||||
case Token_NotEq: runtime_proc = "__complex64_ne"; break;
|
||||
}
|
||||
break;
|
||||
case 128:
|
||||
switch (bo->op) {
|
||||
case Token_CmpEq: runtime_proc = "__complex128_eq"; break;
|
||||
case Token_NotEq: runtime_proc = "__complex128_ne"; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_encoded_global(f, make_string_c(runtime_proc), false);
|
||||
ir_fprintf(f, "(");
|
||||
ir_print_type(f, m, type);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_value(f, m, bo->left, type);
|
||||
ir_fprintf(f, ", ");
|
||||
ir_print_type(f, m, type);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_value(f, m, bo->right, type);
|
||||
ir_fprintf(f, ")\n");
|
||||
return;
|
||||
} else if (is_type_quaternion(elem_type)) {
|
||||
ir_fprintf(f, "call ");
|
||||
ir_print_calling_convention(f, m, ProcCC_Odin);
|
||||
ir_print_type(f, m, t_bool);
|
||||
char *runtime_proc = "";
|
||||
i64 sz = 8*type_size_of(m->allocator, elem_type);
|
||||
switch (sz) {
|
||||
case 128:
|
||||
switch (bo->op) {
|
||||
case Token_CmpEq: runtime_proc = "__quaternion128_eq"; break;
|
||||
case Token_NotEq: runtime_proc = "__quaternion128_ne"; break;
|
||||
}
|
||||
break;
|
||||
case 256:
|
||||
switch (bo->op) {
|
||||
case Token_CmpEq: runtime_proc = "__quaternion256_eq"; break;
|
||||
case Token_NotEq: runtime_proc = "__quaternion256_ne"; break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_encoded_global(f, make_string_c(runtime_proc), false);
|
||||
ir_fprintf(f, "(");
|
||||
ir_print_type(f, m, type);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_value(f, m, bo->left, type);
|
||||
ir_fprintf(f, ", ");
|
||||
ir_print_type(f, m, type);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_value(f, m, bo->right, type);
|
||||
ir_fprintf(f, ")\n");
|
||||
return;
|
||||
} else {
|
||||
ir_fprintf(f, "icmp ");
|
||||
if (bo->op != Token_CmpEq &&
|
||||
@@ -1082,7 +1261,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
ir_fprintf(f, "call ");
|
||||
ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
|
||||
if (result_type) {
|
||||
ir_print_type(f, m, result_type);
|
||||
ir_print_proc_results(f, m, proc_type);
|
||||
} else {
|
||||
ir_fprintf(f, "void");
|
||||
}
|
||||
@@ -1098,7 +1277,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
for (isize i = 0; i < call->arg_count; i++) {
|
||||
Entity *e = params->variables[i];
|
||||
GB_ASSERT(e != NULL);
|
||||
Type *t = e->type;
|
||||
Type *t = proc_type->Proc.abi_compat_params[i];
|
||||
if (i > 0) {
|
||||
ir_fprintf(f, ", ");
|
||||
}
|
||||
@@ -1197,17 +1376,17 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
ir_fprintf(f, "call void ");
|
||||
ir_print_encoded_global(f, str_lit("__bounds_check_error"), false);
|
||||
ir_fprintf(f, "(");
|
||||
ir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string);
|
||||
ir_print_compound_element(f, m, exact_value_string(bc->pos.file), t_string);
|
||||
ir_fprintf(f, ", ");
|
||||
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int);
|
||||
ir_print_exact_value(f, m, exact_value_integer(bc->pos.line), t_int);
|
||||
ir_fprintf(f, ", ");
|
||||
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int);
|
||||
ir_print_exact_value(f, m, exact_value_integer(bc->pos.column), t_int);
|
||||
ir_fprintf(f, ", ");
|
||||
|
||||
ir_print_type(f, m, t_int);
|
||||
@@ -1232,17 +1411,17 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
}
|
||||
|
||||
ir_fprintf(f, "(");
|
||||
ir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string);
|
||||
ir_print_compound_element(f, m, exact_value_string(bc->pos.file), t_string);
|
||||
ir_fprintf(f, ", ");
|
||||
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int);
|
||||
ir_print_exact_value(f, m, exact_value_integer(bc->pos.line), t_int);
|
||||
ir_fprintf(f, ", ");
|
||||
|
||||
ir_print_type(f, m, t_int);
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int);
|
||||
ir_print_exact_value(f, m, exact_value_integer(bc->pos.column), t_int);
|
||||
ir_fprintf(f, ", ");
|
||||
|
||||
ir_print_type(f, m, t_int);
|
||||
@@ -1311,31 +1490,39 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
||||
|
||||
ir_print_calling_convention(f, m, proc_type->calling_convention);
|
||||
|
||||
if (proc_type->result_count == 0) {
|
||||
ir_fprintf(f, "void");
|
||||
} else {
|
||||
ir_print_type(f, m, proc_type->results);
|
||||
}
|
||||
|
||||
isize param_count = proc_type->param_count;
|
||||
isize result_count = proc_type->result_count;
|
||||
ir_print_proc_results(f, m, proc->type);
|
||||
ir_fprintf(f, " ");
|
||||
|
||||
// #ifndef GB_SYSTEM_WINDOWS
|
||||
#if 0
|
||||
if(uses_args)
|
||||
ir_fprintf(f, "@.nix_argpatch_main");
|
||||
else
|
||||
#endif
|
||||
ir_print_encoded_global(f, proc->name, ir_print_is_proc_global(m, proc));
|
||||
|
||||
ir_fprintf(f, "(");
|
||||
|
||||
if (proc_type->param_count > 0) {
|
||||
if (param_count > 0) {
|
||||
TypeTuple *params = &proc_type->params->Tuple;
|
||||
for (isize i = 0; i < params->variable_count; i++) {
|
||||
Entity *e = params->variables[i];
|
||||
Type *original_type = e->type;
|
||||
Type *abi_type = proc_type->abi_compat_params[i];
|
||||
if (i > 0) {
|
||||
ir_fprintf(f, ", ");
|
||||
}
|
||||
ir_print_type(f, m, e->type);
|
||||
ir_print_type(f, m, abi_type);
|
||||
if (e->flags&EntityFlag_NoAlias) {
|
||||
ir_fprintf(f, " noalias");
|
||||
}
|
||||
if (proc->body != NULL) {
|
||||
if (!str_eq(e->token.string, str_lit("")) &&
|
||||
!str_eq(e->token.string, str_lit("_"))) {
|
||||
ir_fprintf(f, " %%%.*s", LIT(e->token.string));
|
||||
ir_fprintf(f, " ");
|
||||
ir_print_encoded_local(f, e->token.string);
|
||||
} else {
|
||||
ir_fprintf(f, " %%_.param_%td", i);
|
||||
}
|
||||
@@ -1408,10 +1595,6 @@ void print_llvm_ir(irGen *ir) {
|
||||
irFileBuffer buf = {0}, *f = &buf;
|
||||
ir_file_buffer_init(f, &ir->output_file);
|
||||
|
||||
if (m->layout.len > 0) {
|
||||
ir_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout));
|
||||
}
|
||||
|
||||
ir_print_encoded_local(f, str_lit("..string"));
|
||||
ir_fprintf(f, " = type {i8*, ");
|
||||
ir_print_type(f, m, t_int);
|
||||
@@ -1419,11 +1602,21 @@ void print_llvm_ir(irGen *ir) {
|
||||
ir_print_encoded_local(f, str_lit("..rawptr"));
|
||||
ir_fprintf(f, " = type i8* ; Basic_rawptr\n");
|
||||
|
||||
ir_print_encoded_local(f, str_lit("..complex64"));
|
||||
ir_fprintf(f, " = type {float, float} ; Basic_complex64\n");
|
||||
ir_print_encoded_local(f, str_lit("..complex128"));
|
||||
ir_fprintf(f, " = type {double, double} ; Basic_complex128\n");
|
||||
ir_print_encoded_local(f, str_lit("..quaternion128"));
|
||||
ir_fprintf(f, " = type {float, float, float, float} ; Basic_quaternion128\n");
|
||||
ir_print_encoded_local(f, str_lit("..quaternion256"));
|
||||
ir_fprintf(f, " = type {double, double, double, double} ; Basic_quaternion256\n");
|
||||
|
||||
|
||||
ir_print_encoded_local(f, str_lit("..any"));
|
||||
ir_fprintf(f, " = type {");
|
||||
ir_print_type(f, m, t_type_info_ptr);
|
||||
ir_fprintf(f, ", ");
|
||||
ir_print_type(f, m, t_rawptr);
|
||||
ir_fprintf(f, ", ");
|
||||
ir_print_type(f, m, t_type_info_ptr);
|
||||
ir_fprintf(f, "} ; Basic_any\n");
|
||||
|
||||
ir_fprintf(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone \n");
|
||||
@@ -1476,9 +1669,12 @@ void print_llvm_ir(irGen *ir) {
|
||||
Scope *scope = g->entity->scope;
|
||||
bool in_global_scope = false;
|
||||
if (scope != NULL) {
|
||||
// TODO(bill): Fix this rule. What should it be?
|
||||
in_global_scope = scope->is_global || scope->is_init;
|
||||
// in_global_scope = value->Global.name_is_not_mangled;
|
||||
}
|
||||
ir_print_encoded_global(f, g->entity->token.string, in_global_scope);
|
||||
|
||||
ir_print_encoded_global(f, ir_get_global_name(m, v), in_global_scope);
|
||||
ir_fprintf(f, " = ");
|
||||
if (g->is_foreign) {
|
||||
ir_fprintf(f, "external ");
|
||||
|
||||
+142
-13
@@ -2,15 +2,15 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define USE_CUSTOM_BACKEND false
|
||||
|
||||
#include "common.c"
|
||||
#include "timings.c"
|
||||
#include "build_settings.c"
|
||||
#include "tokenizer.c"
|
||||
#include "parser.c"
|
||||
// #include "printer.c"
|
||||
#include "checker.c"
|
||||
// #include "ssa.c"
|
||||
#include "ssa.c"
|
||||
#include "ir.c"
|
||||
#include "ir_opt.c"
|
||||
#include "ir_print.c"
|
||||
@@ -102,6 +102,8 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||
// }
|
||||
|
||||
// exit_code = status;
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -144,6 +146,7 @@ int main(int argc, char **argv) {
|
||||
init_scratch_memory(gb_megabytes(10));
|
||||
init_global_error_collector();
|
||||
|
||||
|
||||
#if 1
|
||||
|
||||
init_build_context();
|
||||
@@ -208,7 +211,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
|
||||
#endif
|
||||
#if 0
|
||||
#if USE_CUSTOM_BACKEND
|
||||
if (global_error_collector.count != 0) {
|
||||
return 1;
|
||||
}
|
||||
@@ -217,7 +220,7 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ssa_generate(&checker.info)) {
|
||||
if (!ssa_generate(&parser, &checker.info)) {
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
@@ -241,17 +244,19 @@ int main(int argc, char **argv) {
|
||||
#if 1
|
||||
timings_start_section(&timings, str_lit("llvm-opt"));
|
||||
|
||||
char const *output_name = ir_gen.output_file.filename;
|
||||
isize base_name_len = gb_path_extension(output_name)-1 - output_name;
|
||||
String output = make_string(cast(u8 *)output_name, base_name_len);
|
||||
String output_name = ir_gen.output_name;
|
||||
String output_base = ir_gen.output_base;
|
||||
int base_name_len = output_base.len;
|
||||
|
||||
i32 optimization_level = 0;
|
||||
optimization_level = gb_clamp(optimization_level, 0, 3);
|
||||
|
||||
i32 exit_code = 0;
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
// For more passes arguments: http://llvm.org/docs/Passes.html
|
||||
exit_code = system_exec_command_line_app("llvm-opt", false,
|
||||
"\"%.*sbin/opt\" \"%s\" -o \"%.*s\".bc "
|
||||
"\"%.*sbin/opt\" \"%.*s\".ll -o \"%.*s\".bc "
|
||||
"-mem2reg "
|
||||
"-memcpyopt "
|
||||
"-die "
|
||||
@@ -260,10 +265,33 @@ int main(int argc, char **argv) {
|
||||
// "-S "
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
output_name, LIT(output));
|
||||
LIT(output_base), LIT(output_base));
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
#else
|
||||
// NOTE(zangent): This is separate because it seems that LLVM tools are packaged
|
||||
// with the Windows version, while they will be system-provided on MacOS and GNU/Linux
|
||||
exit_code = system_exec_command_line_app("llvm-opt", false,
|
||||
"opt \"%.*s\".ll -o \"%.*s\".bc "
|
||||
"-mem2reg "
|
||||
"-memcpyopt "
|
||||
"-die "
|
||||
#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)
|
||||
// make sure to also change the `macosx_version_min` param passed to `llc`
|
||||
"-mtriple=x86_64-apple-macosx10.8 "
|
||||
#endif
|
||||
// "-dse "
|
||||
// "-dce "
|
||||
// "-S "
|
||||
"",
|
||||
LIT(output_base), LIT(output_base));
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
timings_start_section(&timings, str_lit("llvm-llc"));
|
||||
@@ -274,7 +302,7 @@ int main(int argc, char **argv) {
|
||||
// "-debug-pass=Arguments "
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
LIT(output),
|
||||
LIT(output_base),
|
||||
optimization_level,
|
||||
LIT(build_context.llc_flags));
|
||||
if (exit_code != 0) {
|
||||
@@ -310,7 +338,7 @@ int main(int argc, char **argv) {
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(output), LIT(output), output_ext,
|
||||
LIT(output_base), LIT(output_base), output_ext,
|
||||
lib_str, LIT(build_context.link_flags),
|
||||
link_settings
|
||||
);
|
||||
@@ -321,11 +349,112 @@ int main(int argc, char **argv) {
|
||||
// timings_print_all(&timings);
|
||||
|
||||
if (run_output) {
|
||||
system_exec_command_line_app("odin run", false, "%.*s.exe", cast(int)base_name_len, output_name);
|
||||
system_exec_command_line_app("odin run", false, "%.*s.exe", LIT(output_base));
|
||||
}
|
||||
|
||||
#else
|
||||
#error Implement build stuff for this platform
|
||||
|
||||
// NOTE(zangent): Linux / Unix is unfinished and not tested very well.
|
||||
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm-llc"));
|
||||
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
|
||||
exit_code = system_exec_command_line_app("llc", false,
|
||||
"llc \"%.*s.bc\" -filetype=obj -O%d "
|
||||
"%.*s "
|
||||
// "-debug-pass=Arguments "
|
||||
"",
|
||||
LIT(output_base),
|
||||
optimization_level,
|
||||
LIT(build_context.llc_flags));
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
timings_start_section(&timings, str_lit("ld-link"));
|
||||
|
||||
gbString lib_str = gb_string_make(heap_allocator(), "");
|
||||
// defer (gb_string_free(lib_str));
|
||||
char lib_str_buf[1024] = {0};
|
||||
for_array(i, ir_gen.module.foreign_library_paths) {
|
||||
String lib = ir_gen.module.foreign_library_paths.e[i];
|
||||
|
||||
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
|
||||
// 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)
|
||||
isize len;
|
||||
if(lib.len > 2 && lib.text[0] == '-' && lib.text[1] == 'f') {
|
||||
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||
" -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
|
||||
} else {
|
||||
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||
" -l%.*s ", LIT(lib));
|
||||
}
|
||||
#else
|
||||
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||
" -l%.*s ", LIT(lib));
|
||||
#endif
|
||||
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
||||
}
|
||||
|
||||
// Unlike the Win32 linker code, the output_ext includes the dot, because
|
||||
// typically executable files on *NIX systems don't have extensions.
|
||||
char *output_ext = "";
|
||||
char *link_settings = "";
|
||||
char *linker;
|
||||
if (build_context.is_dll) {
|
||||
// Shared libraries are .dylib on MacOS and .so on Linux.
|
||||
// TODO(zangent): Is that statement entirely truthful?
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
output_ext = ".dylib";
|
||||
#else
|
||||
output_ext = ".so";
|
||||
#endif
|
||||
|
||||
link_settings = "-shared";
|
||||
} else {
|
||||
// TODO: Do I need anything here?
|
||||
link_settings = "";
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_OSX)
|
||||
linker = "ld";
|
||||
#else
|
||||
// TODO(zangent): Figure out how to make ld work on Linux.
|
||||
// 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_.
|
||||
linker = "clang -Wno-unused-command-line-argument";
|
||||
#endif
|
||||
|
||||
exit_code = system_exec_command_line_app("ld-link", true,
|
||||
"%s \"%.*s\".o -o \"%.*s%s\" %s "
|
||||
"-lc -lm "
|
||||
" %.*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)
|
||||
// make sure to also change the `mtriple` param passed to `opt`
|
||||
" -macosx_version_min 10.8.0 "
|
||||
// This points the linker to where the entry point is
|
||||
" -e _main "
|
||||
#endif
|
||||
, linker, LIT(output_base), LIT(output_base), output_ext,
|
||||
lib_str, LIT(build_context.link_flags),
|
||||
link_settings
|
||||
);
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
// timings_print_all(&timings);
|
||||
|
||||
if (run_output) {
|
||||
system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base));
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+287
-198
@@ -17,6 +17,8 @@ typedef enum ParseFileError {
|
||||
|
||||
typedef Array(AstNode *) AstNodeArray;
|
||||
|
||||
gb_global i32 global_file_id = 0;
|
||||
|
||||
typedef struct AstFile {
|
||||
i32 id;
|
||||
gbArena arena;
|
||||
@@ -30,6 +32,8 @@ typedef struct AstFile {
|
||||
// < 0: In Control Clause
|
||||
// NOTE(bill): Used to prevent type literals in control clauses
|
||||
isize expr_level;
|
||||
bool allow_range;
|
||||
bool ignore_operand;
|
||||
|
||||
AstNodeArray decls;
|
||||
bool is_global_scope;
|
||||
@@ -65,6 +69,8 @@ typedef enum ProcTag {
|
||||
ProcTag_bounds_check = 1<<0,
|
||||
ProcTag_no_bounds_check = 1<<1,
|
||||
|
||||
ProcTag_require_results = 1<<4,
|
||||
|
||||
ProcTag_foreign = 1<<10,
|
||||
ProcTag_export = 1<<11,
|
||||
ProcTag_link_name = 1<<12,
|
||||
@@ -103,6 +109,7 @@ typedef enum FieldFlag {
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_immutable,
|
||||
} FieldListTag;
|
||||
|
||||
|
||||
AstNodeArray make_ast_node_array(AstFile *f) {
|
||||
AstNodeArray a;
|
||||
// array_init(&a, gb_arena_allocator(&f->arena));
|
||||
@@ -139,6 +146,10 @@ AstNodeArray make_ast_node_array(AstFile *f) {
|
||||
AstNodeArray elems; \
|
||||
Token open, close; \
|
||||
}) \
|
||||
AST_NODE_KIND(Alias, "alias", struct { \
|
||||
Token token; \
|
||||
AstNode *expr; \
|
||||
}) \
|
||||
AST_NODE_KIND(_ExprBegin, "", i32) \
|
||||
AST_NODE_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \
|
||||
AST_NODE_KIND(TagExpr, "tag expression", struct { Token token, name; AstNode *expr; }) \
|
||||
@@ -149,9 +160,11 @@ AST_NODE_KIND(_ExprBegin, "", i32) \
|
||||
AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \
|
||||
AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \
|
||||
AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \
|
||||
AST_NODE_KIND(SliceExpr, "slice expression", struct { \
|
||||
AST_NODE_KIND(SliceExpr, "slice expression", struct { \
|
||||
AstNode *expr; \
|
||||
Token open, close; \
|
||||
Token interval0; \
|
||||
Token interval1; \
|
||||
bool index3; \
|
||||
AstNode *low, *high, *max; \
|
||||
}) \
|
||||
@@ -169,10 +182,9 @@ AST_NODE_KIND(_ExprBegin, "", i32) \
|
||||
Token open; \
|
||||
Token close; \
|
||||
}) \
|
||||
AST_NODE_KIND(CastExpr, "cast expression", struct { Token token; AstNode *type, *expr; Token open, close; }) \
|
||||
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
|
||||
AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \
|
||||
AST_NODE_KIND(IntervalExpr, "interval expression", struct { Token op; AstNode *left, *right; }) \
|
||||
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
|
||||
AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \
|
||||
AST_NODE_KIND(TypeAssertion, "type assertion", struct { AstNode *expr; Token dot; AstNode *type; }) \
|
||||
AST_NODE_KIND(_ExprEnd, "", i32) \
|
||||
AST_NODE_KIND(_StmtBegin, "", i32) \
|
||||
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
|
||||
@@ -215,6 +227,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
}) \
|
||||
AST_NODE_KIND(ForStmt, "for statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *init; \
|
||||
AstNode *cond; \
|
||||
AstNode *post; \
|
||||
@@ -222,6 +235,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
}) \
|
||||
AST_NODE_KIND(RangeStmt, "range statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *value; \
|
||||
AstNode *index; \
|
||||
Token in_token; \
|
||||
@@ -235,20 +249,22 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
|
||||
}) \
|
||||
AST_NODE_KIND(MatchStmt, "match statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *init; \
|
||||
AstNode *tag; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(TypeMatchStmt, "type match statement", struct { \
|
||||
Token token; \
|
||||
AstNode *label; \
|
||||
AstNode *tag; \
|
||||
AstNode *body; \
|
||||
}) \
|
||||
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \
|
||||
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \
|
||||
AST_NODE_KIND(UsingStmt, "using statement", struct { \
|
||||
Token token; \
|
||||
AstNode *node; \
|
||||
AstNodeArray list; \
|
||||
}) \
|
||||
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
|
||||
Token string; \
|
||||
@@ -301,6 +317,10 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
|
||||
AstNode *cond; \
|
||||
bool is_system; \
|
||||
}) \
|
||||
AST_NODE_KIND(Label, "label", struct { \
|
||||
Token token; \
|
||||
AstNode *name; \
|
||||
}) \
|
||||
AST_NODE_KIND(_DeclEnd, "", i32) \
|
||||
AST_NODE_KIND(Field, "field", struct { \
|
||||
AstNodeArray names; \
|
||||
@@ -331,6 +351,10 @@ AST_NODE_KIND(_TypeBegin, "", i32) \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
}) \
|
||||
AST_NODE_KIND(AtomicType, "atomic type", struct { \
|
||||
Token token; \
|
||||
AstNode *type; \
|
||||
}) \
|
||||
AST_NODE_KIND(ArrayType, "array type", struct { \
|
||||
Token token; \
|
||||
AstNode *count; \
|
||||
@@ -398,7 +422,6 @@ String const ast_node_strings[] = {
|
||||
|
||||
typedef struct AstNode {
|
||||
AstNodeKind kind;
|
||||
// AstNode *prev, *next; // NOTE(bill): allow for Linked list
|
||||
u32 stmt_state_flags;
|
||||
union {
|
||||
#define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_;
|
||||
@@ -410,7 +433,9 @@ typedef struct AstNode {
|
||||
|
||||
#define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_))
|
||||
#define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_);
|
||||
#ifndef case_end
|
||||
#define case_end } break;
|
||||
#endif
|
||||
|
||||
|
||||
gb_inline bool is_ast_node_expr(AstNode *node) {
|
||||
@@ -445,6 +470,8 @@ Token ast_node_token(AstNode *node) {
|
||||
return ast_node_token(node->CompoundLit.type);
|
||||
}
|
||||
return node->CompoundLit.open;
|
||||
case AstNode_Alias: return node->Alias.token;
|
||||
|
||||
case AstNode_TagExpr: return node->TagExpr.token;
|
||||
case AstNode_RunExpr: return node->RunExpr.token;
|
||||
case AstNode_BadExpr: return node->BadExpr.begin;
|
||||
@@ -458,14 +485,13 @@ Token ast_node_token(AstNode *node) {
|
||||
return ast_node_token(node->SelectorExpr.selector);
|
||||
}
|
||||
return node->SelectorExpr.token;
|
||||
case AstNode_IndexExpr: return node->IndexExpr.open;
|
||||
case AstNode_SliceExpr: return node->SliceExpr.open;
|
||||
case AstNode_Ellipsis: return node->Ellipsis.token;
|
||||
case AstNode_CastExpr: return node->CastExpr.token;
|
||||
case AstNode_FieldValue: return node->FieldValue.eq;
|
||||
case AstNode_DerefExpr: return node->DerefExpr.op;
|
||||
case AstNode_TernaryExpr: return ast_node_token(node->TernaryExpr.cond);
|
||||
case AstNode_IntervalExpr: return ast_node_token(node->IntervalExpr.left);
|
||||
case AstNode_IndexExpr: return node->IndexExpr.open;
|
||||
case AstNode_SliceExpr: return node->SliceExpr.open;
|
||||
case AstNode_Ellipsis: return node->Ellipsis.token;
|
||||
case AstNode_FieldValue: return node->FieldValue.eq;
|
||||
case AstNode_DerefExpr: return node->DerefExpr.op;
|
||||
case AstNode_TernaryExpr: return ast_node_token(node->TernaryExpr.cond);
|
||||
case AstNode_TypeAssertion: return ast_node_token(node->TypeAssertion.expr);
|
||||
|
||||
case AstNode_BadStmt: return node->BadStmt.begin;
|
||||
case AstNode_EmptyStmt: return node->EmptyStmt.token;
|
||||
@@ -492,6 +518,7 @@ Token ast_node_token(AstNode *node) {
|
||||
case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names.e[0]);
|
||||
case AstNode_ImportDecl: return node->ImportDecl.token;
|
||||
case AstNode_ForeignLibrary: return node->ForeignLibrary.token;
|
||||
case AstNode_Label: return node->Label.token;
|
||||
|
||||
|
||||
case AstNode_Field:
|
||||
@@ -507,6 +534,7 @@ Token ast_node_token(AstNode *node) {
|
||||
case AstNode_HelperType: return node->HelperType.token;
|
||||
case AstNode_ProcType: return node->ProcType.token;
|
||||
case AstNode_PointerType: return node->PointerType.token;
|
||||
case AstNode_AtomicType: return node->AtomicType.token;
|
||||
case AstNode_ArrayType: return node->ArrayType.token;
|
||||
case AstNode_DynamicArrayType: return node->DynamicArrayType.token;
|
||||
case AstNode_VectorType: return node->VectorType.token;
|
||||
@@ -668,11 +696,13 @@ AstNode *ast_index_expr(AstFile *f, AstNode *expr, AstNode *index, Token open, T
|
||||
}
|
||||
|
||||
|
||||
AstNode *ast_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, bool index3, AstNode *low, AstNode *high, AstNode *max) {
|
||||
AstNode *ast_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, Token interval0, Token interval1, bool index3, AstNode *low, AstNode *high, AstNode *max) {
|
||||
AstNode *result = make_ast_node(f, AstNode_SliceExpr);
|
||||
result->SliceExpr.expr = expr;
|
||||
result->SliceExpr.open = open;
|
||||
result->SliceExpr.close = close;
|
||||
result->SliceExpr.interval0 = interval0;
|
||||
result->SliceExpr.interval1 = interval1;
|
||||
result->SliceExpr.index3 = index3;
|
||||
result->SliceExpr.low = low;
|
||||
result->SliceExpr.high = high;
|
||||
@@ -687,16 +717,6 @@ AstNode *ast_deref_expr(AstFile *f, AstNode *expr, Token op) {
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_interval_expr(AstFile *f, Token op, AstNode *left, AstNode *right) {
|
||||
AstNode *result = make_ast_node(f, AstNode_IntervalExpr);
|
||||
|
||||
result->IntervalExpr.op = op;
|
||||
result->IntervalExpr.left = left;
|
||||
result->IntervalExpr.right = right;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -753,16 +773,6 @@ AstNode *ast_field_value(AstFile *f, AstNode *field, AstNode *value, Token eq) {
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_cast_expr(AstFile *f, Token token, AstNode *type, AstNode *expr, Token open, Token close) {
|
||||
AstNode *result = make_ast_node(f, AstNode_CastExpr);
|
||||
result->CastExpr.token = token;
|
||||
result->CastExpr.type = type;
|
||||
result->CastExpr.expr = expr;
|
||||
result->CastExpr.open = open;
|
||||
result->CastExpr.close = close;
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token open, Token close) {
|
||||
AstNode *result = make_ast_node(f, AstNode_CompoundLit);
|
||||
result->CompoundLit.type = type;
|
||||
@@ -771,6 +781,13 @@ AstNode *ast_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token o
|
||||
result->CompoundLit.close = close;
|
||||
return result;
|
||||
}
|
||||
AstNode *ast_alias(AstFile *f, Token token, AstNode *expr) {
|
||||
AstNode *result = make_ast_node(f, AstNode_Alias);
|
||||
result->Alias.token = token;
|
||||
result->Alias.expr = expr;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AstNode *ast_ternary_expr(AstFile *f, AstNode *cond, AstNode *x, AstNode *y) {
|
||||
AstNode *result = make_ast_node(f, AstNode_TernaryExpr);
|
||||
@@ -779,6 +796,14 @@ AstNode *ast_ternary_expr(AstFile *f, AstNode *cond, AstNode *x, AstNode *y) {
|
||||
result->TernaryExpr.y = y;
|
||||
return result;
|
||||
}
|
||||
AstNode *ast_type_assertion(AstFile *f, AstNode *expr, Token dot, AstNode *type) {
|
||||
AstNode *result = make_ast_node(f, AstNode_TypeAssertion);
|
||||
result->TypeAssertion.expr = expr;
|
||||
result->TypeAssertion.dot = dot;
|
||||
result->TypeAssertion.type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -910,19 +935,21 @@ AstNode *ast_defer_stmt(AstFile *f, Token token, AstNode *stmt) {
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_branch_stmt(AstFile *f, Token token) {
|
||||
AstNode *ast_branch_stmt(AstFile *f, Token token, AstNode *label) {
|
||||
AstNode *result = make_ast_node(f, AstNode_BranchStmt);
|
||||
result->BranchStmt.token = token;
|
||||
result->BranchStmt.label = label;
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_using_stmt(AstFile *f, Token token, AstNode *node) {
|
||||
AstNode *ast_using_stmt(AstFile *f, Token token, AstNodeArray list) {
|
||||
AstNode *result = make_ast_node(f, AstNode_UsingStmt);
|
||||
result->UsingStmt.token = token;
|
||||
result->UsingStmt.node = node;
|
||||
result->UsingStmt.list = list;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) {
|
||||
AstNode *result = make_ast_node(f, AstNode_AsmOperand);
|
||||
result->AsmOperand.string = string;
|
||||
@@ -1022,6 +1049,13 @@ AstNode *ast_pointer_type(AstFile *f, Token token, AstNode *type) {
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_atomic_type(AstFile *f, Token token, AstNode *type) {
|
||||
AstNode *result = make_ast_node(f, AstNode_AtomicType);
|
||||
result->AtomicType.token = token;
|
||||
result->AtomicType.type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem) {
|
||||
AstNode *result = make_ast_node(f, AstNode_ArrayType);
|
||||
result->ArrayType.token = token;
|
||||
@@ -1124,6 +1158,13 @@ AstNode *ast_foreign_library(AstFile *f, Token token, Token filepath, Token libr
|
||||
return result;
|
||||
}
|
||||
|
||||
AstNode *ast_label_decl(AstFile *f, Token token, AstNode *name) {
|
||||
AstNode *result = make_ast_node(f, AstNode_Label);
|
||||
result->Label.token = token;
|
||||
result->Label.name = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
bool next_token(AstFile *f) {
|
||||
Token prev = f->curr_token;
|
||||
@@ -1165,7 +1206,11 @@ Token expect_token(AstFile *f, TokenKind kind) {
|
||||
syntax_error(f->curr_token, "Expected `%.*s`, got `%.*s`",
|
||||
LIT(token_strings[kind]),
|
||||
LIT(token_strings[prev.kind]));
|
||||
if (prev.kind == Token_EOF) {
|
||||
gb_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
next_token(f);
|
||||
return prev;
|
||||
}
|
||||
@@ -1189,6 +1234,9 @@ Token expect_operator(AstFile *f) {
|
||||
if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
|
||||
syntax_error(f->curr_token, "Expected an operator, got `%.*s`",
|
||||
LIT(token_strings[prev.kind]));
|
||||
} else if (!f->allow_range && (prev.kind == Token_Ellipsis || prev.kind == Token_HalfClosed)) {
|
||||
syntax_error(f->curr_token, "Expected an non-range operator, got `%.*s`",
|
||||
LIT(token_strings[prev.kind]));
|
||||
}
|
||||
next_token(f);
|
||||
return prev;
|
||||
@@ -1293,6 +1341,9 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) {
|
||||
case AstNode_PointerType:
|
||||
return is_semicolon_optional_for_node(f, s->PointerType.type);
|
||||
|
||||
case AstNode_AtomicType:
|
||||
return is_semicolon_optional_for_node(f, s->AtomicType.type);
|
||||
|
||||
case AstNode_StructType:
|
||||
case AstNode_UnionType:
|
||||
case AstNode_RawUnionType:
|
||||
@@ -1534,6 +1585,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, AstNode **foreign_library_token, Str
|
||||
expect_token(f, Token_String);
|
||||
}
|
||||
}
|
||||
ELSE_IF_ADD_TAG(require_results)
|
||||
ELSE_IF_ADD_TAG(export)
|
||||
ELSE_IF_ADD_TAG(bounds_check)
|
||||
ELSE_IF_ADD_TAG(no_bounds_check)
|
||||
@@ -1692,17 +1744,14 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
||||
AstNode *operand = NULL; // Operand
|
||||
switch (f->curr_token.kind) {
|
||||
case Token_Ident:
|
||||
operand = parse_ident(f);
|
||||
if (!lhs) {
|
||||
// TODO(bill): Handle?
|
||||
}
|
||||
return operand;
|
||||
return parse_ident(f);
|
||||
|
||||
case Token_context:
|
||||
return ast_implicit(f, expect_token(f, Token_context));
|
||||
|
||||
case Token_Integer:
|
||||
case Token_Float:
|
||||
case Token_Imag:
|
||||
case Token_Rune:
|
||||
operand = ast_basic_lit(f, f->curr_token);
|
||||
next_token(f);
|
||||
@@ -1762,6 +1811,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
||||
} else if (str_eq(name.string, str_lit("line"))) { return ast_basic_directive(f, token, name.string);
|
||||
} else if (str_eq(name.string, str_lit("procedure"))) { return ast_basic_directive(f, token, name.string);
|
||||
} else if (str_eq(name.string, str_lit("type"))) { return ast_helper_type(f, token, parse_type(f));
|
||||
} else if (!lhs && str_eq(name.string, str_lit("alias"))) { return ast_alias(f, token, parse_expr(f, false));
|
||||
} else {
|
||||
operand = ast_tag_expr(f, token, name, parse_expr(f, false));
|
||||
}
|
||||
@@ -1800,17 +1850,6 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// case Token_if:
|
||||
// if (!lhs && f->expr_level >= 0) {
|
||||
// return parse_if_expr(f);
|
||||
// }
|
||||
// break;
|
||||
// case Token_OpenBrace:
|
||||
// if (!lhs && f->expr_level >= 0) {
|
||||
// return parse_block_expr(f);
|
||||
// }
|
||||
// break;
|
||||
|
||||
default: {
|
||||
AstNode *type = parse_type_or_ident(f);
|
||||
if (type != NULL) {
|
||||
@@ -1824,10 +1863,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
||||
}
|
||||
}
|
||||
|
||||
Token begin = f->curr_token;
|
||||
syntax_error(begin, "Expected an operand");
|
||||
fix_advance_to_next_stmt(f);
|
||||
return ast_bad_expr(f, begin, f->curr_token);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool is_literal_type(AstNode *node) {
|
||||
@@ -1912,6 +1948,12 @@ AstNode *parse_macro_call_expr(AstFile *f, AstNode *operand) {
|
||||
|
||||
AstNode *parse_atom_expr(AstFile *f, bool lhs) {
|
||||
AstNode *operand = parse_operand(f, lhs);
|
||||
if (operand == NULL) {
|
||||
Token begin = f->curr_token;
|
||||
syntax_error(begin, "Expected an operand");
|
||||
fix_advance_to_next_stmt(f);
|
||||
operand = ast_bad_expr(f, begin, f->curr_token);
|
||||
}
|
||||
|
||||
bool loop = true;
|
||||
while (loop) {
|
||||
@@ -1933,6 +1975,13 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
|
||||
// case Token_Integer:
|
||||
// operand = ast_selector_expr(f, token, operand, parse_expr(f, lhs));
|
||||
// break;
|
||||
case Token_OpenParen: {
|
||||
Token open = expect_token(f, Token_OpenParen);
|
||||
AstNode *type = parse_type(f);
|
||||
Token close = expect_token(f, Token_CloseParen);
|
||||
operand = ast_type_assertion(f, operand, token, type);
|
||||
} break;
|
||||
|
||||
default:
|
||||
syntax_error(f->curr_token, "Expected a selector");
|
||||
next_token(f);
|
||||
@@ -1946,6 +1995,9 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
|
||||
if (lhs) {
|
||||
// TODO(bill): Handle this
|
||||
}
|
||||
bool prev_allow_range = f->allow_range;
|
||||
f->allow_range = false;
|
||||
|
||||
Token open = {0}, close = {0}, interval = {0};
|
||||
AstNode *indices[3] = {0};
|
||||
isize ellipsis_count = 0;
|
||||
@@ -1954,15 +2006,19 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
|
||||
f->expr_level++;
|
||||
open = expect_token(f, Token_OpenBracket);
|
||||
|
||||
if (f->curr_token.kind != Token_Ellipsis) {
|
||||
if (f->curr_token.kind != Token_Ellipsis &&
|
||||
f->curr_token.kind != Token_HalfClosed) {
|
||||
indices[0] = parse_expr(f, false);
|
||||
}
|
||||
bool is_index = true;
|
||||
|
||||
while (f->curr_token.kind == Token_Ellipsis && ellipsis_count < gb_count_of(ellipses)) {
|
||||
while ((f->curr_token.kind == Token_Ellipsis ||
|
||||
f->curr_token.kind == Token_HalfClosed)
|
||||
&& ellipsis_count < gb_count_of(ellipses)) {
|
||||
ellipses[ellipsis_count++] = f->curr_token;
|
||||
next_token(f);
|
||||
if (f->curr_token.kind != Token_Ellipsis &&
|
||||
f->curr_token.kind != Token_HalfClosed &&
|
||||
f->curr_token.kind != Token_CloseBracket &&
|
||||
f->curr_token.kind != Token_EOF) {
|
||||
indices[ellipsis_count] = parse_expr(f, false);
|
||||
@@ -1987,10 +2043,12 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
|
||||
indices[2] = ast_bad_expr(f, ellipses[1], close);
|
||||
}
|
||||
}
|
||||
operand = ast_slice_expr(f, operand, open, close, index3, indices[0], indices[1], indices[2]);
|
||||
operand = ast_slice_expr(f, operand, open, close, ellipses[0], ellipses[1], index3, indices[0], indices[1], indices[2]);
|
||||
} else {
|
||||
operand = ast_index_expr(f, operand, indices[0], open, close);
|
||||
}
|
||||
|
||||
f->allow_range = prev_allow_range;
|
||||
} break;
|
||||
|
||||
case Token_Pointer: // Deference
|
||||
@@ -2020,33 +2078,33 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
|
||||
AstNode *parse_unary_expr(AstFile *f, bool lhs) {
|
||||
switch (f->curr_token.kind) {
|
||||
|
||||
case Token_cast:
|
||||
case Token_transmute:
|
||||
case Token_down_cast:
|
||||
case Token_union_cast:
|
||||
{
|
||||
Token token = f->curr_token; next_token(f);
|
||||
Token open = expect_token(f, Token_OpenParen);
|
||||
AstNode *type = parse_type(f);
|
||||
Token close = expect_token(f, Token_CloseParen);
|
||||
AstNode *expr = parse_unary_expr(f, lhs);
|
||||
return ast_cast_expr(f, token, type, expr, open, close);
|
||||
} break;
|
||||
// case Token_cast:
|
||||
// case Token_transmute:
|
||||
// case Token_union_cast:
|
||||
// {
|
||||
// Token token = f->curr_token; next_token(f);
|
||||
// Token open = expect_token(f, Token_OpenParen);
|
||||
// AstNode *type = parse_type(f);
|
||||
// Token close = expect_token(f, Token_CloseParen);
|
||||
// AstNode *expr = parse_unary_expr(f, lhs);
|
||||
// return ast_cast_expr(f, token, type, expr, open, close);
|
||||
// } break;
|
||||
|
||||
// case Token_Pointer: {
|
||||
// Token op = f->curr_token;
|
||||
// next_token(f);
|
||||
// AstNode *expr = parse_unary_expr(f, lhs);
|
||||
// if (is_ast_node_type(expr)) {
|
||||
// return ast_pointer_type(f, op, expr);
|
||||
// }
|
||||
// return ast_unary_expr(f, op, expr);
|
||||
// } break;
|
||||
|
||||
case Token_Pointer: {
|
||||
Token op = f->curr_token;
|
||||
next_token(f);
|
||||
AstNode *expr = parse_unary_expr(f, lhs);
|
||||
if (is_ast_node_type(expr)) {
|
||||
return ast_pointer_type(f, op, expr);
|
||||
}
|
||||
return ast_unary_expr(f, op, expr);
|
||||
} break;
|
||||
// case Token_Maybe:
|
||||
case Token_Add:
|
||||
case Token_Sub:
|
||||
case Token_Not:
|
||||
case Token_Xor: {
|
||||
case Token_Xor:
|
||||
case Token_And: {
|
||||
Token op = f->curr_token;
|
||||
next_token(f);
|
||||
return ast_unary_expr(f, op, parse_unary_expr(f, lhs));
|
||||
@@ -2056,27 +2114,49 @@ AstNode *parse_unary_expr(AstFile *f, bool lhs) {
|
||||
return parse_atom_expr(f, lhs);
|
||||
}
|
||||
|
||||
bool is_ast_node_a_range(AstNode *expr) {
|
||||
if (expr == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (expr->kind != AstNode_BinaryExpr) {
|
||||
return false;
|
||||
}
|
||||
TokenKind op = expr->BinaryExpr.op.kind;
|
||||
switch (op) {
|
||||
case Token_Ellipsis:
|
||||
case Token_HalfClosed:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// NOTE(bill): result == priority
|
||||
i32 token_precedence(TokenKind t) {
|
||||
i32 token_precedence(AstFile *f, TokenKind t) {
|
||||
switch (t) {
|
||||
case Token_Question:
|
||||
return 1;
|
||||
case Token_Ellipsis:
|
||||
case Token_HalfClosed:
|
||||
if (f->allow_range) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
case Token_CmpOr:
|
||||
return 2;
|
||||
case Token_CmpAnd:
|
||||
return 3;
|
||||
case Token_CmpAnd:
|
||||
return 4;
|
||||
case Token_CmpEq:
|
||||
case Token_NotEq:
|
||||
case Token_Lt:
|
||||
case Token_Gt:
|
||||
case Token_LtEq:
|
||||
case Token_GtEq:
|
||||
return 4;
|
||||
return 5;
|
||||
case Token_Add:
|
||||
case Token_Sub:
|
||||
case Token_Or:
|
||||
case Token_Xor:
|
||||
return 5;
|
||||
return 6;
|
||||
case Token_Mul:
|
||||
case Token_Quo:
|
||||
case Token_Mod:
|
||||
@@ -2084,17 +2164,17 @@ i32 token_precedence(TokenKind t) {
|
||||
case Token_AndNot:
|
||||
case Token_Shl:
|
||||
case Token_Shr:
|
||||
return 6;
|
||||
return 7;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
|
||||
AstNode *expr = parse_unary_expr(f, lhs);
|
||||
for (i32 prec = token_precedence(f->curr_token.kind); prec >= prec_in; prec--) {
|
||||
for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) {
|
||||
for (;;) {
|
||||
Token op = f->curr_token;
|
||||
i32 op_prec = token_precedence(op.kind);
|
||||
i32 op_prec = token_precedence(f, op.kind);
|
||||
if (op_prec != prec) {
|
||||
// NOTE(bill): This will also catch operators that are not valid "binary" operators
|
||||
break;
|
||||
@@ -2110,7 +2190,7 @@ AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
|
||||
expr = ast_ternary_expr(f, cond, x, y);
|
||||
} else {
|
||||
AstNode *right = parse_binary_expr(f, false, prec+1);
|
||||
if (!right) {
|
||||
if (right == NULL) {
|
||||
syntax_error(op, "Expected expression on the right-hand side of the binary operator");
|
||||
}
|
||||
expr = ast_binary_expr(f, op, expr, right);
|
||||
@@ -2129,7 +2209,7 @@ AstNode *parse_expr(AstFile *f, bool lhs) {
|
||||
|
||||
AstNodeArray parse_expr_list(AstFile *f, bool lhs) {
|
||||
AstNodeArray list = make_ast_node_array(f);
|
||||
do {
|
||||
for (;;) {
|
||||
AstNode *e = parse_expr(f, lhs);
|
||||
array_add(&list, e);
|
||||
if (f->curr_token.kind != Token_Comma ||
|
||||
@@ -2137,7 +2217,7 @@ AstNodeArray parse_expr_list(AstFile *f, bool lhs) {
|
||||
break;
|
||||
}
|
||||
next_token(f);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
@@ -2238,6 +2318,7 @@ AstNode *parse_value_decl(AstFile *f, AstNodeArray lhs) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
AstNode *parse_simple_stmt(AstFile *f, bool in_stmt_ok) {
|
||||
AstNodeArray lhs = parse_lhs_expr_list(f);
|
||||
Token token = f->curr_token;
|
||||
@@ -2273,15 +2354,10 @@ AstNode *parse_simple_stmt(AstFile *f, bool in_stmt_ok) {
|
||||
case Token_in:
|
||||
if (in_stmt_ok) {
|
||||
allow_token(f, Token_in);
|
||||
bool prev_allow_range = f->allow_range;
|
||||
f->allow_range = true;
|
||||
AstNode *expr = parse_expr(f, false);
|
||||
switch (f->curr_token.kind) {
|
||||
case Token_Ellipsis: {
|
||||
Token op = f->curr_token;
|
||||
next_token(f);
|
||||
AstNode *right = parse_expr(f, false);
|
||||
expr = ast_interval_expr(f, op, expr, right);
|
||||
} break;
|
||||
}
|
||||
f->allow_range = prev_allow_range;
|
||||
|
||||
AstNodeArray rhs = {0};
|
||||
array_init_count(&rhs, heap_allocator(), 1);
|
||||
@@ -2320,7 +2396,7 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
|
||||
return parse_body(f);
|
||||
}
|
||||
|
||||
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow);
|
||||
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow);
|
||||
|
||||
|
||||
AstNode *parse_results(AstFile *f) {
|
||||
@@ -2339,7 +2415,7 @@ AstNode *parse_results(AstFile *f) {
|
||||
|
||||
AstNode *list = NULL;
|
||||
expect_token(f, Token_OpenParen);
|
||||
list = parse_field_list(f, NULL, 0, Token_Comma, Token_CloseParen);
|
||||
list = parse_field_list(f, NULL, 0, Token_CloseParen);
|
||||
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||
return list;
|
||||
}
|
||||
@@ -2350,7 +2426,7 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
|
||||
|
||||
Token proc_token = expect_token(f, Token_proc);
|
||||
expect_token(f, Token_OpenParen);
|
||||
params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen);
|
||||
params = parse_field_list(f, NULL, FieldFlag_Signature, Token_CloseParen);
|
||||
expect_token_after(f, Token_CloseParen, "parameter list");
|
||||
results = parse_results(f);
|
||||
|
||||
@@ -2369,17 +2445,6 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
|
||||
return ast_proc_type(f, proc_token, params, results, tags, cc);
|
||||
}
|
||||
|
||||
bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
|
||||
if (separator == Token_Semicolon) {
|
||||
expect_semicolon(f, param);
|
||||
} else {
|
||||
if (!allow_token(f, separator)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
|
||||
if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
|
||||
Token tok = f->curr_token;
|
||||
@@ -2490,7 +2555,22 @@ AstNodeArray convert_to_ident_list(AstFile *f, AstNodeAndFlagsArray list, bool i
|
||||
return idents;
|
||||
}
|
||||
|
||||
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow) {
|
||||
|
||||
bool parse_expect_field_separator(AstFile *f, AstNode *param) {
|
||||
Token token = f->curr_token;
|
||||
if (allow_token(f, Token_Comma)) {
|
||||
return true;
|
||||
}
|
||||
if (token.kind == Token_Semicolon) {
|
||||
next_token(f);
|
||||
error(f->curr_token, "Expected a comma, got a semicolon");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow) {
|
||||
TokenKind separator = Token_Comma;
|
||||
Token start_token = f->curr_token;
|
||||
|
||||
AstNodeArray params = make_ast_node_array(f);
|
||||
@@ -2528,7 +2608,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
||||
AstNode *param = ast_field(f, names, type, set_flags);
|
||||
array_add(¶ms, param);
|
||||
|
||||
parse_expect_separator(f, separator, type);
|
||||
parse_expect_field_separator(f, type);
|
||||
|
||||
while (f->curr_token.kind != follow &&
|
||||
f->curr_token.kind != Token_EOF) {
|
||||
@@ -2546,7 +2626,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
||||
AstNode *param = ast_field(f, names, type, set_flags);
|
||||
array_add(¶ms, param);
|
||||
|
||||
if (parse_expect_separator(f, separator, param)) {
|
||||
if (!parse_expect_field_separator(f, param)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2575,7 +2655,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
|
||||
|
||||
|
||||
AstNode *parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) {
|
||||
return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace);
|
||||
return parse_field_list(f, field_count_, flags, Token_CloseBrace);
|
||||
}
|
||||
|
||||
AstNode *parse_type_or_ident(AstFile *f) {
|
||||
@@ -2589,10 +2669,11 @@ AstNode *parse_type_or_ident(AstFile *f) {
|
||||
AstNode *sel = parse_ident(f);
|
||||
e = ast_selector_expr(f, token, e, sel);
|
||||
}
|
||||
if (f->curr_token.kind == Token_OpenParen) {
|
||||
// TODO(bill): Merge type_or_ident into the general parsing for expressions
|
||||
// if (f->curr_token.kind == Token_OpenParen) {
|
||||
// HACK NOTE(bill): For type_of_val(expr) et al.
|
||||
e = parse_call_expr(f, e);
|
||||
}
|
||||
// e = parse_call_expr(f, e);
|
||||
// }
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -2614,6 +2695,12 @@ AstNode *parse_type_or_ident(AstFile *f) {
|
||||
return ast_pointer_type(f, token, elem);
|
||||
}
|
||||
|
||||
case Token_atomic: {
|
||||
Token token = expect_token(f, Token_atomic);
|
||||
AstNode *elem = parse_type(f);
|
||||
return ast_atomic_type(f, token, elem);
|
||||
}
|
||||
|
||||
case Token_OpenBracket: {
|
||||
Token token = expect_token(f, Token_OpenBracket);
|
||||
AstNode *count_expr = NULL;
|
||||
@@ -3041,46 +3128,6 @@ AstNode *parse_for_stmt(AstFile *f) {
|
||||
|
||||
cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
|
||||
return ast_for_stmt(f, token, init, cond, post, body);
|
||||
|
||||
#if 0
|
||||
Token token = expect_token(f, Token_for);
|
||||
AstNodeArray names = parse_ident_list(f);
|
||||
parse_check_name_list_for_reserves(f, names);
|
||||
Token colon = expect_token_after(f, Token_in, "for name list");
|
||||
|
||||
isize prev_level = f->expr_level;
|
||||
f->expr_level = -1;
|
||||
AstNode *expr = parse_expr(f, false);
|
||||
switch (f->curr_token.kind) {
|
||||
case Token_HalfOpenRange:
|
||||
case Token_Ellipsis: {
|
||||
Token op = f->curr_token;
|
||||
next_token(f);
|
||||
AstNode *right = parse_expr(f, false);
|
||||
expr = ast_interval_expr(f, op, expr, right);
|
||||
} break;
|
||||
}
|
||||
f->expr_level = prev_level;
|
||||
|
||||
AstNode *value = NULL;
|
||||
AstNode *index = NULL;
|
||||
AstNode *body = parse_block_stmt(f, false);
|
||||
|
||||
switch (names.count) {
|
||||
case 1:
|
||||
value = names.e[0];
|
||||
break;
|
||||
case 2:
|
||||
value = names.e[0];
|
||||
index = names.e[1];
|
||||
break;
|
||||
default:
|
||||
error(token, "Expected at 1 or 2 identifiers");
|
||||
return ast_bad_stmt(f, token, f->curr_token);
|
||||
}
|
||||
|
||||
return ast_range_stmt(f, token, value, index, expr, body);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -3088,7 +3135,10 @@ AstNode *parse_case_clause(AstFile *f) {
|
||||
Token token = f->curr_token;
|
||||
AstNodeArray list = make_ast_node_array(f);
|
||||
if (allow_token(f, Token_case)) {
|
||||
bool prev_allow_range = f->allow_range;
|
||||
f->allow_range = true;
|
||||
list = parse_rhs_expr_list(f);
|
||||
f->allow_range = prev_allow_range;
|
||||
} else {
|
||||
expect_token(f, Token_default);
|
||||
}
|
||||
@@ -3102,9 +3152,17 @@ AstNode *parse_case_clause(AstFile *f) {
|
||||
|
||||
AstNode *parse_type_case_clause(AstFile *f) {
|
||||
Token token = f->curr_token;
|
||||
AstNodeArray clause = make_ast_node_array(f);
|
||||
AstNodeArray list = make_ast_node_array(f);
|
||||
if (allow_token(f, Token_case)) {
|
||||
array_add(&clause, parse_type(f));
|
||||
for (;;) {
|
||||
AstNode *t = parse_type(f);
|
||||
array_add(&list, t);
|
||||
if (f->curr_token.kind != Token_Comma ||
|
||||
f->curr_token.kind == Token_EOF) {
|
||||
break;
|
||||
}
|
||||
next_token(f);
|
||||
}
|
||||
} else {
|
||||
expect_token(f, Token_default);
|
||||
}
|
||||
@@ -3112,7 +3170,7 @@ AstNode *parse_type_case_clause(AstFile *f) {
|
||||
// expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax?
|
||||
AstNodeArray stmts = parse_stmt_list(f);
|
||||
|
||||
return ast_case_clause(f, token, clause, stmts);
|
||||
return ast_case_clause(f, token, list, stmts);
|
||||
}
|
||||
|
||||
|
||||
@@ -3233,6 +3291,7 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
case Token_Ident:
|
||||
case Token_Integer:
|
||||
case Token_Float:
|
||||
case Token_Imag:
|
||||
case Token_Rune:
|
||||
case Token_String:
|
||||
case Token_OpenParen:
|
||||
@@ -3256,43 +3315,51 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
|
||||
case Token_break:
|
||||
case Token_continue:
|
||||
case Token_fallthrough:
|
||||
case Token_fallthrough: {
|
||||
AstNode *label = NULL;
|
||||
next_token(f);
|
||||
s = ast_branch_stmt(f, token);
|
||||
if (token.kind != Token_fallthrough &&
|
||||
f->curr_token.kind == Token_Ident) {
|
||||
label = parse_ident(f);
|
||||
}
|
||||
s = ast_branch_stmt(f, token, label);
|
||||
expect_semicolon(f, s);
|
||||
return s;
|
||||
}
|
||||
|
||||
case Token_using: {
|
||||
// TODO(bill): Make using statements better
|
||||
Token token = expect_token(f, Token_using);
|
||||
AstNode *node = parse_stmt(f);
|
||||
AstNodeArray list = parse_lhs_expr_list(f);
|
||||
if (list.count == 0) {
|
||||
syntax_error(token, "Illegal use of `using` statement");
|
||||
expect_semicolon(f, NULL);
|
||||
return ast_bad_stmt(f, token, f->curr_token);
|
||||
}
|
||||
|
||||
switch (node->kind) {
|
||||
case AstNode_ValueDecl:
|
||||
if (!node->ValueDecl.is_var) {
|
||||
if (f->curr_token.kind != Token_Colon) {
|
||||
expect_semicolon(f, list.e[list.count-1]);
|
||||
return ast_using_stmt(f, token, list);
|
||||
}
|
||||
|
||||
AstNode *decl = parse_value_decl(f, list);
|
||||
expect_semicolon(f, decl);
|
||||
|
||||
if (decl->kind == AstNode_ValueDecl) {
|
||||
#if 1
|
||||
if (!decl->ValueDecl.is_var) {
|
||||
syntax_error(token, "`using` may not be applied to constant declarations");
|
||||
return decl;
|
||||
}
|
||||
if (f->curr_proc == NULL) {
|
||||
syntax_error(token, "`using` is not allowed at the file scope");
|
||||
} else {
|
||||
if (f->curr_proc == NULL) {
|
||||
syntax_error(token, "`using` is not allowed at the file scope");
|
||||
} else {
|
||||
node->ValueDecl.flags |= VarDeclFlag_using;
|
||||
}
|
||||
decl->ValueDecl.flags |= VarDeclFlag_using;
|
||||
}
|
||||
return node;
|
||||
case AstNode_ExprStmt: {
|
||||
AstNode *e = unparen_expr(node->ExprStmt.expr);
|
||||
while (e->kind == AstNode_SelectorExpr) {
|
||||
e = unparen_expr(e->SelectorExpr.selector);
|
||||
}
|
||||
if (e->kind == AstNode_Ident) {
|
||||
return ast_using_stmt(f, token, node);
|
||||
} else if (e->kind == AstNode_Implicit) {
|
||||
syntax_error(token, "Illegal use of `using` statement with implicit value `%.*s`", LIT(e->Implicit.string));
|
||||
return ast_bad_stmt(f, token, f->curr_token);
|
||||
}
|
||||
} break;
|
||||
|
||||
|
||||
#else
|
||||
decl->ValueDecl.flags |= VarDeclFlag_using;
|
||||
#endif
|
||||
return decl;
|
||||
}
|
||||
|
||||
syntax_error(token, "Illegal use of `using` statement");
|
||||
@@ -3345,7 +3412,25 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
Token name = expect_token(f, Token_Ident);
|
||||
String tag = name.string;
|
||||
|
||||
if (str_eq(tag, str_lit("import"))) {
|
||||
if (str_eq(tag, str_lit("label"))) {
|
||||
AstNode *name = parse_ident(f);
|
||||
AstNode *label = ast_label_decl(f, token, name);
|
||||
AstNode *stmt = parse_stmt(f);
|
||||
|
||||
#define _SET_LABEL(Kind_, label_) case GB_JOIN2(AstNode_, Kind_): (stmt->Kind_).label = label_; break
|
||||
switch (stmt->kind) {
|
||||
_SET_LABEL(ForStmt, label);
|
||||
_SET_LABEL(RangeStmt, label);
|
||||
_SET_LABEL(MatchStmt, label);
|
||||
_SET_LABEL(TypeMatchStmt, label);
|
||||
default:
|
||||
syntax_error(token, "#label cannot only be applied to a loop or match statement");
|
||||
break;
|
||||
}
|
||||
#undef _SET_LABEL
|
||||
|
||||
return stmt;
|
||||
} else if (str_eq(tag, str_lit("import"))) {
|
||||
AstNode *cond = NULL;
|
||||
Token import_name = {0};
|
||||
|
||||
@@ -3505,7 +3590,6 @@ AstNode *parse_stmt(AstFile *f) {
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
if (str_eq(tag, str_lit("include"))) {
|
||||
syntax_error(token, "#include is not a valid import declaration kind. Use #load instead");
|
||||
s = ast_bad_stmt(f, token, f->curr_token);
|
||||
@@ -3589,6 +3673,7 @@ ParseFileError init_ast_file(AstFile *f, String fullpath) {
|
||||
gb_arena_init_from_allocator(&f->arena, heap_allocator(), arena_size);
|
||||
|
||||
f->curr_proc = NULL;
|
||||
f->id = ++global_file_id;
|
||||
|
||||
return ParseFile_None;
|
||||
}
|
||||
@@ -3809,6 +3894,10 @@ ParseFileError parse_files(Parser *p, char *init_filename) {
|
||||
|
||||
if (err != ParseFile_None) {
|
||||
if (err == ParseFile_EmptyFile) {
|
||||
if (str_eq(import_path, init_fullpath)) {
|
||||
gb_printf_err("Initial file is empty - %.*s\n", LIT(init_fullpath));
|
||||
gb_exit(1);
|
||||
}
|
||||
return ParseFile_None;
|
||||
}
|
||||
|
||||
@@ -3827,7 +3916,7 @@ ParseFileError parse_files(Parser *p, char *init_filename) {
|
||||
gb_printf_err("File permissions problem");
|
||||
break;
|
||||
case ParseFile_NotFound:
|
||||
gb_printf_err("File cannot be found");
|
||||
gb_printf_err("File cannot be found (`%.*s`)", LIT(import_path));
|
||||
break;
|
||||
case ParseFile_InvalidToken:
|
||||
gb_printf_err("Invalid token found in file");
|
||||
|
||||
+276
@@ -0,0 +1,276 @@
|
||||
#define SSA_OPS \
|
||||
SSA_OP(Invalid)\
|
||||
\
|
||||
SSA_OP(Unknown)\
|
||||
\
|
||||
SSA_OP(Comment) /* Does nothing */\
|
||||
\
|
||||
SSA_OP(SP) /* Stack Pointer */\
|
||||
SSA_OP(SB) /* Stack Base */\
|
||||
\
|
||||
SSA_OP(Local)\
|
||||
SSA_OP(Global)\
|
||||
SSA_OP(Proc)\
|
||||
\
|
||||
SSA_OP(Load)\
|
||||
SSA_OP(Store)\
|
||||
SSA_OP(Move)\
|
||||
SSA_OP(LoadReg)\
|
||||
SSA_OP(StoreReg)\
|
||||
SSA_OP(Zero) /* Zero initialize */\
|
||||
\
|
||||
SSA_OP(ArrayIndex) /* Index for a fixed array */\
|
||||
SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\
|
||||
SSA_OP(PtrOffset)\
|
||||
SSA_OP(ValueIndex) /* Extract for a value from a register */\
|
||||
\
|
||||
SSA_OP(Phi)\
|
||||
SSA_OP(Copy)\
|
||||
\
|
||||
/* TODO(bill): calling conventions */\
|
||||
SSA_OP(CallOdin)\
|
||||
SSA_OP(CallC)\
|
||||
SSA_OP(CallStd)\
|
||||
SSA_OP(CallFast)\
|
||||
\
|
||||
SSA_OP(BoundsCheck)\
|
||||
SSA_OP(SliceBoundsCheck)\
|
||||
\
|
||||
/* Built in operations/procedures */\
|
||||
SSA_OP(Bswap16)\
|
||||
SSA_OP(Bswap32)\
|
||||
SSA_OP(Bswap64)\
|
||||
\
|
||||
SSA_OP(Assume)\
|
||||
SSA_OP(DebugTrap)\
|
||||
SSA_OP(Trap)\
|
||||
SSA_OP(ReadCycleCounter)\
|
||||
\
|
||||
\
|
||||
SSA_OP(ConstBool)\
|
||||
SSA_OP(ConstString)\
|
||||
SSA_OP(ConstSlice)\
|
||||
SSA_OP(ConstNil)\
|
||||
SSA_OP(Const8)\
|
||||
SSA_OP(Const16)\
|
||||
SSA_OP(Const32)\
|
||||
SSA_OP(Const64)\
|
||||
SSA_OP(Const32F)\
|
||||
SSA_OP(Const64F)\
|
||||
\
|
||||
/* These should be all the operations I could possibly need for the mean time */\
|
||||
SSA_OP(Add8)\
|
||||
SSA_OP(Add16)\
|
||||
SSA_OP(Add32)\
|
||||
SSA_OP(Add64)\
|
||||
SSA_OP(AddPtr)\
|
||||
SSA_OP(Add32F)\
|
||||
SSA_OP(Add64F)\
|
||||
SSA_OP(Sub8)\
|
||||
SSA_OP(Sub16)\
|
||||
SSA_OP(Sub32)\
|
||||
SSA_OP(Sub64)\
|
||||
SSA_OP(SubPtr)\
|
||||
SSA_OP(Sub32F)\
|
||||
SSA_OP(Sub64F)\
|
||||
SSA_OP(Mul8)\
|
||||
SSA_OP(Mul16)\
|
||||
SSA_OP(Mul32)\
|
||||
SSA_OP(Mul64)\
|
||||
SSA_OP(Mul32F)\
|
||||
SSA_OP(Mul64F)\
|
||||
SSA_OP(Div8)\
|
||||
SSA_OP(Div8U)\
|
||||
SSA_OP(Div16)\
|
||||
SSA_OP(Div16U)\
|
||||
SSA_OP(Div32)\
|
||||
SSA_OP(Div32U)\
|
||||
SSA_OP(Div64)\
|
||||
SSA_OP(Div64U)\
|
||||
SSA_OP(Div32F)\
|
||||
SSA_OP(Div64F)\
|
||||
SSA_OP(Mod8)\
|
||||
SSA_OP(Mod8U)\
|
||||
SSA_OP(Mod16)\
|
||||
SSA_OP(Mod16U)\
|
||||
SSA_OP(Mod32)\
|
||||
SSA_OP(Mod32U)\
|
||||
SSA_OP(Mod64)\
|
||||
SSA_OP(Mod64U)\
|
||||
\
|
||||
SSA_OP(And8)\
|
||||
SSA_OP(And16)\
|
||||
SSA_OP(And32)\
|
||||
SSA_OP(And64)\
|
||||
SSA_OP(Or8)\
|
||||
SSA_OP(Or16)\
|
||||
SSA_OP(Or32)\
|
||||
SSA_OP(Or64)\
|
||||
SSA_OP(Xor8)\
|
||||
SSA_OP(Xor16)\
|
||||
SSA_OP(Xor32)\
|
||||
SSA_OP(Xor64)\
|
||||
SSA_OP(AndNot8)\
|
||||
SSA_OP(AndNot16)\
|
||||
SSA_OP(AndNot32)\
|
||||
SSA_OP(AndNot64)\
|
||||
\
|
||||
SSA_OP(Lsh8x8)\
|
||||
SSA_OP(Lsh8x16)\
|
||||
SSA_OP(Lsh8x32)\
|
||||
SSA_OP(Lsh8x64)\
|
||||
SSA_OP(Lsh16x8)\
|
||||
SSA_OP(Lsh16x16)\
|
||||
SSA_OP(Lsh16x32)\
|
||||
SSA_OP(Lsh16x64)\
|
||||
SSA_OP(Lsh32x8)\
|
||||
SSA_OP(Lsh32x16)\
|
||||
SSA_OP(Lsh32x32)\
|
||||
SSA_OP(Lsh32x64)\
|
||||
SSA_OP(Lsh64x8)\
|
||||
SSA_OP(Lsh64x16)\
|
||||
SSA_OP(Lsh64x32)\
|
||||
SSA_OP(Lsh64x64)\
|
||||
SSA_OP(Rsh8x8)\
|
||||
SSA_OP(Rsh8x16)\
|
||||
SSA_OP(Rsh8x32)\
|
||||
SSA_OP(Rsh8x64)\
|
||||
SSA_OP(Rsh16x8)\
|
||||
SSA_OP(Rsh16x16)\
|
||||
SSA_OP(Rsh16x32)\
|
||||
SSA_OP(Rsh16x64)\
|
||||
SSA_OP(Rsh32x8)\
|
||||
SSA_OP(Rsh32x16)\
|
||||
SSA_OP(Rsh32x32)\
|
||||
SSA_OP(Rsh32x64)\
|
||||
SSA_OP(Rsh64x8)\
|
||||
SSA_OP(Rsh64x16)\
|
||||
SSA_OP(Rsh64x32)\
|
||||
SSA_OP(Rsh64x64)\
|
||||
SSA_OP(Rsh8Ux8)\
|
||||
SSA_OP(Rsh8Ux16)\
|
||||
SSA_OP(Rsh8Ux32)\
|
||||
SSA_OP(Rsh8Ux64)\
|
||||
SSA_OP(Rsh16Ux8)\
|
||||
SSA_OP(Rsh16Ux16)\
|
||||
SSA_OP(Rsh16Ux32)\
|
||||
SSA_OP(Rsh16Ux64)\
|
||||
SSA_OP(Rsh32Ux8)\
|
||||
SSA_OP(Rsh32Ux16)\
|
||||
SSA_OP(Rsh32Ux32)\
|
||||
SSA_OP(Rsh32Ux64)\
|
||||
SSA_OP(Rsh64Ux8)\
|
||||
SSA_OP(Rsh64Ux16)\
|
||||
SSA_OP(Rsh64Ux32)\
|
||||
SSA_OP(Rsh64Ux64)\
|
||||
\
|
||||
SSA_OP(Eq8)\
|
||||
SSA_OP(Eq16)\
|
||||
SSA_OP(Eq32)\
|
||||
SSA_OP(Eq64)\
|
||||
SSA_OP(EqPtr)\
|
||||
SSA_OP(Eq32F)\
|
||||
SSA_OP(Eq64F)\
|
||||
SSA_OP(Ne8)\
|
||||
SSA_OP(Ne16)\
|
||||
SSA_OP(Ne32)\
|
||||
SSA_OP(Ne64)\
|
||||
SSA_OP(NePtr)\
|
||||
SSA_OP(Ne32F)\
|
||||
SSA_OP(Ne64F)\
|
||||
SSA_OP(Lt8)\
|
||||
SSA_OP(Lt16)\
|
||||
SSA_OP(Lt32)\
|
||||
SSA_OP(Lt64)\
|
||||
SSA_OP(LtPtr)\
|
||||
SSA_OP(Lt32F)\
|
||||
SSA_OP(Lt64F)\
|
||||
SSA_OP(Gt8)\
|
||||
SSA_OP(Gt16)\
|
||||
SSA_OP(Gt32)\
|
||||
SSA_OP(Gt64)\
|
||||
SSA_OP(GtPtr)\
|
||||
SSA_OP(Gt32F)\
|
||||
SSA_OP(Gt64F)\
|
||||
SSA_OP(Le8)\
|
||||
SSA_OP(Le16)\
|
||||
SSA_OP(Le32)\
|
||||
SSA_OP(Le64)\
|
||||
SSA_OP(LePtr)\
|
||||
SSA_OP(Le32F)\
|
||||
SSA_OP(Le64F)\
|
||||
SSA_OP(Ge8)\
|
||||
SSA_OP(Ge16)\
|
||||
SSA_OP(Ge32)\
|
||||
SSA_OP(Ge64)\
|
||||
SSA_OP(GePtr)\
|
||||
SSA_OP(Ge32F)\
|
||||
SSA_OP(Ge64F)\
|
||||
\
|
||||
SSA_OP(NotB)\
|
||||
SSA_OP(EqB)\
|
||||
SSA_OP(NeB)\
|
||||
\
|
||||
SSA_OP(Neg8)\
|
||||
SSA_OP(Neg16)\
|
||||
SSA_OP(Neg32)\
|
||||
SSA_OP(Neg64)\
|
||||
SSA_OP(Neg32F)\
|
||||
SSA_OP(Neg64F)\
|
||||
\
|
||||
SSA_OP(Not8)\
|
||||
SSA_OP(Not16)\
|
||||
SSA_OP(Not32)\
|
||||
SSA_OP(Not64)\
|
||||
\
|
||||
SSA_OP(SignExt8to16)\
|
||||
SSA_OP(SignExt8to32)\
|
||||
SSA_OP(SignExt8to64)\
|
||||
SSA_OP(SignExt16to32)\
|
||||
SSA_OP(SignExt16to64)\
|
||||
SSA_OP(SignExt32to64)\
|
||||
SSA_OP(ZeroExt8to16)\
|
||||
SSA_OP(ZeroExt8to32)\
|
||||
SSA_OP(ZeroExt8to64)\
|
||||
SSA_OP(ZeroExt16to32)\
|
||||
SSA_OP(ZeroExt16to64)\
|
||||
SSA_OP(ZeroExt32to64)\
|
||||
SSA_OP(Trunc16to8)\
|
||||
SSA_OP(Trunc32to8)\
|
||||
SSA_OP(Trunc32to16)\
|
||||
SSA_OP(Trunc64to8)\
|
||||
SSA_OP(Trunc64to16)\
|
||||
SSA_OP(Trunc64to32)\
|
||||
\
|
||||
SSA_OP(Cvt32to32F)\
|
||||
SSA_OP(Cvt32to64F)\
|
||||
SSA_OP(Cvt64to32F)\
|
||||
SSA_OP(Cvt64to64F)\
|
||||
SSA_OP(Cvt32Fto32)\
|
||||
SSA_OP(Cvt32Fto64)\
|
||||
SSA_OP(Cvt64Fto32)\
|
||||
SSA_OP(Cvt64Fto64)\
|
||||
SSA_OP(Cvt32Fto64F)\
|
||||
SSA_OP(Cvt64Fto32F)\
|
||||
SSA_OP(Cvt32Uto32F)\
|
||||
SSA_OP(Cvt32Uto64F)\
|
||||
SSA_OP(Cvt32Fto32U)\
|
||||
SSA_OP(Cvt64Fto32U)\
|
||||
SSA_OP(Cvt64Uto32F)\
|
||||
SSA_OP(Cvt64Uto64F)\
|
||||
SSA_OP(Cvt32Fto64U)\
|
||||
SSA_OP(Cvt64Fto64U)\
|
||||
|
||||
|
||||
enum ssaOp {
|
||||
#define SSA_OP(k) GB_JOIN2(ssaOp_, k),
|
||||
SSA_OPS
|
||||
#undef SSA_OP
|
||||
};
|
||||
typedef enum ssaOp ssaOp;
|
||||
|
||||
String const ssa_op_strings[] = {
|
||||
#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1},
|
||||
SSA_OPS
|
||||
#undef SSA_OP
|
||||
};
|
||||
+18
-2
@@ -188,6 +188,22 @@ bool string_contains_char(String s, u8 c) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String filename_from_path(String s) {
|
||||
isize i = string_extension_position(s);
|
||||
if (i > 0) {
|
||||
isize j = 0;
|
||||
s.len = i;
|
||||
for (j = i-1; j >= 0; j--) {
|
||||
if (s.text[j] == '/' ||
|
||||
s.text[j] == '\\') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
s.text += j+1;
|
||||
s.len = i-j-1;
|
||||
}
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -207,7 +223,7 @@ bool string_contains_char(String s, u8 c) {
|
||||
|
||||
#include <iconv.h>
|
||||
|
||||
int convert_multibyte_to_widechar(char *multibyte_input, int input_length, wchar_t *output, int output_size) {
|
||||
int convert_multibyte_to_widechar(char *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
|
||||
iconv_t conv = iconv_open("WCHAR_T", "UTF-8");
|
||||
size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size);
|
||||
iconv_close(conv);
|
||||
@@ -215,7 +231,7 @@ bool string_contains_char(String s, u8 c) {
|
||||
return (int) result;
|
||||
}
|
||||
|
||||
int convert_widechar_to_multibyte(wchar_t* widechar_input, int input_length, char* output, int output_size) {
|
||||
int convert_widechar_to_multibyte(wchar_t* widechar_input, usize input_length, char* output, usize output_size) {
|
||||
iconv_t conv = iconv_open("UTF-8", "WCHAR_T");
|
||||
size_t result = iconv(conv, (char**) &widechar_input, &input_length, (char**) &output, &output_size);
|
||||
iconv_close(conv);
|
||||
|
||||
+73
-62
@@ -7,6 +7,7 @@ TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \
|
||||
TOKEN_KIND(Token_Ident, "identifier"), \
|
||||
TOKEN_KIND(Token_Integer, "integer"), \
|
||||
TOKEN_KIND(Token_Float, "float"), \
|
||||
TOKEN_KIND(Token_Imag, "imaginary"), \
|
||||
TOKEN_KIND(Token_Rune, "rune"), \
|
||||
TOKEN_KIND(Token_String, "string"), \
|
||||
TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \
|
||||
@@ -63,18 +64,19 @@ TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
|
||||
TOKEN_KIND(Token_GtEq, ">="), \
|
||||
TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
|
||||
\
|
||||
TOKEN_KIND(Token_OpenParen, "("), \
|
||||
TOKEN_KIND(Token_CloseParen, ")"), \
|
||||
TOKEN_KIND(Token_OpenBracket, "["), \
|
||||
TOKEN_KIND(Token_CloseBracket, "]"), \
|
||||
TOKEN_KIND(Token_OpenBrace, "{"), \
|
||||
TOKEN_KIND(Token_CloseBrace, "}"), \
|
||||
TOKEN_KIND(Token_Colon, ":"), \
|
||||
TOKEN_KIND(Token_Semicolon, ";"), \
|
||||
TOKEN_KIND(Token_Period, "."), \
|
||||
TOKEN_KIND(Token_Comma, ","), \
|
||||
TOKEN_KIND(Token_Ellipsis, ".."), \
|
||||
/* TOKEN_KIND(Token_HalfOpenRange, "..<"), */ \
|
||||
TOKEN_KIND(Token_OpenParen, "("), \
|
||||
TOKEN_KIND(Token_CloseParen, ")"), \
|
||||
TOKEN_KIND(Token_OpenBracket, "["), \
|
||||
TOKEN_KIND(Token_CloseBracket, "]"), \
|
||||
TOKEN_KIND(Token_OpenBrace, "{"), \
|
||||
TOKEN_KIND(Token_CloseBrace, "}"), \
|
||||
TOKEN_KIND(Token_Colon, ":"), \
|
||||
TOKEN_KIND(Token_Semicolon, ";"), \
|
||||
TOKEN_KIND(Token_Period, "."), \
|
||||
TOKEN_KIND(Token_Comma, ","), \
|
||||
TOKEN_KIND(Token_Ellipsis, ".."), \
|
||||
TOKEN_KIND(Token_HalfClosed, "..<"), \
|
||||
TOKEN_KIND(Token_BackSlash, "\\"), \
|
||||
TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
|
||||
\
|
||||
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
@@ -105,14 +107,13 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
TOKEN_KIND(Token_using, "using"), \
|
||||
TOKEN_KIND(Token_no_alias, "no_alias"), \
|
||||
TOKEN_KIND(Token_immutable, "immutable"), \
|
||||
TOKEN_KIND(Token_cast, "cast"), \
|
||||
TOKEN_KIND(Token_transmute, "transmute"), \
|
||||
TOKEN_KIND(Token_down_cast, "down_cast"), \
|
||||
TOKEN_KIND(Token_union_cast, "union_cast"), \
|
||||
TOKEN_KIND(Token_context, "context"), \
|
||||
TOKEN_KIND(Token_push_context, "push_context"), \
|
||||
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
|
||||
TOKEN_KIND(Token_asm, "asm"), \
|
||||
TOKEN_KIND(Token_yield, "yield"), \
|
||||
TOKEN_KIND(Token_await, "await"), \
|
||||
TOKEN_KIND(Token_atomic, "atomic"), \
|
||||
TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
|
||||
TOKEN_KIND(Token_Count, "")
|
||||
|
||||
@@ -168,8 +169,8 @@ Token make_token_ident(String s) {
|
||||
|
||||
typedef struct ErrorCollector {
|
||||
TokenPos prev;
|
||||
i64 count;
|
||||
i64 warning_count;
|
||||
i64 count;
|
||||
i64 warning_count;
|
||||
gbMutex mutex;
|
||||
} ErrorCollector;
|
||||
|
||||
@@ -414,7 +415,7 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
|
||||
TokenizerInitError err = TokenizerInit_None;
|
||||
|
||||
char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1);
|
||||
memcpy(c_str, fullpath.text, fullpath.len);
|
||||
gb_memcopy(c_str, fullpath.text, fullpath.len);
|
||||
c_str[fullpath.len] = '\0';
|
||||
|
||||
// TODO(bill): Memory map rather than copy contents
|
||||
@@ -484,15 +485,9 @@ gb_inline i32 digit_value(Rune r) {
|
||||
return 16; // NOTE(bill): Larger than highest possible
|
||||
}
|
||||
|
||||
gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) {
|
||||
if (allow_underscore) {
|
||||
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
} else {
|
||||
while (digit_value(t->curr_rune) < base) {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
|
||||
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -506,7 +501,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
|
||||
|
||||
if (seen_decimal_point) {
|
||||
token.kind = Token_Float;
|
||||
scan_mantissa(t, 10, true);
|
||||
scan_mantissa(t, 10);
|
||||
goto exponent;
|
||||
}
|
||||
|
||||
@@ -515,31 +510,37 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
|
||||
advance_to_next_rune(t);
|
||||
if (t->curr_rune == 'b') { // Binary
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 2, true);
|
||||
scan_mantissa(t, 2);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else if (t->curr_rune == 'o') { // Octal
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 8, true);
|
||||
scan_mantissa(t, 8);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else if (t->curr_rune == 'd') { // Decimal
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 10, true);
|
||||
scan_mantissa(t, 10);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else if (t->curr_rune == 'z') { // Dozenal
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 12);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else if (t->curr_rune == 'x') { // Hexadecimal
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 16, true);
|
||||
scan_mantissa(t, 16);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else {
|
||||
seen_decimal_point = false;
|
||||
scan_mantissa(t, 10, true);
|
||||
scan_mantissa(t, 10);
|
||||
|
||||
if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
|
||||
seen_decimal_point = true;
|
||||
@@ -547,24 +548,24 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
|
||||
}
|
||||
}
|
||||
|
||||
token.string.len = t->curr - token.string.text;
|
||||
return token;
|
||||
goto end;
|
||||
}
|
||||
|
||||
scan_mantissa(t, 10, true);
|
||||
scan_mantissa(t, 10);
|
||||
|
||||
|
||||
fraction:
|
||||
if (t->curr_rune == '.') {
|
||||
// HACK(bill): This may be inefficient
|
||||
TokenizerState state = save_tokenizer_state(t);
|
||||
advance_to_next_rune(t);
|
||||
if (digit_value(t->curr_rune) >= 10) {
|
||||
if (t->curr_rune == '.') {
|
||||
// TODO(bill): Clean up this shit
|
||||
restore_tokenizer_state(t, &state);
|
||||
goto end;
|
||||
}
|
||||
token.kind = Token_Float;
|
||||
scan_mantissa(t, 10, true);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
|
||||
exponent:
|
||||
@@ -574,7 +575,14 @@ exponent:
|
||||
if (t->curr_rune == '-' || t->curr_rune == '+') {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
scan_mantissa(t, 10, false);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
|
||||
switch (t->curr_rune) {
|
||||
case 'i': case 'j': case 'k':
|
||||
token.kind = Token_Imag;
|
||||
advance_to_next_rune(t);
|
||||
break;
|
||||
}
|
||||
|
||||
end:
|
||||
@@ -611,20 +619,22 @@ bool scan_escape(Tokenizer *t, Rune quote) {
|
||||
advance_to_next_rune(t);
|
||||
len = 8; base = 16; max = GB_RUNE_MAX;
|
||||
} else {
|
||||
if (t->curr_rune < 0)
|
||||
if (t->curr_rune < 0) {
|
||||
tokenizer_err(t, "Escape sequence was not terminated");
|
||||
else
|
||||
} else {
|
||||
tokenizer_err(t, "Unknown escape sequence");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
while (len --> 0) {
|
||||
u32 d = cast(u32)digit_value(t->curr_rune);
|
||||
if (d >= base) {
|
||||
if (t->curr_rune < 0)
|
||||
if (t->curr_rune < 0) {
|
||||
tokenizer_err(t, "Escape sequence was not terminated");
|
||||
else
|
||||
} else {
|
||||
tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -859,27 +869,28 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
if (t->curr_rune == '.') { // Could be an ellipsis
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_Ellipsis;
|
||||
// if (t->curr_rune == '<') {
|
||||
// advance_to_next_rune(t);
|
||||
// token.kind = Token_HalfOpenRange;
|
||||
// }
|
||||
if (t->curr_rune == '<') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_HalfClosed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '#': token.kind = Token_Hash; break;
|
||||
case '@': token.kind = Token_At; break;
|
||||
case '$': token.kind = Token_Dollar; break;
|
||||
case '?': token.kind = Token_Question; break;
|
||||
case '^': token.kind = Token_Pointer; break;
|
||||
case ';': token.kind = Token_Semicolon; break;
|
||||
case ',': token.kind = Token_Comma; break;
|
||||
case ':': token.kind = Token_Colon; break;
|
||||
case '(': token.kind = Token_OpenParen; break;
|
||||
case ')': token.kind = Token_CloseParen; break;
|
||||
case '[': token.kind = Token_OpenBracket; break;
|
||||
case ']': token.kind = Token_CloseBracket; break;
|
||||
case '{': token.kind = Token_OpenBrace; break;
|
||||
case '}': token.kind = Token_CloseBrace; break;
|
||||
case '#': token.kind = Token_Hash; break;
|
||||
case '@': token.kind = Token_At; break;
|
||||
case '$': token.kind = Token_Dollar; break;
|
||||
case '?': token.kind = Token_Question; break;
|
||||
case '^': token.kind = Token_Pointer; break;
|
||||
case ';': token.kind = Token_Semicolon; break;
|
||||
case ',': token.kind = Token_Comma; break;
|
||||
case ':': token.kind = Token_Colon; break;
|
||||
case '(': token.kind = Token_OpenParen; break;
|
||||
case ')': token.kind = Token_CloseParen; break;
|
||||
case '[': token.kind = Token_OpenBracket; break;
|
||||
case ']': token.kind = Token_CloseBracket; break;
|
||||
case '{': token.kind = Token_OpenBrace; break;
|
||||
case '}': token.kind = Token_CloseBrace; break;
|
||||
case '\\': token.kind = Token_BackSlash; break;
|
||||
|
||||
case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break;
|
||||
case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break;
|
||||
|
||||
+308
-258
@@ -12,25 +12,15 @@ typedef enum BasicKind {
|
||||
Basic_i64,
|
||||
Basic_u64,
|
||||
|
||||
/* Basic_i16le,
|
||||
Basic_i16be,
|
||||
Basic_u16le,
|
||||
Basic_u16be,
|
||||
Basic_i32le,
|
||||
Basic_i32be,
|
||||
Basic_u32le,
|
||||
Basic_u32be,
|
||||
Basic_i64le,
|
||||
Basic_i64be,
|
||||
Basic_u64le,
|
||||
Basic_u64be, */
|
||||
|
||||
// Basic_i128,
|
||||
// Basic_u128,
|
||||
// Basic_f16,
|
||||
Basic_f32,
|
||||
Basic_f64,
|
||||
// Basic_f128,
|
||||
|
||||
Basic_complex64,
|
||||
Basic_complex128,
|
||||
|
||||
Basic_quaternion128,
|
||||
Basic_quaternion256,
|
||||
|
||||
Basic_int,
|
||||
Basic_uint,
|
||||
Basic_rawptr,
|
||||
@@ -40,6 +30,8 @@ typedef enum BasicKind {
|
||||
Basic_UntypedBool,
|
||||
Basic_UntypedInteger,
|
||||
Basic_UntypedFloat,
|
||||
Basic_UntypedComplex,
|
||||
Basic_UntypedQuaternion,
|
||||
Basic_UntypedString,
|
||||
Basic_UntypedRune,
|
||||
Basic_UntypedNil,
|
||||
@@ -51,17 +43,19 @@ typedef enum BasicKind {
|
||||
} BasicKind;
|
||||
|
||||
typedef enum BasicFlag {
|
||||
BasicFlag_Boolean = GB_BIT(0),
|
||||
BasicFlag_Integer = GB_BIT(1),
|
||||
BasicFlag_Unsigned = GB_BIT(2),
|
||||
BasicFlag_Float = GB_BIT(3),
|
||||
BasicFlag_Pointer = GB_BIT(4),
|
||||
BasicFlag_String = GB_BIT(5),
|
||||
BasicFlag_Rune = GB_BIT(6),
|
||||
BasicFlag_Untyped = GB_BIT(7),
|
||||
BasicFlag_Boolean = GB_BIT(0),
|
||||
BasicFlag_Integer = GB_BIT(1),
|
||||
BasicFlag_Unsigned = GB_BIT(2),
|
||||
BasicFlag_Float = GB_BIT(3),
|
||||
BasicFlag_Complex = GB_BIT(4),
|
||||
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_Numeric = BasicFlag_Integer | BasicFlag_Float,
|
||||
BasicFlag_Ordered = BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer,
|
||||
BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex | BasicFlag_Quaternion,
|
||||
BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer,
|
||||
BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_String | BasicFlag_Rune,
|
||||
} BasicFlag;
|
||||
|
||||
@@ -116,6 +110,7 @@ typedef struct TypeRecord {
|
||||
#define TYPE_KINDS \
|
||||
TYPE_KIND(Basic, BasicType) \
|
||||
TYPE_KIND(Pointer, struct { Type *elem; }) \
|
||||
TYPE_KIND(Atomic, struct { Type *elem; }) \
|
||||
TYPE_KIND(Array, struct { Type *elem; i64 count; }) \
|
||||
TYPE_KIND(DynamicArray, struct { Type *elem; }) \
|
||||
TYPE_KIND(Vector, struct { Type *elem; i64 count; }) \
|
||||
@@ -138,7 +133,10 @@ typedef struct TypeRecord {
|
||||
Type * results; /* Type_Tuple */ \
|
||||
i32 param_count; \
|
||||
i32 result_count; \
|
||||
Type **abi_compat_params; \
|
||||
Type **abi_compat_results; \
|
||||
bool variadic; \
|
||||
bool require_results; \
|
||||
ProcCallingConvention calling_convention; \
|
||||
}) \
|
||||
TYPE_KIND(Map, struct { \
|
||||
@@ -209,33 +207,43 @@ void selection_add_index(Selection *s, isize index) {
|
||||
|
||||
|
||||
gb_global Type basic_types[] = {
|
||||
{Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}},
|
||||
{Type_Basic, {Basic_bool, BasicFlag_Boolean, 1, STR_LIT("bool")}},
|
||||
{Type_Basic, {Basic_i8, BasicFlag_Integer, 1, STR_LIT("i8")}},
|
||||
{Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("u8")}},
|
||||
{Type_Basic, {Basic_i16, BasicFlag_Integer, 2, STR_LIT("i16")}},
|
||||
{Type_Basic, {Basic_u16, BasicFlag_Integer | BasicFlag_Unsigned, 2, STR_LIT("u16")}},
|
||||
{Type_Basic, {Basic_i32, BasicFlag_Integer, 4, STR_LIT("i32")}},
|
||||
{Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}},
|
||||
{Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}},
|
||||
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}},
|
||||
// {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}},
|
||||
// {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
|
||||
// {Type_Basic, {Basic_f16, BasicFlag_Float, 2, STR_LIT("f16")}},
|
||||
{Type_Basic, {Basic_f32, BasicFlag_Float, 4, STR_LIT("f32")}},
|
||||
{Type_Basic, {Basic_f64, BasicFlag_Float, 8, STR_LIT("f64")}},
|
||||
// {Type_Basic, {Basic_f128, BasicFlag_Float, 16, STR_LIT("f128")}},
|
||||
{Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}},
|
||||
{Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}},
|
||||
{Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
|
||||
{Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}},
|
||||
{Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}},
|
||||
{Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}},
|
||||
{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}},
|
||||
{Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}},
|
||||
{Type_Basic, {Basic_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")}},
|
||||
{Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}},
|
||||
|
||||
{Type_Basic, {Basic_bool, BasicFlag_Boolean, 1, STR_LIT("bool")}},
|
||||
|
||||
{Type_Basic, {Basic_i8, BasicFlag_Integer, 1, STR_LIT("i8")}},
|
||||
{Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("u8")}},
|
||||
{Type_Basic, {Basic_i16, BasicFlag_Integer, 2, STR_LIT("i16")}},
|
||||
{Type_Basic, {Basic_u16, BasicFlag_Integer | BasicFlag_Unsigned, 2, STR_LIT("u16")}},
|
||||
{Type_Basic, {Basic_i32, BasicFlag_Integer, 4, STR_LIT("i32")}},
|
||||
{Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}},
|
||||
{Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}},
|
||||
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}},
|
||||
|
||||
{Type_Basic, {Basic_f32, BasicFlag_Float, 4, STR_LIT("f32")}},
|
||||
{Type_Basic, {Basic_f64, BasicFlag_Float, 8, STR_LIT("f64")}},
|
||||
|
||||
{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_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
|
||||
{Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}},
|
||||
{Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}},
|
||||
|
||||
{Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}},
|
||||
{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}},
|
||||
{Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}},
|
||||
{Type_Basic, {Basic_UntypedComplex, BasicFlag_Complex | BasicFlag_Untyped, 0, STR_LIT("untyped complex")}},
|
||||
{Type_Basic, {Basic_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")}},
|
||||
};
|
||||
|
||||
gb_global Type basic_type_aliases[] = {
|
||||
@@ -253,25 +261,33 @@ gb_global Type *t_i32 = &basic_types[Basic_i32];
|
||||
gb_global Type *t_u32 = &basic_types[Basic_u32];
|
||||
gb_global Type *t_i64 = &basic_types[Basic_i64];
|
||||
gb_global Type *t_u64 = &basic_types[Basic_u64];
|
||||
// gb_global Type *t_i128 = &basic_types[Basic_i128];
|
||||
// gb_global Type *t_u128 = &basic_types[Basic_u128];
|
||||
// gb_global Type *t_f16 = &basic_types[Basic_f16];
|
||||
|
||||
gb_global Type *t_f32 = &basic_types[Basic_f32];
|
||||
gb_global Type *t_f64 = &basic_types[Basic_f64];
|
||||
// gb_global Type *t_f128 = &basic_types[Basic_f128];
|
||||
|
||||
gb_global Type *t_complex64 = &basic_types[Basic_complex64];
|
||||
gb_global Type *t_complex128 = &basic_types[Basic_complex128];
|
||||
|
||||
gb_global Type *t_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_rawptr = &basic_types[Basic_rawptr];
|
||||
gb_global Type *t_string = &basic_types[Basic_string];
|
||||
gb_global Type *t_any = &basic_types[Basic_any];
|
||||
gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool];
|
||||
gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger];
|
||||
gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat];
|
||||
gb_global Type *t_untyped_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];
|
||||
gb_global Type *t_byte = &basic_type_aliases[0];
|
||||
gb_global Type *t_rune = &basic_type_aliases[1];
|
||||
|
||||
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];
|
||||
gb_global Type *t_byte = &basic_type_aliases[0];
|
||||
gb_global Type *t_rune = &basic_type_aliases[1];
|
||||
|
||||
|
||||
gb_global Type *t_u8_ptr = NULL;
|
||||
@@ -293,10 +309,13 @@ gb_global Type *t_type_info_enum_value_ptr = NULL;
|
||||
gb_global Type *t_type_info_named = NULL;
|
||||
gb_global Type *t_type_info_integer = NULL;
|
||||
gb_global Type *t_type_info_float = NULL;
|
||||
gb_global Type *t_type_info_complex = NULL;
|
||||
gb_global Type *t_type_info_quaternion = NULL;
|
||||
gb_global Type *t_type_info_any = NULL;
|
||||
gb_global Type *t_type_info_string = NULL;
|
||||
gb_global Type *t_type_info_boolean = NULL;
|
||||
gb_global Type *t_type_info_pointer = NULL;
|
||||
gb_global Type *t_type_info_atomic = NULL;
|
||||
gb_global Type *t_type_info_procedure = NULL;
|
||||
gb_global Type *t_type_info_array = NULL;
|
||||
gb_global Type *t_type_info_dynamic_array = NULL;
|
||||
@@ -312,10 +331,13 @@ gb_global Type *t_type_info_map = NULL;
|
||||
gb_global Type *t_type_info_named_ptr = NULL;
|
||||
gb_global Type *t_type_info_integer_ptr = NULL;
|
||||
gb_global Type *t_type_info_float_ptr = NULL;
|
||||
gb_global Type *t_type_info_complex_ptr = NULL;
|
||||
gb_global Type *t_type_info_quaternion_ptr = NULL;
|
||||
gb_global Type *t_type_info_any_ptr = NULL;
|
||||
gb_global Type *t_type_info_string_ptr = NULL;
|
||||
gb_global Type *t_type_info_boolean_ptr = NULL;
|
||||
gb_global Type *t_type_info_pointer_ptr = NULL;
|
||||
gb_global Type *t_type_info_atomic_ptr = NULL;
|
||||
gb_global Type *t_type_info_procedure_ptr = NULL;
|
||||
gb_global Type *t_type_info_array_ptr = NULL;
|
||||
gb_global Type *t_type_info_dynamic_array_ptr = NULL;
|
||||
@@ -333,8 +355,6 @@ gb_global Type *t_allocator_ptr = NULL;
|
||||
gb_global Type *t_context = NULL;
|
||||
gb_global Type *t_context_ptr = NULL;
|
||||
|
||||
gb_global Type *t_raw_dynamic_array = NULL;
|
||||
gb_global Type *t_raw_dynamic_array_ptr = NULL;
|
||||
gb_global Type *t_map_key = NULL;
|
||||
gb_global Type *t_map_header = NULL;
|
||||
|
||||
@@ -375,6 +395,34 @@ Type *base_enum_type(Type *t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *core_type(Type *t) {
|
||||
for (;;) {
|
||||
if (t == NULL) {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (t->kind) {
|
||||
case Type_Named:
|
||||
if (t == t->Named.base) {
|
||||
return t_invalid;
|
||||
}
|
||||
t = t->Named.base;
|
||||
continue;
|
||||
case Type_Record:
|
||||
if (t->Record.kind == TypeRecord_Enum) {
|
||||
t = t->Record.enum_base_type;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case Type_Atomic:
|
||||
t = t->Atomic.elem;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
void set_base_type(Type *t, Type *base) {
|
||||
if (t && t->kind == Type_Named) {
|
||||
t->Named.base = base;
|
||||
@@ -384,6 +432,7 @@ void set_base_type(Type *t, Type *base) {
|
||||
|
||||
Type *alloc_type(gbAllocator a, TypeKind kind) {
|
||||
Type *t = gb_alloc_item(a, Type);
|
||||
gb_zero_item(t);
|
||||
t->kind = kind;
|
||||
return t;
|
||||
}
|
||||
@@ -401,6 +450,12 @@ Type *make_type_pointer(gbAllocator a, Type *elem) {
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *make_type_atomic(gbAllocator a, Type *elem) {
|
||||
Type *t = alloc_type(a, Type_Atomic);
|
||||
t->Atomic.elem = elem;
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *make_type_array(gbAllocator a, Type *elem, i64 count) {
|
||||
Type *t = alloc_type(a, Type_Array);
|
||||
t->Array.elem = elem;
|
||||
@@ -510,8 +565,6 @@ Type *make_type_map(gbAllocator a, i64 count, Type *key, Type *value) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Type *type_deref(Type *t) {
|
||||
if (t != NULL) {
|
||||
Type *bt = base_type(t);
|
||||
@@ -529,29 +582,43 @@ bool is_type_named(Type *t) {
|
||||
}
|
||||
return t->kind == Type_Named;
|
||||
}
|
||||
bool is_type_named_alias(Type *t) {
|
||||
if (!is_type_named(t)) {
|
||||
return false;
|
||||
}
|
||||
Entity *e = t->Named.type_name;
|
||||
if (e == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (e->kind != Entity_TypeName) {
|
||||
return false;
|
||||
}
|
||||
return e->TypeName.is_type_alias;
|
||||
}
|
||||
|
||||
bool is_type_boolean(Type *t) {
|
||||
t = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Boolean) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_integer(Type *t) {
|
||||
t = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Integer) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_unsigned(Type *t) {
|
||||
t = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Unsigned) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_numeric(Type *t) {
|
||||
t = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Numeric) != 0;
|
||||
}
|
||||
@@ -586,7 +653,7 @@ bool is_type_untyped(Type *t) {
|
||||
return false;
|
||||
}
|
||||
bool is_type_ordered(Type *t) {
|
||||
t = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
return (t->Basic.flags & BasicFlag_Ordered) != 0;
|
||||
@@ -598,28 +665,42 @@ bool is_type_ordered(Type *t) {
|
||||
return false;
|
||||
}
|
||||
bool is_type_constant_type(Type *t) {
|
||||
t = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_ConstantType) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_float(Type *t) {
|
||||
t = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Float) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_complex(Type *t) {
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Complex) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_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 = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_f32;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_f64(Type *t) {
|
||||
t = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return t->Basic.kind == Basic_f64;
|
||||
}
|
||||
@@ -632,6 +713,10 @@ bool is_type_pointer(Type *t) {
|
||||
}
|
||||
return t->kind == Type_Pointer;
|
||||
}
|
||||
bool is_type_atomic(Type *t) {
|
||||
t = base_type(t);
|
||||
return t->kind == Type_Atomic;
|
||||
}
|
||||
bool is_type_tuple(Type *t) {
|
||||
t = base_type(t);
|
||||
return t->kind == Type_Tuple;
|
||||
@@ -691,6 +776,31 @@ Type *base_vector_type(Type *t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *base_complex_elem_type(Type *t) {
|
||||
t = core_type(t);
|
||||
if (is_type_complex(t)) {
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_complex64: return t_f32;
|
||||
case Basic_complex128: return t_f64;
|
||||
case Basic_UntypedComplex: return t_untyped_float;
|
||||
}
|
||||
}
|
||||
GB_PANIC("Invalid complex type");
|
||||
return t_invalid;
|
||||
}
|
||||
Type *base_quaternion_elem_type(Type *t) {
|
||||
t = core_type(t);
|
||||
if (is_type_quaternion(t)) {
|
||||
switch (t->Basic.kind) {
|
||||
case Basic_quaternion128: return t_f32;
|
||||
case Basic_quaternion256: return t_f64;
|
||||
case Basic_UntypedQuaternion: return t_untyped_float;
|
||||
}
|
||||
}
|
||||
GB_PANIC("Invalid quaternion type");
|
||||
return t_invalid;
|
||||
}
|
||||
|
||||
|
||||
bool is_type_struct(Type *t) {
|
||||
t = base_type(t);
|
||||
@@ -737,7 +847,7 @@ bool is_type_untyped_nil(Type *t) {
|
||||
|
||||
|
||||
bool is_type_valid_for_keys(Type *t) {
|
||||
t = base_type(base_enum_type(t));
|
||||
t = core_type(t);
|
||||
if (is_type_untyped(t)) {
|
||||
return false;
|
||||
}
|
||||
@@ -775,10 +885,17 @@ bool type_has_nil(Type *t) {
|
||||
return false;
|
||||
} break;
|
||||
case Type_Slice:
|
||||
case Type_DynamicArray:
|
||||
case Type_Proc:
|
||||
case Type_Pointer:
|
||||
case Type_DynamicArray:
|
||||
case Type_Map:
|
||||
return true;
|
||||
case Type_Record:
|
||||
switch (t->Record.kind) {
|
||||
case TypeRecord_Union:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -798,7 +915,7 @@ bool is_type_comparable(Type *t) {
|
||||
return true;
|
||||
case Type_Record: {
|
||||
if (is_type_enum(t)) {
|
||||
return is_type_comparable(base_enum_type(t));
|
||||
return is_type_comparable(core_type(t));
|
||||
}
|
||||
return false;
|
||||
} break;
|
||||
@@ -867,13 +984,21 @@ bool are_types_identical(Type *x, Type *y) {
|
||||
x->Record.custom_align == y->Record.custom_align) {
|
||||
// TODO(bill); Fix the custom alignment rule
|
||||
for (isize i = 0; i < x->Record.field_count; i++) {
|
||||
if (!are_types_identical(x->Record.fields[i]->type, y->Record.fields[i]->type)) {
|
||||
Entity *xf = x->Record.fields[i];
|
||||
Entity *yf = y->Record.fields[i];
|
||||
if (!are_types_identical(xf->type, yf->type)) {
|
||||
return false;
|
||||
}
|
||||
if (str_ne(x->Record.fields[i]->token.string, y->Record.fields[i]->token.string)) {
|
||||
if (str_ne(xf->token.string, yf->token.string)) {
|
||||
return false;
|
||||
}
|
||||
bool xf_is_using = (xf->flags&EntityFlag_Using) != 0;
|
||||
bool yf_is_using = (yf->flags&EntityFlag_Using) != 0;
|
||||
if (xf_is_using ^ yf_is_using) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// NOTE(bill): zeroth variant is NULL
|
||||
for (isize i = 1; i < x->Record.variant_count; i++) {
|
||||
if (!are_types_identical(x->Record.variants[i]->type, y->Record.variants[i]->type)) {
|
||||
return false;
|
||||
@@ -946,11 +1071,13 @@ Type *default_type(Type *type) {
|
||||
}
|
||||
if (type->kind == Type_Basic) {
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_UntypedBool: return t_bool;
|
||||
case Basic_UntypedInteger: return t_int;
|
||||
case Basic_UntypedFloat: return t_f64;
|
||||
case Basic_UntypedString: return t_string;
|
||||
case Basic_UntypedRune: return t_rune;
|
||||
case Basic_UntypedBool: return t_bool;
|
||||
case Basic_UntypedInteger: return t_int;
|
||||
case Basic_UntypedFloat: return t_f64;
|
||||
case Basic_UntypedComplex: return t_complex128;
|
||||
case Basic_UntypedQuaternion: return t_quaternion256;
|
||||
case Basic_UntypedString: return t_string;
|
||||
case Basic_UntypedRune: return t_rune;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
@@ -1031,10 +1158,13 @@ typedef enum ProcTypeOverloadKind {
|
||||
|
||||
} ProcTypeOverloadKind;
|
||||
|
||||
|
||||
ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
|
||||
if (!is_type_proc(x)) return ProcOverload_NotProcedure;
|
||||
if (!is_type_proc(y)) return ProcOverload_NotProcedure;
|
||||
if (x == NULL && y == NULL) return ProcOverload_NotProcedure;
|
||||
if (x == NULL && y != NULL) return ProcOverload_NotProcedure;
|
||||
if (x != NULL && y == NULL) return ProcOverload_NotProcedure;
|
||||
if (!is_type_proc(x)) return ProcOverload_NotProcedure;
|
||||
if (!is_type_proc(y)) return ProcOverload_NotProcedure;
|
||||
|
||||
TypeProc px = base_type(x)->Proc;
|
||||
TypeProc py = base_type(y)->Proc;
|
||||
|
||||
@@ -1070,12 +1200,11 @@ ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if (px.params != NULL && py.params != NULL) {
|
||||
Entity *ex = px.params->Tuple.variables[0];
|
||||
Entity *ey = py.params->Tuple.variables[0];
|
||||
bool ok = are_types_identical(ex->type, ey->type);
|
||||
if (ok) {
|
||||
gb_printf_err("Here\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1085,20 +1214,6 @@ ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
|
||||
|
||||
|
||||
|
||||
gb_global Entity *entity__any_type_info = NULL;
|
||||
gb_global Entity *entity__any_data = NULL;
|
||||
gb_global Entity *entity__string_data = NULL;
|
||||
gb_global Entity *entity__string_count = NULL;
|
||||
gb_global Entity *entity__slice_count = NULL;
|
||||
gb_global Entity *entity__slice_capacity = NULL;
|
||||
|
||||
gb_global Entity *entity__dynamic_array_count = NULL;
|
||||
gb_global Entity *entity__dynamic_array_capacity = NULL;
|
||||
gb_global Entity *entity__dynamic_array_allocator = NULL;
|
||||
|
||||
gb_global Entity *entity__dynamic_map_count = NULL;
|
||||
gb_global Entity *entity__dynamic_map_capacity = NULL;
|
||||
gb_global Entity *entity__dynamic_map_allocator = NULL;
|
||||
|
||||
Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel);
|
||||
|
||||
@@ -1151,6 +1266,10 @@ Selection lookup_field_from_index(gbAllocator a, Type *type, i64 index) {
|
||||
return empty_selection;
|
||||
}
|
||||
|
||||
|
||||
gb_global Entity *entity__any_data = NULL;
|
||||
gb_global Entity *entity__any_type_info = NULL;
|
||||
|
||||
Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel) {
|
||||
GB_ASSERT(type_ != NULL);
|
||||
|
||||
@@ -1167,66 +1286,33 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
|
||||
if (type->kind == Type_Basic) {
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_any: {
|
||||
#if 1
|
||||
// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
|
||||
// `Raw_Any` type?
|
||||
String data_str = str_lit("data");
|
||||
String type_info_str = str_lit("type_info");
|
||||
String data_str = str_lit("data");
|
||||
if (entity__any_type_info == NULL) {
|
||||
entity__any_type_info = make_entity_field(a, NULL, make_token_ident(type_info_str), t_type_info_ptr, false, 0);
|
||||
}
|
||||
if (entity__any_data == NULL) {
|
||||
entity__any_data = make_entity_field(a, NULL, make_token_ident(data_str), t_rawptr, false, 1);
|
||||
entity__any_data = make_entity_field(a, NULL, make_token_ident(data_str), t_rawptr, false, 0);
|
||||
}
|
||||
|
||||
if (str_eq(field_name, type_info_str)) {
|
||||
selection_add_index(&sel, 0);
|
||||
sel.entity = entity__any_type_info;
|
||||
return sel;
|
||||
} else if (str_eq(field_name, data_str)) {
|
||||
selection_add_index(&sel, 1);
|
||||
sel.entity = entity__any_data;
|
||||
return sel;
|
||||
}
|
||||
} break;
|
||||
case Basic_string: {
|
||||
String data_str = str_lit("data");
|
||||
String count_str = str_lit("count");
|
||||
if (entity__string_data == NULL) {
|
||||
entity__string_data = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, t_u8), false, 0);
|
||||
}
|
||||
|
||||
if (entity__string_count == NULL) {
|
||||
entity__string_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
|
||||
if (entity__any_type_info == NULL) {
|
||||
entity__any_type_info = make_entity_field(a, NULL, make_token_ident(type_info_str), t_type_info_ptr, false, 1);
|
||||
}
|
||||
|
||||
if (str_eq(field_name, data_str)) {
|
||||
selection_add_index(&sel, 0);
|
||||
sel.entity = entity__string_data;
|
||||
sel.entity = entity__any_data;;
|
||||
return sel;
|
||||
} else if (str_eq(field_name, count_str)) {
|
||||
} else if (str_eq(field_name, type_info_str)) {
|
||||
selection_add_index(&sel, 1);
|
||||
sel.entity = entity__string_count;
|
||||
sel.entity = entity__any_type_info;
|
||||
return sel;
|
||||
}
|
||||
#endif
|
||||
} break;
|
||||
}
|
||||
|
||||
return sel;
|
||||
} else if (type->kind == Type_Array) {
|
||||
String count_str = str_lit("count");
|
||||
// NOTE(bill): Underlying memory address cannot be changed
|
||||
if (str_eq(field_name, count_str)) {
|
||||
// HACK(bill): Memory leak
|
||||
sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Array.count));
|
||||
return sel;
|
||||
}
|
||||
} else if (type->kind == Type_Vector) {
|
||||
String count_str = str_lit("count");
|
||||
// NOTE(bill): Vectors are not addressable
|
||||
if (str_eq(field_name, count_str)) {
|
||||
// HACK(bill): Memory leak
|
||||
sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Vector.count));
|
||||
return sel;
|
||||
}
|
||||
|
||||
if (type->Vector.count <= 4 && !is_type_boolean(type->Vector.elem)) {
|
||||
// HACK(bill): Memory leak
|
||||
switch (type->Vector.count) {
|
||||
@@ -1248,98 +1334,6 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
|
||||
#undef _VECTOR_FIELD_CASE
|
||||
}
|
||||
}
|
||||
|
||||
} else if (type->kind == Type_Slice) {
|
||||
String data_str = str_lit("data");
|
||||
String count_str = str_lit("count");
|
||||
String capacity_str = str_lit("capacity");
|
||||
|
||||
if (str_eq(field_name, data_str)) {
|
||||
selection_add_index(&sel, 0);
|
||||
// HACK(bill): Memory leak
|
||||
sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->Slice.elem), false, 0);
|
||||
return sel;
|
||||
} else if (str_eq(field_name, count_str)) {
|
||||
selection_add_index(&sel, 1);
|
||||
if (entity__slice_count == NULL) {
|
||||
entity__slice_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
|
||||
}
|
||||
|
||||
sel.entity = entity__slice_count;
|
||||
return sel;
|
||||
} else if (str_eq(field_name, capacity_str)) {
|
||||
selection_add_index(&sel, 2);
|
||||
if (entity__slice_capacity == NULL) {
|
||||
entity__slice_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2);
|
||||
}
|
||||
|
||||
sel.entity = entity__slice_capacity;
|
||||
return sel;
|
||||
}
|
||||
|
||||
} else if (type->kind == Type_DynamicArray) {
|
||||
String data_str = str_lit("data");
|
||||
String count_str = str_lit("count");
|
||||
String capacity_str = str_lit("capacity");
|
||||
String allocator_str = str_lit("allocator");
|
||||
|
||||
if (str_eq(field_name, data_str)) {
|
||||
selection_add_index(&sel, 0);
|
||||
// HACK(bill): Memory leak
|
||||
sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->DynamicArray.elem), false, 0);
|
||||
return sel;
|
||||
} else if (str_eq(field_name, count_str)) {
|
||||
selection_add_index(&sel, 1);
|
||||
if (entity__dynamic_array_count == NULL) {
|
||||
entity__dynamic_array_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
|
||||
}
|
||||
sel.entity = entity__dynamic_array_count;
|
||||
return sel;
|
||||
} else if (str_eq(field_name, capacity_str)) {
|
||||
selection_add_index(&sel, 2);
|
||||
if (entity__dynamic_array_capacity == NULL) {
|
||||
entity__dynamic_array_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2);
|
||||
}
|
||||
sel.entity = entity__dynamic_array_capacity;
|
||||
return sel;
|
||||
} else if (str_eq(field_name, allocator_str)) {
|
||||
selection_add_index(&sel, 3);
|
||||
if (entity__dynamic_array_allocator == NULL) {
|
||||
entity__dynamic_array_allocator = make_entity_field(a, NULL, make_token_ident(allocator_str), t_allocator, false, 3);
|
||||
}
|
||||
sel.entity = entity__dynamic_array_allocator;
|
||||
return sel;
|
||||
}
|
||||
} else if (type->kind == Type_Map) {
|
||||
String count_str = str_lit("count");
|
||||
String capacity_str = str_lit("capacity");
|
||||
String allocator_str = str_lit("allocator");
|
||||
|
||||
if (str_eq(field_name, count_str)) {
|
||||
selection_add_index(&sel, 0);
|
||||
if (entity__dynamic_map_count == NULL) {
|
||||
entity__dynamic_map_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 0);
|
||||
entity__dynamic_map_count->Variable.is_immutable = true;
|
||||
}
|
||||
sel.entity = entity__dynamic_map_count;
|
||||
return sel;
|
||||
} else if (str_eq(field_name, capacity_str)) {
|
||||
selection_add_index(&sel, 1);
|
||||
if (entity__dynamic_map_capacity == NULL) {
|
||||
entity__dynamic_map_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 1);
|
||||
entity__dynamic_map_capacity->Variable.is_immutable = true;
|
||||
}
|
||||
sel.entity = entity__dynamic_map_capacity;
|
||||
return sel;
|
||||
} else if (str_eq(field_name, allocator_str)) {
|
||||
selection_add_index(&sel, 2);
|
||||
if (entity__dynamic_map_allocator == NULL) {
|
||||
entity__dynamic_map_allocator = make_entity_field(a, NULL, make_token_ident(allocator_str), t_allocator, false, 2);
|
||||
entity__dynamic_map_allocator->Variable.is_immutable = true;
|
||||
}
|
||||
sel.entity = entity__dynamic_map_allocator;
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type) {
|
||||
@@ -1405,7 +1399,7 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
|
||||
return sel;
|
||||
}
|
||||
|
||||
if (f->flags & EntityFlag_Anonymous) {
|
||||
if (f->flags & EntityFlag_Using) {
|
||||
isize prev_count = sel.index.count;
|
||||
selection_add_index(&sel, i); // HACK(bill): Leaky memory
|
||||
|
||||
@@ -1443,7 +1437,7 @@ void type_path_free(TypePath *tp) {
|
||||
TypePath *type_path_push(TypePath *tp, Type *t) {
|
||||
GB_ASSERT(tp != NULL);
|
||||
|
||||
for_array(i, tp->path) {
|
||||
for (isize i = 0; i < tp->path.count; i++) {
|
||||
if (tp->path.e[i] == t) {
|
||||
// TODO(bill):
|
||||
GB_ASSERT(is_type_named(t));
|
||||
@@ -1524,6 +1518,7 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
|
||||
if (t->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
|
||||
t = base_type(t);
|
||||
|
||||
switch (t->kind) {
|
||||
@@ -1535,6 +1530,11 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
|
||||
|
||||
case Basic_int: case Basic_uint: case Basic_rawptr:
|
||||
return build_context.word_size;
|
||||
|
||||
case Basic_complex64: case Basic_complex128:
|
||||
return type_size_of_internal(allocator, t, path) / 2;
|
||||
case Basic_quaternion128: case Basic_quaternion256:
|
||||
return type_size_of_internal(allocator, t, path) / 4;
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -1623,6 +1623,18 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
|
||||
break;
|
||||
case TypeRecord_Union: {
|
||||
i64 max = 1;
|
||||
if (t->Record.field_count > 0) {
|
||||
Type *field_type = t->Record.fields[0]->type;
|
||||
type_path_push(path, field_type);
|
||||
i64 align = type_align_of_internal(allocator, field_type, path);
|
||||
if (path->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
type_path_pop(path);
|
||||
if (max < align) {
|
||||
max = align;
|
||||
}
|
||||
}
|
||||
// NOTE(bill): field zero is null
|
||||
for (isize i = 1; i < t->Record.variant_count; i++) {
|
||||
Type *variant = t->Record.variants[i]->type;
|
||||
@@ -1675,9 +1687,10 @@ i64 *type_set_offsets_of(gbAllocator allocator, Entity **fields, isize field_cou
|
||||
} else {
|
||||
for (isize i = 0; i < field_count; i++) {
|
||||
i64 align = type_align_of(allocator, fields[i]->type);
|
||||
i64 size = type_size_of(allocator, fields[i]->type);
|
||||
curr_offset = align_formula(curr_offset, align);
|
||||
offsets[i] = curr_offset;
|
||||
curr_offset += type_size_of(allocator, fields[i]->type);
|
||||
curr_offset += size;
|
||||
}
|
||||
}
|
||||
return offsets;
|
||||
@@ -1713,8 +1726,18 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
|
||||
if (t->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
t = base_type(t);
|
||||
|
||||
switch (t->kind) {
|
||||
case Type_Named: {
|
||||
type_path_push(path, t);
|
||||
if (path->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
i64 size = type_size_of_internal(allocator, t->Named.base, path);
|
||||
type_path_pop(path);
|
||||
return size;
|
||||
} break;
|
||||
|
||||
case Type_Basic: {
|
||||
GB_ASSERT(is_type_typed(t));
|
||||
BasicKind kind = t->Basic.kind;
|
||||
@@ -1747,7 +1770,8 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
|
||||
} break;
|
||||
|
||||
case Type_DynamicArray:
|
||||
return 3*build_context.word_size + type_size_of(allocator, t_allocator);
|
||||
// data + len + cap + allocator(procedure+data)
|
||||
return 3*build_context.word_size + 2*build_context.word_size;
|
||||
|
||||
case Type_Vector: {
|
||||
#if 0
|
||||
@@ -1825,15 +1849,35 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
|
||||
} break;
|
||||
|
||||
case TypeRecord_Union: {
|
||||
i64 count = t->Record.variant_count;
|
||||
i64 align = type_align_of_internal(allocator, t, path);
|
||||
if (path->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
|
||||
i64 max = 0;
|
||||
isize field_count = t->Record.field_count;
|
||||
isize variant_count = t->Record.variant_count;
|
||||
|
||||
// Check for recursive types
|
||||
for (isize i = 0; i < field_count; i++) {
|
||||
i64 size = type_size_of_internal(allocator, t->Record.fields[i]->type, path);
|
||||
if (path->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
}
|
||||
// NOTE(bill): Zeroth field is invalid
|
||||
for (isize i = 1; i < count; i++) {
|
||||
i64 size = type_size_of_internal(allocator, t->Record.variants[i]->type, path);
|
||||
type_set_offsets(allocator, t);
|
||||
|
||||
if (field_count > 0) {
|
||||
Type *end_type = t->Record.fields[field_count-1]->type;
|
||||
i64 end_offset = t->Record.offsets[field_count-1];
|
||||
i64 end_size = type_size_of_internal(allocator, end_type, path);
|
||||
max = end_offset + end_size ;
|
||||
}
|
||||
|
||||
for (isize i = 1; i < variant_count; i++) {
|
||||
Type *variant_type = t->Record.variants[i]->type;
|
||||
i64 size = type_size_of_internal(allocator, variant_type, path);
|
||||
if (max < size) {
|
||||
max = size;
|
||||
}
|
||||
@@ -1841,7 +1885,8 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
|
||||
// NOTE(bill): Align to int
|
||||
i64 size = align_formula(max, build_context.word_size);
|
||||
size += type_size_of_internal(allocator, t_int, path);
|
||||
return align_formula(size, align);
|
||||
size = align_formula(size, align);
|
||||
return size;
|
||||
} break;
|
||||
|
||||
case TypeRecord_RawUnion: {
|
||||
@@ -1870,7 +1915,7 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
|
||||
|
||||
i64 type_offset_of(gbAllocator allocator, Type *t, i32 index) {
|
||||
t = base_type(t);
|
||||
if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
|
||||
if (t->kind == Type_Record && (t->Record.kind == TypeRecord_Struct || t->Record.kind == TypeRecord_Union)) {
|
||||
type_set_offsets(allocator, t);
|
||||
if (gb_is_between(index, 0, t->Record.field_count-1)) {
|
||||
return t->Record.offsets[index];
|
||||
@@ -1975,13 +2020,18 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
str = write_type_to_string(str, type->Pointer.elem);
|
||||
break;
|
||||
|
||||
case Type_Atomic:
|
||||
str = gb_string_appendc(str, "atomic ");
|
||||
str = write_type_to_string(str, type->Atomic.elem);
|
||||
break;
|
||||
|
||||
case Type_Array:
|
||||
str = gb_string_appendc(str, gb_bprintf("[%lld]", type->Array.count));
|
||||
str = gb_string_appendc(str, gb_bprintf("[%d]", cast(int)type->Array.count));
|
||||
str = write_type_to_string(str, type->Array.elem);
|
||||
break;
|
||||
|
||||
case Type_Vector:
|
||||
str = gb_string_appendc(str, gb_bprintf("[vector %lld]", type->Vector.count));
|
||||
str = gb_string_appendc(str, gb_bprintf("[vector %d]", cast(int)type->Vector.count));
|
||||
str = write_type_to_string(str, type->Vector.elem);
|
||||
break;
|
||||
|
||||
@@ -2029,7 +2079,7 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
}
|
||||
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
|
||||
str = gb_string_appendc(str, ": ");
|
||||
str = write_type_to_string(str, base_type(f->type));
|
||||
str = write_type_to_string(str, f->type);
|
||||
}
|
||||
for (isize i = 1; i < type->Record.variant_count; i++) {
|
||||
Entity *f = type->Record.variants[i];
|
||||
@@ -2095,7 +2145,7 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
case Type_Map: {
|
||||
str = gb_string_appendc(str, "map[");
|
||||
if (type->Map.count > 0) {
|
||||
str = gb_string_appendc(str, gb_bprintf("%lld, ", type->Map.count));
|
||||
str = gb_string_appendc(str, gb_bprintf("%d, ", cast(int)type->Map.count));
|
||||
}
|
||||
str = write_type_to_string(str, type->Map.key);
|
||||
str = gb_string_appendc(str, "]");
|
||||
|
||||
Reference in New Issue
Block a user