mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 03:01:38 -07:00
Compare commits
67 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4afb3f8fa4 | |||
| 207b252f23 | |||
| 1356dfeec2 | |||
| d025791462 | |||
| b07ee9ec23 | |||
| 915b5cdab7 | |||
| c8f99b360f | |||
| b76f6a8c27 | |||
| cff1b3dff6 | |||
| 883dd0642c | |||
| 40f5dd56f7 | |||
| 70d4ca00df | |||
| a86896e4d3 | |||
| a3883a178c | |||
| ce89a1428e | |||
| 9202bd1b06 | |||
| a48e0c7179 | |||
| 3f1195cd03 | |||
| 311b5cb6e2 | |||
| 6fef74317c | |||
| 0c6775ca14 | |||
| 6748f305db | |||
| 2ecafda1d3 | |||
| 23d32f34e5 | |||
| d714bece47 | |||
| 923b039cf6 | |||
| d0e1efe622 | |||
| 478d63424f | |||
| ac1566762b | |||
| f5eeecaca5 | |||
| 77e219d442 | |||
| 4c10fbdcd4 | |||
| e370337f97 | |||
| 5217eb55b4 | |||
| 062a2c63e1 | |||
| b957365b0d | |||
| 625b98eac4 | |||
| 0f809989e3 | |||
| d4457e9fa4 | |||
| 9634b28b07 | |||
| f567983260 | |||
| ad84314143 | |||
| a6f8c9d6e0 | |||
| de9016b7d0 | |||
| e8b4228833 | |||
| 0d69dfcde6 | |||
| fa89d2775a | |||
| 60b6538a7a | |||
| d9bd770992 | |||
| 517b34f798 | |||
| a16bdb215a | |||
| 88aa74bbb9 | |||
| 8ec9811d7a | |||
| c71b547cde | |||
| 76e724718c | |||
| 0b87313f08 | |||
| 4bb45700a5 | |||
| be8b9bda2f | |||
| ab2ca7cf59 | |||
| b76c8abe73 | |||
| d9c686b53d | |||
| b232b9d5ea | |||
| 348bcc3f9a | |||
| 4dd8552c32 | |||
| 492490f7f6 | |||
| 598dab5bc4 | |||
| cbb70c7873 |
@@ -30,31 +30,27 @@ Odin is fast, concise, readable, pragmatic and open sourced. It is designed with
|
||||
## Warnings
|
||||
|
||||
* This is still highly in development and the language's design is quite volatile.
|
||||
* Syntax is not fixed.
|
||||
* Syntax is definitely not fixed
|
||||
|
||||
## Roadmap
|
||||
|
||||
Not in any particular order
|
||||
|
||||
* Compile Time Execution (CTE)
|
||||
- More metaprogramming madness
|
||||
- Compiler as a library
|
||||
- AST inspection and modification
|
||||
* CTE-based build system
|
||||
* Replace LLVM backend with my own custom backend
|
||||
* Improve SSA design to accommodate for lowering to a "bytecode"
|
||||
* SSA optimizations
|
||||
* Parametric Polymorphism ("Generics")
|
||||
* Documentation Generator for "Entities"
|
||||
* Multiple Architecture support
|
||||
* Language level atomics and concurrency support
|
||||
* Linking Options
|
||||
* Custom backend to replace LLVM
|
||||
- Improve SSA design to accommodate for lowering to a "bytecode"
|
||||
- SSA optimizations
|
||||
- COFF generation
|
||||
- linker
|
||||
* Type safe "macros"
|
||||
* Documentation generator for "Entities"
|
||||
* Multiple architecture support
|
||||
* Inline assembly
|
||||
* Linking options
|
||||
- Executable
|
||||
- Static/Dynamic Library
|
||||
* Debug Information
|
||||
* Debug information
|
||||
- pdb format too
|
||||
* Command Line Tooling
|
||||
* Compiler Internals:
|
||||
* Command line tooling
|
||||
* Compiler internals:
|
||||
- Big numbers library
|
||||
- Cyclic Type Checking (at the moment will cause compiler to go into an infinite loop)
|
||||
- Multithreading for performance increase
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ set exe_name=odin.exe
|
||||
:: Debug = 0, Release = 1
|
||||
set release_mode=1
|
||||
|
||||
set compiler_flags= -nologo -Oi -TC -W4 -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||
set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||
@@ -16,7 +16,7 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
)
|
||||
|
||||
set compiler_warnings= ^
|
||||
-we4013 -we4706 -we4002 ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
||||
-wd4201 -wd4204 -wd4244 ^
|
||||
-wd4306 ^
|
||||
@@ -40,19 +40,20 @@ set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
|
||||
set linker_settings=%libs% %linker_flags%
|
||||
|
||||
|
||||
rem set build_dir= "\"
|
||||
rem if not exist %build_dir% mkdir %build_dir%
|
||||
rem pushd %build_dir%
|
||||
del *.pdb > NUL 2> NUL
|
||||
del *.ilk > NUL 2> NUL
|
||||
del *.pdb > NUL 2> NUL
|
||||
del *.ilk > NUL 2> NUL
|
||||
|
||||
cl %compiler_settings% "src\main.c" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
cl %compiler_settings% "src\main.c" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
&& odin run code/demo.odin
|
||||
rem odin run code/demo.odin
|
||||
rem && odin build_dll code/example.odin ^
|
||||
rem && odin run code/demo.odin
|
||||
|
||||
|
||||
:do_not_compile_exe
|
||||
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
|
||||
|
||||
:end_of_build
|
||||
|
||||
|
||||
+65
-41
@@ -1,46 +1,70 @@
|
||||
#import "fmt.odin"
|
||||
#foreign_system_library "winmm" when ODIN_OS == "windows";
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#import "fmt.odin";
|
||||
|
||||
variadic :: proc(args: ..any) {
|
||||
for i := 0; i < args.count; i++ {
|
||||
match type a : args[i] {
|
||||
case u128: fmt.println("u128", a)
|
||||
case i128: fmt.println("i128", a)
|
||||
}
|
||||
}
|
||||
timeGetTime :: proc() -> u32 #foreign #dll_import
|
||||
GetSystemTimeAsFileTime :: proc(SystemTimeAsFileTime : ^win32.FILETIME) #foreign #dll_import
|
||||
|
||||
fmt.println(..args)
|
||||
GetCommandLineArguments :: proc() -> []string {
|
||||
argString := win32.GetCommandLineA();
|
||||
fullArgString := to_odin_string(argString);
|
||||
// Count Spaces
|
||||
for r : fullArgString {
|
||||
fmt.println(r);
|
||||
}
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
fmt.println("Hellope, everybody!")
|
||||
|
||||
variadic(1 as u128, 1 as i128)
|
||||
|
||||
// x: i128 = 321312321
|
||||
// y: i128 = 123123123
|
||||
// z: i128
|
||||
// x *= x; x *= x
|
||||
// y *= y; y *= y
|
||||
// fmt.println("x =", x)
|
||||
// fmt.println("y =", y)
|
||||
// z = x + y; fmt.println("x + y", z)
|
||||
// z = x - y; fmt.println("x - y", z)
|
||||
// z = x * y; fmt.println("x * y", z)
|
||||
// z = x / y; fmt.println("x / y", z)
|
||||
// z = x % y; fmt.println("x % y", z)
|
||||
// z = x & y; fmt.println("x & y", z)
|
||||
// z = x ~ y; fmt.println("x ~ y", z)
|
||||
// z = x | y; fmt.println("x | y", z)
|
||||
// z = x &~ y; fmt.println("x &~ y", z)
|
||||
|
||||
// z = -x
|
||||
// z = ~x
|
||||
|
||||
// b: bool
|
||||
// b = x == y; fmt.println("x == y", b)
|
||||
// b = x != y; fmt.println("x != y", b)
|
||||
// b = x < y; fmt.println("x < y", b)
|
||||
// b = x <= y; fmt.println("x <= y", b)
|
||||
// b = x > y; fmt.println("x > y", b)
|
||||
// b = x >= y; fmt.println("x >= y", b)
|
||||
to_odin_string :: proc(c: ^byte) -> string {
|
||||
s: string;
|
||||
s.data = c;
|
||||
while (c + s.count)^ != 0 {
|
||||
s.count += 1;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
//("Hellope!\x00" as string).data
|
||||
|
||||
MAGIC_VALUE :: 0xCA5E713F;
|
||||
|
||||
timing_file_header :: struct #ordered {
|
||||
MagicValue : u32;
|
||||
}
|
||||
|
||||
timing_file_date :: struct #ordered {
|
||||
E : [2]u32;
|
||||
}
|
||||
|
||||
timing_file_entry_flag :: enum {
|
||||
Complete = 0x1,
|
||||
NoErrors = 0x2,
|
||||
}
|
||||
|
||||
timing_file_entry :: struct #ordered {
|
||||
StarDate : timing_file_date;
|
||||
Flags : u32;
|
||||
MillisecondsElapsed : u32;
|
||||
}
|
||||
|
||||
timing_entry_array :: struct #ordered {
|
||||
Entries : []timing_file_entry;
|
||||
}
|
||||
|
||||
GetClock :: proc () -> u32 {
|
||||
return timeGetTime();
|
||||
}
|
||||
|
||||
GetDate :: proc() -> timing_file_date {
|
||||
Result : timing_file_date;
|
||||
FileTime : win32.FILETIME;
|
||||
GetSystemTimeAsFileTime(^FileTime);
|
||||
|
||||
Result.E[0] = FileTime.lo;
|
||||
Result.E[1] = FileTime.hi;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
main :: proc () {
|
||||
EntryClock := GetClock();
|
||||
GetCommandLineArguments();
|
||||
}
|
||||
|
||||
+94
-94
@@ -1,60 +1,60 @@
|
||||
#import "win32.odin"
|
||||
#import "fmt.odin"
|
||||
#import "math.odin"
|
||||
#import "os.odin"
|
||||
#import "opengl.odin" as gl
|
||||
#import "win32.odin" when ODIN_OS == "windows";
|
||||
#import "fmt.odin";
|
||||
#import "math.odin";
|
||||
#import "os.odin";
|
||||
#import gl "opengl.odin";
|
||||
|
||||
TWO_HEARTS :: #rune "💕"
|
||||
TWO_HEARTS :: '💕';
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency()
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
|
||||
time_now :: proc() -> f64 {
|
||||
assert(win32_perf_count_freq != 0)
|
||||
assert(win32_perf_count_freq != 0);
|
||||
|
||||
counter: i64
|
||||
win32.QueryPerformanceCounter(^counter)
|
||||
result := counter as f64 / win32_perf_count_freq as f64
|
||||
return result
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := counter as f64 / win32_perf_count_freq as f64;
|
||||
return result;
|
||||
}
|
||||
win32_print_last_error :: proc() {
|
||||
err_code := win32.GetLastError() as int
|
||||
err_code := win32.GetLastError() as int;
|
||||
if err_code != 0 {
|
||||
fmt.println("GetLastError: %", err_code)
|
||||
fmt.println("GetLastError: %", err_code);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
return c_str
|
||||
c_str := new_slice(u8, s.count+1);
|
||||
copy(c_str, s as []byte);
|
||||
c_str[s.count] = 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: win32.HGLRC;
|
||||
c_title: []u8;
|
||||
}
|
||||
|
||||
make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC) -> (Window, bool) {
|
||||
using win32
|
||||
using win32;
|
||||
|
||||
w: Window
|
||||
w.width, w.height = msg, height
|
||||
w: Window;
|
||||
w.width, w.height = msg, height;
|
||||
|
||||
class_name := "Win32-Odin-Window\x00"
|
||||
c_class_name := class_name.data
|
||||
class_name := "Win32-Odin-Window\x00";
|
||||
c_class_name := class_name.data;
|
||||
if title[title.count-1] != 0 {
|
||||
w.c_title = to_c_string(title)
|
||||
w.c_title = to_c_string(title);
|
||||
} else {
|
||||
w.c_title = title as []u8
|
||||
w.c_title = title as []u8;
|
||||
}
|
||||
|
||||
instance := GetModuleHandleA(nil)
|
||||
instance := GetModuleHandleA(nil);
|
||||
|
||||
w.wc = WNDCLASSEXA{
|
||||
size = size_of(WNDCLASSEXA) as u32,
|
||||
@@ -65,8 +65,8 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
|
||||
};
|
||||
|
||||
if RegisterClassExA(^w.wc) == 0 {
|
||||
win32_print_last_error()
|
||||
return w, false
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.hwnd = CreateWindowExA(0,
|
||||
@@ -74,14 +74,14 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
|
||||
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
w.width as i32, w.height as i32,
|
||||
nil, nil, instance, nil)
|
||||
nil, nil, instance, nil);
|
||||
|
||||
if w.hwnd == nil {
|
||||
win32_print_last_error()
|
||||
return w, false
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.dc = GetDC(w.hwnd)
|
||||
w.dc = GetDC(w.hwnd);
|
||||
|
||||
{
|
||||
pfd := PIXELFORMATDESCRIPTOR{
|
||||
@@ -94,122 +94,122 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
|
||||
depth_bits = 24,
|
||||
stencil_bits = 8,
|
||||
layer_type = PFD_MAIN_PLANE,
|
||||
}
|
||||
};
|
||||
|
||||
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil)
|
||||
w.opengl_context = wglCreateContext(w.dc)
|
||||
wglMakeCurrent(w.dc, w.opengl_context)
|
||||
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil);
|
||||
w.opengl_context = wglCreateContext(w.dc);
|
||||
wglMakeCurrent(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,
|
||||
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)
|
||||
SwapBuffers(w.dc)
|
||||
wglCreateContextAttribsARB := wglGetProcAddress(("wglCreateContextAttribsARB\x00" as string).data) as wglCreateContextAttribsARBType;
|
||||
w.rc = wglCreateContextAttribsARB(w.dc, 0, ^attribs[0]);
|
||||
wglMakeCurrent(w.dc, w.rc);
|
||||
SwapBuffers(w.dc);
|
||||
}
|
||||
|
||||
return w, true
|
||||
return w, true;
|
||||
}
|
||||
|
||||
destroy_window :: proc(w: ^Window) {
|
||||
free(w.c_title.data)
|
||||
free(w.c_title.data);
|
||||
}
|
||||
|
||||
display_window :: proc(w: ^Window) {
|
||||
win32.SwapBuffers(w.dc)
|
||||
win32.SwapBuffers(w.dc);
|
||||
}
|
||||
|
||||
|
||||
run :: proc() {
|
||||
using win32
|
||||
using math
|
||||
using win32;
|
||||
using math;
|
||||
|
||||
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline {
|
||||
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
|
||||
os.exit(0)
|
||||
return 0
|
||||
os.exit(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam)
|
||||
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, win32_proc);
|
||||
if !window_success {
|
||||
return
|
||||
return;
|
||||
}
|
||||
defer destroy_window(^window)
|
||||
defer destroy_window(^window);
|
||||
|
||||
gl.init()
|
||||
gl.init();
|
||||
|
||||
|
||||
prev_time := time_now()
|
||||
running := true
|
||||
prev_time := time_now();
|
||||
running := true;
|
||||
|
||||
pos := Vec2{100, 100}
|
||||
pos := Vec2{100, 100};
|
||||
|
||||
for running {
|
||||
curr_time := time_now()
|
||||
dt := (curr_time - prev_time) as f32
|
||||
prev_time = curr_time
|
||||
curr_time := time_now();
|
||||
dt := (curr_time - prev_time) as f32;
|
||||
prev_time = curr_time;
|
||||
|
||||
msg: MSG
|
||||
msg: MSG;
|
||||
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
|
||||
if msg.message == WM_QUIT {
|
||||
running = false
|
||||
running = false;
|
||||
}
|
||||
TranslateMessage(^msg)
|
||||
DispatchMessageA(^msg)
|
||||
TranslateMessage(^msg);
|
||||
DispatchMessageA(^msg);
|
||||
}
|
||||
|
||||
if is_key_down(Key_Code.ESCAPE) {
|
||||
running = false
|
||||
running = false;
|
||||
}
|
||||
|
||||
{
|
||||
SPEED :: 500
|
||||
v: Vec2
|
||||
SPEED :: 500;
|
||||
v: Vec2;
|
||||
|
||||
if is_key_down(Key_Code.RIGHT) { v[0] += 1 }
|
||||
if is_key_down(Key_Code.LEFT) { v[0] -= 1 }
|
||||
if is_key_down(Key_Code.UP) { v[1] += 1 }
|
||||
if is_key_down(Key_Code.DOWN) { v[1] -= 1 }
|
||||
if is_key_down(Key_Code.RIGHT) { v[0] += 1; }
|
||||
if is_key_down(Key_Code.LEFT) { v[0] -= 1; }
|
||||
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 = vec2_norm0(v);
|
||||
|
||||
pos += v * Vec2{SPEED * dt}
|
||||
pos += v * Vec2{SPEED * dt};
|
||||
}
|
||||
|
||||
|
||||
gl.ClearColor(0.5, 0.7, 1.0, 1.0)
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT)
|
||||
gl.ClearColor(0.5, 0.7, 1.0, 1.0);
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.LoadIdentity()
|
||||
gl.LoadIdentity();
|
||||
gl.Ortho(0, window.width as f64,
|
||||
0, window.height as f64, 0, 1)
|
||||
0, window.height as f64, 0, 1);
|
||||
|
||||
draw_rect :: proc(x, y, w, h: f32) {
|
||||
gl.Begin(gl.TRIANGLES)
|
||||
defer gl.End()
|
||||
gl.Begin(gl.TRIANGLES);
|
||||
defer gl.End();
|
||||
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0)
|
||||
gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y, 0)
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0)
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||
gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y, 0);
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
||||
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0)
|
||||
gl.Color3f(1, 1, 0); gl.Vertex3f(x, y+h, 0)
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0)
|
||||
gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
|
||||
gl.Color3f(1, 1, 0); gl.Vertex3f(x, y+h, 0);
|
||||
gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0);
|
||||
}
|
||||
|
||||
draw_rect(pos.x, pos.y, 50, 50)
|
||||
draw_rect(pos.x, pos.y, 50, 50);
|
||||
|
||||
display_window(^window)
|
||||
ms_to_sleep := (16 - 1000*dt) as i32
|
||||
display_window(^window);
|
||||
ms_to_sleep := (16 - 1000*dt) as i32;
|
||||
if ms_to_sleep > 0 {
|
||||
win32.Sleep(ms_to_sleep)
|
||||
win32.Sleep(ms_to_sleep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-6
@@ -1,6 +1,6 @@
|
||||
#import "fmt.odin" as fmt
|
||||
#import "fmt.odin"
|
||||
|
||||
#foreign_system_library "Ws2_32"
|
||||
#foreign_system_library "Ws2_32" when ODIN_OS == "windows"
|
||||
|
||||
|
||||
SOCKET :: type uint
|
||||
@@ -95,7 +95,7 @@ WSAGetLastError :: proc() -> i32
|
||||
|
||||
to_c_string :: proc(s: string) -> ^byte {
|
||||
c_str := new_slice(byte, s.count+1)
|
||||
assert(c_str.data != null)
|
||||
assert(c_str.data != nil)
|
||||
copy(c_str, s as []byte)
|
||||
c_str[s.count] = 0
|
||||
return c_str.data
|
||||
@@ -103,7 +103,7 @@ to_c_string :: proc(s: string) -> ^byte {
|
||||
|
||||
run :: proc() {
|
||||
wsa: WSADATA
|
||||
res: ^addrinfo = null
|
||||
res: ^addrinfo = nil
|
||||
hints: addrinfo
|
||||
s, client: SOCKET
|
||||
|
||||
@@ -118,7 +118,7 @@ run :: proc() {
|
||||
hints.protocol = IPPROTO_TCP
|
||||
hints.flags = AI_PASSIVE
|
||||
|
||||
if getaddrinfo(null, to_c_string("8080"), ^hints, ^res) != 0 {
|
||||
if getaddrinfo(nil, to_c_string("8080"), ^hints, ^res) != 0 {
|
||||
fmt.println("getaddrinfo failed: ", WSAGetLastError())
|
||||
return
|
||||
}
|
||||
@@ -134,7 +134,7 @@ run :: proc() {
|
||||
bind(s, res.addr, res.addrlen as i32)
|
||||
listen(s, SOMAXCONN)
|
||||
|
||||
client = accept(s, null, null)
|
||||
client = accept(s, nil, 0)
|
||||
if client == INVALID_SOCKET {
|
||||
fmt.println("socket failed: ", WSAGetLastError())
|
||||
return
|
||||
|
||||
@@ -301,7 +301,7 @@ namespaces_and_files :: proc() {
|
||||
#import "file.odin" as _
|
||||
|
||||
// Exporting import
|
||||
#load "file.odin"
|
||||
#include "file.odin"
|
||||
*/
|
||||
|
||||
// Talk about scope rules and diagram
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Demo 002
|
||||
#load "basic.odin"
|
||||
#load "math.odin"
|
||||
// #load "game.odin"
|
||||
#include "basic.odin"
|
||||
#include "math.odin"
|
||||
// #include "game.odin"
|
||||
|
||||
#thread_local tls_int: int
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#load "win32.odin"
|
||||
#include "win32.odin"
|
||||
|
||||
assume :: proc(cond: bool) #foreign "llvm.assume"
|
||||
|
||||
|
||||
+19
-21
@@ -6,7 +6,7 @@ CANVAS_WIDTH :: 128
|
||||
CANVAS_HEIGHT :: 128
|
||||
CANVAS_SCALE :: 3
|
||||
FRAME_TIME :: 1.0/30.0
|
||||
WINDOW_TITLE : string : "Punity\x00"
|
||||
WINDOW_TITLE :: "Punity\x00"
|
||||
|
||||
_ := compile_assert(CANVAS_WIDTH % 16 == 0)
|
||||
|
||||
@@ -40,7 +40,7 @@ Core :: struct {
|
||||
|
||||
frame: i64
|
||||
|
||||
canvas: Canvas
|
||||
canvas: Canvas
|
||||
draw_list: ^Draw_List
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ Bank_State :: struct {
|
||||
|
||||
|
||||
Color :: raw_union {
|
||||
using channels: struct{ a, b, g, r: byte }
|
||||
using channels: struct{ a, b, g, r: byte; }
|
||||
rgba: u32
|
||||
}
|
||||
|
||||
@@ -297,7 +297,6 @@ _core: Core
|
||||
run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
using win32
|
||||
|
||||
|
||||
_core.running = true
|
||||
|
||||
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline #stdcall {
|
||||
@@ -305,16 +304,16 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
mods: u32 = 0
|
||||
|
||||
if is_key_down(Key_Code.SHIFT) {
|
||||
mods |= Key.MOD_SHIFT as u32;
|
||||
mods |= Key.MOD_SHIFT as u32
|
||||
}
|
||||
if is_key_down(Key_Code.CONTROL) {
|
||||
mods |= Key.MOD_CONTROL as u32;
|
||||
mods |= Key.MOD_CONTROL as u32
|
||||
}
|
||||
if is_key_down(Key_Code.MENU) {
|
||||
mods |= Key.MOD_ALT as u32;
|
||||
mods |= Key.MOD_ALT as u32
|
||||
}
|
||||
if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
|
||||
mods |= Key.MOD_SUPER as u32;
|
||||
mods |= Key.MOD_SUPER as u32
|
||||
}
|
||||
|
||||
return mods
|
||||
@@ -351,7 +350,7 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
class_name = ("Punity\x00" as string).data, // C-style string
|
||||
size = size_of(WNDCLASSEXA) as u32,
|
||||
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
||||
instance = GetModuleHandleA(null) as HINSTANCE,
|
||||
instance = GetModuleHandleA(nil) as HINSTANCE,
|
||||
wnd_proc = win32_proc,
|
||||
// wnd_proc = DefWindowProcA,
|
||||
background = GetStockObject(BLACK_BRUSH) as HBRUSH,
|
||||
@@ -382,16 +381,16 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
style,
|
||||
rc.left, rc.top,
|
||||
rc.right-rc.left, rc.bottom-rc.top,
|
||||
null, null, window_class.instance,
|
||||
null);
|
||||
nil, nil, window_class.instance,
|
||||
nil)
|
||||
|
||||
if win32_window == null {
|
||||
if win32_window == nil {
|
||||
fmt.fprintln(os.stderr, "CreateWindowExA failed")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
window_bmi: BITMAPINFO;
|
||||
window_bmi: BITMAPINFO
|
||||
window_bmi.size = size_of(BITMAPINFO.HEADER) as u32
|
||||
window_bmi.width = CANVAS_WIDTH
|
||||
window_bmi.height = CANVAS_HEIGHT
|
||||
@@ -405,8 +404,8 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
|
||||
ShowWindow(win32_window, SW_SHOW)
|
||||
|
||||
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
||||
assert(window_buffer.data != null)
|
||||
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT)
|
||||
assert(window_buffer.data != nil)
|
||||
defer free(window_buffer.data)
|
||||
|
||||
for i := 0; i < window_buffer.count; i++ {
|
||||
@@ -418,8 +417,8 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
prev_time = time_now()
|
||||
curr_time = time_now()
|
||||
total_time : f64 = 0
|
||||
offset_x := 0;
|
||||
offset_y := 0;
|
||||
offset_x := 0
|
||||
offset_y := 0
|
||||
|
||||
message: MSG
|
||||
for _core.running {
|
||||
@@ -447,10 +446,9 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
}
|
||||
}
|
||||
|
||||
memory_zero(^_core.key_deltas[0], size_of_val(_core.key_deltas[0]))
|
||||
_core.key_deltas = nil
|
||||
|
||||
|
||||
for PeekMessageA(^message, null, 0, 0, PM_REMOVE) != 0 {
|
||||
for PeekMessageA(^message, nil, 0, 0, PM_REMOVE) != 0 {
|
||||
if message.message == WM_QUIT {
|
||||
_core.running = false
|
||||
}
|
||||
@@ -460,7 +458,7 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
|
||||
user_step(^_core)
|
||||
|
||||
dc := GetDC(win32_window);
|
||||
dc := GetDC(win32_window)
|
||||
StretchDIBits(dc,
|
||||
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
||||
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ thing :: proc() {
|
||||
*/
|
||||
|
||||
/*
|
||||
#load "fmt.odin"
|
||||
#include "fmt.odin"
|
||||
|
||||
thing :: proc() {
|
||||
println("Hello5!")
|
||||
|
||||
+179
-210
@@ -1,109 +1,93 @@
|
||||
#shared_global_scope
|
||||
#shared_global_scope;
|
||||
|
||||
#import "os.odin"
|
||||
#import "fmt.odin"
|
||||
#import "mem.odin"
|
||||
#import "os.odin";
|
||||
#import "fmt.odin";
|
||||
#import "mem.odin";
|
||||
#import "utf8.odin";
|
||||
|
||||
/*
|
||||
Optimization_Level :: enum {
|
||||
DEBUG,
|
||||
RELEASE,
|
||||
}
|
||||
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
|
||||
// #shared_global_scope due to the internals of the compiler.
|
||||
// This could change at a later date if the all these data structures are
|
||||
// implemented within the compiler rather than in this "preload" file
|
||||
|
||||
Bounds_Check_Mode :: enum {
|
||||
ON,
|
||||
OFF,
|
||||
}
|
||||
|
||||
Build_Options :: struct {
|
||||
optimization_level: Optimization_Level
|
||||
|
||||
bounds_check: Bounds_Check_Mode
|
||||
|
||||
output_name: string
|
||||
output_path: string
|
||||
}
|
||||
|
||||
build_options: Build_Options
|
||||
*/
|
||||
|
||||
// IMPORTANT NOTE(bill): Do not change the order of any of this data
|
||||
// The compiler relies upon this _exact_ order
|
||||
Type_Info :: union {
|
||||
Member :: struct #ordered {
|
||||
name: string // can be empty if tuple
|
||||
type_info: ^Type_Info
|
||||
offset: int // offsets are not used in tuples
|
||||
}
|
||||
Record :: struct #ordered {
|
||||
fields: []Member
|
||||
size: int // in bytes
|
||||
align: int // in bytes
|
||||
packed: bool
|
||||
ordered: bool
|
||||
}
|
||||
Type_Info_Member :: struct #ordered {
|
||||
name: string; // can be empty if tuple
|
||||
type_info: ^Type_Info;
|
||||
offset: int; // offsets are not used in tuples
|
||||
}
|
||||
Type_Info_Record :: struct #ordered {
|
||||
fields: []Type_Info_Member;
|
||||
size: int; // in bytes
|
||||
align: int; // in bytes
|
||||
packed: bool;
|
||||
ordered: bool;
|
||||
}
|
||||
|
||||
Type_Info :: union {
|
||||
Named: struct #ordered {
|
||||
name: string
|
||||
base: ^Type_Info // This will _not_ be a Type_Info.Named
|
||||
}
|
||||
name: string;
|
||||
base: ^Type_Info; // This will _not_ be a Type_Info.Named
|
||||
};
|
||||
Integer: struct #ordered {
|
||||
size: int // in bytes
|
||||
signed: bool
|
||||
}
|
||||
size: int; // in bytes
|
||||
signed: bool;
|
||||
};
|
||||
Float: struct #ordered {
|
||||
size: int // in bytes
|
||||
}
|
||||
Any: struct #ordered {}
|
||||
String: struct #ordered {}
|
||||
Boolean: struct #ordered {}
|
||||
size: int; // in bytes
|
||||
};
|
||||
Any: struct #ordered {};
|
||||
String: struct #ordered {};
|
||||
Boolean: struct #ordered {};
|
||||
Pointer: struct #ordered {
|
||||
elem: ^Type_Info // nil -> rawptr
|
||||
}
|
||||
elem: ^Type_Info; // nil -> rawptr
|
||||
};
|
||||
Maybe: struct #ordered {
|
||||
elem: ^Type_Info
|
||||
}
|
||||
elem: ^Type_Info;
|
||||
};
|
||||
Procedure: struct #ordered {
|
||||
params: ^Type_Info // Type_Info.Tuple
|
||||
results: ^Type_Info // Type_Info.Tuple
|
||||
variadic: bool
|
||||
}
|
||||
params: ^Type_Info; // Type_Info.Tuple
|
||||
results: ^Type_Info; // Type_Info.Tuple
|
||||
variadic: bool;
|
||||
};
|
||||
Array: struct #ordered {
|
||||
elem: ^Type_Info
|
||||
elem_size: int
|
||||
count: int
|
||||
}
|
||||
elem: ^Type_Info;
|
||||
elem_size: int;
|
||||
count: int;
|
||||
};
|
||||
Slice: struct #ordered {
|
||||
elem: ^Type_Info
|
||||
elem_size: int
|
||||
}
|
||||
elem: ^Type_Info;
|
||||
elem_size: int;
|
||||
};
|
||||
Vector: struct #ordered {
|
||||
elem: ^Type_Info
|
||||
elem_size: int
|
||||
count: int
|
||||
align: int
|
||||
}
|
||||
Tuple: Record
|
||||
Struct: Record
|
||||
Union: Record
|
||||
Raw_Union: Record
|
||||
elem: ^Type_Info;
|
||||
elem_size: int;
|
||||
count: int;
|
||||
align: int;
|
||||
};
|
||||
Tuple: Type_Info_Record;
|
||||
Struct: Type_Info_Record;
|
||||
Union: Type_Info_Record;
|
||||
Raw_Union: Type_Info_Record;
|
||||
Enum: struct #ordered {
|
||||
base: ^Type_Info
|
||||
values: []i64
|
||||
names: []string
|
||||
}
|
||||
base: ^Type_Info;
|
||||
names: []string;
|
||||
// TODO(bill): store values some how. Maybe using a raw_union
|
||||
};
|
||||
}
|
||||
|
||||
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
|
||||
if info == nil {
|
||||
return nil
|
||||
return nil;
|
||||
}
|
||||
base := info
|
||||
base := info;
|
||||
match type i : base {
|
||||
case Type_Info.Named:
|
||||
base = i.base
|
||||
base = i.base;
|
||||
}
|
||||
return base
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,159 +114,157 @@ fmuladd64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64"
|
||||
|
||||
|
||||
|
||||
|
||||
Allocator :: struct #ordered {
|
||||
Mode :: enum {
|
||||
ALLOC,
|
||||
FREE,
|
||||
FREE_ALL,
|
||||
RESIZE,
|
||||
}
|
||||
Proc :: type proc(allocator_data: rawptr, mode: Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr
|
||||
|
||||
|
||||
procedure: Proc;
|
||||
data: rawptr
|
||||
Allocator_Mode :: enum u8 {
|
||||
ALLOC = iota,
|
||||
FREE,
|
||||
FREE_ALL,
|
||||
RESIZE,
|
||||
}
|
||||
Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr;
|
||||
Allocator :: struct #ordered {
|
||||
procedure: Allocator_Proc;
|
||||
data: rawptr;
|
||||
}
|
||||
|
||||
|
||||
Context :: struct #ordered {
|
||||
thread_id: int
|
||||
thread_id: int;
|
||||
|
||||
allocator: Allocator
|
||||
allocator: Allocator;
|
||||
|
||||
user_data: rawptr
|
||||
user_index: int
|
||||
user_data: rawptr;
|
||||
user_index: int;
|
||||
}
|
||||
|
||||
#thread_local __context: Context
|
||||
#thread_local __context: Context;
|
||||
|
||||
|
||||
DEFAULT_ALIGNMENT :: align_of({4}f32)
|
||||
DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
|
||||
|
||||
|
||||
__check_context :: proc() {
|
||||
c := ^__context
|
||||
c := ^__context;
|
||||
|
||||
if c.allocator.procedure == nil {
|
||||
c.allocator = default_allocator()
|
||||
c.allocator = default_allocator();
|
||||
}
|
||||
if c.thread_id == 0 {
|
||||
c.thread_id = os.current_thread_id()
|
||||
c.thread_id = os.current_thread_id();
|
||||
}
|
||||
}
|
||||
|
||||
alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) }
|
||||
alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT); }
|
||||
|
||||
alloc_align :: proc(size, alignment: int) -> rawptr #inline {
|
||||
__check_context()
|
||||
a := context.allocator
|
||||
return a.procedure(a.data, Allocator.Mode.ALLOC, size, alignment, nil, 0, 0)
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
return a.procedure(a.data, Allocator_Mode.ALLOC, size, alignment, nil, 0, 0);
|
||||
}
|
||||
|
||||
free :: proc(ptr: rawptr) #inline {
|
||||
__check_context()
|
||||
a := context.allocator
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
if ptr != nil {
|
||||
a.procedure(a.data, Allocator.Mode.FREE, 0, 0, ptr, 0, 0)
|
||||
a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0);
|
||||
}
|
||||
}
|
||||
free_all :: proc() #inline {
|
||||
__check_context()
|
||||
a := context.allocator
|
||||
a.procedure(a.data, Allocator.Mode.FREE_ALL, 0, 0, nil, 0, 0)
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
a.procedure(a.data, Allocator_Mode.FREE_ALL, 0, 0, nil, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) }
|
||||
resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT); }
|
||||
resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
|
||||
__check_context()
|
||||
a := context.allocator
|
||||
return a.procedure(a.data, Allocator.Mode.RESIZE, new_size, alignment, ptr, old_size, 0)
|
||||
__check_context();
|
||||
a := context.allocator;
|
||||
return a.procedure(a.data, Allocator_Mode.RESIZE, new_size, alignment, ptr, old_size, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr {
|
||||
if old_memory == nil {
|
||||
return alloc_align(new_size, alignment)
|
||||
return alloc_align(new_size, alignment);
|
||||
}
|
||||
|
||||
if new_size == 0 {
|
||||
free(old_memory)
|
||||
return nil
|
||||
free(old_memory);
|
||||
return nil;
|
||||
}
|
||||
|
||||
if new_size == old_size {
|
||||
return old_memory
|
||||
return old_memory;
|
||||
}
|
||||
|
||||
new_memory := alloc_align(new_size, alignment)
|
||||
new_memory := alloc_align(new_size, alignment);
|
||||
if new_memory == nil {
|
||||
return nil
|
||||
return nil;
|
||||
}
|
||||
|
||||
mem.copy(new_memory, old_memory, min(old_size, new_size));
|
||||
free(old_memory)
|
||||
return new_memory
|
||||
mem.copy(new_memory, old_memory, min(old_size, new_size));;
|
||||
free(old_memory);
|
||||
return new_memory;
|
||||
}
|
||||
|
||||
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode,
|
||||
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
using Allocator.Mode
|
||||
/*
|
||||
match mode {
|
||||
case ALLOC:
|
||||
total_size := size + alignment + size_of(mem.AllocationHeader)
|
||||
ptr := os.heap_alloc(total_size)
|
||||
header := ptr as ^mem.AllocationHeader
|
||||
ptr = mem.align_forward(header+1, alignment)
|
||||
mem.allocation_header_fill(header, ptr, size)
|
||||
return mem.zero(ptr, size)
|
||||
using Allocator_Mode;
|
||||
|
||||
case FREE:
|
||||
os.heap_free(mem.allocation_header(old_memory))
|
||||
return nil
|
||||
when false {
|
||||
match mode {
|
||||
case ALLOC:
|
||||
total_size := size + alignment + size_of(mem.AllocationHeader);
|
||||
ptr := os.heap_alloc(total_size);
|
||||
header := ptr as ^mem.AllocationHeader;
|
||||
ptr = mem.align_forward(header+1, alignment);
|
||||
mem.allocation_header_fill(header, ptr, size);
|
||||
return mem.zero(ptr, size);
|
||||
|
||||
case FREE_ALL:
|
||||
// NOTE(bill): Does nothing
|
||||
case FREE:
|
||||
os.heap_free(mem.allocation_header(old_memory));
|
||||
return nil;
|
||||
|
||||
case RESIZE:
|
||||
total_size := size + alignment + size_of(mem.AllocationHeader)
|
||||
ptr := os.heap_resize(mem.allocation_header(old_memory), total_size)
|
||||
header := ptr as ^mem.AllocationHeader
|
||||
ptr = mem.align_forward(header+1, alignment)
|
||||
mem.allocation_header_fill(header, ptr, size)
|
||||
return mem.zero(ptr, size)
|
||||
}
|
||||
*/
|
||||
match mode {
|
||||
case ALLOC:
|
||||
return os.heap_alloc(size)
|
||||
case FREE_ALL:
|
||||
// NOTE(bill): Does nothing
|
||||
|
||||
case FREE:
|
||||
os.heap_free(old_memory)
|
||||
return nil
|
||||
case RESIZE:
|
||||
total_size := size + alignment + size_of(mem.AllocationHeader);
|
||||
ptr := os.heap_resize(mem.allocation_header(old_memory), total_size);
|
||||
header := ptr as ^mem.AllocationHeader;
|
||||
ptr = mem.align_forward(header+1, alignment);
|
||||
mem.allocation_header_fill(header, ptr, size);
|
||||
return mem.zero(ptr, size);
|
||||
}
|
||||
} else {
|
||||
match mode {
|
||||
case ALLOC:
|
||||
return os.heap_alloc(size);
|
||||
|
||||
case FREE_ALL:
|
||||
// NOTE(bill): Does nothing
|
||||
case FREE:
|
||||
os.heap_free(old_memory);
|
||||
return nil;
|
||||
|
||||
case RESIZE:
|
||||
return os.heap_resize(old_memory, size)
|
||||
case FREE_ALL:
|
||||
// NOTE(bill): Does nothing
|
||||
|
||||
case RESIZE:
|
||||
return os.heap_resize(old_memory, size);
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil;
|
||||
}
|
||||
|
||||
default_allocator :: proc() -> Allocator {
|
||||
return Allocator{
|
||||
procedure = default_allocator_proc,
|
||||
data = nil,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -297,71 +279,58 @@ default_allocator :: proc() -> Allocator {
|
||||
|
||||
__string_eq :: proc(a, b: string) -> bool {
|
||||
if a.count != b.count {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
if a.data == b.data {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
return mem.compare(a.data, b.data, a.count) == 0
|
||||
return mem.compare(a.data, b.data, a.count) == 0;
|
||||
}
|
||||
|
||||
__string_cmp :: proc(a, b : string) -> int {
|
||||
return mem.compare(a.data, b.data, min(a.count, b.count))
|
||||
__string_cmp :: proc(a, b: string) -> int {
|
||||
return mem.compare(a.data, b.data, min(a.count, b.count));
|
||||
}
|
||||
|
||||
__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b) }
|
||||
__string_lt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) < 0 }
|
||||
__string_gt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) > 0 }
|
||||
__string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <= 0 }
|
||||
__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0 }
|
||||
__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); }
|
||||
__string_lt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) < 0; }
|
||||
__string_gt :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) > 0; }
|
||||
__string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <= 0; }
|
||||
__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; }
|
||||
|
||||
|
||||
__assert :: proc(file: string, line, column: int, msg: string) #inline {
|
||||
fmt.fprintf(os.stderr, "%(%:%) Runtime assertion: %\n",
|
||||
file, line, column, msg)
|
||||
__debug_trap()
|
||||
file, line, column, msg);
|
||||
__debug_trap();
|
||||
}
|
||||
|
||||
__bounds_check_error :: proc(file: string, line, column: int,
|
||||
index, count: int) {
|
||||
__bounds_check_error :: proc(file: string, line, column: int, index, count: int) {
|
||||
if 0 <= index && index < count {
|
||||
return
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%(%:%) Index % is out of bounds range [0, %)\n",
|
||||
file, line, column, index, count)
|
||||
__debug_trap()
|
||||
file, line, column, index, count);
|
||||
__debug_trap();
|
||||
}
|
||||
|
||||
__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, "%(%:%) Invalid slice indices: [%:%:%]\n",
|
||||
file, line, column, low, high, max)
|
||||
__debug_trap()
|
||||
}
|
||||
__substring_expr_error :: proc(file: string, line, column: int,
|
||||
low, high: int) {
|
||||
__slice_expr_error :: proc(file: string, line, column: int, low, high: int) {
|
||||
if 0 <= low && low <= high {
|
||||
return
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%(%:%) Invalid substring indices: [%:%:%]\n",
|
||||
file, line, column, low, high)
|
||||
__debug_trap()
|
||||
fmt.fprintf(os.stderr, "%(%:%) Invalid slice indices: [%:%]\n",
|
||||
file, line, column, low, high);
|
||||
__debug_trap();
|
||||
}
|
||||
__substring_expr_error :: proc(file: string, line, column: int, low, high: int) {
|
||||
if 0 <= low && low <= high {
|
||||
return;
|
||||
}
|
||||
fmt.fprintf(os.stderr, "%(%:%) Invalid substring indices: [%:%]\n",
|
||||
file, line, column, low, high);
|
||||
__debug_trap();
|
||||
}
|
||||
|
||||
__enum_to_string :: proc(info: ^Type_Info, value: i64) -> string {
|
||||
match type ti : type_info_base(info) {
|
||||
case Type_Info.Enum:
|
||||
// TODO(bill): Search faster than linearly
|
||||
for i := 0; i < ti.values.count; i++ {
|
||||
if ti.values[i] == value {
|
||||
return ti.names[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
__string_decode_rune :: proc(s: string) -> (rune, int) #inline {
|
||||
return utf8.decode_rune(s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+138
-137
@@ -1,157 +1,158 @@
|
||||
#shared_global_scope
|
||||
|
||||
#import "fmt.odin"
|
||||
#shared_global_scope;
|
||||
|
||||
|
||||
__u128_mod :: proc(a, b: u128) -> u128 #link_name "__umodti3" {
|
||||
_, r := __u128_quo_mod(a, b)
|
||||
return r
|
||||
}
|
||||
// import "fmt.odin";
|
||||
|
||||
__u128_quo :: proc(a, b: u128) -> u128 #link_name "__udivti3" {
|
||||
n, _ := __u128_quo_mod(a, b)
|
||||
return n
|
||||
}
|
||||
// proc __u128_mod(a, b: u128) -> u128 #link_name "__umodti3" {
|
||||
// var _, r := __u128_quo_mod(a, b)
|
||||
// return r
|
||||
// }
|
||||
|
||||
__i128_mod :: proc(a, b: i128) -> i128 #link_name "__modti3" {
|
||||
_, r := __i128_quo_mod(a, b)
|
||||
return r
|
||||
}
|
||||
// proc __u128_quo(a, b: u128) -> u128 #link_name "__udivti3" {
|
||||
// var n, _ := __u128_quo_mod(a, b)
|
||||
// return n
|
||||
// }
|
||||
|
||||
__i128_quo :: proc(a, b: i128) -> i128 #link_name "__divti3" {
|
||||
n, _ := __i128_quo_mod(a, b)
|
||||
return n
|
||||
}
|
||||
// proc __i128_mod(a, b: i128) -> i128 #link_name "__modti3" {
|
||||
// var _, r := __i128_quo_mod(a, b)
|
||||
// return r
|
||||
// }
|
||||
|
||||
__i128_quo_mod :: proc(a, b: i128) -> (i128, i128) #link_name "__divmodti4" {
|
||||
s := b >> 127
|
||||
b = (b ~ s) - s
|
||||
s = a >> 127
|
||||
a = (a ~ s) - s
|
||||
// proc __i128_quo(a, b: i128) -> i128 #link_name "__divti3" {
|
||||
// var n, _ := __i128_quo_mod(a, b)
|
||||
// return n
|
||||
// }
|
||||
|
||||
n, r := __u128_quo_mod(a as u128, b as u128)
|
||||
return (n as i128 ~ s) - s, (r as i128 ~ s) - s
|
||||
}
|
||||
// proc __i128_quo_mod(a, b: i128) -> (i128, i128) #link_name "__divmodti4" {
|
||||
// var s := b >> 127
|
||||
// b = (b ~ s) - s
|
||||
// s = a >> 127
|
||||
// a = (a ~ s) - s
|
||||
|
||||
// var n, r := __u128_quo_mod(a as u128, b as u128)
|
||||
// return (n as i128 ~ s) - s, (r as i128 ~ s) - s
|
||||
// }
|
||||
|
||||
|
||||
__u128_quo_mod :: proc(a, b: u128) -> (u128, u128) #link_name "__udivmodti4" {
|
||||
clz :: proc(x: u64) -> u64 {
|
||||
clz_u64 :: proc(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.ctlz.i64"
|
||||
return clz_u64(x, false)
|
||||
}
|
||||
ctz :: proc(x: u64) -> u64 {
|
||||
ctz_u64 :: proc(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.cttz.i64"
|
||||
return ctz_u64(x, false)
|
||||
}
|
||||
// proc __u128_quo_mod(a, b: u128) -> (u128, u128) #link_name "__udivmodti4" {
|
||||
// proc clz(x: u64) -> u64 {
|
||||
// proc clz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.ctlz.i64"
|
||||
// return clz_u64(x, false)
|
||||
// }
|
||||
// proc ctz(x: u64) -> u64 {
|
||||
// proc ctz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.cttz.i64"
|
||||
// return ctz_u64(x, false)
|
||||
// }
|
||||
|
||||
|
||||
u128_lo_hi :: raw_union {
|
||||
all: u128
|
||||
using _lohi: struct {lo, hi: u64}
|
||||
}
|
||||
// u128_lo_hi :: raw_union {
|
||||
// all: u128
|
||||
// using _lohi: struct {lo, hi: u64;}
|
||||
// }
|
||||
|
||||
n, d, q, r: u128_lo_hi
|
||||
sr: u64
|
||||
// n, d, q, r: u128_lo_hi
|
||||
// sr: u64
|
||||
|
||||
n.all = a
|
||||
d.all = b
|
||||
// n.all = a
|
||||
// d.all = b
|
||||
|
||||
if n.hi == 0 {
|
||||
if d.hi == 0 {
|
||||
return (n.lo / d.lo) as u128, (n.lo % d.lo) as u128
|
||||
}
|
||||
return 0, n.lo as u128
|
||||
}
|
||||
if d.lo == 0 {
|
||||
if d.hi == 0 {
|
||||
return (n.hi / d.lo) as u128, (n.hi % d.lo) as u128
|
||||
}
|
||||
if n.lo == 0 {
|
||||
r.hi = n.hi % d.hi
|
||||
r.lo = 0
|
||||
return (n.hi / d.hi) as u128, r.all
|
||||
}
|
||||
if (d.hi & (d.hi-1)) == 0 {
|
||||
r.lo = n.lo
|
||||
r.hi = n.hi & (d.hi-1)
|
||||
return (n.hi >> ctz(d.hi)) as u128, r.all
|
||||
}
|
||||
// if n.hi == 0 {
|
||||
// if d.hi == 0 {
|
||||
// return (n.lo / d.lo) as u128, (n.lo % d.lo) as u128
|
||||
// }
|
||||
// return 0, n.lo as u128
|
||||
// }
|
||||
// if d.lo == 0 {
|
||||
// if d.hi == 0 {
|
||||
// return (n.hi / d.lo) as u128, (n.hi % d.lo) as u128
|
||||
// }
|
||||
// if n.lo == 0 {
|
||||
// r.hi = n.hi % d.hi
|
||||
// r.lo = 0
|
||||
// return (n.hi / d.hi) as u128, r.all
|
||||
// }
|
||||
// if (d.hi & (d.hi-1)) == 0 {
|
||||
// r.lo = n.lo
|
||||
// r.hi = n.hi & (d.hi-1)
|
||||
// return (n.hi >> ctz(d.hi)) as u128, r.all
|
||||
// }
|
||||
|
||||
sr = clz(d.hi) - clz(n.hi)
|
||||
if sr > 64 - 2 {
|
||||
return 0, n.all
|
||||
}
|
||||
sr++
|
||||
q.lo = 0
|
||||
q.hi = n.lo << (64-sr)
|
||||
r.hi = n.hi >> sr
|
||||
r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
} else {
|
||||
if d.hi == 0 {
|
||||
if (d.lo & (d.lo - 1)) == 0 {
|
||||
rem := (n.lo % (d.lo - 1)) as u128
|
||||
if d.lo == 1 {
|
||||
return n.all, rem
|
||||
}
|
||||
sr = ctz(d.lo)
|
||||
q.hi = n.hi >> sr
|
||||
q.lo = (n.hi << (64-sr)) | (n.lo >> sr);
|
||||
return q.all, rem
|
||||
}
|
||||
// sr = clz(d.hi) - clz(n.hi)
|
||||
// if sr > 64 - 2 {
|
||||
// return 0, n.all
|
||||
// }
|
||||
// sr++
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// if d.hi == 0 {
|
||||
// if (d.lo & (d.lo - 1)) == 0 {
|
||||
// var rem := (n.lo % (d.lo - 1)) as u128
|
||||
// if d.lo == 1 {
|
||||
// return n.all, rem
|
||||
// }
|
||||
// sr = ctz(d.lo)
|
||||
// q.hi = n.hi >> sr
|
||||
// q.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// return q.all, rem
|
||||
// }
|
||||
|
||||
sr = 1 + 64 + clz(d.lo) - clz(n.hi)
|
||||
// sr = 1 + 64 + clz(d.lo) - clz(n.hi)
|
||||
|
||||
q.all = n.all << (128-sr)
|
||||
r.all = n.all >> sr
|
||||
if sr == 64 {
|
||||
q.lo = 0
|
||||
q.hi = n.lo
|
||||
r.hi = 0
|
||||
r.lo = n.hi
|
||||
} else if sr < 64 {
|
||||
q.lo = 0
|
||||
q.hi = n.lo << (64-sr)
|
||||
r.hi = n.hi >> sr
|
||||
r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
} else {
|
||||
q.lo = n.lo << (128-sr)
|
||||
q.hi = (n.hi << (128-sr)) | (n.lo >> (sr-64))
|
||||
r.hi = 0
|
||||
r.lo = n.hi >> (sr-64)
|
||||
}
|
||||
} else {
|
||||
sr = clz(d.hi) - clz(n.hi)
|
||||
if sr > 64-1 {
|
||||
return 0, n.all
|
||||
}
|
||||
sr++
|
||||
q.lo = 0
|
||||
q.hi = n.lo << (64-sr)
|
||||
r.all = n.all >> sr
|
||||
if sr < 64 {
|
||||
r.hi = n.hi >> sr
|
||||
r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
} else {
|
||||
r.hi = 0
|
||||
r.lo = n.hi
|
||||
}
|
||||
}
|
||||
}
|
||||
// q.all = n.all << (128-sr)
|
||||
// r.all = n.all >> sr
|
||||
// if sr == 64 {
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo
|
||||
// r.hi = 0
|
||||
// r.lo = n.hi
|
||||
// } else if sr < 64 {
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// q.lo = n.lo << (128-sr)
|
||||
// q.hi = (n.hi << (128-sr)) | (n.lo >> (sr-64))
|
||||
// r.hi = 0
|
||||
// r.lo = n.hi >> (sr-64)
|
||||
// }
|
||||
// } else {
|
||||
// sr = clz(d.hi) - clz(n.hi)
|
||||
// if sr > 64-1 {
|
||||
// return 0, n.all
|
||||
// }
|
||||
// sr++
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.all = n.all >> sr
|
||||
// if sr < 64 {
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// r.hi = 0
|
||||
// r.lo = n.hi
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
carry: u64
|
||||
for ; sr > 0; sr-- {
|
||||
r.hi = (r.hi << 1) | (r.lo >> (64-1))
|
||||
r.lo = (r.lo << 1) | (r.hi >> (64-1))
|
||||
q.hi = (q.hi << 1) | (q.lo >> (64-1))
|
||||
q.lo = (q.lo << 1) | carry
|
||||
// carry: u64
|
||||
// for ; sr > 0; sr-- {
|
||||
// r.hi = (r.hi << 1) | (r.lo >> (64-1))
|
||||
// r.lo = (r.lo << 1) | (r.hi >> (64-1))
|
||||
// q.hi = (q.hi << 1) | (q.lo >> (64-1))
|
||||
// q.lo = (q.lo << 1) | carry
|
||||
|
||||
carry = 0
|
||||
if r.all >= d.all {
|
||||
r.all -= d.all
|
||||
carry = 1
|
||||
}
|
||||
}
|
||||
// carry = 0
|
||||
// if r.all >= d.all {
|
||||
// r.all -= d.all
|
||||
// carry = 1
|
||||
// }
|
||||
// }
|
||||
|
||||
// q.all = (q.all << 1) | (carry as u128)
|
||||
// return q.all, r.all
|
||||
// }
|
||||
|
||||
q.all = (q.all << 1) | (carry as u128)
|
||||
return q.all, r.all
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
// 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(); }
|
||||
|
||||
|
||||
load32 :: proc(a: ^i32) -> i32 {
|
||||
return a^;
|
||||
}
|
||||
store32 :: proc(a: ^i32, value: i32) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange32 :: proc(a: ^i32, expected, desired: i32) -> i32 {
|
||||
return win32.InterlockedCompareExchange(a, desired, expected);
|
||||
}
|
||||
exchanged32 :: proc(a: ^i32, desired: i32) -> i32 {
|
||||
return win32.InterlockedExchange(a, desired);
|
||||
}
|
||||
fetch_add32 :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedExchangeAdd(a, operand);
|
||||
|
||||
}
|
||||
fetch_and32 :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedAnd(a, operand);
|
||||
|
||||
}
|
||||
fetch_or32 :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedOr(a, operand);
|
||||
}
|
||||
spin_lock32 :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange32(a, 1, 0);
|
||||
counter := 0;
|
||||
while old_value != 0 && (time_out < 0 || counter < time_out) {
|
||||
counter += 1;
|
||||
yield_thread();
|
||||
old_value = compare_exchange32(a, 1, 0);
|
||||
mfence();
|
||||
}
|
||||
return old_value == 0;
|
||||
}
|
||||
spin_unlock32 :: proc(a: ^i32) {
|
||||
store32(a, 0);
|
||||
mfence();
|
||||
}
|
||||
try_acquire_lock32 :: proc(a: ^i32) -> bool {
|
||||
yield_thread();
|
||||
old_value := compare_exchange32(a, 1, 0);
|
||||
mfence();
|
||||
return old_value == 0;
|
||||
}
|
||||
|
||||
|
||||
load64 :: proc(a: ^i64) -> i64 {
|
||||
return a^;
|
||||
}
|
||||
store64 :: proc(a: ^i64, value: i64) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange64 :: proc(a: ^i64, expected, desired: i64) -> i64 {
|
||||
return win32.InterlockedCompareExchange64(a, desired, expected);
|
||||
}
|
||||
exchanged64 :: proc(a: ^i64, desired: i64) -> i64 {
|
||||
return win32.InterlockedExchange64(a, desired);
|
||||
}
|
||||
fetch_add64 :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedExchangeAdd64(a, operand);
|
||||
}
|
||||
fetch_and64 :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedAnd64(a, operand);
|
||||
}
|
||||
fetch_or64 :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedOr64(a, operand);
|
||||
}
|
||||
spin_lock64 :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange64(a, 1, 0);
|
||||
counter := 0;
|
||||
while old_value != 0 && (time_out < 0 || counter < time_out) {
|
||||
counter += 1;
|
||||
yield_thread();
|
||||
old_value = compare_exchange64(a, 1, 0);
|
||||
mfence();
|
||||
}
|
||||
return old_value == 0;
|
||||
}
|
||||
spin_unlock64 :: proc(a: ^i64) {
|
||||
store64(a, 0);
|
||||
mfence();
|
||||
}
|
||||
try_acquire_lock64 :: proc(a: ^i64) -> bool {
|
||||
yield_thread();
|
||||
old_value := compare_exchange64(a, 1, 0);
|
||||
mfence();
|
||||
return old_value == 0;
|
||||
}
|
||||
+324
-334
@@ -1,591 +1,581 @@
|
||||
#import "os.odin"
|
||||
#import "mem.odin"
|
||||
#import "utf8.odin"
|
||||
#import "os.odin";
|
||||
#import "mem.odin";
|
||||
#import "utf8.odin";
|
||||
|
||||
PRINT_BUF_SIZE :: 1<<12
|
||||
PRINT_BUF_SIZE :: 1<<12;
|
||||
|
||||
fprint :: proc(f: ^os.File, args: ..any) -> int {
|
||||
data: [PRINT_BUF_SIZE]byte
|
||||
buf := data[:0]
|
||||
bprint(^buf, ..args)
|
||||
os.write(f, buf)
|
||||
return buf.count
|
||||
}
|
||||
|
||||
fprintln :: proc(f: ^os.File, args: ..any) -> int {
|
||||
data: [PRINT_BUF_SIZE]byte
|
||||
buf := data[:0]
|
||||
bprintln(^buf, ..args)
|
||||
os.write(f, buf)
|
||||
return buf.count
|
||||
}
|
||||
fprintf :: proc(f: ^os.File, fmt: string, args: ..any) -> int {
|
||||
data: [PRINT_BUF_SIZE]byte
|
||||
buf := data[:0]
|
||||
bprintf(^buf, fmt, ..args)
|
||||
os.write(f, buf)
|
||||
return buf.count
|
||||
Buffer :: struct {
|
||||
data: []byte;
|
||||
length: int;
|
||||
}
|
||||
|
||||
|
||||
print :: proc(args: ..any) -> int {
|
||||
return fprint(os.stdout, ..args)
|
||||
fprint :: proc(fd: os.Handle, args: ...any) -> int {
|
||||
data: [PRINT_BUF_SIZE]byte;
|
||||
buf := Buffer{data[:], 0};
|
||||
bprint(^buf, ...args);
|
||||
os.write(fd, buf.data[:buf.length]);
|
||||
return buf.length;
|
||||
}
|
||||
println :: proc(args: ..any) -> int {
|
||||
return fprintln(os.stdout, ..args)
|
||||
|
||||
fprintln :: proc(fd: os.Handle, args: ...any) -> int {
|
||||
data: [PRINT_BUF_SIZE]byte;
|
||||
buf := Buffer{data[:], 0};
|
||||
bprintln(^buf, ...args);
|
||||
os.write(fd, buf.data[:buf.length]);
|
||||
return buf.length;
|
||||
}
|
||||
printf :: proc(fmt: string, args: ..any) -> int {
|
||||
return fprintf(os.stdout, fmt, ..args)
|
||||
fprintf :: proc(fd: os.Handle, fmt: string, args: ...any) -> int {
|
||||
data: [PRINT_BUF_SIZE]byte;
|
||||
buf := Buffer{data[:], 0};
|
||||
bprintf(^buf, fmt, ...args);
|
||||
os.write(fd, buf.data[:buf.length]);
|
||||
return buf.length;
|
||||
}
|
||||
|
||||
|
||||
print :: proc(args: ...any) -> int {
|
||||
return fprint(os.stdout, ...args);
|
||||
}
|
||||
println :: proc(args: ...any) -> int {
|
||||
return fprintln(os.stdout, ...args);
|
||||
}
|
||||
printf :: proc(fmt: string, args: ...any) -> int {
|
||||
return fprintf(os.stdout, fmt, ...args);
|
||||
}
|
||||
|
||||
|
||||
|
||||
fprint_type :: proc(f: ^os.File, info: ^Type_Info) {
|
||||
data: [PRINT_BUF_SIZE]byte
|
||||
buf := data[:0]
|
||||
print_type_to_buffer(^buf, info)
|
||||
os.write(f, buf)
|
||||
fprint_type :: proc(fd: os.Handle, info: ^Type_Info) {
|
||||
data: [PRINT_BUF_SIZE]byte;
|
||||
buf := Buffer{data[:], 0};
|
||||
bprint_type(^buf, info);
|
||||
os.write(fd, buf.data[:buf.length]);
|
||||
}
|
||||
|
||||
|
||||
|
||||
print_byte_buffer :: proc(buf: ^[]byte, b: []byte) {
|
||||
if buf.count < buf.capacity {
|
||||
n := min(buf.capacity-buf.count, b.count)
|
||||
print_byte_buffer :: proc(buf: ^Buffer, b: []byte) {
|
||||
if buf.length < buf.data.count {
|
||||
n := min(buf.data.count-buf.length, b.count);
|
||||
if n > 0 {
|
||||
mem.copy(buf.data + buf.count, ^b[0], n)
|
||||
buf.count += n
|
||||
copy(buf.data[buf.length:], b[:n]);
|
||||
buf.length += n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print_string_to_buffer :: proc(buf: ^[]byte, s: string) {
|
||||
print_byte_buffer(buf, s as []byte)
|
||||
bprint_string :: proc(buf: ^Buffer, s: string) {
|
||||
print_byte_buffer(buf, s as []byte);
|
||||
}
|
||||
|
||||
|
||||
byte_reverse :: proc(b: []byte) {
|
||||
n := b.count
|
||||
for i := 0; i < n/2; i++ {
|
||||
b[i], b[n-1-i] = b[n-1-i], b[i]
|
||||
n := b.count;
|
||||
for i : 0..<n/2 {
|
||||
b[i], b[n-1-i] = b[n-1-i], b[i];
|
||||
}
|
||||
}
|
||||
|
||||
print_rune_to_buffer :: proc(buf: ^[]byte, r: rune) {
|
||||
b, n := utf8.encode_rune(r)
|
||||
print_string_to_buffer(buf, b[:n] as string)
|
||||
bprint_rune :: proc(buf: ^Buffer, r: rune) {
|
||||
b, n := utf8.encode_rune(r);
|
||||
bprint_string(buf, b[:n] as string);
|
||||
}
|
||||
|
||||
print_space_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune " ") }
|
||||
print_nl_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune "\n") }
|
||||
bprint_space :: proc(buf: ^Buffer) { bprint_rune(buf, ' '); }
|
||||
bprint_nl :: proc (buf: ^Buffer) { bprint_rune(buf, '\n'); }
|
||||
|
||||
__NUM_TO_CHAR_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$"
|
||||
__NUM_TO_CHAR_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$";
|
||||
|
||||
print_bool_to_buffer :: proc(buffer: ^[]byte, b : bool) {
|
||||
if b { print_string_to_buffer(buffer, "true") }
|
||||
else { print_string_to_buffer(buffer, "false") }
|
||||
bprint_bool :: proc(buffer: ^Buffer, b: bool) {
|
||||
bprint_string(buffer, if b { give "true" } else { give "false" });
|
||||
}
|
||||
|
||||
print_pointer_to_buffer :: proc(buffer: ^[]byte, p: rawptr) #inline {
|
||||
print_string_to_buffer(buffer, "0x")
|
||||
print_u64_to_buffer(buffer, p as uint as u64)
|
||||
bprint_pointer :: proc(buffer: ^Buffer, p: rawptr) #inline {
|
||||
bprint_string(buffer, "0x");
|
||||
bprint_u64(buffer, p as uint as u64);
|
||||
}
|
||||
|
||||
print_f16_to_buffer :: proc(buffer: ^[]byte, f: f32) #inline { print__f64(buffer, f as f64, 4) }
|
||||
print_f32_to_buffer :: proc(buffer: ^[]byte, f: f32) #inline { print__f64(buffer, f as f64, 7) }
|
||||
print_f64_to_buffer :: proc(buffer: ^[]byte, f: f64) #inline { print__f64(buffer, f as f64, 16) }
|
||||
print_u64_to_buffer :: proc(buffer: ^[]byte, value: u64) {
|
||||
i := value
|
||||
buf: [20]byte
|
||||
len := 0
|
||||
// bprint_f16 :: proc(buffer: ^Buffer, f: f32) #inline { print__f64(buffer, f as f64, 4); }
|
||||
bprint_f32 :: proc(buffer: ^Buffer, f: f32) #inline { print__f64(buffer, f as f64, 7); }
|
||||
bprint_f64 :: proc(buffer: ^Buffer, f: f64) #inline { print__f64(buffer, f as f64, 16); }
|
||||
bprint_u64 :: proc(buffer: ^Buffer, value: u64) {
|
||||
i := value;
|
||||
buf :[20]byte;
|
||||
len := 0;
|
||||
if i == 0 {
|
||||
buf[len] = #rune "0"
|
||||
len++
|
||||
buf[len] = '0';
|
||||
len += 1;
|
||||
}
|
||||
for i > 0 {
|
||||
buf[len] = __NUM_TO_CHAR_TABLE[i % 10]
|
||||
len++
|
||||
i /= 10
|
||||
while i > 0 {
|
||||
buf[len] = __NUM_TO_CHAR_TABLE[i % 10];
|
||||
len += 1;
|
||||
i /= 10;
|
||||
}
|
||||
byte_reverse(buf[:len])
|
||||
print_string_to_buffer(buffer, buf[:len] as string)
|
||||
byte_reverse(buf[:len]);
|
||||
bprint_string(buffer, buf[:len] as string);
|
||||
}
|
||||
print_i64_to_buffer :: proc(buffer: ^[]byte, value: i64) {
|
||||
i := value
|
||||
neg := i < 0
|
||||
if neg {
|
||||
i = -i
|
||||
print_rune_to_buffer(buffer, #rune "-")
|
||||
bprint_i64 :: proc(buffer: ^Buffer, value: i64) {
|
||||
// TODO(bill): Cleanup printing
|
||||
i := value;
|
||||
if i < 0 {
|
||||
i = -i;
|
||||
bprint_rune(buffer, '-');
|
||||
}
|
||||
print_u64_to_buffer(buffer, i as u64)
|
||||
bprint_u64(buffer, i as u64);
|
||||
}
|
||||
|
||||
print_u128_to_buffer :: proc(buffer: ^[]byte, value: u128) {
|
||||
a := value transmute [2]u64
|
||||
/*
|
||||
bprint_u128 :: proc(buffer: ^Buffer, value u128) {
|
||||
a := value transmute [2]u64;
|
||||
if a[1] != 0 {
|
||||
print_u64_to_buffer(buffer, a[1])
|
||||
bprint_u64(buffer, a[1]);
|
||||
}
|
||||
print_u64_to_buffer(buffer, a[0])
|
||||
bprint_u64(buffer, a[0]);
|
||||
}
|
||||
print_i128_to_buffer :: proc(buffer: ^[]byte, value: i128) {
|
||||
i := value
|
||||
neg := i < 0
|
||||
if neg {
|
||||
i = -i
|
||||
print_rune_to_buffer(buffer, #rune "-")
|
||||
bprint_i128 :: proc(buffer: ^Buffer, value i128) {
|
||||
i := value;
|
||||
if i < 0 {
|
||||
i = -i;
|
||||
bprint_rune(buffer, '-');
|
||||
}
|
||||
print_u128_to_buffer(buffer, i as u128)
|
||||
bprint_u128(buffer, i as u128);
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
print__f64 :: proc(buffer: ^[]byte, value: f64, decimal_places: int) {
|
||||
f := value
|
||||
print__f64 :: proc(buffer: ^Buffer, value: f64, decimal_places: int) {
|
||||
f := value;
|
||||
if f == 0 {
|
||||
print_rune_to_buffer(buffer, #rune "0")
|
||||
return
|
||||
bprint_rune(buffer, '0');
|
||||
return;
|
||||
}
|
||||
if f < 0 {
|
||||
print_rune_to_buffer(buffer, #rune "-")
|
||||
f = -f
|
||||
bprint_rune(buffer, '-');
|
||||
f = -f;
|
||||
}
|
||||
|
||||
i := f as u64
|
||||
print_u64_to_buffer(buffer, i)
|
||||
f -= i as f64
|
||||
i := f as u64;
|
||||
bprint_u64(buffer, i);
|
||||
f -= i as f64;
|
||||
|
||||
print_rune_to_buffer(buffer, #rune ".")
|
||||
bprint_rune(buffer, '.');
|
||||
|
||||
mult: f64 = 10.0
|
||||
for ; decimal_places >= 0; decimal_places-- {
|
||||
i = (f * mult) as u64
|
||||
print_u64_to_buffer(buffer, i as u64)
|
||||
f -= i as f64 / mult
|
||||
mult *= 10
|
||||
mult: f64 = 10.0;
|
||||
while decimal_places >= 0 {
|
||||
i = (f * mult) as u64;
|
||||
bprint_u64(buffer, i as u64);
|
||||
f -= i as f64 / mult;
|
||||
mult *= 10;
|
||||
decimal_places -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
print_type_to_buffer :: proc(buf: ^[]byte, ti: ^Type_Info) {
|
||||
if ti == nil { return }
|
||||
bprint_type :: proc(buf: ^Buffer, ti: ^Type_Info) {
|
||||
if ti == nil {
|
||||
return;
|
||||
}
|
||||
|
||||
using Type_Info
|
||||
using Type_Info;
|
||||
match type info : ti {
|
||||
case Named:
|
||||
print_string_to_buffer(buf, info.name)
|
||||
bprint_string(buf, info.name);
|
||||
case Integer:
|
||||
match {
|
||||
case ti == type_info(int):
|
||||
print_string_to_buffer(buf, "int")
|
||||
case ti == type_info(uint):
|
||||
print_string_to_buffer(buf, "uint")
|
||||
case ti == type_info(int): bprint_string(buf, "int");
|
||||
case ti == type_info(uint): bprint_string(buf, "uint");
|
||||
default:
|
||||
if info.signed {
|
||||
print_string_to_buffer(buf, "i")
|
||||
} else {
|
||||
print_string_to_buffer(buf, "u")
|
||||
}
|
||||
print_u64_to_buffer(buf, 8*info.size as u64)
|
||||
bprint_string(buf, if info.signed { give "i" } else { give "u"});
|
||||
bprint_u64(buf, 8*info.size as u64);
|
||||
}
|
||||
|
||||
case Float:
|
||||
match info.size {
|
||||
case 4: print_string_to_buffer(buf, "f32")
|
||||
case 8: print_string_to_buffer(buf, "f64")
|
||||
case 4: bprint_string(buf, "f32");
|
||||
case 8: bprint_string(buf, "f64");
|
||||
}
|
||||
case String: print_string_to_buffer(buf, "string")
|
||||
case Boolean: print_string_to_buffer(buf, "bool")
|
||||
case String: bprint_string(buf, "string");
|
||||
case Boolean: bprint_string(buf, "bool");
|
||||
case Pointer:
|
||||
if info.elem == nil {
|
||||
print_string_to_buffer(buf, "rawptr")
|
||||
bprint_string(buf, "rawptr");
|
||||
} else {
|
||||
print_string_to_buffer(buf, "^")
|
||||
print_type_to_buffer(buf, info.elem)
|
||||
bprint_string(buf, "^");
|
||||
bprint_type(buf, info.elem);
|
||||
}
|
||||
case Maybe:
|
||||
print_string_to_buffer(buf, "?")
|
||||
print_type_to_buffer(buf, info.elem)
|
||||
bprint_string(buf, "?");
|
||||
bprint_type(buf, info.elem);
|
||||
case Procedure:
|
||||
print_string_to_buffer(buf, "proc")
|
||||
bprint_string(buf, "proc");
|
||||
if info.params == nil {
|
||||
print_string_to_buffer(buf, "()")
|
||||
bprint_string(buf, "()");
|
||||
} else {
|
||||
count := (info.params as ^Tuple).fields.count
|
||||
if count == 1 { print_string_to_buffer(buf, "(") }
|
||||
print_type_to_buffer(buf, info.params)
|
||||
if count == 1 { print_string_to_buffer(buf, ")") }
|
||||
count := (info.params as ^Tuple).fields.count;
|
||||
if count == 1 { bprint_string(buf, "("); }
|
||||
bprint_type(buf, info.params);
|
||||
if count == 1 { bprint_string(buf, ")"); }
|
||||
}
|
||||
if info.results != nil {
|
||||
print_string_to_buffer(buf, " -> ")
|
||||
print_type_to_buffer(buf, info.results)
|
||||
bprint_string(buf, " -> ");
|
||||
bprint_type(buf, info.results);
|
||||
}
|
||||
case Tuple:
|
||||
count := info.fields.count
|
||||
if count != 1 { print_string_to_buffer(buf, "(") }
|
||||
for i := 0; i < count; i++ {
|
||||
if i > 0 { print_string_to_buffer(buf, ", ") }
|
||||
count := info.fields.count;
|
||||
if count != 1 { bprint_string(buf, "("); }
|
||||
for i : 0..<count {
|
||||
if i > 0 { bprint_string(buf, ", "); }
|
||||
|
||||
f := info.fields[i]
|
||||
f := info.fields[i];
|
||||
|
||||
if f.name.count > 0 {
|
||||
print_string_to_buffer(buf, f.name)
|
||||
print_string_to_buffer(buf, ": ")
|
||||
bprint_string(buf, f.name);
|
||||
bprint_string(buf, ": ");
|
||||
}
|
||||
print_type_to_buffer(buf, f.type_info)
|
||||
bprint_type(buf, f.type_info);
|
||||
}
|
||||
if count != 1 { print_string_to_buffer(buf, ")") }
|
||||
if count != 1 { bprint_string(buf, ")"); }
|
||||
|
||||
case Array:
|
||||
print_string_to_buffer(buf, "[")
|
||||
print_i64_to_buffer(buf, info.count as i64)
|
||||
print_string_to_buffer(buf, "]")
|
||||
print_type_to_buffer(buf, info.elem)
|
||||
bprint_string(buf, "[");
|
||||
bprint_i64(buf, info.count as i64);
|
||||
bprint_string(buf, "]");
|
||||
bprint_type(buf, info.elem);
|
||||
case Slice:
|
||||
print_string_to_buffer(buf, "[")
|
||||
print_string_to_buffer(buf, "]")
|
||||
print_type_to_buffer(buf, info.elem)
|
||||
bprint_string(buf, "[");
|
||||
bprint_string(buf, "]");
|
||||
bprint_type(buf, info.elem);
|
||||
case Vector:
|
||||
print_string_to_buffer(buf, "{")
|
||||
print_i64_to_buffer(buf, info.count as i64)
|
||||
print_string_to_buffer(buf, "}")
|
||||
print_type_to_buffer(buf, info.elem)
|
||||
bprint_string(buf, "[vector ");
|
||||
bprint_i64(buf, info.count as i64);
|
||||
bprint_string(buf, "]");
|
||||
bprint_type(buf, info.elem);
|
||||
|
||||
case Struct:
|
||||
print_string_to_buffer(buf, "struct ")
|
||||
if info.packed { print_string_to_buffer(buf, "#packed ") }
|
||||
if info.ordered { print_string_to_buffer(buf, "#ordered ") }
|
||||
print_string_to_buffer(buf, "{")
|
||||
for i := 0; i < info.fields.count; i++ {
|
||||
bprint_string(buf, "struct ");
|
||||
if info.packed { bprint_string(buf, "#packed "); }
|
||||
if info.ordered { bprint_string(buf, "#ordered "); }
|
||||
bprint_string(buf, "{");
|
||||
for field, i : info.fields {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
bprint_string(buf, ", ");
|
||||
}
|
||||
print_any_to_buffer(buf, info.fields[i].name)
|
||||
print_string_to_buffer(buf, ": ")
|
||||
print_type_to_buffer(buf, info.fields[i].type_info)
|
||||
bprint_any(buf, field.name);
|
||||
bprint_string(buf, ": ");
|
||||
bprint_type(buf, field.type_info);
|
||||
}
|
||||
print_string_to_buffer(buf, "}")
|
||||
bprint_string(buf, "}");
|
||||
|
||||
case Union:
|
||||
print_string_to_buffer(buf, "union {")
|
||||
for i := 0; i < info.fields.count; i++ {
|
||||
bprint_string(buf, "union {");
|
||||
for field, i : info.fields {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
bprint_string(buf, ", ");
|
||||
}
|
||||
print_any_to_buffer(buf, info.fields[i].name)
|
||||
print_string_to_buffer(buf, ": ")
|
||||
print_type_to_buffer(buf, info.fields[i].type_info)
|
||||
bprint_any(buf, field.name);
|
||||
bprint_string(buf, ": ");
|
||||
bprint_type(buf, field.type_info);
|
||||
}
|
||||
print_string_to_buffer(buf, "}")
|
||||
bprint_string(buf, "}");
|
||||
|
||||
case Raw_Union:
|
||||
print_string_to_buffer(buf, "raw_union {")
|
||||
for i := 0; i < info.fields.count; i++ {
|
||||
bprint_string(buf, "raw_union {");
|
||||
for field, i : info.fields {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
bprint_string(buf, ", ");
|
||||
}
|
||||
print_any_to_buffer(buf, info.fields[i].name)
|
||||
print_string_to_buffer(buf, ": ")
|
||||
print_type_to_buffer(buf, info.fields[i].type_info)
|
||||
bprint_any(buf, field.name);
|
||||
bprint_string(buf, ": ");
|
||||
bprint_type(buf, field.type_info);
|
||||
}
|
||||
print_string_to_buffer(buf, "}")
|
||||
bprint_string(buf, "}");
|
||||
|
||||
case Enum:
|
||||
print_string_to_buffer(buf, "enum ")
|
||||
print_type_to_buffer(buf, info.base)
|
||||
print_string_to_buffer(buf, "{}")
|
||||
bprint_string(buf, "enum ");
|
||||
bprint_type(buf, info.base);
|
||||
bprint_string(buf, " {}");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
make_any :: proc(type_info: ^Type_Info, data: rawptr) -> any {
|
||||
a: any
|
||||
a.type_info = type_info
|
||||
a.data = data
|
||||
return a
|
||||
a :any;
|
||||
a.type_info = type_info;
|
||||
a.data = data;
|
||||
return a;
|
||||
}
|
||||
|
||||
print_any_to_buffer :: proc(buf: ^[]byte, arg: any) {
|
||||
bprint_any :: proc(buf: ^Buffer, arg: any) {
|
||||
if arg.type_info == nil {
|
||||
print_string_to_buffer(buf, "<nil>")
|
||||
return
|
||||
bprint_string(buf, "<nil>");
|
||||
return;
|
||||
}
|
||||
|
||||
if arg.data == nil {
|
||||
print_string_to_buffer(buf, "<nil>")
|
||||
return
|
||||
bprint_string(buf, "<nil>");
|
||||
return;
|
||||
}
|
||||
|
||||
using Type_Info
|
||||
using Type_Info;
|
||||
match type info : arg.type_info {
|
||||
case Named:
|
||||
a := make_any(info.base, arg.data)
|
||||
a := make_any(info.base, arg.data);
|
||||
match type b : info.base {
|
||||
case Struct:
|
||||
print_string_to_buffer(buf, info.name)
|
||||
print_string_to_buffer(buf, "{")
|
||||
for i := 0; i < b.fields.count; i++ {
|
||||
f := b.fields[i];
|
||||
bprint_string(buf, info.name);
|
||||
bprint_string(buf, "{");
|
||||
for f, i : b.fields {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
bprint_string(buf, ", ");
|
||||
}
|
||||
print_string_to_buffer(buf, f.name)
|
||||
// print_any_to_buffer(buf, f.offset)
|
||||
print_string_to_buffer(buf, " = ")
|
||||
data := arg.data as ^byte + f.offset
|
||||
print_any_to_buffer(buf, make_any(f.type_info, data))
|
||||
bprint_string(buf, f.name);
|
||||
// bprint_any(buf, f.offset);
|
||||
bprint_string(buf, " = ");
|
||||
data := arg.data as ^byte + f.offset;
|
||||
bprint_any(buf, make_any(f.type_info, data));
|
||||
}
|
||||
print_string_to_buffer(buf, "}")
|
||||
bprint_string(buf, "}");
|
||||
|
||||
default:
|
||||
print_any_to_buffer(buf, a)
|
||||
bprint_any(buf, a);
|
||||
}
|
||||
|
||||
case Integer:
|
||||
match type i : arg {
|
||||
case i8: print_i64_to_buffer(buf, i as i64)
|
||||
case u8: print_u64_to_buffer(buf, i as u64)
|
||||
case i16: print_i64_to_buffer(buf, i as i64)
|
||||
case u16: print_u64_to_buffer(buf, i as u64)
|
||||
case i32: print_i64_to_buffer(buf, i as i64)
|
||||
case u32: print_u64_to_buffer(buf, i as u64)
|
||||
case i64: print_i64_to_buffer(buf, i as i64)
|
||||
case u64: print_u64_to_buffer(buf, i as u64)
|
||||
case i128: print_i128_to_buffer(buf, i)
|
||||
case u128: print_u128_to_buffer(buf, i)
|
||||
case i8: bprint_i64(buf, i as i64);
|
||||
case u8: bprint_u64(buf, i as u64);
|
||||
case i16: bprint_i64(buf, i as i64);
|
||||
case u16: bprint_u64(buf, i as u64);
|
||||
case i32: bprint_i64(buf, i as i64);
|
||||
case u32: bprint_u64(buf, i as u64);
|
||||
case i64: bprint_i64(buf, i);
|
||||
case u64: bprint_u64(buf, i);
|
||||
// case i128: bprint_i128(buf, i);
|
||||
// case u128: bprint_u128(buf, i);
|
||||
|
||||
case int: print_u64_to_buffer(buf, i as u64)
|
||||
case uint: print_u64_to_buffer(buf, i as u64)
|
||||
case int: bprint_i64(buf, i as i64);
|
||||
case uint: bprint_u64(buf, i as u64);
|
||||
}
|
||||
|
||||
case Float:
|
||||
match type f : arg {
|
||||
// case f16: print_f64_to_buffer(buf, f as f64)
|
||||
case f32: print_f64_to_buffer(buf, f as f64)
|
||||
case f64: print_f64_to_buffer(buf, f as f64)
|
||||
// case f128: print_f64_to_buffer(buf, f as f64)
|
||||
// case f16: bprint_f64(buf, f as f64);
|
||||
case f32: bprint_f32(buf, f);
|
||||
case f64: bprint_f64(buf, f);
|
||||
// case f128: bprint_f64(buf, f as f64);
|
||||
}
|
||||
|
||||
case String:
|
||||
match type s : arg {
|
||||
case string: print_string_to_buffer(buf, s)
|
||||
case string: bprint_string(buf, s);
|
||||
}
|
||||
|
||||
case Boolean:
|
||||
match type b : arg {
|
||||
case bool: print_bool_to_buffer(buf, b)
|
||||
case bool: bprint_bool(buf, b);
|
||||
}
|
||||
|
||||
case Pointer:
|
||||
match type p : arg {
|
||||
case ^Type_Info: print_type_to_buffer(buf, p)
|
||||
default: print_pointer_to_buffer(buf, (arg.data as ^rawptr)^)
|
||||
case ^Type_Info: bprint_type(buf, p);
|
||||
default: bprint_pointer(buf, (arg.data as ^rawptr)^);
|
||||
}
|
||||
|
||||
case Maybe:
|
||||
size := mem.size_of_type_info(info.elem)
|
||||
data := slice_ptr(arg.data as ^byte, size+1)
|
||||
size := mem.size_of_type_info(info.elem);
|
||||
data := slice_ptr(arg.data as ^byte, size+1);
|
||||
if data[size] != 0 {
|
||||
print_any_to_buffer(buf, make_any(info.elem, arg.data))
|
||||
bprint_any(buf, make_any(info.elem, arg.data));
|
||||
} else {
|
||||
print_string_to_buffer(buf, "nil")
|
||||
bprint_string(buf, "nil");
|
||||
}
|
||||
|
||||
case Enum:
|
||||
value: i64 = 0
|
||||
|
||||
match type i : make_any(info.base, arg.data) {
|
||||
case i8: value = i as i64
|
||||
case i16: value = i as i64
|
||||
case i32: value = i as i64
|
||||
case i64: value = i as i64
|
||||
case u8: value = i as i64
|
||||
case u16: value = i as i64
|
||||
case u32: value = i as i64
|
||||
case u64: value = i as i64
|
||||
}
|
||||
print_string_to_buffer(buf, __enum_to_string(arg.type_info, value))
|
||||
|
||||
case Array:
|
||||
bprintf(buf, "[%]%{", info.count, info.elem)
|
||||
defer print_string_to_buffer(buf, "}")
|
||||
bprintf(buf, "[%]%{", info.count, info.elem);
|
||||
defer bprint_string(buf, "}");
|
||||
|
||||
for i := 0; i < info.count; i++ {
|
||||
for i : 0..<info.count {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
bprint_string(buf, ", ");
|
||||
}
|
||||
|
||||
data := arg.data as ^byte + i*info.elem_size
|
||||
print_any_to_buffer(buf, make_any(info.elem, data))
|
||||
data := arg.data as ^byte + i*info.elem_size;
|
||||
bprint_any(buf, make_any(info.elem, data));
|
||||
}
|
||||
|
||||
case Slice:
|
||||
slice := arg.data as ^[]byte
|
||||
bprintf(buf, "[]%{", info.elem)
|
||||
defer print_string_to_buffer(buf, "}")
|
||||
slice := arg.data as ^[]byte;
|
||||
bprintf(buf, "[]%{", info.elem);
|
||||
defer bprint_string(buf, "}");
|
||||
|
||||
for i := 0; i < slice.count; i++ {
|
||||
for i : 0..<slice.count {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
bprint_string(buf, ", ");
|
||||
}
|
||||
|
||||
data := slice.data + i*info.elem_size
|
||||
print_any_to_buffer(buf, make_any(info.elem, data))
|
||||
data := slice.data + i*info.elem_size;
|
||||
bprint_any(buf, make_any(info.elem, data));
|
||||
}
|
||||
|
||||
case Vector:
|
||||
is_bool :: proc(type_info: ^Type_Info) -> bool {
|
||||
match type info : type_info {
|
||||
case Named:
|
||||
return is_bool(info.base)
|
||||
return is_bool(info.base);
|
||||
case Boolean:
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
bprintf(buf, "{%}%{", info.count, info.elem)
|
||||
defer print_string_to_buffer(buf, "}")
|
||||
bprintf(buf, "[vector %]%{", info.count, info.elem);
|
||||
defer bprint_string(buf, "}");
|
||||
|
||||
if is_bool(info.elem) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
for i := 0; i < info.count; i++ {
|
||||
for i : 0..<info.count {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
bprint_string(buf, ", ");
|
||||
}
|
||||
|
||||
data := arg.data as ^byte + i*info.elem_size
|
||||
print_any_to_buffer(buf, make_any(info.elem, data))
|
||||
data := arg.data as ^byte + i*info.elem_size;
|
||||
bprint_any(buf, make_any(info.elem, data));
|
||||
}
|
||||
|
||||
|
||||
case Struct:
|
||||
bprintf(buf, "%{", arg.type_info)
|
||||
defer print_string_to_buffer(buf, "}")
|
||||
bprintf(buf, "%{", arg.type_info);
|
||||
defer bprint_string(buf, "}");
|
||||
|
||||
for i := 0; i < info.fields.count; i++ {
|
||||
for f, i : info.fields {
|
||||
if i > 0 {
|
||||
print_string_to_buffer(buf, ", ")
|
||||
bprint_string(buf, ", ");
|
||||
}
|
||||
print_string_to_buffer(buf, info.fields[i].name)
|
||||
print_string_to_buffer(buf, " = ")
|
||||
data := arg.data as ^byte + info.fields[i].offset
|
||||
ti := info.fields[i].type_info
|
||||
print_any_to_buffer(buf, make_any(ti, data))
|
||||
bprint_string(buf, f.name);
|
||||
bprint_string(buf, " = ");
|
||||
data := arg.data as ^byte + f.offset;
|
||||
ti := f.type_info;
|
||||
bprint_any(buf, make_any(ti, data));
|
||||
}
|
||||
|
||||
case Union:
|
||||
print_string_to_buffer(buf, "(union)")
|
||||
bprint_string(buf, "(union)");
|
||||
case Raw_Union:
|
||||
print_string_to_buffer(buf, "(raw_union)")
|
||||
bprint_string(buf, "(raw_union)");
|
||||
|
||||
case Enum:
|
||||
bprint_any(buf, make_any(info.base, arg.data));
|
||||
|
||||
case Procedure:
|
||||
print_type_to_buffer(buf, arg.type_info)
|
||||
print_string_to_buffer(buf, " @ 0x")
|
||||
print_pointer_to_buffer(buf, (arg.data as ^rawptr)^)
|
||||
bprint_type(buf, arg.type_info);
|
||||
bprint_string(buf, " @ ");
|
||||
bprint_pointer(buf, (arg.data as ^rawptr)^);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bprintf :: proc(buf: ^[]byte, fmt: string, args: ..any) -> int {
|
||||
bprintf :: proc(buf: ^Buffer, fmt: string, args: ...any) -> int {
|
||||
is_digit :: proc(r: rune) -> bool #inline {
|
||||
return r >= #rune "0" && r <= #rune "9"
|
||||
return '0' <= r && r <= '9';
|
||||
}
|
||||
|
||||
parse_int :: proc(s: string, offset: int) -> (int, int) {
|
||||
result := 0
|
||||
result := 0;
|
||||
|
||||
for ; offset < s.count; offset++ {
|
||||
c := s[offset] as rune
|
||||
for _ : offset..<s.count {
|
||||
c := s[offset] as rune;
|
||||
if !is_digit(c) {
|
||||
break
|
||||
break;
|
||||
}
|
||||
|
||||
result *= 10
|
||||
result += (c - #rune "0") as int
|
||||
result *= 10;
|
||||
result += (c - '0') as int;
|
||||
}
|
||||
|
||||
return result, offset
|
||||
return result, offset;
|
||||
}
|
||||
|
||||
prev := 0
|
||||
implicit_index := 0
|
||||
prev := 0;
|
||||
implicit_index := 0;
|
||||
|
||||
for i := 0; i < fmt.count; i++ {
|
||||
r := fmt[i] as rune
|
||||
index := implicit_index
|
||||
while i := 0; i < fmt.count { defer i += 1;
|
||||
r := fmt[i] as rune;
|
||||
index := implicit_index;
|
||||
|
||||
if r != #rune "%" {
|
||||
continue
|
||||
if r != '%' {
|
||||
continue;
|
||||
}
|
||||
|
||||
print_string_to_buffer(buf, fmt[prev:i])
|
||||
i++ // Skip %
|
||||
bprint_string(buf, fmt[prev:i]);
|
||||
i += 1; // Skip %
|
||||
if i < fmt.count {
|
||||
next := fmt[i] as rune
|
||||
next := fmt[i] as rune;
|
||||
|
||||
if next == #rune "%" {
|
||||
print_string_to_buffer(buf, "%")
|
||||
i++
|
||||
prev = i
|
||||
continue
|
||||
if next == '%' {
|
||||
bprint_string(buf, "%");
|
||||
i += 1;
|
||||
prev = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if is_digit(next) {
|
||||
index, i = parse_int(fmt, i)
|
||||
index, i = parse_int(fmt, i);
|
||||
}
|
||||
}
|
||||
|
||||
if 0 <= index && index < args.count {
|
||||
print_any_to_buffer(buf, args[index])
|
||||
implicit_index = index+1
|
||||
bprint_any(buf, args[index]);
|
||||
implicit_index = index+1;
|
||||
} else {
|
||||
// TODO(bill): Error check index out bounds
|
||||
print_string_to_buffer(buf, "<invalid>")
|
||||
bprint_string(buf, "<invalid>");
|
||||
}
|
||||
|
||||
prev = i
|
||||
prev = i;
|
||||
}
|
||||
|
||||
print_string_to_buffer(buf, fmt[prev:])
|
||||
return buf.count
|
||||
bprint_string(buf, fmt[prev:]);
|
||||
return buf.length;
|
||||
}
|
||||
|
||||
|
||||
bprint :: proc(buf: ^[]byte, args: ..any) -> int {
|
||||
bprint :: proc(buf: ^Buffer, args: ...any) -> int {
|
||||
is_type_string :: proc(info: ^Type_Info) -> bool {
|
||||
using Type_Info
|
||||
using Type_Info;
|
||||
if info == nil {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
match type i : type_info_base(info) {
|
||||
case String:
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
prev_string := false
|
||||
for i := 0; i < args.count; i++ {
|
||||
arg := args[i]
|
||||
is_string := arg.data != nil && is_type_string(arg.type_info)
|
||||
prev_string := false;
|
||||
for arg, i : args {
|
||||
is_string := arg.data != nil && is_type_string(arg.type_info);
|
||||
if i > 0 && !is_string && !prev_string {
|
||||
print_space_to_buffer(buf)
|
||||
bprint_space(buf);
|
||||
}
|
||||
print_any_to_buffer(buf, arg)
|
||||
bprint_any(buf, arg);
|
||||
prev_string = is_string;
|
||||
}
|
||||
return buf.count
|
||||
return buf.length;
|
||||
}
|
||||
|
||||
bprintln :: proc(buf: ^[]byte, args: ..any) -> int {
|
||||
for i := 0; i < args.count; i++ {
|
||||
bprintln :: proc(buf: ^Buffer, args: ...any) -> int {
|
||||
for arg, i : args {
|
||||
if i > 0 {
|
||||
append(buf, #rune " ")
|
||||
bprint_space(buf);
|
||||
}
|
||||
print_any_to_buffer(buf, args[i])
|
||||
bprint_any(buf, arg);
|
||||
}
|
||||
print_nl_to_buffer(buf)
|
||||
return buf.count
|
||||
bprint_nl(buf);
|
||||
return buf.length;
|
||||
}
|
||||
|
||||
+106
-106
@@ -1,164 +1,164 @@
|
||||
crc32 :: proc(data: rawptr, len: int) -> u32 {
|
||||
result := ~(0 as u32)
|
||||
s := slice_ptr(data as ^u8, len)
|
||||
for i := 0; i < len; i++ {
|
||||
b := s[i] as u32
|
||||
result = result>>8 ~ __CRC32_TABLE[(result ~ b) & 0xff]
|
||||
result := ~(0 as u32);
|
||||
s := slice_ptr(data as ^u8, len);
|
||||
for i : 0..<len {
|
||||
b := s[i] as u32;
|
||||
result = result>>8 ~ __CRC32_TABLE[(result ~ b) & 0xff];
|
||||
}
|
||||
return ~result
|
||||
return ~result;
|
||||
}
|
||||
crc64 :: proc(data: rawptr, len: int) -> u64 {
|
||||
result := ~(0 as u64)
|
||||
s := slice_ptr(data as ^u8, len)
|
||||
for i := 0; i < len; i++ {
|
||||
b := s[i] as u64
|
||||
result = result>>8 ~ __CRC64_TABLE[(result ~ b) & 0xff]
|
||||
result := ~(0 as u64);
|
||||
s := slice_ptr(data as ^u8, len);
|
||||
for i : 0..<len {
|
||||
b := s[i] as u64;
|
||||
result = result>>8 ~ __CRC64_TABLE[(result ~ b) & 0xff];
|
||||
}
|
||||
return ~result
|
||||
return ~result;
|
||||
}
|
||||
|
||||
fnv32 :: proc(data: rawptr, len: int) -> u32 {
|
||||
s := slice_ptr(data as ^u8, len)
|
||||
s := slice_ptr(data as ^u8, len);
|
||||
|
||||
h: u32 = 0x811c9dc5
|
||||
for i := 0; i < len; i++ {
|
||||
h = (h * 0x01000193) ~ s[i] as u32
|
||||
h: u32 = 0x811c9dc5;
|
||||
for i : 0..<len {
|
||||
h = (h * 0x01000193) ~ s[i] as u32;
|
||||
}
|
||||
return h
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64 :: proc(data: rawptr, len: int) -> u64 {
|
||||
s := slice_ptr(data as ^u8, len)
|
||||
s := slice_ptr(data as ^u8, len);
|
||||
|
||||
h: u64 = 0xcbf29ce484222325
|
||||
for i := 0; i < len; i++ {
|
||||
h = (h * 0x100000001b3) ~ s[i] as u64
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for i : 0..<len {
|
||||
h = (h * 0x100000001b3) ~ s[i] as u64;
|
||||
}
|
||||
return h
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv32a :: proc(data: rawptr, len: int) -> u32 {
|
||||
s := slice_ptr(data as ^u8, len)
|
||||
s := slice_ptr(data as ^u8, len);
|
||||
|
||||
h: u32 = 0x811c9dc5
|
||||
for i := 0; i < len; i++ {
|
||||
h = (h ~ s[i] as u32) * 0x01000193
|
||||
h: u32 = 0x811c9dc5;
|
||||
for i : 0..<len {
|
||||
h = (h ~ s[i] as u32) * 0x01000193;
|
||||
}
|
||||
return h
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64a :: proc(data: rawptr, len: int) -> u64 {
|
||||
s := slice_ptr(data as ^u8, len)
|
||||
s := slice_ptr(data as ^u8, len);
|
||||
|
||||
h: u64 = 0xcbf29ce484222325
|
||||
for i := 0; i < len; i++ {
|
||||
h = (h ~ s[i] as u64) * 0x100000001b3
|
||||
h :u64 = 0xcbf29ce484222325;
|
||||
for i : 0..<len {
|
||||
h = (h ~ s[i] as u64) * 0x100000001b3;
|
||||
}
|
||||
return h
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
murmur64 :: proc(data_: rawptr, len: int) -> u64 {
|
||||
SEED :: 0x9747b28c
|
||||
SEED :: 0x9747b28c;
|
||||
|
||||
if size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995
|
||||
r :: 47
|
||||
when size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995;
|
||||
r :: 47;
|
||||
|
||||
h: u64 = SEED ~ (len as u64 * m)
|
||||
h: u64 = SEED ~ (len as u64 * m);
|
||||
|
||||
data := slice_ptr(data_ as ^u64, len/size_of(u64))
|
||||
data2 := slice_ptr(data_ as ^u8, len)
|
||||
data := slice_ptr(data_ as ^u64, len/size_of(u64));
|
||||
data2 := slice_ptr(data_ as ^u8, len);
|
||||
|
||||
for i := 0; i < data.count; i++ {
|
||||
k := data[i]
|
||||
for i : 0 ..< data.count {
|
||||
k := data[i];
|
||||
|
||||
k *= m
|
||||
k ~= k>>r
|
||||
k *= m
|
||||
k *= m;
|
||||
k ~= k>>r;
|
||||
k *= m;
|
||||
|
||||
h ~= k
|
||||
h *= m
|
||||
h ~= k;
|
||||
h *= m;
|
||||
}
|
||||
|
||||
match len & 7 {
|
||||
case 7: h ~= data2[6] as u64 << 48; fallthrough
|
||||
case 6: h ~= data2[5] as u64 << 40; fallthrough
|
||||
case 5: h ~= data2[4] as u64 << 32; fallthrough
|
||||
case 4: h ~= data2[3] as u64 << 24; fallthrough
|
||||
case 3: h ~= data2[2] as u64 << 16; fallthrough
|
||||
case 2: h ~= data2[1] as u64 << 8; fallthrough
|
||||
case 7: h ~= data2[6] as u64 << 48; fallthrough;
|
||||
case 6: h ~= data2[5] as u64 << 40; fallthrough;
|
||||
case 5: h ~= data2[4] as u64 << 32; fallthrough;
|
||||
case 4: h ~= data2[3] as u64 << 24; fallthrough;
|
||||
case 3: h ~= data2[2] as u64 << 16; fallthrough;
|
||||
case 2: h ~= data2[1] as u64 << 8; fallthrough;
|
||||
case 1:
|
||||
h ~= data2[0] as u64
|
||||
h *= m
|
||||
h ~= data2[0] as u64;
|
||||
h *= m;
|
||||
}
|
||||
|
||||
h ~= h>>r
|
||||
h *= m
|
||||
h ~= h>>r
|
||||
h ~= h>>r;
|
||||
h *= m;
|
||||
h ~= h>>r;
|
||||
|
||||
return h
|
||||
return h;
|
||||
} else {
|
||||
m :: 0x5bd1e995
|
||||
r :: 24
|
||||
m :: 0x5bd1e995;
|
||||
r :: 24;
|
||||
|
||||
h1: u32 = SEED as u32 ~ len as u32
|
||||
h2: u32 = SEED >> 32
|
||||
h1: u32 = SEED as u32 ~ len as u32;
|
||||
h2: u32 = SEED >> 32;
|
||||
|
||||
data := slice_ptr(data_ as ^u32, len/size_of(u32))
|
||||
data := slice_ptr(data_ as ^u32, len/size_of(u32));
|
||||
|
||||
i := 0
|
||||
for len >= 8 {
|
||||
k1, k2: u32
|
||||
k1 = data[i]; i++
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
i := 0;
|
||||
while len >= 8 {
|
||||
k1, k2: u32;
|
||||
k1 = data[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
|
||||
k2 = data[i]; i++
|
||||
k2 *= m
|
||||
k2 ~= k2>>r
|
||||
k2 *= m
|
||||
h2 *= m
|
||||
h2 ~= k2
|
||||
len -= 4
|
||||
k2 = data[i]; i += 1;
|
||||
k2 *= m;
|
||||
k2 ~= k2>>r;
|
||||
k2 *= m;
|
||||
h2 *= m;
|
||||
h2 ~= k2;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
if (len >= 4) {
|
||||
k1: u32
|
||||
k1 = data[i]; i++
|
||||
k1 *= m
|
||||
k1 ~= k1>>r
|
||||
k1 *= m
|
||||
h1 *= m
|
||||
h1 ~= k1
|
||||
len -= 4
|
||||
if len >= 4 {
|
||||
k1: u32;
|
||||
k1 = data[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
h1 *= m;
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
data8 := slice_ptr((data.data+i) as ^u8, 3) // NOTE(bill): This is unsafe
|
||||
data8 := slice_ptr((data.data+i) as ^u8, 3); // NOTE(bill): This is unsafe
|
||||
|
||||
match len {
|
||||
case 3: h2 ~= data8[2] as u32 << 16; fallthrough
|
||||
case 2: h2 ~= data8[1] as u32 << 8; fallthrough
|
||||
case 3: h2 ~= data8[2] as u32 << 16; fallthrough;
|
||||
case 2: h2 ~= data8[1] as u32 << 8; fallthrough;
|
||||
case 1:
|
||||
h2 ~= data8[0] as u32
|
||||
h2 *= m
|
||||
h2 ~= data8[0] as u32;
|
||||
h2 *= m;
|
||||
}
|
||||
|
||||
h1 ~= h2>>18
|
||||
h1 *= m
|
||||
h2 ~= h1>>22
|
||||
h2 *= m
|
||||
h1 ~= h2>>17
|
||||
h1 *= m
|
||||
h2 ~= h1>>19
|
||||
h2 *= m
|
||||
h1 ~= h2>>18;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>22;
|
||||
h2 *= m;
|
||||
h1 ~= h2>>17;
|
||||
h1 *= m;
|
||||
h2 ~= h1>>19;
|
||||
h2 *= m;
|
||||
|
||||
h := (h1 as u64)<<32 | h2 as u64
|
||||
return h
|
||||
h := (h1 as u64)<<32 | h2 as u64;
|
||||
return h;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ __CRC32_TABLE := [256]u32{
|
||||
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
}
|
||||
};
|
||||
__CRC64_TABLE := [256]u64{
|
||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||
@@ -295,4 +295,4 @@ __CRC64_TABLE := [256]u64{
|
||||
0xcf8b0890283e370c, 0x8d7be97b81d4019f, 0x4a6acb477bea5a2a, 0x089a2aacd2006cb9,
|
||||
0x14dea25f3af9026d, 0x562e43b4931334fe, 0x913f6188692d6f4b, 0xd3cf8063c0c759d8,
|
||||
0x5dedc41a34bbeeb2, 0x1f1d25f19d51d821, 0xd80c07cd676f8394, 0x9afce626ce85b507,
|
||||
}
|
||||
};
|
||||
|
||||
+219
-226
@@ -1,141 +1,136 @@
|
||||
TAU :: 6.28318530717958647692528676655900576
|
||||
PI :: 3.14159265358979323846264338327950288
|
||||
ONE_OVER_TAU :: 0.636619772367581343075535053490057448
|
||||
ONE_OVER_PI :: 0.159154943091895335768883763372514362
|
||||
TAU :: 6.28318530717958647692528676655900576;
|
||||
PI :: 3.14159265358979323846264338327950288;
|
||||
ONE_OVER_TAU :: 0.636619772367581343075535053490057448;
|
||||
ONE_OVER_PI :: 0.159154943091895335768883763372514362;
|
||||
|
||||
E :: 2.71828182845904523536
|
||||
SQRT_TWO :: 1.41421356237309504880168872420969808
|
||||
SQRT_THREE :: 1.73205080756887729352744634150587236
|
||||
SQRT_FIVE :: 2.23606797749978969640917366873127623
|
||||
E :: 2.71828182845904523536;
|
||||
SQRT_TWO :: 1.41421356237309504880168872420969808;
|
||||
SQRT_THREE :: 1.73205080756887729352744634150587236;
|
||||
SQRT_FIVE :: 2.23606797749978969640917366873127623;
|
||||
|
||||
LOG_TWO :: 0.693147180559945309417232121458176568
|
||||
LOG_TEN :: 2.30258509299404568401799145468436421
|
||||
LOG_TWO :: 0.693147180559945309417232121458176568;
|
||||
LOG_TEN :: 2.30258509299404568401799145468436421;
|
||||
|
||||
EPSILON :: 1.19209290e-7
|
||||
EPSILON :: 1.19209290e-7;
|
||||
|
||||
τ :: TAU
|
||||
π :: PI
|
||||
τ :: TAU;
|
||||
π :: PI;
|
||||
|
||||
Vec2 :: [vector 2]f32;
|
||||
Vec3 :: [vector 3]f32;
|
||||
Vec4 :: [vector 4]f32;
|
||||
|
||||
Vec2 :: type {2}f32
|
||||
Vec3 :: type {3}f32
|
||||
Vec4 :: type {4}f32
|
||||
Mat2 :: [2]Vec2;
|
||||
Mat3 :: [3]Vec3;
|
||||
Mat4 :: [4]Vec4;
|
||||
|
||||
Mat2 :: type [2]Vec2
|
||||
Mat3 :: type [3]Vec3
|
||||
Mat4 :: type [4]Vec4
|
||||
sqrt32 :: proc(x: f32) -> f32 #foreign "llvm.sqrt.f32"
|
||||
sqrt64 :: proc(x: f64) -> f64 #foreign "llvm.sqrt.f64"
|
||||
|
||||
sin32 :: proc(x: f32) -> f32 #foreign "llvm.sin.f32"
|
||||
sin64 :: proc(x: f64) -> f64 #foreign "llvm.sin.f64"
|
||||
|
||||
sqrt32 :: proc(x: f32) -> f32 #foreign "llvm.sqrt.f32"
|
||||
sqrt64 :: proc(x: f64) -> f64 #foreign "llvm.sqrt.f64"
|
||||
cos32 :: proc(x: f32) -> f32 #foreign "llvm.cos.f32"
|
||||
cos64 :: proc(x: f64) -> f64 #foreign "llvm.cos.f64"
|
||||
|
||||
sin32 :: proc(x: f32) -> f32 #foreign "llvm.sin.f32"
|
||||
sin64 :: proc(x: f64) -> f64 #foreign "llvm.sin.f64"
|
||||
tan32 :: proc(x: f32) -> f32 #inline { return sin32(x)/cos32(x); }
|
||||
tan64 :: proc(x: f64) -> f64 #inline { return sin64(x)/cos64(x); }
|
||||
|
||||
cos32 :: proc(x: f32) -> f32 #foreign "llvm.cos.f32"
|
||||
cos64 :: proc(x: f64) -> f64 #foreign "llvm.cos.f64"
|
||||
lerp32 :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
|
||||
lerp64 :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; }
|
||||
|
||||
tan32 :: proc(x: f32) -> f32 #inline { return sin32(x)/cos32(x) }
|
||||
tan64 :: proc(x: f64) -> f64 #inline { return sin64(x)/cos64(x) }
|
||||
|
||||
lerp32 :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t }
|
||||
lerp64 :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t }
|
||||
|
||||
clamp32 :: proc(x, lower, upper: f32) -> f32 { return min(max(x, lower), upper) }
|
||||
clamp64 :: proc(x, lower, upper: f64) -> f64 { return min(max(x, lower), upper) }
|
||||
|
||||
sign32 :: proc(x: f32) -> f32 { if x >= 0 { return +1 } return -1 }
|
||||
sign64 :: proc(x: f64) -> f64 { if x >= 0 { return +1 } return -1 }
|
||||
sign32 :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
|
||||
sign64 :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; }
|
||||
|
||||
|
||||
|
||||
copy_sign32 :: proc(x, y: f32) -> f32 {
|
||||
ix := x transmute u32
|
||||
iy := y transmute u32
|
||||
ix &= 0x7fffffff
|
||||
ix |= iy & 0x80000000
|
||||
return ix transmute f32
|
||||
ix := x transmute u32;
|
||||
iy := y transmute u32;
|
||||
ix &= 0x7fffffff;
|
||||
ix |= iy & 0x80000000;
|
||||
return ix transmute f32;
|
||||
}
|
||||
round32 :: proc(x: f32) -> f32 {
|
||||
if x >= 0 {
|
||||
return floor32(x + 0.5)
|
||||
return floor32(x + 0.5);
|
||||
}
|
||||
return ceil32(x - 0.5)
|
||||
return ceil32(x - 0.5);
|
||||
}
|
||||
floor32 :: proc(x: f32) -> f32 {
|
||||
if x >= 0 {
|
||||
return x as int as f32
|
||||
return x as int as f32;
|
||||
}
|
||||
return (x-0.5) as int as f32
|
||||
return (x-0.5) as int as f32;
|
||||
}
|
||||
ceil32 :: proc(x: f32) -> f32 {
|
||||
if x < 0 {
|
||||
return x as int as f32
|
||||
return x as int as f32;
|
||||
}
|
||||
return ((x as int)+1) as f32
|
||||
return ((x as int)+1) as f32;
|
||||
}
|
||||
|
||||
remainder32 :: proc(x, y: f32) -> f32 {
|
||||
return x - round32(x/y) * y
|
||||
return x - round32(x/y) * y;
|
||||
}
|
||||
|
||||
fmod32 :: proc(x, y: f32) -> f32 {
|
||||
y = abs(y)
|
||||
result := remainder32(abs(x), y)
|
||||
y = abs(y);
|
||||
result := remainder32(abs(x), y);
|
||||
if sign32(result) < 0 {
|
||||
result += y
|
||||
result += y;
|
||||
}
|
||||
return copy_sign32(result, x)
|
||||
return copy_sign32(result, x);
|
||||
}
|
||||
|
||||
|
||||
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360 }
|
||||
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU }
|
||||
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; }
|
||||
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
|
||||
|
||||
|
||||
|
||||
|
||||
dot2 :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y }
|
||||
dot3 :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z }
|
||||
dot4 :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w }
|
||||
dot2 :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; }
|
||||
dot3 :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z; }
|
||||
dot4 :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }
|
||||
|
||||
cross3 :: proc(x, y: Vec3) -> Vec3 {
|
||||
a := swizzle(x, 1, 2, 0) * swizzle(y, 2, 0, 1)
|
||||
b := swizzle(x, 2, 0, 1) * swizzle(y, 1, 2, 0)
|
||||
return a - b
|
||||
a := swizzle(x, 1, 2, 0) * swizzle(y, 2, 0, 1);
|
||||
b := swizzle(x, 2, 0, 1) * swizzle(y, 1, 2, 0);
|
||||
return a - b;
|
||||
}
|
||||
|
||||
|
||||
vec2_mag :: proc(v: Vec2) -> f32 { return sqrt32(dot2(v, v)) }
|
||||
vec3_mag :: proc(v: Vec3) -> f32 { return sqrt32(dot3(v, v)) }
|
||||
vec4_mag :: proc(v: Vec4) -> f32 { return sqrt32(dot4(v, v)) }
|
||||
vec2_mag :: proc(v: Vec2) -> f32 { return sqrt32(dot2(v, v)); }
|
||||
vec3_mag :: proc(v: Vec3) -> f32 { return sqrt32(dot3(v, v)); }
|
||||
vec4_mag :: proc(v: Vec4) -> f32 { return sqrt32(dot4(v, v)); }
|
||||
|
||||
vec2_norm :: proc(v: Vec2) -> Vec2 { return v / Vec2{vec2_mag(v)} }
|
||||
vec3_norm :: proc(v: Vec3) -> Vec3 { return v / Vec3{vec3_mag(v)} }
|
||||
vec4_norm :: proc(v: Vec4) -> Vec4 { return v / Vec4{vec4_mag(v)} }
|
||||
vec2_norm :: proc(v: Vec2) -> Vec2 { return v / Vec2{vec2_mag(v)}; }
|
||||
vec3_norm :: proc(v: Vec3) -> Vec3 { return v / Vec3{vec3_mag(v)}; }
|
||||
vec4_norm :: proc(v: Vec4) -> Vec4 { return v / Vec4{vec4_mag(v)}; }
|
||||
|
||||
vec2_norm0 :: proc(v: Vec2) -> Vec2 {
|
||||
m := vec2_mag(v)
|
||||
m := vec2_mag(v);
|
||||
if m == 0 {
|
||||
return Vec2{0}
|
||||
return Vec2{0};
|
||||
}
|
||||
return v / Vec2{m}
|
||||
return v / Vec2{m};
|
||||
}
|
||||
|
||||
vec3_norm0 :: proc(v: Vec3) -> Vec3 {
|
||||
m := vec3_mag(v)
|
||||
m := vec3_mag(v);
|
||||
if m == 0 {
|
||||
return Vec3{0}
|
||||
return Vec3{0};
|
||||
}
|
||||
return v / Vec3{m}
|
||||
return v / Vec3{m};
|
||||
}
|
||||
|
||||
vec4_norm0 :: proc(v: Vec4) -> Vec4 {
|
||||
m := vec4_mag(v)
|
||||
m := vec4_mag(v);
|
||||
if m == 0 {
|
||||
return Vec4{0}
|
||||
return Vec4{0};
|
||||
}
|
||||
return v / Vec4{m}
|
||||
return v / Vec4{m};
|
||||
}
|
||||
|
||||
|
||||
@@ -146,29 +141,29 @@ mat4_identity :: proc() -> Mat4 {
|
||||
{0, 1, 0, 0},
|
||||
{0, 0, 1, 0},
|
||||
{0, 0, 0, 1},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mat4_transpose :: proc(m: Mat4) -> Mat4 {
|
||||
for j := 0; j < 4; j++ {
|
||||
for i := 0; i < 4; i++ {
|
||||
m[i][j], m[j][i] = m[j][i], m[i][j]
|
||||
for j : 0..<4 {
|
||||
for i : 0..<4 {
|
||||
m[i][j], m[j][i] = m[j][i], m[i][j];
|
||||
}
|
||||
}
|
||||
return m
|
||||
return m;
|
||||
}
|
||||
|
||||
mat4_mul :: proc(a, b: Mat4) -> Mat4 {
|
||||
c: Mat4
|
||||
for j := 0; j < 4; j++ {
|
||||
for i := 0; i < 4; i++ {
|
||||
c[j][i] = a[0][i]*b[j][0]
|
||||
+ a[1][i]*b[j][1]
|
||||
+ a[2][i]*b[j][2]
|
||||
+ a[3][i]*b[j][3]
|
||||
c: Mat4;
|
||||
for j : 0..<4 {
|
||||
for i : 0..<4 {
|
||||
c[j][i] = a[0][i]*b[j][0] +
|
||||
a[1][i]*b[j][1] +
|
||||
a[2][i]*b[j][2] +
|
||||
a[3][i]*b[j][3];
|
||||
}
|
||||
}
|
||||
return c
|
||||
return c;
|
||||
}
|
||||
|
||||
mat4_mul_vec4 :: proc(m: Mat4, v: Vec4) -> Vec4 {
|
||||
@@ -177,197 +172,195 @@ mat4_mul_vec4 :: proc(m: Mat4, v: Vec4) -> Vec4 {
|
||||
m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z + m[3][1]*v.w,
|
||||
m[0][2]*v.x + m[1][2]*v.y + m[2][2]*v.z + m[3][2]*v.w,
|
||||
m[0][3]*v.x + m[1][3]*v.y + m[2][3]*v.z + m[3][3]*v.w,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
mat4_inverse :: proc(m: Mat4) -> Mat4 {
|
||||
o: Mat4
|
||||
o: Mat4;
|
||||
|
||||
sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3]
|
||||
sf01 := m[2][1] * m[3][3] - m[3][1] * m[2][3]
|
||||
sf02 := m[2][1] * m[3][2] - m[3][1] * m[2][2]
|
||||
sf03 := m[2][0] * m[3][3] - m[3][0] * m[2][3]
|
||||
sf04 := m[2][0] * m[3][2] - m[3][0] * m[2][2]
|
||||
sf05 := m[2][0] * m[3][1] - m[3][0] * m[2][1]
|
||||
sf06 := m[1][2] * m[3][3] - m[3][2] * m[1][3]
|
||||
sf07 := m[1][1] * m[3][3] - m[3][1] * m[1][3]
|
||||
sf08 := m[1][1] * m[3][2] - m[3][1] * m[1][2]
|
||||
sf09 := m[1][0] * m[3][3] - m[3][0] * m[1][3]
|
||||
sf10 := m[1][0] * m[3][2] - m[3][0] * m[1][2]
|
||||
sf11 := m[1][1] * m[3][3] - m[3][1] * m[1][3]
|
||||
sf12 := m[1][0] * m[3][1] - m[3][0] * m[1][1]
|
||||
sf13 := m[1][2] * m[2][3] - m[2][2] * m[1][3]
|
||||
sf14 := m[1][1] * m[2][3] - m[2][1] * m[1][3]
|
||||
sf15 := m[1][1] * m[2][2] - m[2][1] * m[1][2]
|
||||
sf16 := m[1][0] * m[2][3] - m[2][0] * m[1][3]
|
||||
sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2]
|
||||
sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1]
|
||||
sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3];
|
||||
sf01 := m[2][1] * m[3][3] - m[3][1] * m[2][3];
|
||||
sf02 := m[2][1] * m[3][2] - m[3][1] * m[2][2];
|
||||
sf03 := m[2][0] * m[3][3] - m[3][0] * m[2][3];
|
||||
sf04 := m[2][0] * m[3][2] - m[3][0] * m[2][2];
|
||||
sf05 := m[2][0] * m[3][1] - m[3][0] * m[2][1];
|
||||
sf06 := m[1][2] * m[3][3] - m[3][2] * m[1][3];
|
||||
sf07 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf08 := m[1][1] * m[3][2] - m[3][1] * m[1][2];
|
||||
sf09 := m[1][0] * m[3][3] - m[3][0] * m[1][3];
|
||||
sf10 := m[1][0] * m[3][2] - m[3][0] * m[1][2];
|
||||
sf11 := m[1][1] * m[3][3] - m[3][1] * m[1][3];
|
||||
sf12 := m[1][0] * m[3][1] - m[3][0] * m[1][1];
|
||||
sf13 := m[1][2] * m[2][3] - m[2][2] * m[1][3];
|
||||
sf14 := m[1][1] * m[2][3] - m[2][1] * m[1][3];
|
||||
sf15 := m[1][1] * m[2][2] - m[2][1] * m[1][2];
|
||||
sf16 := m[1][0] * m[2][3] - m[2][0] * m[1][3];
|
||||
sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2];
|
||||
sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1];
|
||||
|
||||
o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02)
|
||||
o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04)
|
||||
o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05)
|
||||
o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05)
|
||||
o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02);
|
||||
o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04);
|
||||
o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05);
|
||||
o[0][3] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05);
|
||||
|
||||
o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02)
|
||||
o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04)
|
||||
o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05)
|
||||
o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05)
|
||||
o[1][0] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02);
|
||||
o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04);
|
||||
o[1][2] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05);
|
||||
o[1][3] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05);
|
||||
|
||||
o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08)
|
||||
o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10)
|
||||
o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12)
|
||||
o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12)
|
||||
o[2][0] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08);
|
||||
o[2][1] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10);
|
||||
o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12);
|
||||
o[2][3] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12);
|
||||
|
||||
o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15)
|
||||
o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17)
|
||||
o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18)
|
||||
o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18)
|
||||
o[3][0] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15);
|
||||
o[3][1] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17);
|
||||
o[3][2] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18);
|
||||
o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18);
|
||||
|
||||
ood := 1.0 / (m[0][0] * o[0][0] +
|
||||
m[0][1] * o[0][1] +
|
||||
m[0][2] * o[0][2] +
|
||||
m[0][3] * o[0][3])
|
||||
m[0][3] * o[0][3]);
|
||||
|
||||
o[0][0] *= ood
|
||||
o[0][1] *= ood
|
||||
o[0][2] *= ood
|
||||
o[0][3] *= ood
|
||||
o[1][0] *= ood
|
||||
o[1][1] *= ood
|
||||
o[1][2] *= ood
|
||||
o[1][3] *= ood
|
||||
o[2][0] *= ood
|
||||
o[2][1] *= ood
|
||||
o[2][2] *= ood
|
||||
o[2][3] *= ood
|
||||
o[3][0] *= ood
|
||||
o[3][1] *= ood
|
||||
o[3][2] *= ood
|
||||
o[3][3] *= ood
|
||||
o[0][0] *= ood;
|
||||
o[0][1] *= ood;
|
||||
o[0][2] *= ood;
|
||||
o[0][3] *= ood;
|
||||
o[1][0] *= ood;
|
||||
o[1][1] *= ood;
|
||||
o[1][2] *= ood;
|
||||
o[1][3] *= ood;
|
||||
o[2][0] *= ood;
|
||||
o[2][1] *= ood;
|
||||
o[2][2] *= ood;
|
||||
o[2][3] *= ood;
|
||||
o[3][0] *= ood;
|
||||
o[3][1] *= ood;
|
||||
o[3][2] *= ood;
|
||||
o[3][3] *= ood;
|
||||
|
||||
return o
|
||||
return o;
|
||||
}
|
||||
|
||||
|
||||
mat4_translate :: proc(v: Vec3) -> Mat4 {
|
||||
m := mat4_identity()
|
||||
m[3][0] = v.x
|
||||
m[3][1] = v.y
|
||||
m[3][2] = v.z
|
||||
m[3][3] = 1
|
||||
return m
|
||||
m := mat4_identity();
|
||||
m[3][0] = v.x;
|
||||
m[3][1] = v.y;
|
||||
m[3][2] = v.z;
|
||||
m[3][3] = 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
|
||||
c := cos32(angle_radians)
|
||||
s := sin32(angle_radians)
|
||||
c := cos32(angle_radians);
|
||||
s := sin32(angle_radians);
|
||||
|
||||
a := vec3_norm(v)
|
||||
t := a * Vec3{1-c}
|
||||
a := vec3_norm(v);
|
||||
t := a * Vec3{1-c};
|
||||
|
||||
rot := mat4_identity()
|
||||
rot := mat4_identity();
|
||||
|
||||
rot[0][0] = c + t.x*a.x
|
||||
rot[0][1] = 0 + t.x*a.y + s*a.z
|
||||
rot[0][2] = 0 + t.x*a.z - s*a.y
|
||||
rot[0][3] = 0
|
||||
rot[0][0] = c + t.x*a.x;
|
||||
rot[0][1] = 0 + t.x*a.y + s*a.z;
|
||||
rot[0][2] = 0 + t.x*a.z - s*a.y;
|
||||
rot[0][3] = 0;
|
||||
|
||||
rot[1][0] = 0 + t.y*a.x - s*a.z
|
||||
rot[1][1] = c + t.y*a.y
|
||||
rot[1][2] = 0 + t.y*a.z + s*a.x
|
||||
rot[1][3] = 0
|
||||
rot[1][0] = 0 + t.y*a.x - s*a.z;
|
||||
rot[1][1] = c + t.y*a.y;
|
||||
rot[1][2] = 0 + t.y*a.z + s*a.x;
|
||||
rot[1][3] = 0;
|
||||
|
||||
rot[2][0] = 0 + t.z*a.x + s*a.y
|
||||
rot[2][1] = 0 + t.z*a.y - s*a.x
|
||||
rot[2][2] = c + t.z*a.z
|
||||
rot[2][3] = 0
|
||||
rot[2][0] = 0 + t.z*a.x + s*a.y;
|
||||
rot[2][1] = 0 + t.z*a.y - s*a.x;
|
||||
rot[2][2] = c + t.z*a.z;
|
||||
rot[2][3] = 0;
|
||||
|
||||
return rot
|
||||
return rot;
|
||||
}
|
||||
|
||||
mat4_scale :: proc(m: Mat4, v: Vec3) -> Mat4 {
|
||||
m[0][0] = v.x
|
||||
m[1][1] = v.y
|
||||
m[2][2] = v.z
|
||||
return m
|
||||
m[0][0] *= v.x;
|
||||
m[1][1] *= v.y;
|
||||
m[2][2] *= v.z;
|
||||
return m;
|
||||
}
|
||||
|
||||
mat4_scalef :: proc(m: Mat4, s: f32) -> Mat4 {
|
||||
m[0][0] = s
|
||||
m[1][1] = s
|
||||
m[2][2] = s
|
||||
return m
|
||||
m[0][0] *= s;
|
||||
m[1][1] *= s;
|
||||
m[2][2] *= s;
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
mat4_look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
|
||||
f := vec3_norm(centre - eye)
|
||||
s := vec3_norm(cross3(f, up))
|
||||
u := cross3(s, f)
|
||||
f := vec3_norm(centre - eye);
|
||||
s := vec3_norm(cross3(f, up));
|
||||
u := cross3(s, f);
|
||||
|
||||
m: Mat4
|
||||
m: Mat4;
|
||||
|
||||
m[0] = Vec4{+s.x, +s.y, +s.z, 0}
|
||||
m[1] = Vec4{+u.x, +u.y, +u.z, 0}
|
||||
m[2] = Vec4{-f.x, -f.y, -f.z, 0}
|
||||
m[3] = Vec4{dot3(s, eye), dot3(u, eye), dot3(f, eye), 1}
|
||||
m[0] = Vec4{+s.x, +s.y, +s.z, 0};
|
||||
m[1] = Vec4{+u.x, +u.y, +u.z, 0};
|
||||
m[2] = Vec4{-f.x, -f.y, -f.z, 0};
|
||||
m[3] = Vec4{dot3(s, eye), dot3(u, eye), dot3(f, eye), 1};
|
||||
|
||||
return m
|
||||
return m;
|
||||
}
|
||||
mat4_perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
|
||||
m: Mat4
|
||||
tan_half_fovy := tan32(0.5 * fovy)
|
||||
m[0][0] = 1.0 / (aspect*tan_half_fovy)
|
||||
m[1][1] = 1.0 / (tan_half_fovy)
|
||||
m[2][2] = -(far + near) / (far - near)
|
||||
m[2][3] = -1.0
|
||||
m[3][2] = -2.0*far*near / (far - near)
|
||||
return m
|
||||
m: Mat4;
|
||||
tan_half_fovy := tan32(0.5 * fovy);
|
||||
m[0][0] = 1.0 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1.0 / (tan_half_fovy);
|
||||
m[2][2] = -(far + near) / (far - near);
|
||||
m[2][3] = -1.0;
|
||||
m[3][2] = -2.0*far*near / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
mat4_ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
|
||||
m := mat4_identity()
|
||||
|
||||
m[0][0] = +2.0 / (right - left)
|
||||
m[1][1] = +2.0 / (top - bottom)
|
||||
m[2][2] = -2.0 / (far - near)
|
||||
m[3][0] = -(right + left) / (right - left)
|
||||
m[3][1] = -(top + bottom) / (top - bottom)
|
||||
m[3][2] = -(far + near) / (far - near)
|
||||
|
||||
return m
|
||||
m := mat4_identity();
|
||||
m[0][0] = +2.0 / (right - left);
|
||||
m[1][1] = +2.0 / (top - bottom);
|
||||
m[2][2] = -2.0 / (far - near);
|
||||
m[3][0] = -(right + left) / (right - left);
|
||||
m[3][1] = -(top + bottom) / (top - bottom);
|
||||
m[3][2] = -(far + near) / (far - near);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
F32_DIG :: 6
|
||||
F32_EPSILON :: 1.192092896e-07
|
||||
F32_GUARD :: 0
|
||||
F32_MANT_DIG :: 24
|
||||
F32_MAX :: 3.402823466e+38
|
||||
F32_MAX_10_EXP :: 38
|
||||
F32_MAX_EXP :: 128
|
||||
F32_MIN :: 1.175494351e-38
|
||||
F32_MIN_10_EXP :: -37
|
||||
F32_MIN_EXP :: -125
|
||||
F32_NORMALIZE :: 0
|
||||
F32_RADIX :: 2
|
||||
F32_ROUNDS :: 1
|
||||
F32_DIG :: 6;
|
||||
F32_EPSILON :: 1.192092896e-07;
|
||||
F32_GUARD :: 0;
|
||||
F32_MANT_DIG :: 24;
|
||||
F32_MAX :: 3.402823466e+38;
|
||||
F32_MAX_10_EXP :: 38;
|
||||
F32_MAX_EXP :: 128;
|
||||
F32_MIN :: 1.175494351e-38;
|
||||
F32_MIN_10_EXP :: -37;
|
||||
F32_MIN_EXP :: -125;
|
||||
F32_NORMALIZE :: 0;
|
||||
F32_RADIX :: 2;
|
||||
F32_ROUNDS :: 1;
|
||||
|
||||
F64_DIG :: 15 // # of decimal digits of precision
|
||||
F64_EPSILON :: 2.2204460492503131e-016 // smallest such that 1.0+F64_EPSILON != 1.0
|
||||
F64_MANT_DIG :: 53 // # of bits in mantissa
|
||||
F64_MAX :: 1.7976931348623158e+308 // max value
|
||||
F64_MAX_10_EXP :: 308 // max decimal exponent
|
||||
F64_MAX_EXP :: 1024 // max binary exponent
|
||||
F64_MIN :: 2.2250738585072014e-308 // min positive value
|
||||
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
|
||||
F64_DIG :: 15; // # of decimal digits of precision
|
||||
F64_EPSILON :: 2.2204460492503131e-016; // smallest such that 1.0+F64_EPSILON != 1.0
|
||||
F64_MANT_DIG :: 53; // # of bits in mantissa
|
||||
F64_MAX :: 1.7976931348623158e+308; // max value
|
||||
F64_MAX_10_EXP :: 308; // max decimal exponent
|
||||
F64_MAX_EXP :: 1024; // max binary exponent
|
||||
F64_MIN :: 2.2250738585072014e-308; // min positive value
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
+164
-150
@@ -1,111 +1,112 @@
|
||||
#import "fmt.odin"
|
||||
#import "os.odin"
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
|
||||
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.memset.p0i8.i64"
|
||||
llvm_memset_64bit(data, value as byte, len, 1, false)
|
||||
return data
|
||||
llvm_memset_64bit(data, value as byte, len, 1, false);
|
||||
return data;
|
||||
}
|
||||
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr {
|
||||
return set(data, 0, len)
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr #link_name "__mem_zero" {
|
||||
return set(data, 0, len);
|
||||
}
|
||||
|
||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy" {
|
||||
// NOTE(bill): This _must_ implemented like C's memmove
|
||||
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memmove.p0i8.p0i8.i64"
|
||||
llvm_memmove_64bit(dst, src, len, 1, false)
|
||||
return dst
|
||||
llvm_memmove_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy_non_overlapping" {
|
||||
// NOTE(bill): This _must_ implemented like C's memcpy
|
||||
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memcpy.p0i8.p0i8.i64"
|
||||
llvm_memcpy_64bit(dst, src, len, 1, false)
|
||||
return dst
|
||||
llvm_memcpy_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
compare :: proc(dst, src: rawptr, n: int) -> int #link_name "__mem_compare" {
|
||||
// Translation of http://mgronhol.github.io/fast-strcmp/
|
||||
a := slice_ptr(dst as ^byte, n)
|
||||
b := slice_ptr(src as ^byte, n)
|
||||
a := slice_ptr(dst as ^byte, n);
|
||||
b := slice_ptr(src as ^byte, n);
|
||||
|
||||
fast := n/size_of(int) + 1
|
||||
offset := (fast-1)*size_of(int)
|
||||
curr_block := 0
|
||||
fast := n/size_of(int) + 1;
|
||||
offset := (fast-1)*size_of(int);
|
||||
curr_block := 0;
|
||||
if n <= size_of(int) {
|
||||
fast = 0
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
la := slice_ptr(^a[0] as ^int, fast)
|
||||
lb := slice_ptr(^b[0] as ^int, fast)
|
||||
la := slice_ptr(^a[0] as ^int, fast);
|
||||
lb := slice_ptr(^b[0] as ^int, fast);
|
||||
|
||||
for ; curr_block < fast; curr_block++ {
|
||||
for _ : curr_block ..< fast {
|
||||
if (la[curr_block] ~ lb[curr_block]) != 0 {
|
||||
for pos := curr_block*size_of(int); pos < n; pos++ {
|
||||
for pos : curr_block*size_of(int) ..< n {
|
||||
if (a[pos] ~ b[pos]) != 0 {
|
||||
return a[pos] as int - b[pos] as int
|
||||
return a[pos] as int - b[pos] as int;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ; offset < n; offset++ {
|
||||
for _ : offset ..< n {
|
||||
if (a[offset] ~ b[offset]) != 0 {
|
||||
return a[offset] as int - b[offset] as int
|
||||
return a[offset] as int - b[offset] as int;
|
||||
}
|
||||
}
|
||||
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
kilobytes :: proc(x: int) -> int #inline { return (x) * 1024 }
|
||||
megabytes :: proc(x: int) -> int #inline { return kilobytes(x) * 1024 }
|
||||
gigabytes :: proc(x: int) -> int #inline { return gigabytes(x) * 1024 }
|
||||
terabytes :: proc(x: int) -> int #inline { return terabytes(x) * 1024 }
|
||||
kilobytes :: proc(x: int) -> int #inline { return (x) * 1024; }
|
||||
megabytes :: proc(x: int) -> int #inline { return kilobytes(x) * 1024; }
|
||||
gigabytes :: proc(x: int) -> int #inline { return gigabytes(x) * 1024; }
|
||||
terabytes :: proc(x: int) -> int #inline { return terabytes(x) * 1024; }
|
||||
|
||||
is_power_of_two :: proc(x: int) -> bool {
|
||||
if x <= 0 {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return (x & (x-1)) == 0
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
|
||||
assert(is_power_of_two(align))
|
||||
assert(is_power_of_two(align));
|
||||
|
||||
a := align as uint
|
||||
p := ptr as uint
|
||||
modulo := p & (a-1)
|
||||
a := align as uint;
|
||||
p := ptr as uint;
|
||||
modulo := p & (a-1);
|
||||
if modulo != 0 {
|
||||
p += a - modulo
|
||||
p += a - modulo;
|
||||
}
|
||||
return p as rawptr
|
||||
return p as rawptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
AllocationHeader :: struct {
|
||||
size: int
|
||||
Allocation_Header :: struct {
|
||||
size: int;
|
||||
}
|
||||
allocation_header_fill :: proc(header: ^AllocationHeader, data: rawptr, size: int) {
|
||||
header.size = size
|
||||
ptr := (header+1) as ^int
|
||||
|
||||
for i := 0; ptr as rawptr < data; i++ {
|
||||
(ptr+i)^ = -1
|
||||
allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: int) {
|
||||
header.size = size;
|
||||
ptr := (header+1) as ^int;
|
||||
|
||||
while i := 0; ptr as rawptr < data {
|
||||
(ptr+i)^ = -1;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
|
||||
p := data as ^int
|
||||
for (p-1)^ == -1 {
|
||||
p = (p-1)
|
||||
allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
|
||||
p := data as ^int;
|
||||
while (p-1)^ == -1 {
|
||||
p = (p-1);
|
||||
}
|
||||
return (p as ^AllocationHeader)-1
|
||||
return (p as ^Allocation_Header)-1;
|
||||
}
|
||||
|
||||
|
||||
@@ -113,16 +114,16 @@ allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
|
||||
|
||||
|
||||
// Custom allocators
|
||||
|
||||
Arena :: struct {
|
||||
backing: Allocator
|
||||
memory: []byte
|
||||
temp_count: int
|
||||
backing: Allocator;
|
||||
offset: int;
|
||||
memory: []byte;
|
||||
temp_count: int;
|
||||
}
|
||||
|
||||
Temp_Memory :: struct {
|
||||
arena: ^Arena
|
||||
original_count: int
|
||||
}
|
||||
Arena_Temp_Memory :: struct {
|
||||
arena: ^Arena;
|
||||
original_count: int;
|
||||
}
|
||||
|
||||
|
||||
@@ -130,22 +131,23 @@ Arena :: struct {
|
||||
|
||||
|
||||
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
|
||||
backing = Allocator{}
|
||||
memory = data[:0]
|
||||
temp_count = 0
|
||||
backing = Allocator{};
|
||||
memory = data[:0];
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
init_arena_from_context :: proc(using a: ^Arena, size: int) {
|
||||
backing = context.allocator
|
||||
memory = new_slice(byte, 0, size)
|
||||
temp_count = 0
|
||||
backing = context.allocator;
|
||||
memory = new_slice(byte, size);
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
free_arena :: proc(using a: ^Arena) {
|
||||
if backing.procedure != nil {
|
||||
push_allocator backing {
|
||||
free(memory.data)
|
||||
memory = memory[0:0:0]
|
||||
free(memory.data);
|
||||
memory = memory[0:0];
|
||||
offset = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,57 +156,57 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
return Allocator{
|
||||
procedure = arena_allocator_proc,
|
||||
data = arena,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
arena := allocator_data as ^Arena
|
||||
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 := allocator_data as ^Arena;
|
||||
|
||||
using Allocator.Mode
|
||||
match mode {
|
||||
case ALLOC:
|
||||
total_size := size + alignment
|
||||
total_size := size + alignment;
|
||||
|
||||
if arena.memory.count + total_size > arena.memory.capacity {
|
||||
fmt.fprintln(os.stderr, "Arena out of memory")
|
||||
return nil
|
||||
if arena.offset + total_size > arena.memory.count {
|
||||
fmt.fprintln(os.stderr, "Arena out of memory");
|
||||
return nil;
|
||||
}
|
||||
|
||||
#no_bounds_check end := ^arena.memory[arena.memory.count]
|
||||
#no_bounds_check end := ^arena.memory[arena.offset];
|
||||
|
||||
ptr := align_forward(end, alignment)
|
||||
arena.memory.count += total_size
|
||||
return zero(ptr, size)
|
||||
ptr := align_forward(end, alignment);
|
||||
arena.offset += total_size;
|
||||
return zero(ptr, size);
|
||||
|
||||
case FREE:
|
||||
// NOTE(bill): Free all at once
|
||||
// Use Arena.Temp_Memory if you want to free a block
|
||||
// Use Arena_Temp_Memory if you want to free a block
|
||||
|
||||
case FREE_ALL:
|
||||
arena.memory.count = 0
|
||||
arena.offset = 0;
|
||||
|
||||
case RESIZE:
|
||||
return default_resize_align(old_memory, old_size, size, alignment)
|
||||
return default_resize_align(old_memory, old_size, size, alignment);
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena.Temp_Memory {
|
||||
tmp: Arena.Temp_Memory
|
||||
tmp.arena = a
|
||||
tmp.original_count = a.memory.count
|
||||
a.temp_count++
|
||||
return tmp
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
tmp: Arena_Temp_Memory;
|
||||
tmp.arena = a;
|
||||
tmp.original_count = a.memory.count;
|
||||
a.temp_count += 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
end_arena_temp_memory :: proc(using tmp: Arena.Temp_Memory) {
|
||||
assert(arena.memory.count >= original_count)
|
||||
assert(arena.temp_count > 0)
|
||||
arena.memory.count = original_count
|
||||
arena.temp_count--
|
||||
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
assert(arena.memory.count >= original_count);
|
||||
assert(arena.temp_count > 0);
|
||||
arena.memory.count = original_count;
|
||||
arena.temp_count -= 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -214,119 +216,131 @@ end_arena_temp_memory :: proc(using tmp: Arena.Temp_Memory) {
|
||||
|
||||
|
||||
align_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||
WORD_SIZE :: size_of(int)
|
||||
using Type_Info
|
||||
prev_pow2 :: proc(n: i64) -> i64 {
|
||||
if n <= 0 {
|
||||
return 0;
|
||||
}
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
|
||||
WORD_SIZE :: size_of(int);
|
||||
MAX_ALIGN :: size_of([vector 64]f64); // TODO(bill): Should these constants be builtin constants?
|
||||
using Type_Info;
|
||||
|
||||
match type info : type_info {
|
||||
case Named:
|
||||
return align_of_type_info(info.base)
|
||||
return align_of_type_info(info.base);
|
||||
case Integer:
|
||||
return info.size
|
||||
return info.size;
|
||||
case Float:
|
||||
return info.size
|
||||
return info.size;
|
||||
case String:
|
||||
return WORD_SIZE
|
||||
return WORD_SIZE;
|
||||
case Boolean:
|
||||
return 1
|
||||
return 1;
|
||||
case Pointer:
|
||||
return WORD_SIZE
|
||||
return WORD_SIZE;
|
||||
case Maybe:
|
||||
return max(align_of_type_info(info.elem), 1)
|
||||
return max(align_of_type_info(info.elem), 1);
|
||||
case Procedure:
|
||||
return WORD_SIZE
|
||||
return WORD_SIZE;
|
||||
case Array:
|
||||
return align_of_type_info(info.elem)
|
||||
return align_of_type_info(info.elem);
|
||||
case Slice:
|
||||
return WORD_SIZE
|
||||
return WORD_SIZE;
|
||||
case Vector:
|
||||
return align_of_type_info(info.elem)
|
||||
size := size_of_type_info(info.elem);
|
||||
count := max(prev_pow2(info.count as i64), 1) as int;
|
||||
total := size * count;
|
||||
return clamp(total, 1, MAX_ALIGN);
|
||||
case Struct:
|
||||
return info.align
|
||||
return info.align;
|
||||
case Union:
|
||||
return info.align
|
||||
return info.align;
|
||||
case Raw_Union:
|
||||
return info.align
|
||||
case Enum:
|
||||
return align_of_type_info(info.base)
|
||||
return info.align;
|
||||
}
|
||||
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
align_formula :: proc(size, align: int) -> int {
|
||||
result := size + align-1
|
||||
return result - result%align
|
||||
result := size + align-1;
|
||||
return result - result%align;
|
||||
}
|
||||
|
||||
size_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||
WORD_SIZE :: size_of(int)
|
||||
using Type_Info
|
||||
|
||||
WORD_SIZE :: size_of(int);
|
||||
using Type_Info;
|
||||
match type info : type_info {
|
||||
case Named:
|
||||
return size_of_type_info(info.base)
|
||||
return size_of_type_info(info.base);
|
||||
case Integer:
|
||||
return info.size
|
||||
return info.size;
|
||||
case Float:
|
||||
return info.size
|
||||
return info.size;
|
||||
case Any:
|
||||
return 2*WORD_SIZE
|
||||
return 2*WORD_SIZE;
|
||||
case String:
|
||||
return 2*WORD_SIZE
|
||||
return 2*WORD_SIZE;
|
||||
case Boolean:
|
||||
return 1
|
||||
return 1;
|
||||
case Pointer:
|
||||
return WORD_SIZE
|
||||
return WORD_SIZE;
|
||||
case Maybe:
|
||||
return size_of_type_info(info.elem) + 1
|
||||
return size_of_type_info(info.elem) + 1;
|
||||
case Procedure:
|
||||
return WORD_SIZE
|
||||
return WORD_SIZE;
|
||||
case Array:
|
||||
count := info.count
|
||||
count := info.count;
|
||||
if count == 0 {
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
size := size_of_type_info(info.elem)
|
||||
align := align_of_type_info(info.elem)
|
||||
alignment := align_formula(size, align)
|
||||
return alignment*(count-1) + size
|
||||
size := size_of_type_info(info.elem);
|
||||
align := align_of_type_info(info.elem);
|
||||
alignment := align_formula(size, align);
|
||||
return alignment*(count-1) + size;
|
||||
case Slice:
|
||||
return 3*WORD_SIZE
|
||||
return 3*WORD_SIZE;
|
||||
case Vector:
|
||||
is_bool :: proc(type_info: ^Type_Info) -> bool {
|
||||
match type info : type_info {
|
||||
case Named:
|
||||
return is_bool(info.base)
|
||||
return is_bool(info.base);
|
||||
case Boolean:
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
|
||||
count := info.count
|
||||
count := info.count;
|
||||
if count == 0 {
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
bit_size := 8*size_of_type_info(info.elem)
|
||||
bit_size := 8*size_of_type_info(info.elem);
|
||||
if is_bool(info.elem) {
|
||||
// NOTE(bill): LLVM can store booleans as 1 bit because a boolean _is_ an `i1`
|
||||
// Silly LLVM spec
|
||||
bit_size = 1
|
||||
bit_size = 1;
|
||||
}
|
||||
total_size_in_bits := bit_size * count
|
||||
total_size := (total_size_in_bits+7)/8
|
||||
return total_size
|
||||
total_size_in_bits := bit_size * count;
|
||||
total_size := (total_size_in_bits+7)/8;
|
||||
return total_size;
|
||||
|
||||
case Struct:
|
||||
return info.size
|
||||
return info.size;
|
||||
case Union:
|
||||
return info.size
|
||||
return info.size;
|
||||
case Raw_Union:
|
||||
return info.size
|
||||
case Enum:
|
||||
return size_of_type_info(info.base)
|
||||
return info.size;
|
||||
}
|
||||
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+115
-116
@@ -1,28 +1,28 @@
|
||||
#foreign_system_library "opengl32"
|
||||
#import "win32.odin"
|
||||
#load "opengl_constants.odin"
|
||||
#foreign_system_library "opengl32" when ODIN_OS == "windows";
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#include "opengl_constants.odin";
|
||||
|
||||
Clear :: proc(mask: u32) #foreign "glClear"
|
||||
ClearColor :: proc(r, g, b, a: f32) #foreign "glClearColor"
|
||||
Begin :: proc(mode: i32) #foreign "glBegin"
|
||||
End :: proc() #foreign "glEnd"
|
||||
Finish :: proc() #foreign "glFinish"
|
||||
BlendFunc :: proc(sfactor, dfactor: i32) #foreign "glBlendFunc"
|
||||
Enable :: proc(cap: i32) #foreign "glEnable"
|
||||
Disable :: proc(cap: i32) #foreign "glDisable"
|
||||
GenTextures :: proc(count: i32, result: ^u32) #foreign "glGenTextures"
|
||||
DeleteTextures :: proc(count: i32, result: ^u32) #foreign "glDeleteTextures"
|
||||
TexParameteri :: proc(target, pname, param: i32) #foreign "glTexParameteri"
|
||||
TexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign "glTexParameterf"
|
||||
BindTexture :: proc(target: i32, texture: u32) #foreign "glBindTexture"
|
||||
LoadIdentity :: proc() #foreign "glLoadIdentity"
|
||||
Viewport :: proc(x, y, width, height: i32) #foreign "glViewport"
|
||||
Ortho :: proc(left, right, bottom, top, near, far: f64) #foreign "glOrtho"
|
||||
Color3f :: proc(r, g, b: f32) #foreign "glColor3f"
|
||||
Vertex3f :: proc(x, y, z: f32) #foreign "glVertex3f"
|
||||
TexImage2D :: proc(target, level, internal_format,
|
||||
width, height, border,
|
||||
format, _type: i32, pixels: rawptr) #foreign "glTexImage2D"
|
||||
Clear :: proc(mask: u32) #foreign "glClear"
|
||||
ClearColor :: proc(r, g, b, a: f32) #foreign "glClearColor"
|
||||
Begin :: proc(mode: i32) #foreign "glBegin"
|
||||
End :: proc() #foreign "glEnd"
|
||||
Finish :: proc() #foreign "glFinish"
|
||||
BlendFunc :: proc(sfactor, dfactor: i32) #foreign "glBlendFunc"
|
||||
Enable :: proc(cap: i32) #foreign "glEnable"
|
||||
Disable :: proc(cap: i32) #foreign "glDisable"
|
||||
GenTextures :: proc(count: i32, result: ^u32) #foreign "glGenTextures"
|
||||
DeleteTextures:: proc(count: i32, result: ^u32) #foreign "glDeleteTextures"
|
||||
TexParameteri :: proc(target, pname, param: i32) #foreign "glTexParameteri"
|
||||
TexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign "glTexParameterf"
|
||||
BindTexture :: proc(target: i32, texture: u32) #foreign "glBindTexture"
|
||||
LoadIdentity :: proc() #foreign "glLoadIdentity"
|
||||
Viewport :: proc(x, y, width, height: i32) #foreign "glViewport"
|
||||
Ortho :: proc(left, right, bottom, top, near, far: f64) #foreign "glOrtho"
|
||||
Color3f :: proc(r, g, b: f32) #foreign "glColor3f"
|
||||
Vertex3f :: proc(x, y, z: f32) #foreign "glVertex3f"
|
||||
TexImage2D :: proc(target, level, internal_format,
|
||||
width, height, border,
|
||||
format, _type: i32, pixels: rawptr) #foreign "glTexImage2D"
|
||||
|
||||
GetError :: proc() -> i32 #foreign "glGetError"
|
||||
GetString :: proc(name: i32) -> ^byte #foreign "glGetString"
|
||||
@@ -30,126 +30,125 @@ GetIntegerv :: proc(name: i32, v: ^i32) #foreign "glGetIntegerv"
|
||||
|
||||
|
||||
|
||||
_libgl := win32.LoadLibraryA(("opengl32.dll\x00" as string).data)
|
||||
_libgl := win32.LoadLibraryA(("opengl32.dll\x00" as string).data);
|
||||
|
||||
GetProcAddress :: proc(name: string) -> proc() {
|
||||
assert(name[name.count-1] == 0)
|
||||
res := win32.wglGetProcAddress(name.data)
|
||||
GetProcAddress :: proc(name: string) -> proc() #cc_c {
|
||||
assert(name[name.count-1] == 0);
|
||||
res := win32.wglGetProcAddress(name.data);
|
||||
if res == nil {
|
||||
res = win32.GetProcAddress(_libgl, name.data);
|
||||
}
|
||||
return res
|
||||
return res;
|
||||
}
|
||||
|
||||
GenBuffers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenSamplers: 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;
|
||||
BufferData: proc(target: i32, size: int, data: rawptr, usage: i32) #cc_c;
|
||||
BufferSubData: proc(target: i32, offset, size: int, data: rawptr) #cc_c;
|
||||
|
||||
GenBuffers: proc(count: i32, buffers: ^u32)
|
||||
GenVertexArrays: proc(count: i32, buffers: ^u32)
|
||||
GenSamplers: proc(count: i32, buffers: ^u32)
|
||||
BindBuffer: proc(target: i32, buffer: u32)
|
||||
BindVertexArray: proc(buffer: u32)
|
||||
BindSampler: proc(position: i32, sampler: u32)
|
||||
BufferData: proc(target: i32, size: int, data: rawptr, usage: i32)
|
||||
BufferSubData: proc(target: i32, offset, size: int, data: rawptr)
|
||||
DrawArrays: proc(mode, first: i32, count: u32) #cc_c;
|
||||
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c;
|
||||
|
||||
DrawArrays: proc(mode, first: i32, count: u32)
|
||||
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr)
|
||||
MapBuffer: proc(target, access: i32) -> rawptr #cc_c;
|
||||
UnmapBuffer: proc(target: i32) #cc_c;
|
||||
|
||||
MapBuffer: proc(target, access: i32) -> rawptr
|
||||
UnmapBuffer: proc(target: i32)
|
||||
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c;
|
||||
EnableVertexAttribArray: proc(index: u32) #cc_c;
|
||||
|
||||
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr)
|
||||
EnableVertexAttribArray: proc(index: u32)
|
||||
|
||||
CreateShader: proc(shader_type: i32) -> u32
|
||||
ShaderSource: proc(shader: u32, count: u32, string: ^^byte, length: ^i32)
|
||||
CompileShader: proc(shader: u32)
|
||||
CreateProgram: proc() -> u32
|
||||
AttachShader: proc(program, shader: u32)
|
||||
DetachShader: proc(program, shader: u32)
|
||||
DeleteShader: proc(shader: u32)
|
||||
LinkProgram: proc(program: u32)
|
||||
UseProgram: proc(program: u32)
|
||||
DeleteProgram: proc(program: u32)
|
||||
CreateShader: proc(shader_type: i32) -> u32 #cc_c;
|
||||
ShaderSource: proc(shader: u32, count: u32, str: ^^byte, length: ^i32) #cc_c;
|
||||
CompileShader: proc(shader: u32) #cc_c;
|
||||
CreateProgram: proc() -> u32 #cc_c;
|
||||
AttachShader: proc(program, shader: u32) #cc_c;
|
||||
DetachShader: proc(program, shader: u32) #cc_c;
|
||||
DeleteShader: proc(shader: u32) #cc_c;
|
||||
LinkProgram: proc(program: u32) #cc_c;
|
||||
UseProgram: proc(program: u32) #cc_c;
|
||||
DeleteProgram: proc(program: u32) #cc_c;
|
||||
|
||||
|
||||
GetShaderiv: proc(shader: u32, pname: i32, params: ^i32)
|
||||
GetProgramiv: proc(program: u32, pname: i32, params: ^i32)
|
||||
GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^byte)
|
||||
GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^byte)
|
||||
GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c;
|
||||
GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c;
|
||||
|
||||
ActiveTexture: proc(texture: i32)
|
||||
GenerateMipmap: proc(target: i32)
|
||||
ActiveTexture: proc(texture: i32) #cc_c;
|
||||
GenerateMipmap: proc(target: i32) #cc_c;
|
||||
|
||||
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32)
|
||||
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32)
|
||||
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32)
|
||||
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32)
|
||||
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32)
|
||||
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32)
|
||||
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c;
|
||||
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c;
|
||||
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c;
|
||||
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c;
|
||||
|
||||
|
||||
Uniform1i: proc(loc: i32, v0: i32)
|
||||
Uniform2i: proc(loc: i32, v0, v1: i32)
|
||||
Uniform3i: proc(loc: i32, v0, v1, v2: i32)
|
||||
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32)
|
||||
Uniform1f: proc(loc: i32, v0: f32)
|
||||
Uniform2f: proc(loc: i32, v0, v1: f32)
|
||||
Uniform3f: proc(loc: i32, v0, v1, v2: f32)
|
||||
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32)
|
||||
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32)
|
||||
Uniform1i: proc(loc: i32, v0: i32) #cc_c;
|
||||
Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c;
|
||||
Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c;
|
||||
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c;
|
||||
Uniform1f: proc(loc: i32, v0: f32) #cc_c;
|
||||
Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c;
|
||||
Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c;
|
||||
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c;
|
||||
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c;
|
||||
|
||||
GetUniformLocation: proc(program: u32, name: ^byte) -> i32
|
||||
GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c;
|
||||
|
||||
init :: proc() {
|
||||
set_proc_address :: proc(p: rawptr, name: string) #inline { (p as ^proc())^ = GetProcAddress(name) }
|
||||
set_proc_address :: proc(p: rawptr, name: string) #inline { (p as ^(proc() #cc_c))^ = 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(^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");
|
||||
}
|
||||
|
||||
|
||||
+1366
-1365
File diff suppressed because it is too large
Load Diff
+1
-171
@@ -1,172 +1,2 @@
|
||||
#import "win32.odin"
|
||||
#import "fmt.odin"
|
||||
|
||||
File_Time :: type u64
|
||||
|
||||
File :: struct {
|
||||
Handle :: type win32.HANDLE
|
||||
handle: Handle
|
||||
last_write_time: File_Time
|
||||
}
|
||||
|
||||
open :: proc(name: string) -> (File, bool) {
|
||||
using win32
|
||||
buf: [300]byte
|
||||
copy(buf[:], name as []byte)
|
||||
f := File{
|
||||
handle = CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, nil),
|
||||
}
|
||||
success := f.handle != INVALID_HANDLE_VALUE as File.Handle
|
||||
f.last_write_time = last_write_time(^f)
|
||||
return f, success
|
||||
}
|
||||
|
||||
create :: proc(name: string) -> (File, bool) {
|
||||
using win32
|
||||
buf: [300]byte
|
||||
copy(buf[:], name as []byte)
|
||||
f := File{
|
||||
handle = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, 0, nil),
|
||||
}
|
||||
success := f.handle != INVALID_HANDLE_VALUE as File.Handle
|
||||
f.last_write_time = last_write_time(^f)
|
||||
return f, success
|
||||
}
|
||||
|
||||
close :: proc(using f: ^File) {
|
||||
win32.CloseHandle(handle)
|
||||
}
|
||||
|
||||
write :: proc(using f: ^File, buf: []byte) -> bool {
|
||||
bytes_written: i32
|
||||
return win32.WriteFile(handle, buf.data, buf.count as i32, ^bytes_written, nil) != 0
|
||||
}
|
||||
|
||||
file_has_changed :: proc(f: ^File) -> bool {
|
||||
last_write_time := last_write_time(f)
|
||||
if f.last_write_time != last_write_time {
|
||||
f.last_write_time = last_write_time
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
last_write_time :: proc(f: ^File) -> File_Time {
|
||||
file_info: win32.BY_HANDLE_FILE_INFORMATION
|
||||
win32.GetFileInformationByHandle(f.handle, ^file_info)
|
||||
l := file_info.last_write_time.low_date_time as File_Time
|
||||
h := file_info.last_write_time.high_date_time as File_Time
|
||||
return l | h << 32
|
||||
}
|
||||
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {
|
||||
last_write_time: win32.FILETIME
|
||||
data: win32.WIN32_FILE_ATTRIBUTE_DATA
|
||||
|
||||
buf: [1024]byte
|
||||
path := buf[:0]
|
||||
fmt.bprint(^path, name, "\x00")
|
||||
|
||||
if win32.GetFileAttributesExA(path.data, win32.GetFileExInfoStandard, ^data) != 0 {
|
||||
last_write_time = data.last_write_time
|
||||
}
|
||||
|
||||
l := last_write_time.low_date_time as File_Time
|
||||
h := last_write_time.high_date_time as File_Time
|
||||
return l | h << 32
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
File_Standard :: type enum {
|
||||
INPUT,
|
||||
OUTPUT,
|
||||
ERROR,
|
||||
}
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
__std_files := [File_Standard.count]File{
|
||||
{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)},
|
||||
{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)},
|
||||
{handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE)},
|
||||
}
|
||||
|
||||
stdin := ^__std_files[File_Standard.INPUT]
|
||||
stdout := ^__std_files[File_Standard.OUTPUT]
|
||||
stderr := ^__std_files[File_Standard.ERROR]
|
||||
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
buf: [300]byte
|
||||
copy(buf[:], name as []byte)
|
||||
|
||||
f, file_ok := open(name)
|
||||
if !file_ok {
|
||||
return nil, false
|
||||
}
|
||||
defer close(^f)
|
||||
|
||||
length: i64
|
||||
file_size_ok := win32.GetFileSizeEx(f.handle as win32.HANDLE, ^length) != 0
|
||||
if !file_size_ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
data := new_slice(u8, length)
|
||||
if data.data == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
single_read_length: i32
|
||||
total_read: i64
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read
|
||||
to_read: u32
|
||||
MAX :: 1<<32-1
|
||||
if remaining <= MAX {
|
||||
to_read = remaining as u32
|
||||
} else {
|
||||
to_read = MAX
|
||||
}
|
||||
|
||||
win32.ReadFile(f.handle as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil)
|
||||
if single_read_length <= 0 {
|
||||
free(data.data)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
total_read += single_read_length as i64
|
||||
}
|
||||
|
||||
return data, true
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size)
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size)
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
win32.HeapFree(win32.GetProcessHeap(), 0, ptr)
|
||||
}
|
||||
|
||||
|
||||
exit :: proc(code: int) {
|
||||
win32.ExitProcess(code as u32)
|
||||
}
|
||||
|
||||
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
GetCurrentThreadId :: proc() -> u32 #foreign #dll_import
|
||||
return GetCurrentThreadId() as int
|
||||
}
|
||||
#include "os_windows.odin" when ODIN_OS == "windows"
|
||||
|
||||
|
||||
@@ -0,0 +1,272 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
#import "fmt.odin";
|
||||
|
||||
|
||||
Handle :: uint;
|
||||
File_Time :: u64;
|
||||
Error :: int;
|
||||
|
||||
INVALID_HANDLE: Handle : ~(0 as Handle);
|
||||
|
||||
|
||||
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;
|
||||
|
||||
ERROR_NONE: Error : 0;
|
||||
ERROR_FILE_NOT_FOUND: Error : 2;
|
||||
ERROR_PATH_NOT_FOUND: Error : 3;
|
||||
ERROR_ACCESS_DENIED: Error : 5;
|
||||
ERROR_NO_MORE_FILES: Error : 18;
|
||||
ERROR_HANDLE_EOF: Error : 38;
|
||||
ERROR_NETNAME_DELETED: Error : 64;
|
||||
ERROR_FILE_EXISTS: Error : 80;
|
||||
ERROR_BROKEN_PIPE: Error : 109;
|
||||
ERROR_BUFFER_OVERFLOW: Error : 111;
|
||||
ERROR_INSUFFICIENT_BUFFER: Error : 122;
|
||||
ERROR_MOD_NOT_FOUND: Error : 126;
|
||||
ERROR_PROC_NOT_FOUND: Error : 127;
|
||||
ERROR_DIR_NOT_EMPTY: Error : 145;
|
||||
ERROR_ALREADY_EXISTS: Error : 183;
|
||||
ERROR_ENVVAR_NOT_FOUND: Error : 203;
|
||||
ERROR_MORE_DATA: Error : 234;
|
||||
ERROR_OPERATION_ABORTED: Error : 995;
|
||||
ERROR_IO_PENDING: Error : 997;
|
||||
ERROR_NOT_FOUND: Error : 1168;
|
||||
ERROR_PRIVILEGE_NOT_HELD: Error : 1314;
|
||||
WSAEACCES: Error : 10013;
|
||||
WSAECONNRESET: Error : 10054;
|
||||
|
||||
// Windows reserves errors >= 1<<29 for application use
|
||||
ERROR_FILE_IS_PIPE: Error : 1<<29 + 0;
|
||||
|
||||
|
||||
|
||||
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Error) {
|
||||
using win32;
|
||||
if path.count == 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;
|
||||
}
|
||||
|
||||
if mode&O_CREAT != 0 {
|
||||
access |= FILE_GENERIC_WRITE;
|
||||
}
|
||||
if mode&O_APPEND != 0 {
|
||||
access &~= FILE_GENERIC_WRITE;
|
||||
access |= FILE_APPEND_DATA;
|
||||
}
|
||||
|
||||
share_mode := (FILE_SHARE_READ|FILE_SHARE_WRITE) as u32;
|
||||
sa: ^SECURITY_ATTRIBUTES = nil;
|
||||
sa_inherit := SECURITY_ATTRIBUTES{length = size_of(SECURITY_ATTRIBUTES), inherit_handle = 1};
|
||||
if mode&O_CLOEXEC == 0 {
|
||||
sa = ^sa_inherit;
|
||||
}
|
||||
|
||||
create_mode: u32;
|
||||
match {
|
||||
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
|
||||
create_mode = CREATE_NEW;
|
||||
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
|
||||
create_mode = CREATE_ALWAYS;
|
||||
case mode&O_CREAT == O_CREAT:
|
||||
create_mode = OPEN_ALWAYS;
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = TRUNCATE_EXISTING;
|
||||
default:
|
||||
create_mode = OPEN_EXISTING;
|
||||
}
|
||||
|
||||
buf: [300]byte;
|
||||
copy(buf[:], path as []byte);
|
||||
|
||||
handle := CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil) as Handle;
|
||||
if handle == INVALID_HANDLE {
|
||||
return handle, ERROR_NONE;
|
||||
}
|
||||
err := GetLastError();
|
||||
return INVALID_HANDLE, err as Error;
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
win32.CloseHandle(fd as win32.HANDLE);
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Error) {
|
||||
bytes_written: i32;
|
||||
e := win32.WriteFile(fd as win32.HANDLE, data.data, data.count as i32, ^bytes_written, nil);
|
||||
if e != 0 {
|
||||
return 0, e as Error;
|
||||
}
|
||||
return bytes_written as int, ERROR_NONE;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Error) {
|
||||
bytes_read: i32;
|
||||
e := win32.ReadFile(fd as win32.HANDLE, data.data, data.count as u32, ^bytes_read, nil);
|
||||
if e != win32.FALSE {
|
||||
err := win32.GetLastError();
|
||||
return 0, err as Error;
|
||||
}
|
||||
return bytes_read as int, ERROR_NONE;
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
|
||||
using win32;
|
||||
w: u32;
|
||||
match whence {
|
||||
case 0: w = FILE_BEGIN;
|
||||
case 1: w = FILE_CURRENT;
|
||||
case 2: w = FILE_END;
|
||||
}
|
||||
hi := (offset>>32) as i32;
|
||||
lo := offset as i32;
|
||||
ft := GetFileType(fd as HANDLE);
|
||||
if ft == FILE_TYPE_PIPE {
|
||||
return 0, ERROR_FILE_IS_PIPE;
|
||||
}
|
||||
dw_ptr := SetFilePointer(fd as HANDLE, lo, ^hi, w);
|
||||
if dw_ptr == INVALID_SET_FILE_POINTER {
|
||||
err := GetLastError();
|
||||
return 0, err as Error;
|
||||
}
|
||||
return (hi as i64)<<32 + (dw_ptr as i64), ERROR_NONE;
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin := get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
stdout := get_std_handle(win32.STD_OUTPUT_HANDLE);
|
||||
stderr := get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
|
||||
get_std_handle :: proc(h: int) -> Handle {
|
||||
fd := win32.GetStdHandle(h as i32);
|
||||
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
return fd as Handle;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {
|
||||
file_info: win32.BY_HANDLE_FILE_INFORMATION;
|
||||
win32.GetFileInformationByHandle(fd as win32.HANDLE, ^file_info);
|
||||
lo := file_info.last_write_time.lo as File_Time;
|
||||
hi := file_info.last_write_time.hi as File_Time;
|
||||
return lo | hi << 32;
|
||||
}
|
||||
|
||||
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[:], name as []byte);
|
||||
|
||||
if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
|
||||
last_write_time = data.last_write_time;
|
||||
}
|
||||
|
||||
l := last_write_time.lo as File_Time;
|
||||
h := last_write_time.hi as File_Time;
|
||||
return l | h << 32;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
buf: [300]byte;
|
||||
copy(buf[:], name as []byte);
|
||||
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != ERROR_NONE {
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
length: i64;
|
||||
file_size_ok := win32.GetFileSizeEx(fd as win32.HANDLE, ^length) != 0;
|
||||
if !file_size_ok {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
data := new_slice(u8, length);
|
||||
if data.data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
single_read_length: i32;
|
||||
total_read: i64;
|
||||
|
||||
while total_read < length {
|
||||
remaining := length - total_read;
|
||||
to_read: u32;
|
||||
MAX :: 1<<32-1;
|
||||
if remaining <= MAX {
|
||||
to_read = remaining as u32;
|
||||
} else {
|
||||
to_read = MAX;
|
||||
}
|
||||
|
||||
win32.ReadFile(fd as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil);
|
||||
if single_read_length <= 0 {
|
||||
free(data.data);
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
total_read += single_read_length as i64;
|
||||
}
|
||||
|
||||
return data, true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
|
||||
}
|
||||
|
||||
|
||||
exit :: proc(code: int) {
|
||||
win32.ExitProcess(code as u32);
|
||||
}
|
||||
|
||||
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
GetCurrentThreadId :: proc() -> u32 #foreign #dll_import
|
||||
return GetCurrentThreadId() as int;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
#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 win32.GetCurrentThreadId() as i32;
|
||||
}
|
||||
|
||||
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, count as i32, 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.store32(^m.counter, 0);
|
||||
atomic.store32(^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_add32(^m.counter, 1) > 0 {
|
||||
if thread_id != atomic.load32(^m.owner) {
|
||||
semaphore_wait(^m.semaphore);
|
||||
}
|
||||
}
|
||||
atomic.store32(^m.owner, thread_id);
|
||||
m.recursion += 1;
|
||||
}
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
thread_id := current_thread_id();
|
||||
if atomic.load32(^m.owner) == thread_id {
|
||||
atomic.fetch_add32(^m.counter, 1);
|
||||
} else {
|
||||
expected: i32 = 0;
|
||||
if atomic.load32(^m.counter) != 0 {
|
||||
return false;
|
||||
}
|
||||
if atomic.compare_exchange32(^m.counter, expected, 1) == 0 {
|
||||
return false;
|
||||
}
|
||||
atomic.store32(^m.owner, thread_id);
|
||||
}
|
||||
m.recursion += 1;
|
||||
return true;
|
||||
}
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
recursion: i32;
|
||||
thread_id := current_thread_id();
|
||||
assert(thread_id == atomic.load32(^m.owner));
|
||||
|
||||
m.recursion -= 1;
|
||||
recursion = m.recursion;
|
||||
if recursion == 0 {
|
||||
atomic.store32(^m.owner, thread_id);
|
||||
}
|
||||
|
||||
if atomic.fetch_add32(^m.counter, -1) > 1 {
|
||||
if recursion == 0 {
|
||||
semaphore_release(^m.semaphore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,555 @@
|
||||
#foreign_system_library "user32" when ODIN_OS == "windows";
|
||||
#foreign_system_library "gdi32" 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: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT;
|
||||
|
||||
|
||||
INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE;
|
||||
|
||||
FALSE: BOOL : 0;
|
||||
TRUE: BOOL : 1;
|
||||
|
||||
CS_VREDRAW :: 0x0001;
|
||||
CS_HREDRAW :: 0x0002;
|
||||
CS_OWNDC :: 0x0020;
|
||||
CW_USEDEFAULT :: -0x80000000;
|
||||
|
||||
WS_OVERLAPPED :: 0;
|
||||
WS_MAXIMIZEBOX :: 0x00010000;
|
||||
WS_MINIMIZEBOX :: 0x00020000;
|
||||
WS_THICKFRAME :: 0x00040000;
|
||||
WS_SYSMENU :: 0x00080000;
|
||||
WS_CAPTION :: 0x00C00000;
|
||||
WS_VISIBLE :: 0x10000000;
|
||||
WS_OVERLAPPEDWINDOW :: WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX;
|
||||
|
||||
WM_DESTROY :: 0x0002;
|
||||
WM_CLOSE :: 0x0010;
|
||||
WM_QUIT :: 0x0012;
|
||||
WM_KEYDOWN :: 0x0100;
|
||||
WM_KEYUP :: 0x0101;
|
||||
|
||||
PM_REMOVE :: 1;
|
||||
|
||||
COLOR_BACKGROUND :: 1 as HBRUSH;
|
||||
BLACK_BRUSH :: 4;
|
||||
|
||||
SM_CXSCREEN :: 0;
|
||||
SM_CYSCREEN :: 1;
|
||||
|
||||
SW_SHOW :: 5;
|
||||
|
||||
|
||||
POINT :: struct #ordered {
|
||||
x, y: i32;
|
||||
}
|
||||
|
||||
WNDCLASSEXA :: struct #ordered {
|
||||
size, style: u32;
|
||||
wnd_proc: WNDPROC;
|
||||
cls_extra, wnd_extra: i32;
|
||||
instance: HINSTANCE;
|
||||
icon: HICON;
|
||||
cursor: HCURSOR;
|
||||
background: HBRUSH;
|
||||
menu_name, class_name: ^u8;
|
||||
sm: HICON;
|
||||
}
|
||||
|
||||
MSG :: struct #ordered {
|
||||
hwnd: HWND;
|
||||
message: u32;
|
||||
wparam: WPARAM;
|
||||
lparam: LPARAM;
|
||||
time: u32;
|
||||
pt: POINT;
|
||||
}
|
||||
|
||||
RECT :: struct #ordered {
|
||||
left: i32;
|
||||
top: i32;
|
||||
right: i32;
|
||||
bottom: i32;
|
||||
}
|
||||
|
||||
FILETIME :: struct #ordered {
|
||||
lo, hi: u32;
|
||||
}
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION :: struct #ordered {
|
||||
file_attributes: u32;
|
||||
creation_time,
|
||||
last_access_time,
|
||||
last_write_time: FILETIME;
|
||||
volume_serial_number,
|
||||
file_size_high,
|
||||
file_size_low,
|
||||
number_of_links,
|
||||
file_index_high,
|
||||
file_index_low: u32;
|
||||
}
|
||||
|
||||
FILE_ATTRIBUTE_DATA :: struct #ordered {
|
||||
file_attributes: u32;
|
||||
creation_time,
|
||||
last_access_time,
|
||||
last_write_time: FILETIME;
|
||||
file_size_high,
|
||||
file_size_low: u32;
|
||||
}
|
||||
|
||||
GET_FILEEX_INFO_LEVELS :: i32;
|
||||
|
||||
GetFileExInfoStandard: GET_FILEEX_INFO_LEVELS : 0;
|
||||
GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1;
|
||||
|
||||
GetLastError :: proc() -> i32 #foreign #dll_import
|
||||
ExitProcess :: proc(exit_code: u32) #foreign #dll_import
|
||||
GetDesktopWindow :: proc() -> HWND #foreign #dll_import
|
||||
GetCursorPos :: proc(p: ^POINT) -> i32 #foreign #dll_import
|
||||
ScreenToClient :: proc(h: HWND, p: ^POINT) -> i32 #foreign #dll_import
|
||||
GetModuleHandleA :: proc(module_name: ^u8) -> HINSTANCE #foreign #dll_import
|
||||
GetStockObject :: proc(fn_object: i32) -> HGDIOBJ #foreign #dll_import
|
||||
PostQuitMessage :: proc(exit_code: i32) #foreign #dll_import
|
||||
SetWindowTextA :: proc(hwnd: HWND, c_string: ^u8) -> BOOL #foreign #dll_import
|
||||
|
||||
QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign #dll_import
|
||||
QueryPerformanceCounter :: proc(result: ^i64) -> i32 #foreign #dll_import
|
||||
|
||||
Sleep :: proc(ms: i32) -> i32 #foreign #dll_import
|
||||
|
||||
OutputDebugStringA :: proc(c_str: ^u8) #foreign #dll_import
|
||||
|
||||
|
||||
RegisterClassExA :: proc(wc: ^WNDCLASSEXA) -> ATOM #foreign #dll_import
|
||||
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 #dll_import
|
||||
|
||||
ShowWindow :: proc(hwnd: HWND, cmd_show: i32) -> BOOL #foreign #dll_import
|
||||
TranslateMessage :: proc(msg: ^MSG) -> BOOL #foreign #dll_import
|
||||
DispatchMessageA :: proc(msg: ^MSG) -> LRESULT #foreign #dll_import
|
||||
UpdateWindow :: proc(hwnd: HWND) -> BOOL #foreign #dll_import
|
||||
PeekMessageA :: proc(msg: ^MSG, hwnd: HWND,
|
||||
msg_filter_min, msg_filter_max, remove_msg: u32) -> BOOL #foreign #dll_import
|
||||
|
||||
DefWindowProcA :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #foreign #dll_import
|
||||
|
||||
AdjustWindowRect :: proc(rect: ^RECT, style: u32, menu: BOOL) -> BOOL #foreign #dll_import
|
||||
GetActiveWindow :: proc() -> HWND #foreign #dll_import
|
||||
|
||||
|
||||
GetQueryPerformanceFrequency :: proc() -> i64 {
|
||||
r: i64;
|
||||
QueryPerformanceFrequency(^r);
|
||||
return r;
|
||||
}
|
||||
|
||||
GetCommandLineA :: proc() -> ^u8 #foreign #dll_import
|
||||
GetSystemMetrics :: proc(index: i32) -> i32 #foreign #dll_import
|
||||
GetCurrentThreadId :: proc() -> u32 #foreign #dll_import
|
||||
|
||||
// File Stuff
|
||||
|
||||
CloseHandle :: proc(h: HANDLE) -> i32 #foreign #dll_import
|
||||
GetStdHandle :: proc(h: i32) -> HANDLE #foreign #dll_import
|
||||
CreateFileA :: proc(filename: ^u8, desired_access, share_mode: u32,
|
||||
security: rawptr,
|
||||
creation, flags_and_attribs: u32, template_file: HANDLE) -> HANDLE #foreign #dll_import
|
||||
ReadFile :: proc(h: HANDLE, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> BOOL #foreign #dll_import
|
||||
WriteFile :: proc(h: HANDLE, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> i32 #foreign #dll_import
|
||||
|
||||
GetFileSizeEx :: proc(file_handle: HANDLE, file_size: ^i64) -> BOOL #foreign #dll_import
|
||||
GetFileAttributesExA :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> BOOL #foreign #dll_import
|
||||
GetFileInformationByHandle :: proc(file_handle: HANDLE, file_info: ^BY_HANDLE_FILE_INFORMATION) -> BOOL #foreign #dll_import
|
||||
|
||||
GetFileType :: proc(file_handle: HANDLE) -> u32 #foreign #dll_import
|
||||
SetFilePointer :: proc(file_handle: HANDLE, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign #dll_import
|
||||
|
||||
SetHandleInformation :: proc(obj: HANDLE, mask, flags: u32) -> BOOL #foreign #dll_import
|
||||
|
||||
HANDLE_FLAG_INHERIT :: 1;
|
||||
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2;
|
||||
|
||||
|
||||
FILE_BEGIN :: 0;
|
||||
FILE_CURRENT :: 1;
|
||||
FILE_END :: 2;
|
||||
|
||||
FILE_SHARE_READ :: 0x00000001;
|
||||
FILE_SHARE_WRITE :: 0x00000002;
|
||||
FILE_SHARE_DELETE :: 0x00000004;
|
||||
FILE_GENERIC_ALL :: 0x10000000;
|
||||
FILE_GENERIC_EXECUTE :: 0x20000000;
|
||||
FILE_GENERIC_WRITE :: 0x40000000;
|
||||
FILE_GENERIC_READ :: 0x80000000;
|
||||
|
||||
FILE_APPEND_DATA :: 0x0004;
|
||||
|
||||
STD_INPUT_HANDLE :: -10;
|
||||
STD_OUTPUT_HANDLE :: -11;
|
||||
STD_ERROR_HANDLE :: -12;
|
||||
|
||||
CREATE_NEW :: 1;
|
||||
CREATE_ALWAYS :: 2;
|
||||
OPEN_EXISTING :: 3;
|
||||
OPEN_ALWAYS :: 4;
|
||||
TRUNCATE_EXISTING :: 5;
|
||||
|
||||
FILE_ATTRIBUTE_READONLY :: 0x00000001;
|
||||
FILE_ATTRIBUTE_HIDDEN :: 0x00000002;
|
||||
FILE_ATTRIBUTE_SYSTEM :: 0x00000004;
|
||||
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_COMPRESSED :: 0x00000800;
|
||||
FILE_ATTRIBUTE_OFFLINE :: 0x00001000;
|
||||
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED :: 0x00002000;
|
||||
FILE_ATTRIBUTE_ENCRYPTED :: 0x00004000;
|
||||
|
||||
FILE_TYPE_DISK :: 0x0001;
|
||||
FILE_TYPE_CHAR :: 0x0002;
|
||||
FILE_TYPE_PIPE :: 0x0003;
|
||||
|
||||
INVALID_SET_FILE_POINTER :: ~(0 as u32);
|
||||
|
||||
|
||||
|
||||
|
||||
HeapAlloc :: proc (h: HANDLE, flags: u32, bytes: int) -> rawptr #foreign #dll_import
|
||||
HeapReAlloc :: proc (h: HANDLE, flags: u32, memory: rawptr, bytes: int) -> rawptr #foreign #dll_import
|
||||
HeapFree :: proc (h: HANDLE, flags: u32, memory: rawptr) -> BOOL #foreign #dll_import
|
||||
GetProcessHeap :: proc () -> HANDLE #foreign #dll_import
|
||||
|
||||
|
||||
HEAP_ZERO_MEMORY :: 0x00000008;
|
||||
|
||||
// Synchronization
|
||||
|
||||
SECURITY_ATTRIBUTES :: struct #ordered {
|
||||
length: u32;
|
||||
security_descriptor: rawptr;
|
||||
inherit_handle: BOOL;
|
||||
}
|
||||
|
||||
INFINITE :: 0xffffffff;
|
||||
|
||||
CreateSemaphoreA :: proc(attributes: ^SECURITY_ATTRIBUTES, initial_count, maximum_count: i32, name: ^byte) -> HANDLE #foreign #dll_import
|
||||
ReleaseSemaphore :: proc(semaphore: HANDLE, release_count: i32, previous_count: ^i32) -> BOOL #foreign #dll_import
|
||||
WaitForSingleObject :: proc(handle: HANDLE, milliseconds: u32) -> u32 #foreign #dll_import
|
||||
|
||||
|
||||
InterlockedCompareExchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 #foreign
|
||||
InterlockedExchange :: proc(dst: ^i32, desired: i32) -> i32 #foreign
|
||||
InterlockedExchangeAdd :: proc(dst: ^i32, desired: i32) -> i32 #foreign
|
||||
InterlockedAnd :: proc(dst: ^i32, desired: i32) -> i32 #foreign
|
||||
InterlockedOr :: proc(dst: ^i32, desired: i32) -> i32 #foreign
|
||||
|
||||
InterlockedCompareExchange64 :: proc(dst: ^i64, exchange, comparand: i64) -> i64 #foreign
|
||||
InterlockedExchange64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign
|
||||
InterlockedExchangeAdd64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign
|
||||
InterlockedAnd64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign
|
||||
InterlockedOr64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign
|
||||
|
||||
_mm_pause :: proc() #foreign
|
||||
ReadWriteBarrier :: proc() #foreign
|
||||
WriteBarrier :: proc() #foreign
|
||||
ReadBarrier :: proc() #foreign
|
||||
|
||||
|
||||
// GDI
|
||||
BITMAPINFOHEADER :: struct #ordered {
|
||||
size: u32;
|
||||
width, height: i32;
|
||||
planes, bit_count: i16;
|
||||
compression: u32;
|
||||
size_image: u32;
|
||||
x_pels_per_meter: i32;
|
||||
y_pels_per_meter: i32;
|
||||
clr_used: u32;
|
||||
clr_important: u32;
|
||||
}
|
||||
BITMAPINFO :: struct #ordered {
|
||||
using header: BITMAPINFOHEADER;
|
||||
colors: [1]RGBQUAD;
|
||||
}
|
||||
|
||||
|
||||
RGBQUAD :: struct #ordered {
|
||||
blue, green, red, reserved: byte;
|
||||
}
|
||||
|
||||
BI_RGB :: 0;
|
||||
DIB_RGB_COLORS :: 0x00;
|
||||
SRCCOPY: u32 : 0x00cc0020;
|
||||
|
||||
|
||||
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,
|
||||
usage: u32,
|
||||
rop: u32) -> i32 #foreign #dll_import
|
||||
|
||||
|
||||
|
||||
LoadLibraryA :: proc (c_str: ^u8) -> HMODULE #foreign
|
||||
FreeLibrary :: proc (h: HMODULE) #foreign
|
||||
GetProcAddress :: proc (h: HMODULE, c_str: ^u8) -> PROC #foreign
|
||||
|
||||
GetClientRect :: proc(hwnd: HWND, rect: ^RECT) -> BOOL #foreign
|
||||
|
||||
|
||||
|
||||
// Windows OpenGL
|
||||
PFD_TYPE_RGBA :: 0;
|
||||
PFD_TYPE_COLORINDEX :: 1;
|
||||
PFD_MAIN_PLANE :: 0;
|
||||
PFD_OVERLAY_PLANE :: 1;
|
||||
PFD_UNDERLAY_PLANE :: -1;
|
||||
PFD_DOUBLEBUFFER :: 1;
|
||||
PFD_STEREO :: 2;
|
||||
PFD_DRAW_TO_WINDOW :: 4;
|
||||
PFD_DRAW_TO_BITMAP :: 8;
|
||||
PFD_SUPPORT_GDI :: 16;
|
||||
PFD_SUPPORT_OPENGL :: 32;
|
||||
PFD_GENERIC_FORMAT :: 64;
|
||||
PFD_NEED_PALETTE :: 128;
|
||||
PFD_NEED_SYSTEM_PALETTE :: 0x00000100;
|
||||
PFD_SWAP_EXCHANGE :: 0x00000200;
|
||||
PFD_SWAP_COPY :: 0x00000400;
|
||||
PFD_SWAP_LAYER_BUFFERS :: 0x00000800;
|
||||
PFD_GENERIC_ACCELERATED :: 0x00001000;
|
||||
PFD_DEPTH_DONTCARE :: 0x20000000;
|
||||
PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000;
|
||||
PFD_STEREO_DONTCARE :: 0x80000000;
|
||||
|
||||
HGLRC :: HANDLE;
|
||||
PROC :: type proc() #cc_c;
|
||||
wglCreateContextAttribsARBType :: proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC;
|
||||
|
||||
|
||||
PIXELFORMATDESCRIPTOR :: struct #ordered {
|
||||
size,
|
||||
version,
|
||||
flags: u32;
|
||||
|
||||
pixel_type,
|
||||
color_bits,
|
||||
red_bits,
|
||||
red_shift,
|
||||
green_bits,
|
||||
green_shift,
|
||||
blue_bits,
|
||||
blue_shift,
|
||||
alpha_bits,
|
||||
alpha_shift,
|
||||
accum_bits,
|
||||
accum_red_bits,
|
||||
accum_green_bits,
|
||||
accum_blue_bits,
|
||||
accum_alpha_bits,
|
||||
depth_bits,
|
||||
stencil_bits,
|
||||
aux_buffers,
|
||||
layer_type,
|
||||
reserved: byte;
|
||||
|
||||
layer_mask,
|
||||
visible_mask,
|
||||
damage_mask: u32;
|
||||
}
|
||||
|
||||
GetDC :: proc(h: HANDLE) -> HDC #foreign
|
||||
SetPixelFormat :: proc(hdc: HDC, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR ) -> BOOL #foreign #dll_import
|
||||
ChoosePixelFormat :: proc(hdc: HDC, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign #dll_import
|
||||
SwapBuffers :: proc(hdc: HDC) -> BOOL #foreign #dll_import
|
||||
ReleaseDC :: proc(wnd: HWND, hdc: HDC) -> i32 #foreign #dll_import
|
||||
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB :: 0x2091;
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB :: 0x2092;
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB :: 0x9126;
|
||||
WGL_CONTEXT_CORE_PROFILE_BIT_ARB :: 0x0001;
|
||||
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x0002;
|
||||
|
||||
wglCreateContext :: proc(hdc: HDC) -> HGLRC #foreign #dll_import
|
||||
wglMakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign #dll_import
|
||||
wglGetProcAddress :: proc(c_str: ^u8) -> PROC #foreign #dll_import
|
||||
wglDeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign #dll_import
|
||||
|
||||
|
||||
|
||||
GetKeyState :: proc(v_key: i32) -> i16 #foreign #dll_import
|
||||
GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign #dll_import
|
||||
|
||||
is_key_down :: proc(key: Key_Code) -> bool #inline { return GetAsyncKeyState(key as i32) < 0; }
|
||||
|
||||
Key_Code :: enum i32 {
|
||||
LBUTTON = 0x01,
|
||||
RBUTTON = 0x02,
|
||||
CANCEL = 0x03,
|
||||
MBUTTON = 0x04,
|
||||
BACK = 0x08,
|
||||
TAB = 0x09,
|
||||
CLEAR = 0x0C,
|
||||
RETURN = 0x0D,
|
||||
|
||||
SHIFT = 0x10,
|
||||
CONTROL = 0x11,
|
||||
MENU = 0x12,
|
||||
PAUSE = 0x13,
|
||||
CAPITAL = 0x14,
|
||||
KANA = 0x15,
|
||||
HANGEUL = 0x15,
|
||||
HANGUL = 0x15,
|
||||
JUNJA = 0x17,
|
||||
FINAL = 0x18,
|
||||
HANJA = 0x19,
|
||||
KANJI = 0x19,
|
||||
ESCAPE = 0x1B,
|
||||
CONVERT = 0x1C,
|
||||
NONCONVERT = 0x1D,
|
||||
ACCEPT = 0x1E,
|
||||
MODECHANGE = 0x1F,
|
||||
SPACE = 0x20,
|
||||
PRIOR = 0x21,
|
||||
NEXT = 0x22,
|
||||
END = 0x23,
|
||||
HOME = 0x24,
|
||||
LEFT = 0x25,
|
||||
UP = 0x26,
|
||||
RIGHT = 0x27,
|
||||
DOWN = 0x28,
|
||||
SELECT = 0x29,
|
||||
PRINT = 0x2A,
|
||||
EXECUTE = 0x2B,
|
||||
SNAPSHOT = 0x2C,
|
||||
INSERT = 0x2D,
|
||||
DELETE = 0x2E,
|
||||
HELP = 0x2F,
|
||||
|
||||
NUM0 = '0',
|
||||
NUM1 = '1',
|
||||
NUM2 = '2',
|
||||
NUM3 = '3',
|
||||
NUM4 = '4',
|
||||
NUM5 = '5',
|
||||
NUM6 = '6',
|
||||
NUM7 = '7',
|
||||
NUM8 = '8',
|
||||
NUM9 = '9',
|
||||
A = 'A',
|
||||
B = 'B',
|
||||
C = 'C',
|
||||
D = 'D',
|
||||
E = 'E',
|
||||
F = 'F',
|
||||
G = 'G',
|
||||
H = 'H',
|
||||
I = 'I',
|
||||
J = 'J',
|
||||
K = 'K',
|
||||
L = 'L',
|
||||
M = 'M',
|
||||
N = 'N',
|
||||
O = 'O',
|
||||
P = 'P',
|
||||
Q = 'Q',
|
||||
R = 'R',
|
||||
S = 'S',
|
||||
T = 'T',
|
||||
U = 'U',
|
||||
V = 'V',
|
||||
W = 'W',
|
||||
X = 'X',
|
||||
Y = 'Y',
|
||||
Z = 'Z',
|
||||
|
||||
LWIN = 0x5B,
|
||||
RWIN = 0x5C,
|
||||
APPS = 0x5D,
|
||||
|
||||
NUMPAD0 = 0x60,
|
||||
NUMPAD1 = 0x61,
|
||||
NUMPAD2 = 0x62,
|
||||
NUMPAD3 = 0x63,
|
||||
NUMPAD4 = 0x64,
|
||||
NUMPAD5 = 0x65,
|
||||
NUMPAD6 = 0x66,
|
||||
NUMPAD7 = 0x67,
|
||||
NUMPAD8 = 0x68,
|
||||
NUMPAD9 = 0x69,
|
||||
MULTIPLY = 0x6A,
|
||||
ADD = 0x6B,
|
||||
SEPARATOR = 0x6C,
|
||||
SUBTRACT = 0x6D,
|
||||
DECIMAL = 0x6E,
|
||||
DIVIDE = 0x6F,
|
||||
|
||||
F1 = 0x70,
|
||||
F2 = 0x71,
|
||||
F3 = 0x72,
|
||||
F4 = 0x73,
|
||||
F5 = 0x74,
|
||||
F6 = 0x75,
|
||||
F7 = 0x76,
|
||||
F8 = 0x77,
|
||||
F9 = 0x78,
|
||||
F10 = 0x79,
|
||||
F11 = 0x7A,
|
||||
F12 = 0x7B,
|
||||
F13 = 0x7C,
|
||||
F14 = 0x7D,
|
||||
F15 = 0x7E,
|
||||
F16 = 0x7F,
|
||||
F17 = 0x80,
|
||||
F18 = 0x81,
|
||||
F19 = 0x82,
|
||||
F20 = 0x83,
|
||||
F21 = 0x84,
|
||||
F22 = 0x85,
|
||||
F23 = 0x86,
|
||||
F24 = 0x87,
|
||||
|
||||
NUMLOCK = 0x90,
|
||||
SCROLL = 0x91,
|
||||
LSHIFT = 0xA0,
|
||||
RSHIFT = 0xA1,
|
||||
LCONTROL = 0xA2,
|
||||
RCONTROL = 0xA3,
|
||||
LMENU = 0xA4,
|
||||
RMENU = 0xA5,
|
||||
PROCESSKEY = 0xE5,
|
||||
ATTN = 0xF6,
|
||||
CRSEL = 0xF7,
|
||||
EXSEL = 0xF8,
|
||||
EREOF = 0xF9,
|
||||
PLAY = 0xFA,
|
||||
ZOOM = 0xFB,
|
||||
NONAME = 0xFC,
|
||||
PA1 = 0xFD,
|
||||
OEM_CLEAR = 0xFE,
|
||||
}
|
||||
|
||||
+99
-99
@@ -1,17 +1,15 @@
|
||||
RUNE_ERROR :: #rune "\ufffd"
|
||||
RUNE_SELF :: 0x80
|
||||
RUNE_BOM :: 0xfeff
|
||||
RUNE_EOF :: ~(0 as rune)
|
||||
MAX_RUNE :: #rune "\U0010ffff"
|
||||
UTF_MAX :: 4
|
||||
|
||||
|
||||
SURROGATE_MIN :: 0xd800
|
||||
SURROGATE_MAX :: 0xdfff
|
||||
RUNE_ERROR :: '\ufffd';
|
||||
RUNE_SELF :: 0x80;
|
||||
RUNE_BOM :: 0xfeff;
|
||||
RUNE_EOF :: ~(0 as rune);
|
||||
MAX_RUNE :: '\U0010ffff';
|
||||
UTF_MAX :: 4;
|
||||
|
||||
SURROGATE_MIN :: 0xd800;
|
||||
SURROGATE_MAX :: 0xdfff;
|
||||
|
||||
Accept_Range :: struct {
|
||||
lo, hi: u8
|
||||
lo, hi: u8;
|
||||
}
|
||||
|
||||
accept_ranges := [5]Accept_Range{
|
||||
@@ -20,7 +18,7 @@ accept_ranges := [5]Accept_Range{
|
||||
{0x80, 0x9f},
|
||||
{0x90, 0xbf},
|
||||
{0x80, 0x8f},
|
||||
}
|
||||
};
|
||||
|
||||
accept_sizes := [256]byte{
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
|
||||
@@ -40,177 +38,179 @@ accept_sizes := [256]byte{
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xd0-0xdf
|
||||
0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xe0-0xef
|
||||
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
|
||||
}
|
||||
};
|
||||
|
||||
encode_rune :: proc(r_: rune) -> ([4]byte, int) {
|
||||
r := r_
|
||||
buf: [4]byte
|
||||
i := r as u32
|
||||
mask: byte : 0x3f
|
||||
encode_rune :: proc(r: rune) -> ([4]byte, int) {
|
||||
buf: [4]byte;
|
||||
i := r as u32;
|
||||
mask: byte : 0x3f;
|
||||
if i <= 1<<7-1 {
|
||||
buf[0] = r as byte
|
||||
return buf, 1
|
||||
buf[0] = r as byte;
|
||||
return buf, 1;
|
||||
}
|
||||
if i <= 1<<11-1 {
|
||||
buf[0] = 0xc0 | (r>>6) as byte
|
||||
buf[1] = 0x80 | (r) as byte & mask
|
||||
return buf, 2
|
||||
buf[0] = 0xc0 | (r>>6) as byte;
|
||||
buf[1] = 0x80 | (r) as byte & mask;
|
||||
return buf, 2;
|
||||
}
|
||||
|
||||
// Invalid or Surrogate range
|
||||
if i > 0x0010ffff ||
|
||||
(0xd800 <= i && i <= 0xdfff) {
|
||||
r = 0xfffd
|
||||
r = 0xfffd;
|
||||
}
|
||||
|
||||
if i <= 1<<16-1 {
|
||||
buf[0] = 0xe0 | (r>>12) as byte
|
||||
buf[1] = 0x80 | (r>>6) as byte & mask
|
||||
buf[2] = 0x80 | (r) as byte & mask
|
||||
return buf, 3
|
||||
buf[0] = 0xe0 | (r>>12) as byte;
|
||||
buf[1] = 0x80 | (r>>6) as byte & mask;
|
||||
buf[2] = 0x80 | (r) as byte & mask;
|
||||
return buf, 3;
|
||||
}
|
||||
|
||||
buf[0] = 0xf0 | (r>>18) as byte
|
||||
buf[1] = 0x80 | (r>>12) as byte & mask
|
||||
buf[2] = 0x80 | (r>>6) as byte & mask
|
||||
buf[3] = 0x80 | (r) as byte & mask
|
||||
return buf, 4
|
||||
buf[0] = 0xf0 | (r>>18) as byte;
|
||||
buf[1] = 0x80 | (r>>12) as byte & mask;
|
||||
buf[2] = 0x80 | (r>>6) as byte & mask;
|
||||
buf[3] = 0x80 | (r) as byte & mask;
|
||||
return buf, 4;
|
||||
}
|
||||
|
||||
decode_rune :: proc(s: string) -> (rune, int) {
|
||||
n := s.count
|
||||
n := s.count;
|
||||
if n < 1 {
|
||||
return RUNE_ERROR, 0
|
||||
return RUNE_ERROR, 0;
|
||||
}
|
||||
b0 := s[0]
|
||||
x := accept_sizes[b0]
|
||||
b0 := s[0];
|
||||
x := accept_sizes[b0];
|
||||
if x >= 0xf0 {
|
||||
mask := (x as rune << 31) >> 31 // all zeros or all ones
|
||||
return (b0 as rune) &~ mask | RUNE_ERROR&mask, 1
|
||||
mask := (x as rune << 31) >> 31; // all zeros or all ones
|
||||
return (b0 as rune) &~ mask | RUNE_ERROR&mask, 1;
|
||||
}
|
||||
size := x & 7
|
||||
ar := accept_ranges[x>>4]
|
||||
size := x & 7;
|
||||
ar := accept_ranges[x>>4];
|
||||
if n < size as int {
|
||||
return RUNE_ERROR, 1
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
b1 := s[1]
|
||||
b1 := s[1];
|
||||
if b1 < ar.lo || ar.hi < b1 {
|
||||
return RUNE_ERROR, 1
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
|
||||
MASK_X :: 0b00111111
|
||||
MASK_2 :: 0b00011111
|
||||
MASK_3 :: 0b00001111
|
||||
MASK_4 :: 0b00000111
|
||||
MASK_X :: 0b00111111;
|
||||
MASK_2 :: 0b00011111;
|
||||
MASK_3 :: 0b00001111;
|
||||
MASK_4 :: 0b00000111;
|
||||
|
||||
if size == 2 {
|
||||
return (b0&MASK_2) as rune <<6 | (b1&MASK_X) as rune, 2
|
||||
return (b0&MASK_2) as rune <<6 | (b1&MASK_X) as rune, 2;
|
||||
}
|
||||
b2 := s[2]
|
||||
b2 := s[2];
|
||||
if b2 < 0x80 || 0xbf < b2 {
|
||||
return RUNE_ERROR, 1
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
if size == 3 {
|
||||
return (b0&MASK_3) as rune <<12 | (b1&MASK_X) as rune <<6 | (b2&MASK_X) as rune, 3
|
||||
return (b0&MASK_3) as rune <<12 | (b1&MASK_X) as rune <<6 | (b2&MASK_X) as rune, 3;
|
||||
}
|
||||
b3 := s[3]
|
||||
b3 := s[3];
|
||||
if b3 < 0x80 || 0xbf < b3 {
|
||||
return RUNE_ERROR, 1
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
return (b0&MASK_4) as rune <<18 | (b1&MASK_X) as rune <<12 | (b3&MASK_X) as rune <<6 | (b3&MASK_X) as rune, 4
|
||||
return (b0&MASK_4) as rune <<18 | (b1&MASK_X) as rune <<12 | (b3&MASK_X) as rune <<6 | (b3&MASK_X) as rune, 4;
|
||||
|
||||
}
|
||||
|
||||
|
||||
valid_rune :: proc(r: rune) -> bool {
|
||||
if r < 0 {
|
||||
return false
|
||||
return false;
|
||||
} else if SURROGATE_MIN <= r && r <= SURROGATE_MAX {
|
||||
return false
|
||||
return false;
|
||||
} else if r > MAX_RUNE {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
valid_string :: proc(s: string) -> bool {
|
||||
n := s.count
|
||||
for i := 0; i < n; {
|
||||
si := s[i]
|
||||
n := s.count;
|
||||
i := 0;
|
||||
while i < n {
|
||||
si := s[i];
|
||||
if si < RUNE_SELF { // ascii
|
||||
i++
|
||||
continue
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
x := accept_sizes[si]
|
||||
x := accept_sizes[si];
|
||||
if x == 0xf1 {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
size := (x & 7) as int
|
||||
size := (x & 7) as int;
|
||||
if i+size > n {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
ar := accept_ranges[x>>4]
|
||||
ar := accept_ranges[x>>4];
|
||||
if b := s[i+1]; b < ar.lo || ar.hi < b {
|
||||
return false
|
||||
return false;
|
||||
} else if size == 2 {
|
||||
// Okay
|
||||
} else if b := s[i+2]; b < 0x80 || 0xbf < b {
|
||||
return false
|
||||
return false;
|
||||
} else if size == 3 {
|
||||
// Okay
|
||||
} else if b := s[i+3]; b < 0x80 || 0xbf < b {
|
||||
return false
|
||||
return false;
|
||||
}
|
||||
i += size
|
||||
i += size;
|
||||
}
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
rune_count :: proc(s: string) -> int {
|
||||
count := 0
|
||||
n := s.count
|
||||
for i := 0; i < n; count++ {
|
||||
si := s[i]
|
||||
count := 0;
|
||||
n := s.count;
|
||||
i := 0;
|
||||
while i < n {
|
||||
defer count += 1;
|
||||
si := s[i];
|
||||
if si < RUNE_SELF { // ascii
|
||||
i++
|
||||
continue
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
x := accept_sizes[si]
|
||||
x := accept_sizes[si];
|
||||
if x == 0xf1 {
|
||||
i++
|
||||
continue
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
size := (x & 7) as int
|
||||
size := (x & 7) as int;
|
||||
if i+size > n {
|
||||
i++
|
||||
continue
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
ar := accept_ranges[x>>4]
|
||||
ar := accept_ranges[x>>4];
|
||||
if b := s[i+1]; b < ar.lo || ar.hi < b {
|
||||
size = 1
|
||||
size = 1;
|
||||
} else if size == 2 {
|
||||
// Okay
|
||||
} else if b := s[i+2]; b < 0x80 || 0xbf < b {
|
||||
size = 1
|
||||
size = 1;
|
||||
} else if size == 3 {
|
||||
// Okay
|
||||
} else if b := s[i+3]; b < 0x80 || 0xbf < b {
|
||||
size = 1
|
||||
size = 1;
|
||||
}
|
||||
i += size
|
||||
i += size;
|
||||
}
|
||||
return count
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
rune_size :: proc(r: rune) -> int {
|
||||
match {
|
||||
case r < 0: return -1
|
||||
case r <= 1<<7 - 1: return 1
|
||||
case r <= 1<<11 - 1: return 2
|
||||
case SURROGATE_MIN <= r && r <= SURROGATE_MAX: return -1
|
||||
case r <= 1<<16 - 1: return 3
|
||||
case r <= MAX_RUNE: return 4
|
||||
case r < 0: return -1;
|
||||
case r <= 1<<7 - 1: return 1;
|
||||
case r <= 1<<11 - 1: return 2;
|
||||
case SURROGATE_MIN <= r && r <= SURROGATE_MAX: return -1;
|
||||
case r <= 1<<16 - 1: return 3;
|
||||
case r <= MAX_RUNE: return 4;
|
||||
}
|
||||
return -1
|
||||
return -1;
|
||||
}
|
||||
|
||||
-493
@@ -1,493 +0,0 @@
|
||||
#foreign_system_library "user32"
|
||||
#foreign_system_library "gdi32"
|
||||
|
||||
HANDLE :: type rawptr
|
||||
HWND :: type HANDLE
|
||||
HDC :: type HANDLE
|
||||
HINSTANCE :: type HANDLE
|
||||
HICON :: type HANDLE
|
||||
HCURSOR :: type HANDLE
|
||||
HMENU :: type HANDLE
|
||||
HBRUSH :: type HANDLE
|
||||
HGDIOBJ :: type HANDLE
|
||||
HMODULE :: type HANDLE
|
||||
WPARAM :: type uint
|
||||
LPARAM :: type int
|
||||
LRESULT :: type int
|
||||
ATOM :: type i16
|
||||
BOOL :: type i32
|
||||
WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT
|
||||
|
||||
INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE
|
||||
|
||||
CS_VREDRAW :: 0x0001
|
||||
CS_HREDRAW :: 0x0002
|
||||
CS_OWNDC :: 0x0020
|
||||
CW_USEDEFAULT :: -0x80000000
|
||||
|
||||
WS_OVERLAPPED :: 0
|
||||
WS_MAXIMIZEBOX :: 0x00010000
|
||||
WS_MINIMIZEBOX :: 0x00020000
|
||||
WS_THICKFRAME :: 0x00040000
|
||||
WS_SYSMENU :: 0x00080000
|
||||
WS_CAPTION :: 0x00C00000
|
||||
WS_VISIBLE :: 0x10000000
|
||||
WS_OVERLAPPEDWINDOW :: WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME|WS_MINIMIZEBOX|WS_MAXIMIZEBOX
|
||||
|
||||
WM_DESTROY :: 0x0002
|
||||
WM_CLOSE :: 0x0010
|
||||
WM_QUIT :: 0x0012
|
||||
WM_KEYDOWN :: 0x0100
|
||||
WM_KEYUP :: 0x0101
|
||||
|
||||
PM_REMOVE :: 1
|
||||
|
||||
COLOR_BACKGROUND :: 1 as HBRUSH
|
||||
BLACK_BRUSH :: 4
|
||||
|
||||
SM_CXSCREEN :: 0
|
||||
SM_CYSCREEN :: 1
|
||||
|
||||
SW_SHOW :: 5
|
||||
|
||||
POINT :: struct #ordered {
|
||||
x, y: i32
|
||||
}
|
||||
|
||||
|
||||
WNDCLASSEXA :: struct #ordered {
|
||||
size, style: u32
|
||||
wnd_proc: WNDPROC
|
||||
cls_extra, wnd_extra: i32
|
||||
instance: HINSTANCE
|
||||
icon: HICON
|
||||
cursor: HCURSOR
|
||||
background: HBRUSH
|
||||
menu_name, class_name: ^u8
|
||||
sm: HICON
|
||||
}
|
||||
|
||||
MSG :: struct #ordered {
|
||||
hwnd: HWND
|
||||
message: u32
|
||||
wparam: WPARAM
|
||||
lparam: LPARAM
|
||||
time: u32
|
||||
pt: POINT
|
||||
}
|
||||
|
||||
RECT :: struct #ordered {
|
||||
left: i32
|
||||
top: i32
|
||||
right: i32
|
||||
bottom: i32
|
||||
}
|
||||
|
||||
FILETIME :: struct #ordered {
|
||||
low_date_time, high_date_time: u32
|
||||
}
|
||||
|
||||
BY_HANDLE_FILE_INFORMATION :: struct #ordered {
|
||||
file_attributes: u32
|
||||
creation_time,
|
||||
last_access_time,
|
||||
last_write_time: FILETIME
|
||||
volume_serial_number,
|
||||
file_size_high,
|
||||
file_size_low,
|
||||
number_of_links,
|
||||
file_index_high,
|
||||
file_index_low: u32
|
||||
}
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA :: struct #ordered {
|
||||
file_attributes: u32
|
||||
creation_time,
|
||||
last_access_time,
|
||||
last_write_time: FILETIME
|
||||
file_size_high,
|
||||
file_size_low: u32
|
||||
}
|
||||
|
||||
GET_FILEEX_INFO_LEVELS :: type i32
|
||||
GetFileExInfoStandard : GET_FILEEX_INFO_LEVELS : 0
|
||||
GetFileExMaxInfoLevel : GET_FILEEX_INFO_LEVELS : 1
|
||||
|
||||
GetLastError :: proc() -> i32 #foreign #dll_import
|
||||
ExitProcess :: proc(exit_code: u32) #foreign #dll_import
|
||||
GetDesktopWindow :: proc() -> HWND #foreign #dll_import
|
||||
GetCursorPos :: proc(p: ^POINT) -> i32 #foreign #dll_import
|
||||
ScreenToClient :: proc(h: HWND, p: ^POINT) -> i32 #foreign #dll_import
|
||||
GetModuleHandleA :: proc(module_name: ^u8) -> HINSTANCE #foreign #dll_import
|
||||
GetStockObject :: proc(fn_object: i32) -> HGDIOBJ #foreign #dll_import
|
||||
PostQuitMessage :: proc(exit_code: i32) #foreign #dll_import
|
||||
SetWindowTextA :: proc(hwnd: HWND, c_string: ^u8) -> BOOL #foreign #dll_import
|
||||
|
||||
QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign #dll_import
|
||||
QueryPerformanceCounter :: proc(result: ^i64) -> i32 #foreign #dll_import
|
||||
|
||||
Sleep :: proc(ms: i32) -> i32 #foreign #dll_import
|
||||
|
||||
OutputDebugStringA :: proc(c_str: ^u8) #foreign #dll_import
|
||||
|
||||
|
||||
RegisterClassExA :: proc(wc: ^WNDCLASSEXA) -> ATOM #foreign #dll_import
|
||||
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 #dll_import
|
||||
|
||||
ShowWindow :: proc(hwnd: HWND, cmd_show: i32) -> BOOL #foreign #dll_import
|
||||
TranslateMessage :: proc(msg: ^MSG) -> BOOL #foreign #dll_import
|
||||
DispatchMessageA :: proc(msg: ^MSG) -> LRESULT #foreign #dll_import
|
||||
UpdateWindow :: proc(hwnd: HWND) -> BOOL #foreign #dll_import
|
||||
PeekMessageA :: proc(msg: ^MSG, hwnd: HWND,
|
||||
msg_filter_min, msg_filter_max, remove_msg: u32) -> BOOL #foreign #dll_import
|
||||
|
||||
DefWindowProcA :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #foreign #dll_import
|
||||
|
||||
AdjustWindowRect :: proc(rect: ^RECT, style: u32, menu: BOOL) -> BOOL #foreign #dll_import
|
||||
|
||||
|
||||
GetQueryPerformanceFrequency :: proc() -> i64 {
|
||||
r: i64
|
||||
QueryPerformanceFrequency(^r)
|
||||
return r
|
||||
}
|
||||
|
||||
GetCommandLineA :: proc() -> ^u8 #foreign #dll_import
|
||||
GetSystemMetrics :: proc(index: i32) -> i32 #foreign #dll_import
|
||||
GetCurrentThreadId :: proc() -> u32 #foreign #dll_import
|
||||
|
||||
// File Stuff
|
||||
|
||||
CloseHandle :: proc(h: HANDLE) -> i32 #foreign #dll_import
|
||||
GetStdHandle :: proc(h: i32) -> HANDLE #foreign #dll_import
|
||||
CreateFileA :: proc(filename: ^u8, desired_access, share_mode: u32,
|
||||
security: rawptr,
|
||||
creation, flags_and_attribs: u32, template_file: HANDLE) -> HANDLE #foreign #dll_import
|
||||
ReadFile :: proc(h: HANDLE, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> BOOL #foreign #dll_import
|
||||
WriteFile :: proc(h: HANDLE, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> i32 #foreign #dll_import
|
||||
|
||||
GetFileSizeEx :: proc(file_handle: HANDLE, file_size: ^i64) -> BOOL #foreign #dll_import
|
||||
GetFileAttributesExA :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> BOOL #foreign #dll_import
|
||||
GetFileInformationByHandle :: proc(file_handle: HANDLE, file_info: ^BY_HANDLE_FILE_INFORMATION) -> BOOL #foreign #dll_import
|
||||
|
||||
FILE_SHARE_READ :: 0x00000001
|
||||
FILE_SHARE_WRITE :: 0x00000002
|
||||
FILE_SHARE_DELETE :: 0x00000004
|
||||
FILE_GENERIC_ALL :: 0x10000000
|
||||
FILE_GENERIC_EXECUTE :: 0x20000000
|
||||
FILE_GENERIC_WRITE :: 0x40000000
|
||||
FILE_GENERIC_READ :: 0x80000000
|
||||
|
||||
STD_INPUT_HANDLE :: -10
|
||||
STD_OUTPUT_HANDLE :: -11
|
||||
STD_ERROR_HANDLE :: -12
|
||||
|
||||
CREATE_NEW :: 1
|
||||
CREATE_ALWAYS :: 2
|
||||
OPEN_EXISTING :: 3
|
||||
OPEN_ALWAYS :: 4
|
||||
TRUNCATE_EXISTING :: 5
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
HeapAlloc :: proc(h: HANDLE, flags: u32, bytes: int) -> rawptr #foreign #dll_import
|
||||
HeapReAlloc :: proc(h: HANDLE, flags: u32, memory: rawptr, bytes: int) -> rawptr #foreign #dll_import
|
||||
HeapFree :: proc(h: HANDLE, flags: u32, memory: rawptr) -> BOOL #foreign #dll_import
|
||||
GetProcessHeap :: proc() -> HANDLE #foreign #dll_import
|
||||
|
||||
|
||||
HEAP_ZERO_MEMORY :: 0x00000008
|
||||
|
||||
|
||||
|
||||
// GDI
|
||||
|
||||
BITMAPINFO :: struct #ordered {
|
||||
HEADER :: struct #ordered {
|
||||
size: u32
|
||||
width, height: i32
|
||||
planes, bit_count: i16
|
||||
compression: u32
|
||||
size_image: u32
|
||||
x_pels_per_meter: i32
|
||||
y_pels_per_meter: i32
|
||||
clr_used: u32
|
||||
clr_important: u32
|
||||
}
|
||||
using header: HEADER
|
||||
colors: [1]RGBQUAD
|
||||
}
|
||||
|
||||
|
||||
RGBQUAD :: struct #ordered {
|
||||
blue, green, red, reserved: byte
|
||||
}
|
||||
|
||||
BI_RGB :: 0
|
||||
DIB_RGB_COLORS :: 0x00
|
||||
SRCCOPY : u32 : 0x00cc0020
|
||||
|
||||
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,
|
||||
usage: u32,
|
||||
rop: u32) -> i32 #foreign #dll_import
|
||||
|
||||
|
||||
|
||||
LoadLibraryA :: proc(c_str: ^u8) -> HMODULE #foreign
|
||||
FreeLibrary :: proc(h: HMODULE) #foreign
|
||||
GetProcAddress :: proc(h: HMODULE, c_str: ^u8) -> proc() #foreign
|
||||
|
||||
GetClientRect :: proc(hwnd: HWND, rect: ^RECT) -> BOOL #foreign
|
||||
|
||||
|
||||
|
||||
// Windows OpenGL
|
||||
|
||||
PFD_TYPE_RGBA :: 0
|
||||
PFD_TYPE_COLORINDEX :: 1
|
||||
PFD_MAIN_PLANE :: 0
|
||||
PFD_OVERLAY_PLANE :: 1
|
||||
PFD_UNDERLAY_PLANE :: -1
|
||||
PFD_DOUBLEBUFFER :: 1
|
||||
PFD_STEREO :: 2
|
||||
PFD_DRAW_TO_WINDOW :: 4
|
||||
PFD_DRAW_TO_BITMAP :: 8
|
||||
PFD_SUPPORT_GDI :: 16
|
||||
PFD_SUPPORT_OPENGL :: 32
|
||||
PFD_GENERIC_FORMAT :: 64
|
||||
PFD_NEED_PALETTE :: 128
|
||||
PFD_NEED_SYSTEM_PALETTE :: 0x00000100
|
||||
PFD_SWAP_EXCHANGE :: 0x00000200
|
||||
PFD_SWAP_COPY :: 0x00000400
|
||||
PFD_SWAP_LAYER_BUFFERS :: 0x00000800
|
||||
PFD_GENERIC_ACCELERATED :: 0x00001000
|
||||
PFD_DEPTH_DONTCARE :: 0x20000000
|
||||
PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000
|
||||
PFD_STEREO_DONTCARE :: 0x80000000
|
||||
|
||||
HGLRC :: type HANDLE
|
||||
PROC :: type proc()
|
||||
wglCreateContextAttribsARBType :: type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC
|
||||
|
||||
|
||||
PIXELFORMATDESCRIPTOR :: struct #ordered {
|
||||
size,
|
||||
version,
|
||||
flags: u32
|
||||
|
||||
pixel_type,
|
||||
color_bits,
|
||||
red_bits,
|
||||
red_shift,
|
||||
green_bits,
|
||||
green_shift,
|
||||
blue_bits,
|
||||
blue_shift,
|
||||
alpha_bits,
|
||||
alpha_shift,
|
||||
accum_bits,
|
||||
accum_red_bits,
|
||||
accum_green_bits,
|
||||
accum_blue_bits,
|
||||
accum_alpha_bits,
|
||||
depth_bits,
|
||||
stencil_bits,
|
||||
aux_buffers,
|
||||
layer_type,
|
||||
reserved: byte
|
||||
|
||||
layer_mask,
|
||||
visible_mask,
|
||||
damage_mask: u32
|
||||
}
|
||||
|
||||
GetDC :: proc(h: HANDLE) -> HDC #foreign
|
||||
SetPixelFormat :: proc(hdc: HDC, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR ) -> BOOL #foreign #dll_import
|
||||
ChoosePixelFormat :: proc(hdc: HDC, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign #dll_import
|
||||
SwapBuffers :: proc(hdc: HDC) -> BOOL #foreign #dll_import
|
||||
ReleaseDC :: proc(wnd: HWND, hdc: HDC) -> i32 #foreign #dll_import
|
||||
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB :: 0x2091
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB :: 0x2092
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB :: 0x9126
|
||||
WGL_CONTEXT_CORE_PROFILE_BIT_ARB :: 0x0001
|
||||
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x0002
|
||||
|
||||
wglCreateContext :: proc(hdc: HDC) -> HGLRC #foreign #dll_import
|
||||
wglMakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign #dll_import
|
||||
wglGetProcAddress :: proc(c_str: ^u8) -> PROC #foreign #dll_import
|
||||
wglDeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign #dll_import
|
||||
|
||||
|
||||
|
||||
GetKeyState :: proc(v_key: i32) -> i16 #foreign #dll_import
|
||||
GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign #dll_import
|
||||
|
||||
is_key_down :: proc(key: Key_Code) -> bool {
|
||||
return GetAsyncKeyState(key as i32) < 0
|
||||
}
|
||||
|
||||
Key_Code :: enum i32 {
|
||||
LBUTTON = 0x01,
|
||||
RBUTTON = 0x02,
|
||||
CANCEL = 0x03,
|
||||
MBUTTON = 0x04,
|
||||
|
||||
BACK = 0x08,
|
||||
TAB = 0x09,
|
||||
|
||||
CLEAR = 0x0C,
|
||||
RETURN = 0x0D,
|
||||
|
||||
SHIFT = 0x10,
|
||||
CONTROL = 0x11,
|
||||
MENU = 0x12,
|
||||
PAUSE = 0x13,
|
||||
CAPITAL = 0x14,
|
||||
|
||||
KANA = 0x15,
|
||||
HANGEUL = 0x15,
|
||||
HANGUL = 0x15,
|
||||
JUNJA = 0x17,
|
||||
FINAL = 0x18,
|
||||
HANJA = 0x19,
|
||||
KANJI = 0x19,
|
||||
|
||||
ESCAPE = 0x1B,
|
||||
|
||||
CONVERT = 0x1C,
|
||||
NONCONVERT = 0x1D,
|
||||
ACCEPT = 0x1E,
|
||||
MODECHANGE = 0x1F,
|
||||
|
||||
SPACE = 0x20,
|
||||
PRIOR = 0x21,
|
||||
NEXT = 0x22,
|
||||
END = 0x23,
|
||||
HOME = 0x24,
|
||||
LEFT = 0x25,
|
||||
UP = 0x26,
|
||||
RIGHT = 0x27,
|
||||
DOWN = 0x28,
|
||||
SELECT = 0x29,
|
||||
PRINT = 0x2A,
|
||||
EXECUTE = 0x2B,
|
||||
SNAPSHOT = 0x2C,
|
||||
INSERT = 0x2D,
|
||||
DELETE = 0x2E,
|
||||
HELP = 0x2F,
|
||||
|
||||
NUM0 = #rune "0",
|
||||
NUM1 = #rune "1",
|
||||
NUM2 = #rune "2",
|
||||
NUM3 = #rune "3",
|
||||
NUM4 = #rune "4",
|
||||
NUM5 = #rune "5",
|
||||
NUM6 = #rune "6",
|
||||
NUM7 = #rune "7",
|
||||
NUM8 = #rune "8",
|
||||
NUM9 = #rune "9",
|
||||
|
||||
A = #rune "A",
|
||||
B = #rune "B",
|
||||
C = #rune "C",
|
||||
D = #rune "D",
|
||||
E = #rune "E",
|
||||
F = #rune "F",
|
||||
G = #rune "G",
|
||||
H = #rune "H",
|
||||
I = #rune "I",
|
||||
J = #rune "J",
|
||||
K = #rune "K",
|
||||
L = #rune "L",
|
||||
M = #rune "M",
|
||||
N = #rune "N",
|
||||
O = #rune "O",
|
||||
P = #rune "P",
|
||||
Q = #rune "Q",
|
||||
R = #rune "R",
|
||||
S = #rune "S",
|
||||
T = #rune "T",
|
||||
U = #rune "U",
|
||||
V = #rune "V",
|
||||
W = #rune "W",
|
||||
X = #rune "X",
|
||||
Y = #rune "Y",
|
||||
Z = #rune "Z",
|
||||
|
||||
LWIN = 0x5B,
|
||||
RWIN = 0x5C,
|
||||
APPS = 0x5D,
|
||||
|
||||
NUMPAD0 = 0x60,
|
||||
NUMPAD1 = 0x61,
|
||||
NUMPAD2 = 0x62,
|
||||
NUMPAD3 = 0x63,
|
||||
NUMPAD4 = 0x64,
|
||||
NUMPAD5 = 0x65,
|
||||
NUMPAD6 = 0x66,
|
||||
NUMPAD7 = 0x67,
|
||||
NUMPAD8 = 0x68,
|
||||
NUMPAD9 = 0x69,
|
||||
MULTIPLY = 0x6A,
|
||||
ADD = 0x6B,
|
||||
SEPARATOR = 0x6C,
|
||||
SUBTRACT = 0x6D,
|
||||
DECIMAL = 0x6E,
|
||||
DIVIDE = 0x6F,
|
||||
F1 = 0x70,
|
||||
F2 = 0x71,
|
||||
F3 = 0x72,
|
||||
F4 = 0x73,
|
||||
F5 = 0x74,
|
||||
F6 = 0x75,
|
||||
F7 = 0x76,
|
||||
F8 = 0x77,
|
||||
F9 = 0x78,
|
||||
F10 = 0x79,
|
||||
F11 = 0x7A,
|
||||
F12 = 0x7B,
|
||||
F13 = 0x7C,
|
||||
F14 = 0x7D,
|
||||
F15 = 0x7E,
|
||||
F16 = 0x7F,
|
||||
F17 = 0x80,
|
||||
F18 = 0x81,
|
||||
F19 = 0x82,
|
||||
F20 = 0x83,
|
||||
F21 = 0x84,
|
||||
F22 = 0x85,
|
||||
F23 = 0x86,
|
||||
F24 = 0x87,
|
||||
|
||||
NUMLOCK = 0x90,
|
||||
SCROLL = 0x91,
|
||||
|
||||
LSHIFT = 0xA0,
|
||||
RSHIFT = 0xA1,
|
||||
LCONTROL = 0xA2,
|
||||
RCONTROL = 0xA3,
|
||||
LMENU = 0xA4,
|
||||
RMENU = 0xA5,
|
||||
PROCESSKEY = 0xE5,
|
||||
ATTN = 0xF6,
|
||||
CRSEL = 0xF7,
|
||||
EXSEL = 0xF8,
|
||||
EREOF = 0xF9,
|
||||
PLAY = 0xFA,
|
||||
ZOOM = 0xFB,
|
||||
NONAME = 0xFC,
|
||||
PA1 = 0xFD,
|
||||
OEM_CLEAR = 0xFE,
|
||||
}
|
||||
|
||||
+13
-16
@@ -2,23 +2,20 @@
|
||||
|
||||
Not in any particular order
|
||||
|
||||
* Compile Time Execution (CTE)
|
||||
- More metaprogramming madness
|
||||
- Compiler as a library
|
||||
- AST inspection and modification
|
||||
* CTE-based build system
|
||||
* Replace LLVM backend with my own custom backend
|
||||
* Improve SSA design to accommodate for lowering to a "bytecode"
|
||||
* SSA optimizations
|
||||
* Parametric Polymorphism
|
||||
* Documentation Generator for "Entities"
|
||||
* Multiple Architecture support
|
||||
* Linking Options
|
||||
* Custom backend to replace LLVM
|
||||
- Improve SSA design to accommodate for lowering to a "bytecode"
|
||||
- SSA optimizations
|
||||
- COFF generation
|
||||
- linker
|
||||
* Type safe "macros"
|
||||
* Documentation generator for "Entities"
|
||||
* Multiple architecture support
|
||||
* Inline assembly
|
||||
* Linking options
|
||||
- Executable
|
||||
- Static/Dynamic Library
|
||||
* Debug Information
|
||||
* Debug information
|
||||
- pdb format too
|
||||
* Command Line Tooling
|
||||
* Compiler Internals:
|
||||
* Command line tooling
|
||||
* Compiler internals:
|
||||
- Big numbers library
|
||||
- Cyclic Type Checking (at the moment will cause compiler to go into an infinite loop)
|
||||
|
||||
+3
-3
@@ -11,8 +11,8 @@ GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
|
||||
typedef Array(void) ArrayVoid;
|
||||
|
||||
#define array_init_reserve(x_, allocator_, init_capacity_) do { \
|
||||
GB_ASSERT((x_) != NULL); \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != NULL); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = 0; \
|
||||
(x_)->capacity = (init_capacity_); \
|
||||
@@ -20,8 +20,8 @@ typedef Array(void) ArrayVoid;
|
||||
} while (0)
|
||||
|
||||
#define array_init_count(x_, allocator_, init_count_) do { \
|
||||
GB_ASSERT((x_) != NULL); \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != NULL); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = (init_count_); \
|
||||
(x_)->capacity = (init_count_); \
|
||||
@@ -67,8 +67,8 @@ typedef Array(void) ArrayVoid;
|
||||
|
||||
|
||||
void array__set_capacity(void *ptr, isize capacity, isize element_size) {
|
||||
GB_ASSERT(ptr != NULL);
|
||||
ArrayVoid *x = cast(ArrayVoid *)ptr;
|
||||
GB_ASSERT(ptr != NULL);
|
||||
|
||||
GB_ASSERT(element_size > 0);
|
||||
|
||||
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
typedef struct BuildContext {
|
||||
String ODIN_OS; // target operating system
|
||||
String ODIN_ARCH; // target architecture
|
||||
String ODIN_VENDOR; // compiler vendor
|
||||
String ODIN_VERSION; // compiler version
|
||||
String ODIN_ROOT; // Odin ROOT
|
||||
|
||||
i64 word_size;
|
||||
i64 max_align;
|
||||
String llc_flags;
|
||||
String link_flags;
|
||||
bool is_dll;
|
||||
} BuildContext;
|
||||
|
||||
// TODO(bill): OS dependent versions for the BuildContext
|
||||
// join_path
|
||||
// is_dir
|
||||
// is_file
|
||||
// is_abs_path
|
||||
// has_subdir
|
||||
|
||||
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
|
||||
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
|
||||
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
Array(wchar_t) path_buf;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
wchar_t *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
array_init_count(&path_buf, heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
len = GetModuleFileNameW(NULL, &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, wchar_t, len+1);
|
||||
|
||||
GetModuleFileNameW(NULL, text, len);
|
||||
path = string16_to_string(heap_allocator(), make_string16(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;
|
||||
}
|
||||
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
String16 string16 = string_to_string16(string_buffer_allocator, s);
|
||||
String result = {0};
|
||||
|
||||
DWORD len = GetFullPathNameW(string16.text, 0, NULL, NULL);
|
||||
if (len != 0) {
|
||||
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
GetFullPathNameW(string16.text, len, text, NULL);
|
||||
text[len] = 0;
|
||||
result = string16_to_string(a, make_string16(text, len));
|
||||
}
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
|
||||
String res = {0};
|
||||
isize str_len = base_dir.len+path.len;
|
||||
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len;
|
||||
gb_memmove(str+i, path.text, path.len);
|
||||
str[str_len] = '\0';
|
||||
res = path_to_fullpath(a, make_string(str, str_len));
|
||||
gb_free(heap_allocator(), str);
|
||||
return res;
|
||||
}
|
||||
|
||||
String get_fullpath_core(gbAllocator a, String path) {
|
||||
String module_dir = odin_root_dir();
|
||||
String res = {0};
|
||||
|
||||
char core[] = "core/";
|
||||
isize core_len = gb_size_of(core)-1;
|
||||
|
||||
isize str_len = module_dir.len + core_len + path.len;
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
|
||||
|
||||
gb_memmove(str, module_dir.text, module_dir.len);
|
||||
gb_memmove(str+module_dir.len, core, core_len);
|
||||
gb_memmove(str+module_dir.len+core_len, path.text, path.len);
|
||||
str[str_len] = '\0';
|
||||
|
||||
res = path_to_fullpath(a, make_string(str, str_len));
|
||||
gb_free(heap_allocator(), str);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
String get_filepath_extension(String path) {
|
||||
isize dot = 0;
|
||||
bool seen_slash = false;
|
||||
for (isize i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path.text[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
seen_slash = true;
|
||||
}
|
||||
|
||||
if (c == '.') {
|
||||
if (seen_slash) {
|
||||
return str_lit("");
|
||||
}
|
||||
|
||||
dot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return make_string(path.text, dot);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void init_build_context(BuildContext *bc) {
|
||||
bc->ODIN_VENDOR = str_lit("odin");
|
||||
bc->ODIN_VERSION = str_lit("0.0.5a");
|
||||
bc->ODIN_ROOT = odin_root_dir();
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
bc->ODIN_OS = str_lit("windows");
|
||||
bc->ODIN_ARCH = str_lit("amd64");
|
||||
#else
|
||||
#error Implement system
|
||||
#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 ");
|
||||
} 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 ");
|
||||
}
|
||||
}
|
||||
+533
-327
File diff suppressed because it is too large
Load Diff
+200
-151
@@ -1,10 +1,6 @@
|
||||
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);
|
||||
void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker);
|
||||
void check_const_decl (Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr);
|
||||
void check_proc_decl (Checker *c, Entity *e, DeclInfo *d);
|
||||
void check_var_decl (Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr);
|
||||
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) {
|
||||
@@ -16,7 +12,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
|
||||
gbString expr_str = expr_to_string(operand->expr);
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
error(ast_node_token(operand->expr),
|
||||
error_node(operand->expr,
|
||||
"Cannot assign builtin procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
@@ -96,58 +92,23 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
|
||||
}
|
||||
|
||||
if (rhs_count > 0 && lhs_count != rhs_count) {
|
||||
error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
|
||||
error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (lhs[0]->kind == Entity_Variable &&
|
||||
lhs[0]->Variable.is_let) {
|
||||
if (lhs_count != rhs_count) {
|
||||
error(lhs[0]->token, "`let` variables must be initialized, `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, CycleChecker *cycle_checker) {
|
||||
if (e->type != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d == NULL) {
|
||||
DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e));
|
||||
if (found) {
|
||||
d = *found;
|
||||
} else {
|
||||
e->type = t_invalid;
|
||||
set_base_type(named_type, t_invalid);
|
||||
return;
|
||||
// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
|
||||
}
|
||||
}
|
||||
|
||||
if (e->kind == Entity_Procedure) {
|
||||
check_proc_decl(c, e, d);
|
||||
return;
|
||||
}
|
||||
CheckerContext prev = c->context;
|
||||
c->context.scope = d->scope;
|
||||
c->context.decl = d;
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_Constant:
|
||||
check_const_decl(c, e, d->type_expr, d->init_expr);
|
||||
break;
|
||||
case Entity_Variable:
|
||||
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
|
||||
break;
|
||||
case Entity_TypeName:
|
||||
check_type_decl(c, e, d->type_expr, named_type, cycle_checker);
|
||||
break;
|
||||
}
|
||||
|
||||
c->context = prev;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check_var_decl_node(Checker *c, AstNode *node) {
|
||||
ast_node(vd, VarDecl, node);
|
||||
void check_var_decl_node(Checker *c, AstNodeValueDecl *vd) {
|
||||
GB_ASSERT(vd->is_var == true);
|
||||
isize entity_count = vd->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
@@ -175,7 +136,7 @@ void check_var_decl_node(Checker *c, AstNode *node) {
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
error(ast_node_token(name), "A variable declaration must be an identifier");
|
||||
error_node(name, "A variable declaration must be an identifier");
|
||||
}
|
||||
if (entity == NULL) {
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
||||
@@ -185,9 +146,10 @@ void check_var_decl_node(Checker *c, AstNode *node) {
|
||||
|
||||
Type *init_type = NULL;
|
||||
if (vd->type) {
|
||||
init_type = check_type_extra(c, vd->type, NULL, NULL);
|
||||
if (init_type == NULL)
|
||||
init_type = check_type_extra(c, vd->type, NULL);
|
||||
if (init_type == NULL) {
|
||||
init_type = t_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
@@ -199,10 +161,12 @@ void check_var_decl_node(Checker *c, AstNode *node) {
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (e->type == NULL)
|
||||
if (e->type == NULL) {
|
||||
e->type = init_type;
|
||||
}
|
||||
}
|
||||
|
||||
check_arity_match(c, vd);
|
||||
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
||||
|
||||
for_array(i, vd->names) {
|
||||
@@ -227,23 +191,23 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
// TODO(bill): better error
|
||||
error(ast_node_token(operand->expr),
|
||||
"`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string));
|
||||
gbString str = expr_to_string(operand->expr);
|
||||
error_node(operand->expr, "`%s` is not a constant", str);
|
||||
gb_string_free(str);
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!is_type_constant_type(operand->type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
error_node(operand->expr, "Invalid constant type: `%s`", type_str);
|
||||
gb_string_free(type_str);
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
// if (!is_type_constant_type(operand->type)) {
|
||||
// gbString type_str = type_to_string(operand->type);
|
||||
// defer (gb_string_free(type_str));
|
||||
// error(ast_node_token(operand->expr),
|
||||
// "Invalid constant type: `%s`", type_str);
|
||||
// if (e->type == NULL) {
|
||||
// e->type = t_invalid;
|
||||
// }
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (e->type == NULL) { // NOTE(bill): type inference
|
||||
e->type = operand->type;
|
||||
@@ -257,37 +221,7 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||
e->Constant.value = operand->value;
|
||||
}
|
||||
|
||||
|
||||
void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (type_expr) {
|
||||
Type *t = check_type(c, type_expr);
|
||||
// if (!is_type_constant_type(t)) {
|
||||
// gbString str = type_to_string(t);
|
||||
// defer (gb_string_free(str));
|
||||
// error(ast_node_token(type_expr),
|
||||
// "Invalid constant type `%s`", str);
|
||||
// e->type = t_invalid;
|
||||
// return;
|
||||
// }
|
||||
e->type = t;
|
||||
}
|
||||
|
||||
Operand operand = {0};
|
||||
if (init_expr) {
|
||||
check_expr(c, &operand, init_expr);
|
||||
}
|
||||
check_init_constant(c, e, &operand);
|
||||
}
|
||||
|
||||
void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker) {
|
||||
void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
Type *named = make_type_named(c->allocator, e->token.string, NULL, e);
|
||||
named->Named.type_name = e;
|
||||
@@ -296,22 +230,67 @@ void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, Cycle
|
||||
}
|
||||
e->type = named;
|
||||
|
||||
CycleChecker local_cycle_checker = {0};
|
||||
if (cycle_checker == NULL) {
|
||||
cycle_checker = &local_cycle_checker;
|
||||
}
|
||||
// gb_printf_err("%.*s %p\n", LIT(e->token.string), e);
|
||||
|
||||
Type *bt = check_type_extra(c, type_expr, named, cycle_checker_add(cycle_checker, e));
|
||||
named->Named.base = bt;
|
||||
named->Named.base = base_type(named->Named.base);
|
||||
Type *bt = check_type_extra(c, type_expr, named);
|
||||
named->Named.base = base_type(bt);
|
||||
if (named->Named.base == t_invalid) {
|
||||
gb_printf("check_type_decl: %s\n", type_to_string(named));
|
||||
// gb_printf("check_type_decl: %s\n", type_to_string(named));
|
||||
}
|
||||
}
|
||||
|
||||
void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init, Type *named_type) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
GB_ASSERT(e->kind == Entity_Constant);
|
||||
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
c->context.iota = e->Constant.value;
|
||||
e->Constant.value = (ExactValue){0};
|
||||
|
||||
if (type_expr) {
|
||||
Type *t = check_type(c, type_expr);
|
||||
if (!is_type_constant_type(t)) {
|
||||
gbString str = type_to_string(t);
|
||||
error_node(type_expr, "Invalid constant type `%s`", str);
|
||||
gb_string_free(str);
|
||||
e->type = t_invalid;
|
||||
c->context.iota = (ExactValue){0};
|
||||
return;
|
||||
}
|
||||
e->type = t;
|
||||
}
|
||||
|
||||
cycle_checker_destroy(&local_cycle_checker);
|
||||
Operand operand = {0};
|
||||
if (init != NULL) {
|
||||
check_expr_or_type(c, &operand, init);
|
||||
}
|
||||
if (operand.mode == Addressing_Type) {
|
||||
c->context.iota = (ExactValue){0};
|
||||
|
||||
e->Constant.value = (ExactValue){0};
|
||||
e->kind = Entity_TypeName;
|
||||
|
||||
DeclInfo *d = c->context.decl;
|
||||
d->type_expr = d->init_expr;
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
return;
|
||||
}
|
||||
|
||||
check_init_constant(c, e, &operand);
|
||||
c->context.iota = (ExactValue){0};
|
||||
|
||||
if (operand.mode == Addressing_Invalid) {
|
||||
error(e->token, "Illegal cyclic declaration");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool are_signatures_similar_enough(Type *a_, Type *b_) {
|
||||
GB_ASSERT(a_->kind == Type_Proc);
|
||||
GB_ASSERT(b_->kind == Type_Proc);
|
||||
@@ -350,18 +329,24 @@ bool are_signatures_similar_enough(Type *a_, Type *b_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
if (d->proc_lit->kind != AstNode_ProcLit) {
|
||||
// TOOD(bill): Better error message
|
||||
error_node(d->proc_lit, "Expected a procedure to check");
|
||||
return;
|
||||
}
|
||||
|
||||
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false);
|
||||
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false, ProcCC_Odin);
|
||||
e->type = proc_type;
|
||||
ast_node(pd, ProcDecl, d->proc_decl);
|
||||
ast_node(pd, ProcLit, d->proc_lit);
|
||||
|
||||
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;
|
||||
|
||||
@@ -370,29 +355,33 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
if (proc_type != NULL) {
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
if (pt->param_count != 0 ||
|
||||
pt->result_count) {
|
||||
pt->result_count != 0) {
|
||||
gbString str = type_to_string(proc_type);
|
||||
error(e->token,
|
||||
"Procedure type of `main` was expected to be `proc()`, got %s", str);
|
||||
error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_inline && is_no_inline) {
|
||||
error(ast_node_token(pd->type),
|
||||
"You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
error_node(pd->type, "You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
}
|
||||
|
||||
if (is_foreign && is_link_name) {
|
||||
error(ast_node_token(pd->type),
|
||||
"You cannot apply both `foreign` and `link_name` to a procedure");
|
||||
error_node(pd->type, "You cannot apply both `foreign` and `link_name` to a procedure");
|
||||
} else if (is_foreign && is_export) {
|
||||
error_node(pd->type, "You cannot apply both `foreign` and `export` to a procedure");
|
||||
}
|
||||
|
||||
|
||||
if (pd->body != NULL) {
|
||||
if (is_foreign) {
|
||||
error(ast_node_token(pd->body),
|
||||
"A procedure tagged as `#foreign` cannot have a body");
|
||||
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -403,11 +392,14 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
|
||||
if (is_foreign) {
|
||||
MapEntity *fp = &c->info.foreign_procs;
|
||||
AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl;
|
||||
String name = proc_decl->name->Ident.string;
|
||||
if (proc_decl->foreign_name.len > 0) {
|
||||
name = proc_decl->foreign_name;
|
||||
String name = e->token.string;
|
||||
if (pd->foreign_name.len > 0) {
|
||||
name = pd->foreign_name;
|
||||
}
|
||||
|
||||
e->Procedure.is_foreign = true;
|
||||
e->Procedure.foreign_name = name;
|
||||
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(fp, key);
|
||||
if (found) {
|
||||
@@ -416,30 +408,38 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
Type *this_type = base_type(e->type);
|
||||
Type *other_type = base_type(f->type);
|
||||
if (!are_signatures_similar_enough(this_type, other_type)) {
|
||||
error(ast_node_token(d->proc_decl),
|
||||
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
map_entity_set(fp, key, e);
|
||||
}
|
||||
} else if (is_link_name) {
|
||||
MapEntity *fp = &c->info.foreign_procs;
|
||||
AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl;
|
||||
String name = proc_decl->link_name;
|
||||
} else {
|
||||
String name = e->token.string;
|
||||
if (is_link_name) {
|
||||
name = pd->link_name;
|
||||
}
|
||||
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
error(ast_node_token(d->proc_decl),
|
||||
"Non unique #link_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);
|
||||
if (is_link_name || is_export) {
|
||||
MapEntity *fp = &c->info.foreign_procs;
|
||||
|
||||
e->Procedure.link_name = name;
|
||||
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
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);
|
||||
} else {
|
||||
map_entity_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,12 +456,14 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (type_expr != NULL)
|
||||
e->type = check_type_extra(c, type_expr, NULL, NULL);
|
||||
if (type_expr != NULL) {
|
||||
e->type = check_type_extra(c, type_expr, NULL);
|
||||
}
|
||||
|
||||
if (init_expr == NULL) {
|
||||
if (type_expr == NULL)
|
||||
if (type_expr == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -473,8 +475,9 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
|
||||
}
|
||||
|
||||
if (type_expr != NULL) {
|
||||
for (isize i = 0; i < entity_count; i++)
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
entities[i]->type = e->type;
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeArray inits;
|
||||
@@ -483,6 +486,49 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
|
||||
check_init_variables(c, entities, entity_count, inits, str_lit("variable declaration"));
|
||||
}
|
||||
|
||||
|
||||
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
if (e->type != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d == NULL) {
|
||||
DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e));
|
||||
if (found) {
|
||||
d = *found;
|
||||
} else {
|
||||
// TODO(bill): Err here?
|
||||
e->type = t_invalid;
|
||||
set_base_type(named_type, t_invalid);
|
||||
return;
|
||||
// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
|
||||
}
|
||||
}
|
||||
|
||||
CheckerContext prev = c->context;
|
||||
c->context.scope = d->scope;
|
||||
c->context.decl = d;
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_Variable:
|
||||
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
|
||||
break;
|
||||
case Entity_Constant:
|
||||
check_const_decl(c, e, d->type_expr, d->init_expr, named_type);
|
||||
break;
|
||||
case Entity_TypeName:
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
break;
|
||||
case Entity_Procedure:
|
||||
check_proc_lit(c, e, d);
|
||||
break;
|
||||
}
|
||||
|
||||
c->context = prev;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
|
||||
GB_ASSERT(body->kind == AstNode_BlockStmt);
|
||||
|
||||
@@ -525,11 +571,14 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
|
||||
push_procedure(c, type);
|
||||
{
|
||||
ast_node(bs, BlockStmt, body);
|
||||
// TODO(bill): Check declarations first (except mutable variable declarations)
|
||||
check_stmt_list(c, bs->stmts, 0);
|
||||
if (type->Proc.result_count > 0) {
|
||||
if (!check_is_terminating(body)) {
|
||||
error(bs->close, "Missing return statement at the end of the procedure");
|
||||
if (token.kind == Token_Ident) {
|
||||
error(bs->close, "Missing return statement at the end of the procedure `%.*s`", LIT(token.string));
|
||||
} else {
|
||||
error(bs->close, "Missing return statement at the end of the procedure");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-7
@@ -55,11 +55,17 @@ struct Entity {
|
||||
ExactValue value;
|
||||
} Constant;
|
||||
struct {
|
||||
i32 field_index;
|
||||
i32 field_src_index;
|
||||
i32 field_index;
|
||||
i32 field_src_index;
|
||||
bool is_immutable;
|
||||
} Variable;
|
||||
i32 TypeName;
|
||||
i32 Procedure;
|
||||
struct {
|
||||
bool is_foreign;
|
||||
String foreign_name;
|
||||
String link_name;
|
||||
u64 tags;
|
||||
} Procedure;
|
||||
struct {
|
||||
BuiltinProcId id;
|
||||
} Builtin;
|
||||
@@ -67,7 +73,7 @@ struct Entity {
|
||||
String path;
|
||||
String name;
|
||||
Scope *scope;
|
||||
bool used;
|
||||
bool used;
|
||||
} ImportName;
|
||||
i32 Nil;
|
||||
struct {
|
||||
@@ -78,6 +84,10 @@ struct Entity {
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
Entity *e_iota = NULL;
|
||||
|
||||
|
||||
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
|
||||
Entity *entity = gb_alloc_item(a, Entity);
|
||||
entity->kind = kind;
|
||||
@@ -138,8 +148,9 @@ Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type) {
|
||||
Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type, u64 tags) {
|
||||
Entity *entity = alloc_entity(a, Entity_Procedure, scope, token, signature_type);
|
||||
entity->Procedure.tags = tags;
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -172,8 +183,8 @@ Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, Impli
|
||||
}
|
||||
|
||||
|
||||
Entity *make_entity_dummy_variable(gbAllocator a, Scope *file_scope, Token token) {
|
||||
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
|
||||
token.string = str_lit("_");
|
||||
return make_entity_variable(a, file_scope, token, NULL);
|
||||
return make_entity_variable(a, scope, token, NULL);
|
||||
}
|
||||
|
||||
|
||||
+972
-678
File diff suppressed because it is too large
Load Diff
+382
-213
@@ -1,105 +1,49 @@
|
||||
bool check_is_terminating(AstNode *node);
|
||||
bool check_has_break (AstNode *stmt, bool implicit);
|
||||
void check_stmt (Checker *c, AstNode *node, u32 flags);
|
||||
|
||||
|
||||
// Statements and Declarations
|
||||
typedef enum StmtFlag {
|
||||
Stmt_BreakAllowed = GB_BIT(0),
|
||||
Stmt_ContinueAllowed = GB_BIT(1),
|
||||
Stmt_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
|
||||
} StmtFlag;
|
||||
|
||||
|
||||
|
||||
void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
|
||||
if (stmts.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
|
||||
typedef struct {
|
||||
Entity *e;
|
||||
DeclInfo *d;
|
||||
} Delay;
|
||||
Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, stmts.count);
|
||||
Array(Delay) delayed_type; array_init_reserve(&delayed_type, c->tmp_allocator, stmts.count);
|
||||
|
||||
for_array(i, stmts) {
|
||||
AstNode *node = stmts.e[i];
|
||||
switch (node->kind) {
|
||||
case_ast_node(cd, ConstDecl, node);
|
||||
for_array(i, cd->values) {
|
||||
AstNode *name = cd->names.e[i];
|
||||
AstNode *value = cd->values.e[i];
|
||||
ExactValue v = {ExactValue_Invalid};
|
||||
|
||||
Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
|
||||
e->identifier = name;
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, e->scope);
|
||||
d->type_expr = cd->type;
|
||||
d->init_expr = value;
|
||||
|
||||
add_entity_and_decl_info(c, name, e, d);
|
||||
|
||||
Delay delay = {e, d};
|
||||
array_add(&delayed_const, delay);
|
||||
}
|
||||
|
||||
isize lhs_count = cd->names.count;
|
||||
isize rhs_count = cd->values.count;
|
||||
|
||||
if (rhs_count == 0 && cd->type == NULL) {
|
||||
error(ast_node_token(node), "Missing type or initial expression");
|
||||
} else if (lhs_count < rhs_count) {
|
||||
error(ast_node_token(node), "Extra initial expression");
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(td, TypeDecl, node);
|
||||
Entity *e = make_entity_type_name(c->allocator, c->context.scope, td->name->Ident, NULL);
|
||||
e->identifier = td->name;
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, e->scope);
|
||||
d->type_expr = td->type;
|
||||
|
||||
add_entity_and_decl_info(c, td->name, e, d);
|
||||
|
||||
Delay delay = {e, d};
|
||||
array_add(&delayed_type, delay);
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, delayed_type) {
|
||||
check_entity_decl(c, delayed_type.e[i].e, delayed_type.e[i].d, NULL, NULL);
|
||||
}
|
||||
for_array(i, delayed_const) {
|
||||
check_entity_decl(c, delayed_const.e[i].e, delayed_const.e[i].d, NULL, NULL);
|
||||
}
|
||||
check_scope_decls(c, stmts, 1.2*stmts.count, NULL);
|
||||
|
||||
bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
|
||||
u32 f = flags & (~Stmt_FallthroughAllowed);
|
||||
flags &= ~Stmt_FallthroughAllowed;
|
||||
|
||||
for_array(i, stmts) {
|
||||
isize max = stmts.count;
|
||||
for (isize i = stmts.count-1; i >= 0; i--) {
|
||||
if (stmts.e[i]->kind != AstNode_EmptyStmt) {
|
||||
break;
|
||||
}
|
||||
max--;
|
||||
}
|
||||
for (isize i = 0; i < max; i++) {
|
||||
AstNode *n = stmts.e[i];
|
||||
if (n->kind == AstNode_EmptyStmt) {
|
||||
continue;
|
||||
}
|
||||
u32 new_flags = f;
|
||||
if (ft_ok && i+1 == stmts.count) {
|
||||
u32 new_flags = flags;
|
||||
if (ft_ok && i+1 == max) {
|
||||
new_flags |= Stmt_FallthroughAllowed;
|
||||
}
|
||||
|
||||
if (i+1 < max) {
|
||||
switch (n->kind) {
|
||||
case AstNode_ReturnStmt:
|
||||
error_node(n, "Statements after this `return` are never executed");
|
||||
break;
|
||||
case AstNode_ExprStmt:
|
||||
if (n->ExprStmt.expr->kind == AstNode_GiveExpr) {
|
||||
error_node(n, "A `give` must be the last statement in a block");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
check_stmt(c, n, new_flags);
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
|
||||
bool check_is_terminating_list(AstNodeArray stmts) {
|
||||
|
||||
// Iterate backwards
|
||||
for (isize n = stmts.count-1; n >= 0; n--) {
|
||||
AstNode *stmt = stmts.e[n];
|
||||
@@ -174,9 +118,24 @@ bool check_is_terminating(AstNode *node) {
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(fs, ForStmt, node);
|
||||
if (fs->cond == NULL && !check_has_break(fs->body, true)) {
|
||||
return true;
|
||||
case_ast_node(ws, WhenStmt, node);
|
||||
if (ws->else_stmt != NULL) {
|
||||
if (check_is_terminating(ws->body) &&
|
||||
check_is_terminating(ws->else_stmt)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(ws, WhileStmt, node);
|
||||
if (ws->cond != NULL && !check_has_break(ws->body, true)) {
|
||||
return check_is_terminating(ws->body);
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(rs, ForStmt, node);
|
||||
if (!check_has_break(rs->body, true)) {
|
||||
return check_is_terminating(rs->body);
|
||||
}
|
||||
case_end;
|
||||
|
||||
@@ -279,10 +238,10 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
|
||||
gbString str = expr_to_string(op_b.expr);
|
||||
switch (op_b.mode) {
|
||||
case Addressing_Value:
|
||||
error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
|
||||
error_node(op_b.expr, "Cannot assign to `%s`", str);
|
||||
break;
|
||||
default:
|
||||
error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
|
||||
error_node(op_b.expr, "Cannot assign to `%s`", str);
|
||||
break;
|
||||
}
|
||||
gb_string_free(str);
|
||||
@@ -333,6 +292,8 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
|
||||
c->context.stmt_state_flags = prev_stmt_state_flags;
|
||||
}
|
||||
|
||||
|
||||
|
||||
typedef struct TypeAndToken {
|
||||
Type *type;
|
||||
Token token;
|
||||
@@ -343,6 +304,35 @@ typedef struct TypeAndToken {
|
||||
#define MAP_NAME MapTypeAndToken
|
||||
#include "../map.c"
|
||||
|
||||
void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
|
||||
error_node(ws->cond, "Non-constant boolean `when` condition");
|
||||
return;
|
||||
}
|
||||
if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) {
|
||||
error_node(ws->cond, "Invalid body for `when` statement");
|
||||
return;
|
||||
}
|
||||
if (operand.value.kind == ExactValue_Bool &&
|
||||
operand.value.value_bool) {
|
||||
check_stmt_list(c, ws->body->BlockStmt.stmts, flags);
|
||||
} else if (ws->else_stmt) {
|
||||
switch (ws->else_stmt->kind) {
|
||||
case AstNode_BlockStmt:
|
||||
check_stmt_list(c, ws->else_stmt->BlockStmt.stmts, flags);
|
||||
break;
|
||||
case AstNode_WhenStmt:
|
||||
check_when_stmt(c, &ws->else_stmt->WhenStmt, flags);
|
||||
break;
|
||||
default:
|
||||
error_node(ws->else_stmt, "Invalid `else` statement in `when` statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
|
||||
switch (node->kind) {
|
||||
@@ -355,7 +345,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
ExprKind kind = check_expr_base(c, &operand, es->expr, NULL);
|
||||
switch (operand.mode) {
|
||||
case Addressing_Type:
|
||||
error(ast_node_token(node), "Is not an expression");
|
||||
error_node(node, "Is not an expression");
|
||||
break;
|
||||
case Addressing_NoValue:
|
||||
return;
|
||||
@@ -366,8 +356,14 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
if (operand.expr->kind == AstNode_CallExpr) {
|
||||
return;
|
||||
}
|
||||
if (operand.expr->kind == AstNode_GiveExpr) {
|
||||
if ((flags&Stmt_GiveAllowed) != 0) {
|
||||
return;
|
||||
}
|
||||
error_node(node, "Illegal use of `give`");
|
||||
}
|
||||
gbString expr_str = expr_to_string(operand.expr);
|
||||
error(ast_node_token(node), "Expression is not used: `%s`", expr_str);
|
||||
error_node(node, "Expression is not used: `%s`", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
} break;
|
||||
}
|
||||
@@ -375,49 +371,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
case_ast_node(ts, TagStmt, node);
|
||||
// TODO(bill): Tag Statements
|
||||
error(ast_node_token(node), "Tag statements are not supported yet");
|
||||
error_node(node, "Tag statements are not supported yet");
|
||||
check_stmt(c, ts->stmt, flags);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ids, IncDecStmt, node);
|
||||
Token op = ids->op;
|
||||
switch (ids->op.kind) {
|
||||
case Token_Increment:
|
||||
op.kind = Token_Add;
|
||||
op.string.len = 1;
|
||||
break;
|
||||
case Token_Decrement:
|
||||
op.kind = Token_Sub;
|
||||
op.string.len = 1;
|
||||
break;
|
||||
default:
|
||||
error(ids->op, "Unknown inc/dec operation %.*s", LIT(ids->op.string));
|
||||
return;
|
||||
}
|
||||
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, ids->expr);
|
||||
if (operand.mode == Addressing_Invalid)
|
||||
return;
|
||||
if (!is_type_numeric(operand.type)) {
|
||||
error(ids->op, "Non numeric type");
|
||||
return;
|
||||
}
|
||||
|
||||
AstNode basic_lit = {AstNode_BasicLit};
|
||||
ast_node(bl, BasicLit, &basic_lit);
|
||||
*bl = ids->op;
|
||||
bl->kind = Token_Integer;
|
||||
bl->string = str_lit("1");
|
||||
|
||||
AstNode binary_expr = {AstNode_BinaryExpr};
|
||||
ast_node(be, BinaryExpr, &binary_expr);
|
||||
be->op = op;
|
||||
be->left = ids->expr;
|
||||
be->right = &basic_lit;
|
||||
check_binary_expr(c, &operand, &binary_expr);
|
||||
case_end;
|
||||
|
||||
case_ast_node(as, AssignStmt, node);
|
||||
switch (as->op.kind) {
|
||||
case Token_Eq: {
|
||||
@@ -458,7 +415,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
check_assignment_variable(c, &operands.e[i], lhs);
|
||||
}
|
||||
if (lhs_count != rhs_count) {
|
||||
error(ast_node_token(as->lhs.e[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
error_node(as->lhs.e[0], "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
@@ -510,23 +467,20 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, is->cond);
|
||||
if (operand.mode != Addressing_Invalid &&
|
||||
!is_type_boolean(operand.type)) {
|
||||
error(ast_node_token(is->cond),
|
||||
"Non-boolean condition in `if` statement");
|
||||
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
|
||||
error_node(is->cond, "Non-boolean condition in `if` statement");
|
||||
}
|
||||
|
||||
check_stmt(c, is->body, mod_flags);
|
||||
|
||||
if (is->else_stmt) {
|
||||
if (is->else_stmt != NULL) {
|
||||
switch (is->else_stmt->kind) {
|
||||
case AstNode_IfStmt:
|
||||
case AstNode_BlockStmt:
|
||||
check_stmt(c, is->else_stmt, mod_flags);
|
||||
break;
|
||||
default:
|
||||
error(ast_node_token(is->else_stmt),
|
||||
"Invalid `else` statement in `if` statement");
|
||||
error_node(is->else_stmt, "Invalid `else` statement in `if` statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -534,6 +488,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
check_close_scope(c);
|
||||
case_end;
|
||||
|
||||
case_ast_node(ws, WhenStmt, node);
|
||||
check_when_stmt(c, ws, flags);
|
||||
case_end;
|
||||
|
||||
case_ast_node(rs, ReturnStmt, node);
|
||||
GB_ASSERT(c->proc_stack.count > 0);
|
||||
|
||||
@@ -557,36 +515,203 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
variables = tuple->variables;
|
||||
}
|
||||
if (rs->results.count == 0) {
|
||||
error(ast_node_token(node), "Expected %td return values, got 0", result_count);
|
||||
error_node(node, "Expected %td return values, got 0", result_count);
|
||||
} else {
|
||||
check_init_variables(c, variables, result_count,
|
||||
rs->results, str_lit("return statement"));
|
||||
}
|
||||
} else if (rs->results.count > 0) {
|
||||
error(ast_node_token(rs->results.e[0]), "No return values expected");
|
||||
error_node(rs->results.e[0], "No return values expected");
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(fs, ForStmt, node);
|
||||
case_ast_node(ws, WhileStmt, node);
|
||||
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||
check_open_scope(c, node);
|
||||
|
||||
if (fs->init != NULL) {
|
||||
check_stmt(c, fs->init, 0);
|
||||
if (ws->init != NULL) {
|
||||
check_stmt(c, ws->init, 0);
|
||||
}
|
||||
if (fs->cond) {
|
||||
if (ws->cond) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, fs->cond);
|
||||
check_expr(c, &operand, ws->cond);
|
||||
if (operand.mode != Addressing_Invalid &&
|
||||
!is_type_boolean(operand.type)) {
|
||||
error(ast_node_token(fs->cond),
|
||||
"Non-boolean condition in `for` statement");
|
||||
error_node(ws->cond, "Non-boolean condition in `while` statement");
|
||||
}
|
||||
}
|
||||
if (fs->post != NULL) {
|
||||
check_stmt(c, fs->post, 0);
|
||||
check_stmt(c, ws->body, new_flags);
|
||||
|
||||
check_close_scope(c);
|
||||
case_end;
|
||||
|
||||
case_ast_node(rs, ForStmt, node);
|
||||
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||
check_open_scope(c, node);
|
||||
|
||||
Type *val = NULL;
|
||||
Type *idx = NULL;
|
||||
Entity *entities[2] = {0};
|
||||
isize entity_count = 0;
|
||||
|
||||
|
||||
if (rs->expr != NULL && rs->expr->kind == AstNode_IntervalExpr) {
|
||||
ast_node(ie, IntervalExpr, rs->expr);
|
||||
Operand x = {Addressing_Invalid};
|
||||
Operand y = {Addressing_Invalid};
|
||||
|
||||
check_expr(c, &x, ie->left);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
check_expr(c, &y, ie->right);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
convert_to_typed(c, &x, y.type, 0);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
convert_to_typed(c, &y, x.type, 0);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
convert_to_typed(c, &x, default_type(y.type), 0);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
convert_to_typed(c, &y, default_type(x.type), 0);
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
if (!are_types_identical(x.type, y.type)) {
|
||||
if (x.type != t_invalid &&
|
||||
y.type != t_invalid) {
|
||||
gbString xt = type_to_string(x.type);
|
||||
gbString yt = type_to_string(y.type);
|
||||
gbString expr_str = expr_to_string(x.expr);
|
||||
error(ie->op, "Mismatched types in interval expression `%s` : `%s` vs `%s`", expr_str, xt, yt);
|
||||
gb_string_free(expr_str);
|
||||
gb_string_free(yt);
|
||||
gb_string_free(xt);
|
||||
}
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
Type *type = x.type;
|
||||
Type *bt = base_type(base_enum_type(type));
|
||||
|
||||
if (!is_type_integer(bt) && !is_type_float(bt) && !is_type_pointer(bt)) {
|
||||
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
|
||||
goto skip_expr;
|
||||
}
|
||||
|
||||
if (x.mode == Addressing_Constant &&
|
||||
y.mode == Addressing_Constant) {
|
||||
ExactValue a = x.value;
|
||||
ExactValue b = y.value;
|
||||
|
||||
GB_ASSERT(are_types_identical(x.type, y.type));
|
||||
|
||||
bool ok = compare_exact_values(Token_Lt, a, b);
|
||||
if (!ok) {
|
||||
// TODO(bill): Better error message
|
||||
error(ie->op, "Invalid interval range");
|
||||
goto skip_expr;
|
||||
}
|
||||
}
|
||||
|
||||
add_type_and_value(&c->info, ie->left, x.mode, x.type, x.value);
|
||||
add_type_and_value(&c->info, ie->right, y.mode, y.type, y.value);
|
||||
val = type;
|
||||
idx = t_int;
|
||||
} else {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
check_expr(c, &operand, rs->expr);
|
||||
|
||||
if (operand.mode != Addressing_Invalid) {
|
||||
Type *t = base_type(type_deref(operand.type));
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
if (is_type_string(t)) {
|
||||
val = t_rune;
|
||||
idx = t_int;
|
||||
}
|
||||
break;
|
||||
case Type_Array:
|
||||
val = t->Array.elem;
|
||||
idx = t_int;
|
||||
break;
|
||||
case Type_Slice:
|
||||
val = t->Array.elem;
|
||||
idx = t_int;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (val == NULL) {
|
||||
gbString s = expr_to_string(operand.expr);
|
||||
error_node(node, "Cannot iterate over %s", s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
}
|
||||
check_stmt(c, fs->body, new_flags);
|
||||
|
||||
skip_expr:
|
||||
AstNode *lhs[2] = {rs->value, rs->index};
|
||||
Type * rhs[2] = {val, idx};
|
||||
|
||||
for (isize i = 0; i < 2; i++) {
|
||||
if (lhs[i] == NULL) {
|
||||
continue;
|
||||
}
|
||||
AstNode *name = lhs[i];
|
||||
Type * type = rhs[i];
|
||||
|
||||
Entity *entity = NULL;
|
||||
if (name->kind == AstNode_Ident) {
|
||||
Token token = name->Ident;
|
||||
String str = token.string;
|
||||
Entity *found = NULL;
|
||||
|
||||
if (str_ne(str, str_lit("_"))) {
|
||||
found = current_scope_lookup_entity(c->context.scope, str);
|
||||
}
|
||||
if (found == NULL) {
|
||||
entity = make_entity_variable(c->allocator, c->context.scope, token, type);
|
||||
entity->Variable.is_immutable = true;
|
||||
add_entity_definition(&c->info, name, entity);
|
||||
} else {
|
||||
TokenPos pos = found->token.pos;
|
||||
error(token,
|
||||
"Redeclaration of `%.*s` in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
error_node(name, "A variable declaration must be an identifier");
|
||||
}
|
||||
|
||||
if (entity == NULL) {
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
||||
}
|
||||
|
||||
entities[entity_count++] = entity;
|
||||
|
||||
if (type == NULL) {
|
||||
entity->type = t_invalid;
|
||||
entity->flags |= EntityFlag_Used;
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
add_entity(c, c->context.scope, entities[i]->identifier, entities[i]);
|
||||
}
|
||||
|
||||
check_stmt(c, rs->body, new_flags);
|
||||
|
||||
check_close_scope(c);
|
||||
case_end;
|
||||
@@ -626,21 +751,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
default_stmt = stmt;
|
||||
}
|
||||
} else {
|
||||
error(ast_node_token(stmt), "Invalid AST - expected case clause");
|
||||
error_node(stmt, "Invalid AST - expected case clause");
|
||||
}
|
||||
|
||||
if (default_stmt != NULL) {
|
||||
if (first_default != NULL) {
|
||||
TokenPos pos = ast_node_token(first_default).pos;
|
||||
error(ast_node_token(stmt),
|
||||
"multiple `default` clauses\n"
|
||||
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
|
||||
error_node(stmt,
|
||||
"multiple `default` clauses\n"
|
||||
"\tfirst at %.*s(%td:%td)",
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
first_default = default_stmt;
|
||||
}
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
MapTypeAndToken seen = {0}; // NOTE(bill): Multimap
|
||||
map_type_and_token_init(&seen, heap_allocator());
|
||||
@@ -695,11 +820,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
if (are_types_identical(y.type, tap.type)) {
|
||||
TokenPos pos = tap.token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error(ast_node_token(y.expr),
|
||||
"Duplicate case `%s`\n"
|
||||
"\tprevious case at %.*s(%td:%td)",
|
||||
expr_str,
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
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;
|
||||
@@ -744,8 +869,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
check_assignment(c, &x, NULL, str_lit("type match expression"));
|
||||
if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
|
||||
gbString str = type_to_string(x.type);
|
||||
error(ast_node_token(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;
|
||||
}
|
||||
@@ -763,15 +888,15 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
default_stmt = stmt;
|
||||
}
|
||||
} else {
|
||||
error(ast_node_token(stmt), "Invalid AST - expected case clause");
|
||||
error_node(stmt, "Invalid AST - expected case clause");
|
||||
}
|
||||
|
||||
if (default_stmt != NULL) {
|
||||
if (first_default != NULL) {
|
||||
TokenPos pos = ast_node_token(first_default).pos;
|
||||
error(ast_node_token(stmt),
|
||||
"multiple `default` clauses\n"
|
||||
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
|
||||
error_node(stmt,
|
||||
"Multiple `default` clauses\n"
|
||||
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
first_default = default_stmt;
|
||||
}
|
||||
@@ -816,8 +941,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
if (!tag_type_found) {
|
||||
gbString type_str = type_to_string(y.type);
|
||||
error(ast_node_token(y.expr),
|
||||
"Unknown tag type, got `%s`", type_str);
|
||||
error_node(y.expr,
|
||||
"Unknown tag type, got `%s`", type_str);
|
||||
gb_string_free(type_str);
|
||||
continue;
|
||||
}
|
||||
@@ -833,11 +958,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
if (found) {
|
||||
TokenPos pos = cc->token.pos;
|
||||
gbString expr_str = expr_to_string(y.expr);
|
||||
error(ast_node_token(y.expr),
|
||||
"Duplicate type case `%s`\n"
|
||||
"\tprevious type case at %.*s(%td:%td)",
|
||||
expr_str,
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
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;
|
||||
}
|
||||
@@ -856,6 +981,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tt);
|
||||
tag_var->flags |= EntityFlag_Used;
|
||||
tag_var->Variable.is_immutable = true;
|
||||
add_entity(c, c->context.scope, ms->var, tag_var);
|
||||
add_entity_use(c, ms->var, tag_var);
|
||||
}
|
||||
@@ -884,12 +1010,12 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
switch (token.kind) {
|
||||
case Token_break:
|
||||
if ((flags & Stmt_BreakAllowed) == 0) {
|
||||
error(token, "`break` only allowed in `for` or `match` statements");
|
||||
error(token, "`break` only allowed in loops or `match` statements");
|
||||
}
|
||||
break;
|
||||
case Token_continue:
|
||||
if ((flags & Stmt_ContinueAllowed) == 0) {
|
||||
error(token, "`continue` only allowed in `for` statements");
|
||||
error(token, "`continue` only allowed in loops");
|
||||
}
|
||||
break;
|
||||
case Token_fallthrough:
|
||||
@@ -928,19 +1054,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
switch (e->kind) {
|
||||
case Entity_TypeName: {
|
||||
Type *t = base_type(e->type);
|
||||
if (is_type_struct(t) || is_type_enum(t)) {
|
||||
for (isize i = 0; i < t->Record.other_field_count; i++) {
|
||||
Entity *f = t->Record.other_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 if (is_type_union(t)) {
|
||||
if (is_type_union(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);
|
||||
@@ -952,8 +1066,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
f->using_parent = e;
|
||||
}
|
||||
for (isize i = 0; i < t->Record.other_field_count; i++) {
|
||||
Entity *f = t->Record.other_fields[i];
|
||||
} 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);
|
||||
@@ -963,6 +1078,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
f->using_parent = e;
|
||||
}
|
||||
|
||||
} else {
|
||||
error(us->token, "`using` can be only applied to `union` or `enum` type entities");
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -1040,14 +1158,24 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
case_end;
|
||||
|
||||
case_ast_node(vd, VarDecl, us->node);
|
||||
case_ast_node(vd, ValueDecl, us->node);
|
||||
if (!vd->is_var) {
|
||||
error_node(us->node, "`using` can only be applied to a variable declaration");
|
||||
return;
|
||||
}
|
||||
|
||||
if (vd->names.count > 1 && vd->type != NULL) {
|
||||
error(us->token, "`using` can only be applied to one variable of the same type");
|
||||
}
|
||||
check_var_decl_node(c, us->node);
|
||||
|
||||
check_var_decl_node(c, vd);
|
||||
|
||||
for_array(name_index, vd->names) {
|
||||
AstNode *item = vd->names.e[name_index];
|
||||
if (item->kind != AstNode_Ident) {
|
||||
// TODO(bill): Handle error here???
|
||||
continue;
|
||||
}
|
||||
ast_node(i, Ident, item);
|
||||
String name = i->string;
|
||||
Entity *e = scope_lookup_entity(c->context.scope, name);
|
||||
@@ -1073,7 +1201,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
}
|
||||
case_end;
|
||||
|
||||
|
||||
default:
|
||||
error(us->token, "Invalid AST: Using Statement");
|
||||
break;
|
||||
@@ -1098,33 +1225,75 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
||||
case_end;
|
||||
|
||||
|
||||
case_ast_node(vd, ValueDecl, node);
|
||||
if (vd->is_var) {
|
||||
isize entity_count = vd->names.count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
|
||||
for_array(i, vd->names) {
|
||||
AstNode *name = vd->names.e[i];
|
||||
Entity *entity = NULL;
|
||||
if (name->kind == AstNode_Ident) {
|
||||
Token token = name->Ident;
|
||||
String str = token.string;
|
||||
Entity *found = NULL;
|
||||
// NOTE(bill): Ignore assignments to `_`
|
||||
if (str_ne(str, str_lit("_"))) {
|
||||
found = current_scope_lookup_entity(c->context.scope, str);
|
||||
}
|
||||
if (found == NULL) {
|
||||
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
|
||||
add_entity_definition(&c->info, name, entity);
|
||||
} else {
|
||||
TokenPos pos = found->token.pos;
|
||||
error(token,
|
||||
"Redeclaration of `%.*s` in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(str), LIT(pos.file), pos.line, pos.column);
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
error_node(name, "A variable declaration must be an identifier");
|
||||
}
|
||||
if (entity == NULL) {
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
|
||||
}
|
||||
entities[entity_index++] = entity;
|
||||
}
|
||||
|
||||
Type *init_type = NULL;
|
||||
if (vd->type) {
|
||||
init_type = check_type_extra(c, vd->type, NULL);
|
||||
if (init_type == NULL) {
|
||||
init_type = t_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
GB_ASSERT(e != NULL);
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
continue;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
case_ast_node(vd, VarDecl, node);
|
||||
check_var_decl_node(c, node);
|
||||
case_end;
|
||||
if (e->type == NULL)
|
||||
e->type = init_type;
|
||||
}
|
||||
check_arity_match(c, vd);
|
||||
|
||||
case_ast_node(cd, ConstDecl, node);
|
||||
// NOTE(bill): Handled elsewhere
|
||||
case_end;
|
||||
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
|
||||
|
||||
case_ast_node(td, TypeDecl, node);
|
||||
// NOTE(bill): Handled elsewhere
|
||||
case_end;
|
||||
|
||||
case_ast_node(pd, ProcDecl, node);
|
||||
// NOTE(bill): This must be handled here so it has access to the parent scope stuff
|
||||
// e.g. using
|
||||
Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL);
|
||||
e->identifier = pd->name;
|
||||
|
||||
DeclInfo *d = make_declaration_info(c->allocator, e->scope);
|
||||
d->proc_decl = node;
|
||||
|
||||
add_entity_and_decl_info(c, pd->name, e, d);
|
||||
check_entity_decl(c, e, d, NULL, NULL);
|
||||
for_array(i, vd->names) {
|
||||
if (entities[i] != NULL) {
|
||||
add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// NOTE(bill): Handled elsewhere
|
||||
}
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
|
||||
+344
-149
@@ -11,8 +11,8 @@ typedef enum BasicKind {
|
||||
Basic_u32,
|
||||
Basic_i64,
|
||||
Basic_u64,
|
||||
Basic_i128,
|
||||
Basic_u128,
|
||||
// Basic_i128,
|
||||
// Basic_u128,
|
||||
// Basic_f16,
|
||||
Basic_f32,
|
||||
Basic_f64,
|
||||
@@ -62,9 +62,9 @@ typedef enum TypeRecordKind {
|
||||
TypeRecord_Invalid,
|
||||
|
||||
TypeRecord_Struct,
|
||||
TypeRecord_Enum,
|
||||
TypeRecord_RawUnion,
|
||||
TypeRecord_Union, // Tagged
|
||||
TypeRecord_Enum,
|
||||
|
||||
TypeRecord_Count,
|
||||
} TypeRecordKind;
|
||||
@@ -78,25 +78,13 @@ typedef struct TypeRecord {
|
||||
i32 field_count; // == offset_count is struct
|
||||
AstNode *node;
|
||||
|
||||
union { // NOTE(bill): Reduce size_of Type
|
||||
struct { // enum only
|
||||
Type * enum_base; // Default is `int`
|
||||
Entity * enum_count;
|
||||
Entity * min_value;
|
||||
Entity * max_value;
|
||||
};
|
||||
struct { // struct only
|
||||
i64 * struct_offsets;
|
||||
bool struct_are_offsets_set;
|
||||
bool struct_is_packed;
|
||||
bool struct_is_ordered;
|
||||
Entity **fields_in_src_order; // Entity_Variable
|
||||
};
|
||||
};
|
||||
i64 * struct_offsets;
|
||||
bool struct_are_offsets_set;
|
||||
bool struct_is_packed;
|
||||
bool struct_is_ordered;
|
||||
Entity **fields_in_src_order; // Entity_Variable
|
||||
|
||||
// Entity_Constant or Entity_TypeName
|
||||
Entity **other_fields;
|
||||
i32 other_field_count;
|
||||
Type * enum_base_type;
|
||||
} TypeRecord;
|
||||
|
||||
#define TYPE_KINDS \
|
||||
@@ -125,8 +113,11 @@ typedef struct TypeRecord {
|
||||
i32 param_count; \
|
||||
i32 result_count; \
|
||||
bool variadic; \
|
||||
ProcCallingConvention calling_convention; \
|
||||
})
|
||||
|
||||
|
||||
|
||||
typedef enum TypeKind {
|
||||
Type_Invalid,
|
||||
#define TYPE_KIND(k, ...) GB_JOIN2(Type_, k),
|
||||
@@ -153,6 +144,7 @@ typedef struct Type {
|
||||
TYPE_KINDS
|
||||
#undef TYPE_KIND
|
||||
};
|
||||
bool failure;
|
||||
} Type;
|
||||
|
||||
// NOTE(bill): Internal sizes of certain types
|
||||
@@ -204,8 +196,8 @@ gb_global Type basic_types[] = {
|
||||
{Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}},
|
||||
{Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}},
|
||||
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}},
|
||||
{Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}},
|
||||
{Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
|
||||
// {Type_Basic, {Basic_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")}},
|
||||
@@ -238,8 +230,8 @@ 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_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];
|
||||
@@ -299,14 +291,30 @@ gbString type_to_string(Type *type);
|
||||
|
||||
Type *base_type(Type *t) {
|
||||
for (;;) {
|
||||
if (t == NULL || t->kind != Type_Named) {
|
||||
if (t == NULL) {
|
||||
break;
|
||||
}
|
||||
if (t->kind != Type_Named) {
|
||||
break;
|
||||
}
|
||||
if (t == t->Named.base) {
|
||||
return t_invalid;
|
||||
}
|
||||
t = t->Named.base;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *base_enum_type(Type *t) {
|
||||
Type *bt = base_type(t);
|
||||
if (bt != NULL &&
|
||||
bt->kind == Type_Record &&
|
||||
bt->Record.kind == TypeRecord_Enum) {
|
||||
return bt->Record.enum_base_type;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
void set_base_type(Type *t, Type *base) {
|
||||
if (t && t->kind == Type_Named) {
|
||||
t->Named.base = base;
|
||||
@@ -386,6 +394,8 @@ Type *make_type_enum(gbAllocator a) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Type *make_type_named(gbAllocator a, String name, Type *base, Entity *type_name) {
|
||||
Type *t = alloc_type(a, Type_Named);
|
||||
t->Named.name = name;
|
||||
@@ -399,7 +409,7 @@ Type *make_type_tuple(gbAllocator a) {
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count, bool variadic) {
|
||||
Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_count, Type *results, isize result_count, bool variadic, ProcCallingConvention calling_convention) {
|
||||
Type *t = alloc_type(a, Type_Proc);
|
||||
|
||||
if (variadic) {
|
||||
@@ -420,6 +430,7 @@ Type *make_type_proc(gbAllocator a, Scope *scope, Type *params, isize param_coun
|
||||
t->Proc.results = results;
|
||||
t->Proc.result_count = result_count;
|
||||
t->Proc.variadic = variadic;
|
||||
t->Proc.calling_convention = calling_convention;
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -435,15 +446,6 @@ Type *type_deref(Type *t) {
|
||||
return t;
|
||||
}
|
||||
|
||||
Type *get_enum_base_type(Type *t) {
|
||||
Type *bt = base_type(t);
|
||||
if (bt->kind == Type_Record && bt->Record.kind == TypeRecord_Enum) {
|
||||
GB_ASSERT(bt->Record.enum_base != NULL);
|
||||
return bt->Record.enum_base;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
bool is_type_named(Type *t) {
|
||||
if (t->kind == Type_Basic) {
|
||||
return true;
|
||||
@@ -490,6 +492,9 @@ bool is_type_string(Type *t) {
|
||||
}
|
||||
bool is_type_typed(Type *t) {
|
||||
t = base_type(t);
|
||||
if (t == NULL) {
|
||||
return false;
|
||||
}
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Untyped) == 0;
|
||||
}
|
||||
@@ -503,7 +508,7 @@ bool is_type_untyped(Type *t) {
|
||||
return false;
|
||||
}
|
||||
bool is_type_ordered(Type *t) {
|
||||
t = base_type(get_enum_base_type(t));
|
||||
t = base_type(t);
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_Ordered) != 0;
|
||||
}
|
||||
@@ -513,13 +518,10 @@ bool is_type_ordered(Type *t) {
|
||||
return false;
|
||||
}
|
||||
bool is_type_constant_type(Type *t) {
|
||||
t = base_type(t);
|
||||
t = base_type(base_enum_type(t));
|
||||
if (t->kind == Type_Basic) {
|
||||
return (t->Basic.flags & BasicFlag_ConstantType) != 0;
|
||||
}
|
||||
if (t->kind == Type_Record) {
|
||||
return t->Record.kind == TypeRecord_Enum;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool is_type_float(Type *t) {
|
||||
@@ -610,10 +612,6 @@ Type *base_vector_type(Type *t) {
|
||||
}
|
||||
|
||||
|
||||
bool is_type_enum(Type *t) {
|
||||
t = base_type(t);
|
||||
return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum);
|
||||
}
|
||||
bool is_type_struct(Type *t) {
|
||||
t = base_type(t);
|
||||
return (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct);
|
||||
@@ -626,6 +624,11 @@ bool is_type_raw_union(Type *t) {
|
||||
t = base_type(t);
|
||||
return (t->kind == Type_Record && t->Record.kind == TypeRecord_RawUnion);
|
||||
}
|
||||
bool is_type_enum(Type *t) {
|
||||
t = base_type(t);
|
||||
return (t->kind == Type_Record && t->Record.kind == TypeRecord_Enum);
|
||||
}
|
||||
|
||||
|
||||
bool is_type_any(Type *t) {
|
||||
t = base_type(t);
|
||||
@@ -648,23 +651,18 @@ bool type_has_nil(Type *t) {
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
return is_type_rawptr(t);
|
||||
|
||||
case Type_Tuple:
|
||||
return false;
|
||||
|
||||
case Type_Record:
|
||||
switch (t->Record.kind) {
|
||||
case TypeRecord_Enum:
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case Type_Slice:
|
||||
case Type_Proc:
|
||||
case Type_Pointer:
|
||||
case Type_Maybe:
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool is_type_comparable(Type *t) {
|
||||
t = base_type(get_enum_base_type(t));
|
||||
t = base_type(t);
|
||||
switch (t->kind) {
|
||||
case Type_Basic:
|
||||
return t->kind != Basic_UntypedNil;
|
||||
@@ -678,12 +676,13 @@ bool is_type_comparable(Type *t) {
|
||||
return false;
|
||||
}
|
||||
} else if (is_type_enum(t)) {
|
||||
return is_type_comparable(t->Record.enum_base);
|
||||
return is_type_comparable(base_enum_type(t));
|
||||
}
|
||||
return false;
|
||||
} break;
|
||||
case Type_Array:
|
||||
return is_type_comparable(t->Array.elem);
|
||||
return false;
|
||||
// return is_type_comparable(t->Array.elem);
|
||||
case Type_Vector:
|
||||
return is_type_comparable(t->Vector.elem);
|
||||
case Type_Proc:
|
||||
@@ -693,8 +692,9 @@ bool is_type_comparable(Type *t) {
|
||||
}
|
||||
|
||||
bool are_types_identical(Type *x, Type *y) {
|
||||
if (x == y)
|
||||
if (x == y) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((x == NULL && y != NULL) ||
|
||||
(x != NULL && y == NULL)) {
|
||||
@@ -747,10 +747,8 @@ bool are_types_identical(Type *x, Type *y) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case TypeRecord_Enum:
|
||||
// NOTE(bill): Each enum is unique
|
||||
return x == y;
|
||||
return x == y; // NOTE(bill): All enums are unique
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -789,7 +787,8 @@ bool are_types_identical(Type *x, Type *y) {
|
||||
|
||||
case Type_Proc:
|
||||
if (y->kind == Type_Proc) {
|
||||
return are_types_identical(x->Proc.params, y->Proc.params) &&
|
||||
return x->Proc.calling_convention == y->Proc.calling_convention &&
|
||||
are_types_identical(x->Proc.params, y->Proc.params) &&
|
||||
are_types_identical(x->Proc.results, y->Proc.results);
|
||||
}
|
||||
break;
|
||||
@@ -801,6 +800,9 @@ bool are_types_identical(Type *x, Type *y) {
|
||||
|
||||
|
||||
Type *default_type(Type *type) {
|
||||
if (type == NULL) {
|
||||
return t_invalid;
|
||||
}
|
||||
if (type->kind == Type_Basic) {
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_UntypedBool: return t_bool;
|
||||
@@ -813,6 +815,65 @@ Type *default_type(Type *type) {
|
||||
return type;
|
||||
}
|
||||
|
||||
// NOTE(bill): Valid Compile time execution #run type
|
||||
bool is_type_cte_safe(Type *type) {
|
||||
type = default_type(base_type(type));
|
||||
switch (type->kind) {
|
||||
case Type_Basic:
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_rawptr:
|
||||
case Basic_any:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case Type_Pointer:
|
||||
return false;
|
||||
|
||||
case Type_Array:
|
||||
return is_type_cte_safe(type->Array.elem);
|
||||
|
||||
case Type_Vector: // NOTE(bill): This should always to be true but this is for sanity reasons
|
||||
return is_type_cte_safe(type->Vector.elem);
|
||||
|
||||
case Type_Slice:
|
||||
return false;
|
||||
|
||||
case Type_Maybe:
|
||||
return is_type_cte_safe(type->Maybe.elem);
|
||||
|
||||
case Type_Record: {
|
||||
if (type->Record.kind != TypeRecord_Struct) {
|
||||
return false;
|
||||
}
|
||||
for (isize i = 0; i < type->Record.field_count; i++) {
|
||||
Entity *v = type->Record.fields[i];
|
||||
if (!is_type_cte_safe(v->type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case Type_Tuple: {
|
||||
for (isize i = 0; i < type->Tuple.variable_count; i++) {
|
||||
Entity *v = type->Tuple.variables[i];
|
||||
if (!is_type_cte_safe(v->type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case Type_Proc:
|
||||
// TODO(bill): How should I handle procedures in the CTE stage?
|
||||
// return type->Proc.calling_convention == ProcCC_Odin;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -930,7 +991,6 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
|
||||
} 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);
|
||||
@@ -945,14 +1005,6 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -974,34 +1026,20 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (is_type_enum(type)) {
|
||||
for (isize i = 0; i < type->Record.field_count; i++) {
|
||||
Entity *f = type->Record.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Constant);
|
||||
String str = f->token.string;
|
||||
|
||||
for (isize i = 0; i < type->Record.other_field_count; i++) {
|
||||
Entity *f = type->Record.other_fields[i];
|
||||
GB_ASSERT(f->kind != Entity_Variable);
|
||||
String str = f->token.string;
|
||||
|
||||
if (str_eq(field_name, str)) {
|
||||
sel.entity = f;
|
||||
selection_add_index(&sel, i);
|
||||
return sel;
|
||||
if (str_eq(field_name, str)) {
|
||||
sel.entity = f;
|
||||
selection_add_index(&sel, i);
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_enum(type)) {
|
||||
if (str_eq(field_name, str_lit("count"))) {
|
||||
sel.entity = type->Record.enum_count;
|
||||
return sel;
|
||||
} else if (str_eq(field_name, str_lit("min_value"))) {
|
||||
sel.entity = type->Record.min_value;
|
||||
return sel;
|
||||
} else if (str_eq(field_name, str_lit("max_value"))) {
|
||||
sel.entity = type->Record.max_value;
|
||||
return sel;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (!is_type_enum(type) && !is_type_union(type)) {
|
||||
} else if (!is_type_union(type)) {
|
||||
for (isize i = 0; i < type->Record.field_count; i++) {
|
||||
Entity *f = type->Record.fields[i];
|
||||
GB_ASSERT(f->kind == Entity_Variable && f->flags & EntityFlag_Field);
|
||||
@@ -1033,11 +1071,71 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
|
||||
}
|
||||
|
||||
|
||||
typedef struct TypePath {
|
||||
Array(Type *) path; // Entity_TypeName;
|
||||
bool failure;
|
||||
} TypePath;
|
||||
|
||||
void type_path_init(TypePath *tp) {
|
||||
// TODO(bill): Use an allocator that uses a backing array if it can and then use alternative allocator when exhausted
|
||||
array_init(&tp->path, heap_allocator());
|
||||
}
|
||||
|
||||
void type_path_free(TypePath *tp) {
|
||||
array_free(&tp->path);
|
||||
}
|
||||
|
||||
TypePath *type_path_push(TypePath *tp, Type *t) {
|
||||
GB_ASSERT(tp != NULL);
|
||||
|
||||
for_array(i, tp->path) {
|
||||
if (tp->path.e[i] == t) {
|
||||
// TODO(bill):
|
||||
GB_ASSERT(is_type_named(t));
|
||||
Entity *e = t->Named.type_name;
|
||||
error(e->token, "Illegal declaration cycle of `%.*s`", LIT(t->Named.name));
|
||||
// NOTE(bill): Print cycle, if it's deep enough
|
||||
for (isize j = 0; j < tp->path.count; j++) {
|
||||
Type *t = tp->path.e[j];
|
||||
GB_ASSERT(is_type_named(t));
|
||||
Entity *e = t->Named.type_name;
|
||||
error(e->token, "\t%.*s refers to", LIT(t->Named.name));
|
||||
}
|
||||
// NOTE(bill): This will only print if the path count > 1
|
||||
error(e->token, "\t%.*s", LIT(t->Named.name));
|
||||
tp->failure = true;
|
||||
t->failure = true;
|
||||
|
||||
// NOTE(bill): Just quit immediately
|
||||
// TODO(bill): Try and solve this gracefully
|
||||
// gb_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!tp->failure) {
|
||||
array_add(&tp->path, t);
|
||||
}
|
||||
return tp;
|
||||
}
|
||||
|
||||
void type_path_pop(TypePath *tp) {
|
||||
if (tp != NULL) {
|
||||
array_pop(&tp->path);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define FAILURE_SIZE 0
|
||||
#define FAILURE_ALIGNMENT 0
|
||||
|
||||
|
||||
i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t);
|
||||
i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t);
|
||||
i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, i64 index);
|
||||
|
||||
i64 type_size_of_internal (BaseTypeSizes s, gbAllocator allocator, Type *t, TypePath *path);
|
||||
i64 type_align_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, TypePath *path);
|
||||
|
||||
i64 align_formula(i64 size, i64 align) {
|
||||
if (align > 0) {
|
||||
i64 result = size + align-1;
|
||||
@@ -1046,14 +1144,50 @@ i64 align_formula(i64 size, i64 align) {
|
||||
return size;
|
||||
}
|
||||
|
||||
i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
i64 size;
|
||||
TypePath path = {0};
|
||||
type_path_init(&path);
|
||||
size = type_size_of_internal(s, allocator, t, &path);
|
||||
type_path_free(&path);
|
||||
return size;
|
||||
}
|
||||
|
||||
i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
i64 align;
|
||||
TypePath path = {0};
|
||||
type_path_init(&path);
|
||||
align = type_align_of_internal(s, allocator, t, &path);
|
||||
type_path_free(&path);
|
||||
return align;
|
||||
}
|
||||
|
||||
|
||||
i64 type_align_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, TypePath *path) {
|
||||
if (t->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
t = base_type(t);
|
||||
|
||||
switch (t->kind) {
|
||||
case Type_Array:
|
||||
return type_align_of(s, allocator, t->Array.elem);
|
||||
case Type_Array: {
|
||||
Type *elem = t->Array.elem;
|
||||
type_path_push(path, elem);
|
||||
if (path->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
i64 align = type_align_of_internal(s, allocator, t->Array.elem, path);
|
||||
type_path_pop(path);
|
||||
return align;
|
||||
}
|
||||
case Type_Vector: {
|
||||
i64 size = type_size_of(s, allocator, t->Vector.elem);
|
||||
Type *elem = t->Vector.elem;
|
||||
type_path_push(path, elem);
|
||||
if (path->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
i64 size = type_size_of_internal(s, allocator, t->Vector.elem, path);
|
||||
type_path_pop(path);
|
||||
i64 count = gb_max(prev_pow2(t->Vector.count), 1);
|
||||
i64 total = size * count;
|
||||
return gb_clamp(total, 1, s.max_align);
|
||||
@@ -1062,7 +1196,7 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
case Type_Tuple: {
|
||||
i64 max = 1;
|
||||
for (isize i = 0; i < t->Tuple.variable_count; i++) {
|
||||
i64 align = type_align_of(s, allocator, t->Tuple.variables[i]->type);
|
||||
i64 align = type_align_of_internal(s, allocator, t->Tuple.variables[i]->type, path);
|
||||
if (max < align) {
|
||||
max = align;
|
||||
}
|
||||
@@ -1070,8 +1204,16 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
return max;
|
||||
} break;
|
||||
|
||||
case Type_Maybe:
|
||||
return gb_max(type_align_of(s, allocator, t->Maybe.elem), type_align_of(s, allocator, t_bool));
|
||||
case Type_Maybe: {
|
||||
Type *elem = t->Maybe.elem;
|
||||
type_path_push(path, elem);
|
||||
if (path->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
i64 align = gb_max(type_align_of_internal(s, allocator, t->Maybe.elem, path), type_align_of_internal(s, allocator, t_bool, path));
|
||||
type_path_pop(path);
|
||||
return align;
|
||||
}
|
||||
|
||||
case Type_Record: {
|
||||
switch (t->Record.kind) {
|
||||
@@ -1080,23 +1222,41 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
// TODO(bill): What is this supposed to be?
|
||||
if (t->Record.struct_is_packed) {
|
||||
i64 max = s.word_size;
|
||||
for (isize i = 1; i < t->Record.field_count; i++) {
|
||||
// NOTE(bill): field zero is null
|
||||
i64 align = type_align_of(s, allocator, t->Record.fields[i]->type);
|
||||
for (isize i = 0; i < t->Record.field_count; i++) {
|
||||
Type *field_type = t->Record.fields[i]->type;
|
||||
type_path_push(path, field_type);
|
||||
if (path->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
i64 align = type_align_of_internal(s, allocator, field_type, path);
|
||||
type_path_pop(path);
|
||||
if (max < align) {
|
||||
max = align;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
return type_align_of(s, allocator, t->Record.fields[0]->type);
|
||||
Type *field_type = t->Record.fields[0]->type;
|
||||
type_path_push(path, field_type);
|
||||
if (path->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
i64 align = type_align_of_internal(s, allocator, field_type, path);
|
||||
type_path_pop(path);
|
||||
return align;
|
||||
}
|
||||
break;
|
||||
case TypeRecord_Union: {
|
||||
i64 max = 1;
|
||||
// NOTE(bill): field zero is null
|
||||
for (isize i = 1; i < t->Record.field_count; i++) {
|
||||
// NOTE(bill): field zero is null
|
||||
i64 align = type_align_of(s, allocator, t->Record.fields[i]->type);
|
||||
Type *field_type = t->Record.fields[i]->type;
|
||||
type_path_push(path, field_type);
|
||||
if (path->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
i64 align = type_align_of_internal(s, allocator, field_type, path);
|
||||
type_path_pop(path);
|
||||
if (max < align) {
|
||||
max = align;
|
||||
}
|
||||
@@ -1106,15 +1266,19 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
case TypeRecord_RawUnion: {
|
||||
i64 max = 1;
|
||||
for (isize i = 0; i < t->Record.field_count; i++) {
|
||||
i64 align = type_align_of(s, allocator, t->Record.fields[i]->type);
|
||||
Type *field_type = t->Record.fields[i]->type;
|
||||
type_path_push(path, field_type);
|
||||
if (path->failure) {
|
||||
return FAILURE_ALIGNMENT;
|
||||
}
|
||||
i64 align = type_align_of_internal(s, allocator, field_type, path);
|
||||
type_path_pop(path);
|
||||
if (max < align) {
|
||||
max = align;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
} break;
|
||||
case TypeRecord_Enum:
|
||||
return type_align_of(s, allocator, t->Record.enum_base);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@@ -1122,7 +1286,7 @@ i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
// return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.max_align);
|
||||
// NOTE(bill): Things that are bigger than s.word_size, are actually comprised of smaller types
|
||||
// TODO(bill): Is this correct for 128-bit types (integers)?
|
||||
return gb_clamp(next_pow2(type_size_of(s, allocator, t)), 1, s.word_size);
|
||||
return gb_clamp(next_pow2(type_size_of_internal(s, allocator, t, path)), 1, s.word_size);
|
||||
}
|
||||
|
||||
i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields, isize field_count, bool is_packed) {
|
||||
@@ -1165,7 +1329,10 @@ bool type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
i64 type_size_of_internal(BaseTypeSizes s, gbAllocator allocator, Type *t, TypePath *path) {
|
||||
if (t->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
t = base_type(t);
|
||||
switch (t->kind) {
|
||||
case Type_Basic: {
|
||||
@@ -1185,52 +1352,66 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
} break;
|
||||
|
||||
case Type_Array: {
|
||||
i64 count = t->Array.count;
|
||||
i64 count, align, size, alignment;
|
||||
count = t->Array.count;
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
i64 align = type_align_of(s, allocator, t->Array.elem);
|
||||
i64 size = type_size_of(s, allocator, t->Array.elem);
|
||||
i64 alignment = align_formula(size, align);
|
||||
align = type_align_of_internal(s, allocator, t->Array.elem, path);
|
||||
if (path->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
size = type_size_of_internal(s, allocator, t->Array.elem, path);
|
||||
alignment = align_formula(size, align);
|
||||
return alignment*(count-1) + size;
|
||||
} break;
|
||||
|
||||
case Type_Vector: {
|
||||
i64 count = t->Vector.count;
|
||||
i64 count, bit_size, total_size_in_bits, total_size;
|
||||
count = t->Vector.count;
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
// i64 align = type_align_of(s, allocator, t->Vector.elem);
|
||||
i64 bit_size = 8*type_size_of(s, allocator, t->Vector.elem);
|
||||
type_path_push(path, t->Vector.elem);
|
||||
if (path->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
bit_size = 8*type_size_of_internal(s, allocator, t->Vector.elem, path);
|
||||
type_path_pop(path);
|
||||
if (is_type_boolean(t->Vector.elem)) {
|
||||
bit_size = 1; // NOTE(bill): LLVM can store booleans as 1 bit because a boolean _is_ an `i1`
|
||||
// Silly LLVM spec
|
||||
}
|
||||
i64 total_size_in_bits = bit_size * count;
|
||||
i64 total_size = (total_size_in_bits+7)/8;
|
||||
total_size_in_bits = bit_size * count;
|
||||
total_size = (total_size_in_bits+7)/8;
|
||||
return total_size;
|
||||
} break;
|
||||
|
||||
|
||||
case Type_Slice: // ptr + len + cap
|
||||
return 3 * s.word_size;
|
||||
case Type_Slice: // ptr + count
|
||||
return 2 * s.word_size;
|
||||
|
||||
case Type_Maybe: { // value + bool
|
||||
i64 align, size;
|
||||
Type *elem = t->Maybe.elem;
|
||||
i64 align = type_align_of(s, allocator, elem);
|
||||
i64 size = align_formula(type_size_of(s, allocator, elem), align);
|
||||
size += type_size_of(s, allocator, t_bool);
|
||||
align = type_align_of_internal(s, allocator, elem, path);
|
||||
if (path->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
size = align_formula(type_size_of_internal(s, allocator, elem, path), align);
|
||||
size += type_size_of_internal(s, allocator, t_bool, path);
|
||||
return align_formula(size, align);
|
||||
}
|
||||
|
||||
case Type_Tuple: {
|
||||
i64 count = t->Tuple.variable_count;
|
||||
i64 count, align, size;
|
||||
count = t->Tuple.variable_count;
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
align = type_align_of_internal(s, allocator, t, path);
|
||||
type_set_offsets(s, allocator, t);
|
||||
i64 size = t->Tuple.offsets[count-1] + type_size_of(s, allocator, t->Tuple.variables[count-1]->type);
|
||||
i64 align = type_align_of(s, allocator, t);
|
||||
size = t->Tuple.offsets[count-1] + type_size_of_internal(s, allocator, t->Tuple.variables[count-1]->type, path);
|
||||
return align_formula(size, align);
|
||||
} break;
|
||||
|
||||
@@ -1241,46 +1422,51 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
|
||||
if (count == 0) {
|
||||
return 0;
|
||||
}
|
||||
i64 align = type_align_of_internal(s, allocator, t, path);
|
||||
if (path->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
type_set_offsets(s, allocator, t);
|
||||
i64 size = t->Record.struct_offsets[count-1] + type_size_of(s, allocator, t->Record.fields[count-1]->type);
|
||||
i64 align = type_align_of(s, allocator, t);
|
||||
i64 size = t->Record.struct_offsets[count-1] + type_size_of_internal(s, allocator, t->Record.fields[count-1]->type, path);
|
||||
return align_formula(size, align);
|
||||
} break;
|
||||
|
||||
case TypeRecord_Union: {
|
||||
i64 count = t->Record.field_count;
|
||||
i64 align = type_align_of_internal(s, allocator, t, path);
|
||||
if (path->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
i64 max = 0;
|
||||
// NOTE(bill): Zeroth field is invalid
|
||||
for (isize i = 1; i < count; i++) {
|
||||
i64 size = type_size_of(s, allocator, t->Record.fields[i]->type);
|
||||
i64 size = type_size_of_internal(s, allocator, t->Record.fields[i]->type, path);
|
||||
if (max < size) {
|
||||
max = size;
|
||||
}
|
||||
}
|
||||
// NOTE(bill): Align to int
|
||||
i64 align = type_align_of(s, allocator, t);
|
||||
isize size = align_formula(max, s.word_size);
|
||||
size += type_size_of(s, allocator, t_int);
|
||||
size += type_size_of_internal(s, allocator, t_int, path);
|
||||
return align_formula(size, align);
|
||||
} break;
|
||||
|
||||
case TypeRecord_RawUnion: {
|
||||
i64 count = t->Record.field_count;
|
||||
i64 align = type_align_of_internal(s, allocator, t, path);
|
||||
if (path->failure) {
|
||||
return FAILURE_SIZE;
|
||||
}
|
||||
i64 max = 0;
|
||||
for (isize i = 0; i < count; i++) {
|
||||
i64 size = type_size_of(s, allocator, t->Record.fields[i]->type);
|
||||
i64 size = type_size_of_internal(s, allocator, t->Record.fields[i]->type, path);
|
||||
if (max < size) {
|
||||
max = size;
|
||||
}
|
||||
}
|
||||
// TODO(bill): Is this how it should work?
|
||||
i64 align = type_align_of(s, allocator, t);
|
||||
return align_formula(max, align);
|
||||
} break;
|
||||
|
||||
case TypeRecord_Enum: {
|
||||
return type_size_of(s, allocator, t->Record.enum_base);
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@@ -1370,7 +1556,7 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
break;
|
||||
|
||||
case Type_Vector:
|
||||
str = gb_string_appendc(str, gb_bprintf("{%td}", type->Vector.count));
|
||||
str = gb_string_appendc(str, gb_bprintf("[vector %td]", type->Vector.count));
|
||||
str = write_type_to_string(str, type->Vector.elem);
|
||||
break;
|
||||
|
||||
@@ -1431,11 +1617,6 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
}
|
||||
str = gb_string_appendc(str, "}");
|
||||
break;
|
||||
|
||||
case TypeRecord_Enum:
|
||||
str = gb_string_appendc(str, "enum ");
|
||||
str = write_type_to_string(str, type->Record.enum_base);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -1472,6 +1653,20 @@ gbString write_type_to_string(gbString str, Type *type) {
|
||||
str = gb_string_appendc(str, " -> ");
|
||||
str = write_type_to_string(str, type->Proc.results);
|
||||
}
|
||||
switch (type->Proc.calling_convention) {
|
||||
case ProcCC_Odin:
|
||||
// str = gb_string_appendc(str, " #cc_odin");
|
||||
break;
|
||||
case ProcCC_C:
|
||||
str = gb_string_appendc(str, " #cc_c");
|
||||
break;
|
||||
case ProcCC_Std:
|
||||
str = gb_string_appendc(str, " #cc_std");
|
||||
break;
|
||||
case ProcCC_Fast:
|
||||
str = gb_string_appendc(str, " #cc_fast");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+2
-61
@@ -13,66 +13,6 @@ gb_global String global_module_path = {0};
|
||||
gb_global bool global_module_path_set = false;
|
||||
|
||||
|
||||
String get_module_dir() {
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
Array(wchar_t) path_buf;
|
||||
array_init_count(&path_buf, heap_allocator(), 300);
|
||||
|
||||
isize len = 0;
|
||||
for (;;) {
|
||||
len = GetModuleFileNameW(NULL, &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);
|
||||
}
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
|
||||
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
|
||||
GetModuleFileNameW(NULL, text, len);
|
||||
String path = string16_to_string(heap_allocator(), make_string16(text, len));
|
||||
for (isize 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;
|
||||
}
|
||||
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
String16 string16 = string_to_string16(string_buffer_allocator, s);
|
||||
String result = {0};
|
||||
|
||||
DWORD len = GetFullPathNameW(string16.text, 0, NULL, NULL);
|
||||
if (len != 0) {
|
||||
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
GetFullPathNameW(string16.text, len, text, NULL);
|
||||
text[len] = 0;
|
||||
result = string16_to_string(a, make_string16(text, len));
|
||||
}
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
i64 next_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
@@ -141,8 +81,9 @@ i16 f32_to_f16(f32 value) {
|
||||
if (e > 30) {
|
||||
float volatile f = 1e12f;
|
||||
int j;
|
||||
for (j = 0; j < 10; j++)
|
||||
for (j = 0; j < 10; j++) {
|
||||
f *= f; /* NOTE(bill): Cause overflow */
|
||||
}
|
||||
|
||||
return cast(i16)(s | 0x7c00);
|
||||
}
|
||||
|
||||
+21
-20
@@ -21,7 +21,7 @@ typedef enum ExactValueKind {
|
||||
typedef struct ExactValue {
|
||||
ExactValueKind kind;
|
||||
union {
|
||||
bool value_bool;
|
||||
bool value_bool;
|
||||
String value_string;
|
||||
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
|
||||
f64 value_float;
|
||||
@@ -58,7 +58,7 @@ ExactValue make_exact_value_integer_from_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
ExactValue result = {ExactValue_Integer};
|
||||
i32 base = 10;
|
||||
if (string.text[0] == '0') {
|
||||
if (string.len > 2 && string.text[0] == '0') {
|
||||
switch (string.text[1]) {
|
||||
case 'b': base = 2; break;
|
||||
case 'o': base = 8; break;
|
||||
@@ -148,8 +148,8 @@ ExactValue exact_value_to_float(ExactValue v) {
|
||||
}
|
||||
|
||||
|
||||
ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) {
|
||||
switch (op.kind) {
|
||||
ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
|
||||
switch (op) {
|
||||
case Token_Add: {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid:
|
||||
@@ -207,7 +207,7 @@ ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) {
|
||||
}
|
||||
|
||||
failure:
|
||||
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op.kind]));
|
||||
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op]));
|
||||
|
||||
ExactValue error_value = {0};
|
||||
return error_value;
|
||||
@@ -270,7 +270,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||
}
|
||||
|
||||
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
|
||||
ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
|
||||
ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
|
||||
match_exact_values(&x, &y);
|
||||
|
||||
switch (x.kind) {
|
||||
@@ -278,7 +278,7 @@ ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
|
||||
return x;
|
||||
|
||||
case ExactValue_Bool:
|
||||
switch (op.kind) {
|
||||
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);
|
||||
@@ -291,7 +291,7 @@ ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
|
||||
i64 a = x.value_integer;
|
||||
i64 b = y.value_integer;
|
||||
i64 c = 0;
|
||||
switch (op.kind) {
|
||||
switch (op) {
|
||||
case Token_Add: c = a + b; break;
|
||||
case Token_Sub: c = a - b; break;
|
||||
case Token_Mul: c = a * b; break;
|
||||
@@ -306,13 +306,14 @@ ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
|
||||
case Token_Shr: c = a >> b; break;
|
||||
default: goto error;
|
||||
}
|
||||
|
||||
return make_exact_value_integer(c);
|
||||
} break;
|
||||
|
||||
case ExactValue_Float: {
|
||||
f64 a = x.value_float;
|
||||
f64 b = y.value_float;
|
||||
switch (op.kind) {
|
||||
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);
|
||||
@@ -324,22 +325,22 @@ ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
|
||||
|
||||
error:
|
||||
ExactValue error_value = {0};
|
||||
// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op.kind));
|
||||
// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op));
|
||||
return error_value;
|
||||
}
|
||||
|
||||
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { Token op = {Token_Add}; return exact_binary_operator_value(op, x, y); }
|
||||
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { Token op = {Token_Sub}; return exact_binary_operator_value(op, x, y); }
|
||||
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { Token op = {Token_Mul}; return exact_binary_operator_value(op, x, y); }
|
||||
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { Token op = {Token_Quo}; return exact_binary_operator_value(op, x, y); }
|
||||
gb_inline ExactValue exact_value_shift(Token op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
|
||||
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Add, x, y); }
|
||||
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Sub, x, y); }
|
||||
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Mul, x, y); }
|
||||
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Quo, x, y); }
|
||||
gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
|
||||
|
||||
|
||||
i32 cmp_f64(f64 a, f64 b) {
|
||||
return (a > b) - (a < b);
|
||||
}
|
||||
|
||||
bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
|
||||
bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
||||
match_exact_values(&x, &y);
|
||||
|
||||
switch (x.kind) {
|
||||
@@ -347,7 +348,7 @@ bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
|
||||
return false;
|
||||
|
||||
case ExactValue_Bool:
|
||||
switch (op.kind) {
|
||||
switch (op) {
|
||||
case Token_CmpEq: return x.value_bool == y.value_bool;
|
||||
case Token_NotEq: return x.value_bool != y.value_bool;
|
||||
}
|
||||
@@ -356,7 +357,7 @@ bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
|
||||
case ExactValue_Integer: {
|
||||
i64 a = x.value_integer;
|
||||
i64 b = y.value_integer;
|
||||
switch (op.kind) {
|
||||
switch (op) {
|
||||
case Token_CmpEq: return a == b;
|
||||
case Token_NotEq: return a != b;
|
||||
case Token_Lt: return a < b;
|
||||
@@ -369,7 +370,7 @@ bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
|
||||
case ExactValue_Float: {
|
||||
f64 a = x.value_float;
|
||||
f64 b = y.value_float;
|
||||
switch (op.kind) {
|
||||
switch (op) {
|
||||
case Token_CmpEq: return cmp_f64(a, b) == 0;
|
||||
case Token_NotEq: return cmp_f64(a, b) != 0;
|
||||
case Token_Lt: return cmp_f64(a, b) < 0;
|
||||
@@ -384,7 +385,7 @@ bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
|
||||
String b = y.value_string;
|
||||
isize len = gb_min(a.len, b.len);
|
||||
// TODO(bill): gb_memcompare is used because the strings are UTF-8
|
||||
switch (op.kind) {
|
||||
switch (op) {
|
||||
case Token_CmpEq: return gb_memcompare(a.text, b.text, len) == 0;
|
||||
case Token_NotEq: return gb_memcompare(a.text, b.text, len) != 0;
|
||||
case Token_Lt: return gb_memcompare(a.text, b.text, len) < 0;
|
||||
|
||||
+1
-1
@@ -4112,7 +4112,7 @@ gb_inline i64 gb_atomic64_fetch_and(gbAtomic64 volatile *a, i64 operand) {
|
||||
|
||||
gb_inline i64 gb_atomic64_fetch_or(gbAtomic64 volatile *a, i64 operand) {
|
||||
#if defined(GB_ARCH_64_BIT)
|
||||
return _InterlockedAnd64(cast(i64 volatile *)a, operand);
|
||||
return _InterlockedOr64(cast(i64 volatile *)a, operand);
|
||||
#elif GB_CPU_X86
|
||||
i64 expected = a->value;
|
||||
for (;;) {
|
||||
|
||||
+118
-117
@@ -1,95 +1,96 @@
|
||||
// Optimizations for the SSA code
|
||||
// Optimizations for the IR code
|
||||
|
||||
void ssa_opt_add_operands(ssaValueArray *ops, ssaInstr *i) {
|
||||
void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
|
||||
switch (i->kind) {
|
||||
case ssaInstr_Comment:
|
||||
case irInstr_Comment:
|
||||
break;
|
||||
case ssaInstr_Local:
|
||||
case irInstr_Local:
|
||||
break;
|
||||
case ssaInstr_ZeroInit:
|
||||
case irInstr_ZeroInit:
|
||||
array_add(ops, i->ZeroInit.address);
|
||||
break;
|
||||
case ssaInstr_Store:
|
||||
case irInstr_Store:
|
||||
array_add(ops, i->Store.address);
|
||||
array_add(ops, i->Store.value);
|
||||
break;
|
||||
case ssaInstr_Load:
|
||||
case irInstr_Load:
|
||||
array_add(ops, i->Load.address);
|
||||
break;
|
||||
case ssaInstr_ArrayElementPtr:
|
||||
case irInstr_ArrayElementPtr:
|
||||
array_add(ops, i->ArrayElementPtr.address);
|
||||
array_add(ops, i->ArrayElementPtr.elem_index);
|
||||
break;
|
||||
case ssaInstr_StructElementPtr:
|
||||
case irInstr_StructElementPtr:
|
||||
array_add(ops, i->StructElementPtr.address);
|
||||
break;
|
||||
case ssaInstr_PtrOffset:
|
||||
case irInstr_PtrOffset:
|
||||
array_add(ops, i->PtrOffset.address);
|
||||
array_add(ops, i->PtrOffset.offset);
|
||||
break;
|
||||
case ssaInstr_ArrayExtractValue:
|
||||
case irInstr_ArrayExtractValue:
|
||||
array_add(ops, i->ArrayExtractValue.address);
|
||||
break;
|
||||
case ssaInstr_StructExtractValue:
|
||||
case irInstr_StructExtractValue:
|
||||
array_add(ops, i->StructExtractValue.address);
|
||||
break;
|
||||
case ssaInstr_Conv:
|
||||
case irInstr_Conv:
|
||||
array_add(ops, i->Conv.value);
|
||||
break;
|
||||
case ssaInstr_Jump:
|
||||
case irInstr_Jump:
|
||||
break;
|
||||
case ssaInstr_If:
|
||||
case irInstr_If:
|
||||
array_add(ops, i->If.cond);
|
||||
break;
|
||||
case ssaInstr_Return:
|
||||
case irInstr_Return:
|
||||
if (i->Return.value != NULL) {
|
||||
array_add(ops, i->Return.value);
|
||||
}
|
||||
break;
|
||||
case ssaInstr_Select:
|
||||
case irInstr_Select:
|
||||
array_add(ops, i->Select.cond);
|
||||
break;
|
||||
case ssaInstr_Phi:
|
||||
case irInstr_Phi:
|
||||
for_array(j, i->Phi.edges) {
|
||||
array_add(ops, i->Phi.edges.e[j]);
|
||||
}
|
||||
break;
|
||||
case ssaInstr_Unreachable: break;
|
||||
case ssaInstr_BinaryOp:
|
||||
case irInstr_Unreachable:
|
||||
break;
|
||||
case irInstr_UnaryOp:
|
||||
array_add(ops, i->UnaryOp.expr);
|
||||
break;
|
||||
case irInstr_BinaryOp:
|
||||
array_add(ops, i->BinaryOp.left);
|
||||
array_add(ops, i->BinaryOp.right);
|
||||
break;
|
||||
case ssaInstr_Call:
|
||||
case irInstr_Call:
|
||||
array_add(ops, i->Call.value);
|
||||
for (isize j = 0; j < i->Call.arg_count; j++) {
|
||||
array_add(ops, i->Call.args[j]);
|
||||
}
|
||||
break;
|
||||
case ssaInstr_VectorExtractElement:
|
||||
case irInstr_VectorExtractElement:
|
||||
array_add(ops, i->VectorExtractElement.vector);
|
||||
array_add(ops, i->VectorExtractElement.index);
|
||||
break;
|
||||
case ssaInstr_VectorInsertElement:
|
||||
case irInstr_VectorInsertElement:
|
||||
array_add(ops, i->VectorInsertElement.vector);
|
||||
array_add(ops, i->VectorInsertElement.elem);
|
||||
array_add(ops, i->VectorInsertElement.index);
|
||||
break;
|
||||
case ssaInstr_VectorShuffle:
|
||||
case irInstr_VectorShuffle:
|
||||
array_add(ops, i->VectorShuffle.vector);
|
||||
break;
|
||||
case ssaInstr_StartupRuntime:
|
||||
case irInstr_StartupRuntime:
|
||||
break;
|
||||
case ssaInstr_BoundsCheck:
|
||||
case irInstr_BoundsCheck:
|
||||
array_add(ops, i->BoundsCheck.index);
|
||||
array_add(ops, i->BoundsCheck.len);
|
||||
break;
|
||||
case ssaInstr_SliceBoundsCheck:
|
||||
case irInstr_SliceBoundsCheck:
|
||||
array_add(ops, i->SliceBoundsCheck.low);
|
||||
array_add(ops, i->SliceBoundsCheck.high);
|
||||
array_add(ops, i->SliceBoundsCheck.max);
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,26 +98,26 @@ void ssa_opt_add_operands(ssaValueArray *ops, ssaInstr *i) {
|
||||
|
||||
|
||||
|
||||
void ssa_opt_block_replace_pred(ssaBlock *b, ssaBlock *from, ssaBlock *to) {
|
||||
void ir_opt_block_replace_pred(irBlock *b, irBlock *from, irBlock *to) {
|
||||
for_array(i, b->preds) {
|
||||
ssaBlock *pred = b->preds.e[i];
|
||||
irBlock *pred = b->preds.e[i];
|
||||
if (pred == from) {
|
||||
b->preds.e[i] = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ssa_opt_block_replace_succ(ssaBlock *b, ssaBlock *from, ssaBlock *to) {
|
||||
void ir_opt_block_replace_succ(irBlock *b, irBlock *from, irBlock *to) {
|
||||
for_array(i, b->succs) {
|
||||
ssaBlock *succ = b->succs.e[i];
|
||||
irBlock *succ = b->succs.e[i];
|
||||
if (succ == from) {
|
||||
b->succs.e[i] = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ssa_opt_block_has_phi(ssaBlock *b) {
|
||||
return b->instrs.e[0]->Instr.kind == ssaInstr_Phi;
|
||||
bool ir_opt_block_has_phi(irBlock *b) {
|
||||
return b->instrs.e[0]->Instr.kind == irInstr_Phi;
|
||||
}
|
||||
|
||||
|
||||
@@ -128,11 +129,11 @@ bool ssa_opt_block_has_phi(ssaBlock *b) {
|
||||
|
||||
|
||||
|
||||
ssaValueArray ssa_get_block_phi_nodes(ssaBlock *b) {
|
||||
ssaValueArray phis = {0};
|
||||
irValueArray ir_get_block_phi_nodes(irBlock *b) {
|
||||
irValueArray phis = {0};
|
||||
for_array(i, b->instrs) {
|
||||
ssaInstr *instr = &b->instrs.e[i]->Instr;
|
||||
if (instr->kind != ssaInstr_Phi) {
|
||||
irInstr *instr = &b->instrs.e[i]->Instr;
|
||||
if (instr->kind != irInstr_Phi) {
|
||||
phis = b->instrs;
|
||||
phis.count = i;
|
||||
return phis;
|
||||
@@ -141,15 +142,15 @@ ssaValueArray ssa_get_block_phi_nodes(ssaBlock *b) {
|
||||
return phis;
|
||||
}
|
||||
|
||||
void ssa_remove_pred(ssaBlock *b, ssaBlock *p) {
|
||||
ssaValueArray phis = ssa_get_block_phi_nodes(b);
|
||||
void ir_remove_pred(irBlock *b, irBlock *p) {
|
||||
irValueArray phis = ir_get_block_phi_nodes(b);
|
||||
isize i = 0;
|
||||
for_array(j, b->preds) {
|
||||
ssaBlock *pred = b->preds.e[j];
|
||||
irBlock *pred = b->preds.e[j];
|
||||
if (pred != p) {
|
||||
b->preds.e[i] = b->preds.e[j];
|
||||
for_array(k, phis) {
|
||||
ssaInstrPhi *phi = &phis.e[k]->Instr.Phi;
|
||||
irInstrPhi *phi = &phis.e[k]->Instr.Phi;
|
||||
phi->edges.e[i] = phi->edges.e[j];
|
||||
}
|
||||
i++;
|
||||
@@ -157,16 +158,16 @@ void ssa_remove_pred(ssaBlock *b, ssaBlock *p) {
|
||||
}
|
||||
b->preds.count = i;
|
||||
for_array(k, phis) {
|
||||
ssaInstrPhi *phi = &phis.e[k]->Instr.Phi;
|
||||
irInstrPhi *phi = &phis.e[k]->Instr.Phi;
|
||||
phi->edges.count = i;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ssa_remove_dead_blocks(ssaProcedure *proc) {
|
||||
void ir_remove_dead_blocks(irProcedure *proc) {
|
||||
isize j = 0;
|
||||
for_array(i, proc->blocks) {
|
||||
ssaBlock *b = proc->blocks.e[i];
|
||||
irBlock *b = proc->blocks.e[i];
|
||||
if (b == NULL) {
|
||||
continue;
|
||||
}
|
||||
@@ -177,34 +178,34 @@ void ssa_remove_dead_blocks(ssaProcedure *proc) {
|
||||
proc->blocks.count = j;
|
||||
}
|
||||
|
||||
void ssa_mark_reachable(ssaBlock *b) {
|
||||
void ir_mark_reachable(irBlock *b) {
|
||||
isize const WHITE = 0;
|
||||
isize const BLACK = -1;
|
||||
b->index = BLACK;
|
||||
for_array(i, b->succs) {
|
||||
ssaBlock *succ = b->succs.e[i];
|
||||
irBlock *succ = b->succs.e[i];
|
||||
if (succ->index == WHITE) {
|
||||
ssa_mark_reachable(succ);
|
||||
ir_mark_reachable(succ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ssa_remove_unreachable_blocks(ssaProcedure *proc) {
|
||||
void ir_remove_unreachable_blocks(irProcedure *proc) {
|
||||
isize const WHITE = 0;
|
||||
isize const BLACK = -1;
|
||||
for_array(i, proc->blocks) {
|
||||
proc->blocks.e[i]->index = WHITE;
|
||||
}
|
||||
|
||||
ssa_mark_reachable(proc->blocks.e[0]);
|
||||
ir_mark_reachable(proc->blocks.e[0]);
|
||||
|
||||
for_array(i, proc->blocks) {
|
||||
ssaBlock *b = proc->blocks.e[i];
|
||||
irBlock *b = proc->blocks.e[i];
|
||||
if (b->index == WHITE) {
|
||||
for_array(j, b->succs) {
|
||||
ssaBlock *c = b->succs.e[j];
|
||||
irBlock *c = b->succs.e[j];
|
||||
if (c->index == BLACK) {
|
||||
ssa_remove_pred(c, b);
|
||||
ir_remove_pred(c, b);
|
||||
}
|
||||
}
|
||||
// NOTE(bill): Mark as empty but don't actually free it
|
||||
@@ -212,26 +213,26 @@ void ssa_remove_unreachable_blocks(ssaProcedure *proc) {
|
||||
proc->blocks.e[i] = NULL;
|
||||
}
|
||||
}
|
||||
ssa_remove_dead_blocks(proc);
|
||||
ir_remove_dead_blocks(proc);
|
||||
}
|
||||
|
||||
bool ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) {
|
||||
bool ir_opt_block_fusion(irProcedure *proc, irBlock *a) {
|
||||
if (a->succs.count != 1) {
|
||||
return false;
|
||||
}
|
||||
ssaBlock *b = a->succs.e[0];
|
||||
irBlock *b = a->succs.e[0];
|
||||
if (b->preds.count != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ssa_opt_block_has_phi(b)) {
|
||||
if (ir_opt_block_has_phi(b)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
array_pop(&a->instrs); // Remove branch at end
|
||||
for_array(i, b->instrs) {
|
||||
array_add(&a->instrs, b->instrs.e[i]);
|
||||
ssa_set_instr_parent(b->instrs.e[i], a);
|
||||
ir_set_instr_parent(b->instrs.e[i], a);
|
||||
}
|
||||
|
||||
array_clear(&a->succs);
|
||||
@@ -241,28 +242,28 @@ bool ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) {
|
||||
|
||||
// Fix preds links
|
||||
for_array(i, b->succs) {
|
||||
ssa_opt_block_replace_pred(b->succs.e[i], b, a);
|
||||
ir_opt_block_replace_pred(b->succs.e[i], b, a);
|
||||
}
|
||||
|
||||
proc->blocks.e[b->index] = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ssa_opt_blocks(ssaProcedure *proc) {
|
||||
ssa_remove_unreachable_blocks(proc);
|
||||
void ir_opt_blocks(irProcedure *proc) {
|
||||
ir_remove_unreachable_blocks(proc);
|
||||
|
||||
#if 1
|
||||
bool changed = true;
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for_array(i, proc->blocks) {
|
||||
ssaBlock *b = proc->blocks.e[i];
|
||||
irBlock *b = proc->blocks.e[i];
|
||||
if (b == NULL) {
|
||||
continue;
|
||||
}
|
||||
GB_ASSERT(b->index == i);
|
||||
|
||||
if (ssa_opt_block_fusion(proc, b)) {
|
||||
if (ir_opt_block_fusion(proc, b)) {
|
||||
changed = true;
|
||||
}
|
||||
// TODO(bill): other simple block optimizations
|
||||
@@ -270,25 +271,25 @@ void ssa_opt_blocks(ssaProcedure *proc) {
|
||||
}
|
||||
#endif
|
||||
|
||||
ssa_remove_dead_blocks(proc);
|
||||
ir_remove_dead_blocks(proc);
|
||||
}
|
||||
void ssa_opt_build_referrers(ssaProcedure *proc) {
|
||||
void ir_opt_build_referrers(irProcedure *proc) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
||||
|
||||
ssaValueArray ops = {0}; // NOTE(bill): Act as a buffer
|
||||
irValueArray ops = {0}; // NOTE(bill): Act as a buffer
|
||||
array_init_reserve(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena
|
||||
for_array(i, proc->blocks) {
|
||||
ssaBlock *b = proc->blocks.e[i];
|
||||
irBlock *b = proc->blocks.e[i];
|
||||
for_array(j, b->instrs) {
|
||||
ssaValue *instr = b->instrs.e[j];
|
||||
irValue *instr = b->instrs.e[j];
|
||||
array_clear(&ops);
|
||||
ssa_opt_add_operands(&ops, &instr->Instr);
|
||||
ir_opt_add_operands(&ops, &instr->Instr);
|
||||
for_array(k, ops) {
|
||||
ssaValue *op = ops.e[k];
|
||||
irValue *op = ops.e[k];
|
||||
if (op == NULL) {
|
||||
continue;
|
||||
}
|
||||
ssaValueArray *refs = ssa_value_referrers(op);
|
||||
irValueArray *refs = ir_value_referrers(op);
|
||||
if (refs != NULL) {
|
||||
array_add(refs, instr);
|
||||
}
|
||||
@@ -307,36 +308,36 @@ void ssa_opt_build_referrers(ssaProcedure *proc) {
|
||||
|
||||
// State of Lengauer-Tarjan algorithm
|
||||
// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
|
||||
typedef struct ssaLTState {
|
||||
typedef struct irLTState {
|
||||
isize count;
|
||||
// NOTE(bill): These are arrays
|
||||
ssaBlock **sdom; // Semidominator
|
||||
ssaBlock **parent; // Parent in DFS traversal of CFG
|
||||
ssaBlock **ancestor;
|
||||
} ssaLTState;
|
||||
irBlock **sdom; // Semidominator
|
||||
irBlock **parent; // Parent in DFS traversal of CFG
|
||||
irBlock **ancestor;
|
||||
} irLTState;
|
||||
|
||||
// §2.2 - bottom of page
|
||||
void ssa_lt_link(ssaLTState *lt, ssaBlock *p, ssaBlock *q) {
|
||||
void ir_lt_link(irLTState *lt, irBlock *p, irBlock *q) {
|
||||
lt->ancestor[q->index] = p;
|
||||
}
|
||||
|
||||
i32 ssa_lt_depth_first_search(ssaLTState *lt, ssaBlock *p, i32 i, ssaBlock **preorder) {
|
||||
i32 ir_lt_depth_first_search(irLTState *lt, irBlock *p, i32 i, irBlock **preorder) {
|
||||
preorder[i] = p;
|
||||
p->dom.pre = i++;
|
||||
lt->sdom[p->index] = p;
|
||||
ssa_lt_link(lt, NULL, p);
|
||||
ir_lt_link(lt, NULL, p);
|
||||
for_array(index, p->succs) {
|
||||
ssaBlock *q = p->succs.e[index];
|
||||
irBlock *q = p->succs.e[index];
|
||||
if (lt->sdom[q->index] == NULL) {
|
||||
lt->parent[q->index] = p;
|
||||
i = ssa_lt_depth_first_search(lt, q, i, preorder);
|
||||
i = ir_lt_depth_first_search(lt, q, i, preorder);
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) {
|
||||
ssaBlock *u = v;
|
||||
irBlock *ir_lt_eval(irLTState *lt, irBlock *v) {
|
||||
irBlock *u = v;
|
||||
for (;
|
||||
lt->ancestor[v->index] != NULL;
|
||||
v = lt->ancestor[v->index]) {
|
||||
@@ -347,16 +348,16 @@ ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) {
|
||||
return u;
|
||||
}
|
||||
|
||||
typedef struct ssaDomPrePost {
|
||||
typedef struct irDomPrePost {
|
||||
i32 pre, post;
|
||||
} ssaDomPrePost;
|
||||
} irDomPrePost;
|
||||
|
||||
ssaDomPrePost ssa_opt_number_dom_tree(ssaBlock *v, i32 pre, i32 post) {
|
||||
ssaDomPrePost result = {pre, post};
|
||||
irDomPrePost ir_opt_number_dom_tree(irBlock *v, i32 pre, i32 post) {
|
||||
irDomPrePost result = {pre, post};
|
||||
|
||||
v->dom.pre = pre++;
|
||||
for_array(i, v->dom.children) {
|
||||
result = ssa_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post);
|
||||
result = ir_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post);
|
||||
}
|
||||
v->dom.post = post++;
|
||||
|
||||
@@ -366,35 +367,35 @@ ssaDomPrePost ssa_opt_number_dom_tree(ssaBlock *v, i32 pre, i32 post) {
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Requires `ssa_opt_blocks` to be called before this
|
||||
void ssa_opt_build_dom_tree(ssaProcedure *proc) {
|
||||
// NOTE(bill): Requires `ir_opt_blocks` to be called before this
|
||||
void ir_opt_build_dom_tree(irProcedure *proc) {
|
||||
// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
||||
|
||||
isize n = proc->blocks.count;
|
||||
ssaBlock **buf = gb_alloc_array(proc->module->tmp_allocator, ssaBlock *, 5*n);
|
||||
irBlock **buf = gb_alloc_array(proc->module->tmp_allocator, irBlock *, 5*n);
|
||||
|
||||
ssaLTState lt = {0};
|
||||
irLTState lt = {0};
|
||||
lt.count = n;
|
||||
lt.sdom = &buf[0*n];
|
||||
lt.parent = &buf[1*n];
|
||||
lt.ancestor = &buf[2*n];
|
||||
|
||||
ssaBlock **preorder = &buf[3*n];
|
||||
ssaBlock **buckets = &buf[4*n];
|
||||
ssaBlock *root = proc->blocks.e[0];
|
||||
irBlock **preorder = &buf[3*n];
|
||||
irBlock **buckets = &buf[4*n];
|
||||
irBlock *root = proc->blocks.e[0];
|
||||
|
||||
// Step 1 - number vertices
|
||||
i32 pre_num = ssa_lt_depth_first_search(<, root, 0, preorder);
|
||||
i32 pre_num = ir_lt_depth_first_search(<, root, 0, preorder);
|
||||
gb_memmove(buckets, preorder, n*gb_size_of(preorder[0]));
|
||||
|
||||
for (i32 i = n-1; i > 0; i--) {
|
||||
ssaBlock *w = preorder[i];
|
||||
irBlock *w = preorder[i];
|
||||
|
||||
// Step 3 - Implicitly define idom for nodes
|
||||
for (ssaBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) {
|
||||
ssaBlock *u = ssa_lt_eval(<, v);
|
||||
for (irBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) {
|
||||
irBlock *u = ir_lt_eval(<, v);
|
||||
if (lt.sdom[u->index]->dom.pre < i) {
|
||||
v->dom.idom = u;
|
||||
} else {
|
||||
@@ -405,14 +406,14 @@ void ssa_opt_build_dom_tree(ssaProcedure *proc) {
|
||||
// Step 2 - Compute all sdoms
|
||||
lt.sdom[w->index] = lt.parent[w->index];
|
||||
for_array(pred_index, w->preds) {
|
||||
ssaBlock *v = w->preds.e[pred_index];
|
||||
ssaBlock *u = ssa_lt_eval(<, v);
|
||||
irBlock *v = w->preds.e[pred_index];
|
||||
irBlock *u = ir_lt_eval(<, v);
|
||||
if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) {
|
||||
lt.sdom[w->index] = lt.sdom[u->index];
|
||||
}
|
||||
}
|
||||
|
||||
ssa_lt_link(<, lt.parent[w->index], w);
|
||||
ir_lt_link(<, lt.parent[w->index], w);
|
||||
|
||||
if (lt.parent[w->index] == lt.sdom[w->index]) {
|
||||
w->dom.idom = lt.parent[w->index];
|
||||
@@ -423,13 +424,13 @@ void ssa_opt_build_dom_tree(ssaProcedure *proc) {
|
||||
}
|
||||
|
||||
// The rest of Step 3
|
||||
for (ssaBlock *v = buckets[0]; v != root; v = buckets[v->dom.pre]) {
|
||||
for (irBlock *v = buckets[0]; v != root; v = buckets[v->dom.pre]) {
|
||||
v->dom.idom = root;
|
||||
}
|
||||
|
||||
// Step 4 - Explicitly define idom for nodes (in preorder)
|
||||
for (isize i = 1; i < n; i++) {
|
||||
ssaBlock *w = preorder[i];
|
||||
irBlock *w = preorder[i];
|
||||
if (w == root) {
|
||||
w->dom.idom = NULL;
|
||||
} else {
|
||||
@@ -448,32 +449,32 @@ void ssa_opt_build_dom_tree(ssaProcedure *proc) {
|
||||
}
|
||||
}
|
||||
|
||||
ssa_opt_number_dom_tree(root, 0, 0);
|
||||
ir_opt_number_dom_tree(root, 0, 0);
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
|
||||
void ssa_opt_mem2reg(ssaProcedure *proc) {
|
||||
// TODO(bill): ssa_opt_mem2reg
|
||||
void ir_opt_mem2reg(irProcedure *proc) {
|
||||
// TODO(bill): ir_opt_mem2reg
|
||||
}
|
||||
|
||||
|
||||
|
||||
void ssa_opt_tree(ssaGen *s) {
|
||||
void ir_opt_tree(irGen *s) {
|
||||
s->opt_called = true;
|
||||
|
||||
for_array(member_index, s->module.procs) {
|
||||
ssaProcedure *proc = s->module.procs.e[member_index];
|
||||
irProcedure *proc = s->module.procs.e[member_index];
|
||||
if (proc->blocks.count == 0) { // Prototype/external procedure
|
||||
continue;
|
||||
}
|
||||
|
||||
ssa_opt_blocks(proc);
|
||||
ir_opt_blocks(proc);
|
||||
#if 1
|
||||
ssa_opt_build_referrers(proc);
|
||||
ssa_opt_build_dom_tree(proc);
|
||||
ir_opt_build_referrers(proc);
|
||||
ir_opt_build_dom_tree(proc);
|
||||
|
||||
// TODO(bill): ssa optimization
|
||||
// TODO(bill): ir optimization
|
||||
// [ ] cse (common-subexpression) elim
|
||||
// [ ] copy elim
|
||||
// [ ] dead code elim
|
||||
@@ -484,10 +485,10 @@ void ssa_opt_tree(ssaGen *s) {
|
||||
// [ ] lift/mem2reg
|
||||
// [ ] lift/mem2reg
|
||||
|
||||
ssa_opt_mem2reg(proc);
|
||||
ir_opt_mem2reg(proc);
|
||||
#endif
|
||||
|
||||
GB_ASSERT(proc->blocks.count > 0);
|
||||
ssa_number_proc_registers(proc);
|
||||
ir_number_proc_registers(proc);
|
||||
}
|
||||
}
|
||||
+1516
File diff suppressed because it is too large
Load Diff
+91
-78
@@ -1,22 +1,23 @@
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#define VERSION_STRING "v0.0.3c"
|
||||
|
||||
#include "common.c"
|
||||
#include "timings.c"
|
||||
#include "unicode.c"
|
||||
#include "build.c"
|
||||
#include "tokenizer.c"
|
||||
#include "parser.c"
|
||||
// #include "printer.c"
|
||||
#include "checker/checker.c"
|
||||
#include "ssa.c"
|
||||
#include "ssa_opt.c"
|
||||
#include "ssa_print.c"
|
||||
// #include "ssa.c"
|
||||
#include "ir.c"
|
||||
#include "ir_opt.c"
|
||||
#include "ir_print.c"
|
||||
// #include "vm.c"
|
||||
|
||||
// NOTE(bill): `name` is used in debugging and profiling modes
|
||||
i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
|
||||
i32 win32_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
char cmd_line[4096] = {0};
|
||||
@@ -59,49 +60,26 @@ i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
typedef enum ArchKind {
|
||||
ArchKind_x64,
|
||||
ArchKind_x86,
|
||||
} ArchKind;
|
||||
|
||||
typedef struct ArchData {
|
||||
BaseTypeSizes sizes;
|
||||
String llc_flags;
|
||||
String link_flags;
|
||||
} ArchData;
|
||||
|
||||
ArchData make_arch_data(ArchKind kind) {
|
||||
ArchData data = {0};
|
||||
|
||||
switch (kind) {
|
||||
case ArchKind_x64:
|
||||
default:
|
||||
data.sizes.word_size = 8;
|
||||
data.sizes.max_align = 16;
|
||||
data.llc_flags = str_lit("-march=x86-64 ");
|
||||
data.link_flags = str_lit("/machine:x64 ");
|
||||
break;
|
||||
|
||||
case ArchKind_x86:
|
||||
data.sizes.word_size = 4;
|
||||
data.sizes.max_align = 8;
|
||||
data.llc_flags = str_lit("-march=x86 ");
|
||||
data.link_flags = str_lit("/machine:x86 ");
|
||||
break;
|
||||
void print_usage_line(i32 indent, char *fmt, ...) {
|
||||
while (indent --> 0) {
|
||||
gb_printf_err("\t");
|
||||
}
|
||||
|
||||
return data;
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
gb_printf_err_va(fmt, va);
|
||||
va_end(va);
|
||||
gb_printf_err("\n");
|
||||
}
|
||||
|
||||
void usage(char *argv0) {
|
||||
gb_printf_err("%s is a tool for managing Odin source code\n", argv0);
|
||||
gb_printf_err("Usage:");
|
||||
gb_printf_err("\n\t%s command [arguments]\n", argv0);
|
||||
gb_printf_err("Commands:");
|
||||
gb_printf_err("\n\tbuild compile .odin file");
|
||||
gb_printf_err("\n\trun compile and run .odin file");
|
||||
gb_printf_err("\n\tversion print Odin version");
|
||||
gb_printf_err("\n\n");
|
||||
print_usage_line(0, "%s is a tool for managing Odin source code", argv0);
|
||||
print_usage_line(0, "Usage:");
|
||||
print_usage_line(1, "%s command [arguments]", argv0);
|
||||
print_usage_line(0, "Commands:");
|
||||
print_usage_line(1, "build compile .odin file as executable");
|
||||
print_usage_line(1, "build_dll compile .odin file as dll");
|
||||
print_usage_line(1, "run compile and run .odin file");
|
||||
print_usage_line(1, "version print version");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
@@ -113,25 +91,41 @@ int main(int argc, char **argv) {
|
||||
Timings timings = {0};
|
||||
timings_init(&timings, str_lit("Total Time"), 128);
|
||||
// defer (timings_destroy(&timings));
|
||||
|
||||
#if 1
|
||||
init_string_buffer_memory();
|
||||
init_global_error_collector();
|
||||
|
||||
String module_dir = get_module_dir();
|
||||
#if 1
|
||||
|
||||
init_universal_scope();
|
||||
BuildContext build_context = {0};
|
||||
init_build_context(&build_context);
|
||||
|
||||
init_universal_scope(&build_context);
|
||||
|
||||
char *init_filename = NULL;
|
||||
bool run_output = false;
|
||||
String arg1 = make_string_c(argv[1]);
|
||||
if (str_eq(arg1, str_lit("run"))) {
|
||||
run_output = true;
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
init_filename = argv[2];
|
||||
run_output = true;
|
||||
} else if (str_eq(arg1, str_lit("build_dll"))) {
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
init_filename = argv[2];
|
||||
build_context.is_dll = true;
|
||||
} else if (str_eq(arg1, str_lit("build"))) {
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
init_filename = argv[2];
|
||||
} else if (str_eq(arg1, str_lit("version"))) {
|
||||
gb_printf("%s version %s", argv[0], VERSION_STRING);
|
||||
gb_printf("%s version %.*s", argv[0], LIT(build_context.ODIN_VERSION));
|
||||
return 0;
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
@@ -157,38 +151,48 @@ int main(int argc, char **argv) {
|
||||
timings_start_section(&timings, str_lit("type check"));
|
||||
|
||||
Checker checker = {0};
|
||||
ArchData arch_data = make_arch_data(ArchKind_x64);
|
||||
|
||||
init_checker(&checker, &parser, arch_data.sizes);
|
||||
init_checker(&checker, &parser, &build_context);
|
||||
// defer (destroy_checker(&checker));
|
||||
|
||||
check_parsed_files(&checker);
|
||||
|
||||
|
||||
#endif
|
||||
#if 1
|
||||
|
||||
ssaGen ssa = {0};
|
||||
if (!ssa_gen_init(&ssa, &checker)) {
|
||||
#if 0
|
||||
if (global_error_collector.count != 0) {
|
||||
return 1;
|
||||
}
|
||||
// defer (ssa_gen_destroy(&ssa));
|
||||
|
||||
timings_start_section(&timings, str_lit("ssa gen"));
|
||||
ssa_gen_tree(&ssa);
|
||||
if (checker.parser->total_token_count < 2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
timings_start_section(&timings, str_lit("ssa opt"));
|
||||
ssa_opt_tree(&ssa);
|
||||
ssa_generate(&checker.info, &build_context);
|
||||
#endif
|
||||
#if 1
|
||||
|
||||
timings_start_section(&timings, str_lit("ssa print"));
|
||||
ssa_print_llvm_ir(&ssa);
|
||||
irGen ir_gen = {0};
|
||||
if (!ir_gen_init(&ir_gen, &checker, &build_context)) {
|
||||
return 1;
|
||||
}
|
||||
// defer (ssa_gen_destroy(&ir_gen));
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm ir gen"));
|
||||
ir_gen_tree(&ir_gen);
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm ir opt tree"));
|
||||
ir_opt_tree(&ir_gen);
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm ir print"));
|
||||
print_llvm_ir(&ir_gen);
|
||||
|
||||
// prof_print_all();
|
||||
|
||||
#if 1
|
||||
timings_start_section(&timings, str_lit("llvm-opt"));
|
||||
|
||||
char const *output_name = ssa.output_file.filename;
|
||||
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);
|
||||
|
||||
@@ -197,7 +201,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
i32 exit_code = 0;
|
||||
// For more passes arguments: http://llvm.org/docs/Passes.html
|
||||
exit_code = win32_exec_command_line_app("llvm-opt",
|
||||
exit_code = win32_exec_command_line_app("llvm-opt", false,
|
||||
"%.*sbin/opt %s -o %.*s.bc "
|
||||
"-mem2reg "
|
||||
"-memcpyopt "
|
||||
@@ -206,7 +210,7 @@ int main(int argc, char **argv) {
|
||||
// "-dce "
|
||||
// "-S "
|
||||
"",
|
||||
LIT(module_dir),
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
output_name, LIT(output));
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
@@ -215,15 +219,15 @@ int main(int argc, char **argv) {
|
||||
#if 1
|
||||
timings_start_section(&timings, str_lit("llvm-llc"));
|
||||
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
|
||||
exit_code = win32_exec_command_line_app("llvm-llc",
|
||||
exit_code = win32_exec_command_line_app("llvm-llc", false,
|
||||
"%.*sbin/llc %.*s.bc -filetype=obj -O%d "
|
||||
"%.*s "
|
||||
// "-debug-pass=Arguments "
|
||||
"",
|
||||
LIT(module_dir),
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
LIT(output),
|
||||
optimization_level,
|
||||
LIT(arch_data.llc_flags));
|
||||
LIT(build_context.llc_flags));
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
@@ -233,21 +237,31 @@ int main(int argc, char **argv) {
|
||||
gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib");
|
||||
// defer (gb_string_free(lib_str));
|
||||
char lib_str_buf[1024] = {0};
|
||||
for_array(i, parser.foreign_libraries) {
|
||||
String lib = parser.foreign_libraries.e[i];
|
||||
for_array(i, checker.info.foreign_libraries) {
|
||||
String lib = checker.info.foreign_libraries.e[i];
|
||||
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||
" %.*s.lib", LIT(lib));
|
||||
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
||||
}
|
||||
|
||||
exit_code = win32_exec_command_line_app("msvc-link",
|
||||
"link %.*s.obj -OUT:%.*s.exe %s "
|
||||
char *output_ext = "exe";
|
||||
char *link_settings = "";
|
||||
if (build_context.is_dll) {
|
||||
output_ext = "dll";
|
||||
link_settings = "/DLL";
|
||||
}
|
||||
|
||||
exit_code = win32_exec_command_line_app("msvc-link", true,
|
||||
"link %.*s.obj -OUT:%.*s.%s %s "
|
||||
"/defaultlib:libcmt "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:console "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(output), LIT(output),
|
||||
lib_str, LIT(arch_data.link_flags));
|
||||
LIT(output), LIT(output), output_ext,
|
||||
lib_str, LIT(build_context.link_flags),
|
||||
link_settings
|
||||
);
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
@@ -255,8 +269,7 @@ int main(int argc, char **argv) {
|
||||
// timings_print_all(&timings);
|
||||
|
||||
if (run_output) {
|
||||
win32_exec_command_line_app("odin run",
|
||||
"%.*s.exe", cast(int)base_name_len, output_name);
|
||||
win32_exec_command_line_app("odin run", false, "%.*s.exe", cast(int)base_name_len, output_name);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -243,6 +243,7 @@ void _J2(MAP_PROC,set)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
|
||||
|
||||
|
||||
void _J2(MAP_PROC,_erase)(MAP_NAME *h, MapFindResult fr) {
|
||||
MapFindResult last;
|
||||
if (fr.entry_prev < 0) {
|
||||
h->hashes.e[fr.hash_index] = h->entries.e[fr.entry_index].next;
|
||||
} else {
|
||||
@@ -253,7 +254,7 @@ void _J2(MAP_PROC,_erase)(MAP_NAME *h, MapFindResult fr) {
|
||||
return;
|
||||
}
|
||||
h->entries.e[fr.entry_index] = h->entries.e[h->entries.count-1];
|
||||
MapFindResult last = _J2(MAP_PROC,_find)(h, h->entries.e[fr.entry_index].key);
|
||||
last = _J2(MAP_PROC,_find)(h, h->entries.e[fr.entry_index].key);
|
||||
if (last.entry_prev >= 0) {
|
||||
h->entries.e[last.entry_prev].next = fr.entry_index;
|
||||
} else {
|
||||
@@ -314,11 +315,13 @@ void _J2(MAP_PROC,multi_get_all)(MAP_NAME *h, HashKey key, MAP_TYPE *items) {
|
||||
}
|
||||
|
||||
void _J2(MAP_PROC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
|
||||
MapFindResult fr;
|
||||
isize i;
|
||||
if (h->hashes.count == 0) {
|
||||
_J2(MAP_PROC,grow)(h);
|
||||
}
|
||||
MapFindResult fr = _J2(MAP_PROC,_find)(h, key);
|
||||
isize i = _J2(MAP_PROC,_add_entry)(h, key);
|
||||
fr = _J2(MAP_PROC,_find)(h, key);
|
||||
i = _J2(MAP_PROC,_add_entry)(h, key);
|
||||
if (fr.entry_prev < 0) {
|
||||
h->hashes.e[fr.hash_index] = i;
|
||||
} else {
|
||||
|
||||
+1176
-942
File diff suppressed because it is too large
Load Diff
-1439
File diff suppressed because it is too large
Load Diff
+105
-97
@@ -49,16 +49,11 @@ gb_inline String make_string_c(char *text) {
|
||||
#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
|
||||
|
||||
|
||||
gb_inline bool are_strings_equal(String a, String b) {
|
||||
if (a.len == b.len) {
|
||||
return gb_memcompare(a.text, b.text, a.len) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_inline bool str_eq_ignore_case(String a, String b) {
|
||||
if (a.len == b.len) {
|
||||
for (isize i = 0; i < a.len; i++) {
|
||||
isize i;
|
||||
for (i = 0; i < a.len; i++) {
|
||||
char x = cast(char)a.text[i];
|
||||
char y = cast(char)b.text[i];
|
||||
if (gb_char_to_lower(x) != gb_char_to_lower(y))
|
||||
@@ -70,39 +65,40 @@ gb_inline bool str_eq_ignore_case(String a, String b) {
|
||||
}
|
||||
|
||||
int string_compare(String x, String y) {
|
||||
if (x.len == y.len &&
|
||||
x.text == y.text) {
|
||||
return 0;
|
||||
}
|
||||
if (!(x.len == y.len &&
|
||||
x.text == y.text)) {
|
||||
isize n, fast, offset, curr_block;
|
||||
isize *la, *lb;
|
||||
isize pos;
|
||||
|
||||
isize n = gb_min(x.len, y.len);
|
||||
n = gb_min(x.len, y.len);
|
||||
|
||||
isize fast = n/gb_size_of(isize) + 1;
|
||||
isize offset = (fast-1)*gb_size_of(isize);
|
||||
isize curr_block = 0;
|
||||
if (n <= gb_size_of(isize)) {
|
||||
fast = 0;
|
||||
}
|
||||
fast = n/gb_size_of(isize) + 1;
|
||||
offset = (fast-1)*gb_size_of(isize);
|
||||
curr_block = 0;
|
||||
if (n <= gb_size_of(isize)) {
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
isize *la = cast(isize *)x.text;
|
||||
isize *lb = cast(isize *)y.text;
|
||||
la = cast(isize *)x.text;
|
||||
lb = cast(isize *)y.text;
|
||||
|
||||
for (; curr_block < fast; curr_block++) {
|
||||
if (la[curr_block] ^ lb[curr_block]) {
|
||||
for (isize pos = curr_block*gb_size_of(isize); pos < n; pos++) {
|
||||
if (x.text[pos] ^ y.text[pos]) {
|
||||
return cast(int)x.text[pos] - cast(int)y.text[pos];
|
||||
for (; curr_block < fast; curr_block++) {
|
||||
if (la[curr_block] ^ lb[curr_block]) {
|
||||
for (pos = curr_block*gb_size_of(isize); pos < n; pos++) {
|
||||
if (x.text[pos] ^ y.text[pos]) {
|
||||
return cast(int)x.text[pos] - cast(int)y.text[pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; offset < n; offset++) {
|
||||
if (x.text[offset] ^ y.text[offset]) {
|
||||
return cast(int)x.text[offset] - cast(int)y.text[offset];
|
||||
for (; offset < n; offset++) {
|
||||
if (x.text[offset] ^ y.text[offset]) {
|
||||
return cast(int)x.text[offset] - cast(int)y.text[offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -112,27 +108,25 @@ GB_COMPARE_PROC(string_cmp_proc) {
|
||||
return string_compare(x, y);
|
||||
}
|
||||
|
||||
|
||||
// gb_inline bool operator ==(String a, String b) { return are_strings_equal(a, b) != 0; }
|
||||
// gb_inline bool operator !=(String a, String b) { return !operator==(a, b); }
|
||||
// gb_inline bool operator < (String a, String b) { return string_compare(a, b) < 0; }
|
||||
// gb_inline bool operator > (String a, String b) { return string_compare(a, b) > 0; }
|
||||
// gb_inline bool operator <=(String a, String b) { return string_compare(a, b) <= 0; }
|
||||
// gb_inline bool operator >=(String a, String b) { return string_compare(a, b) >= 0; }
|
||||
|
||||
// template <size_t N> gb_inline bool operator ==(String a, char const (&b)[N]) { return a == make_string(cast(u8 *)b, N-1); }
|
||||
// template <size_t N> gb_inline bool operator !=(String a, char const (&b)[N]) { return a != make_string(cast(u8 *)b, N-1); }
|
||||
// template <size_t N> gb_inline bool operator ==(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) == b; }
|
||||
// template <size_t N> gb_inline bool operator !=(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) != b; }
|
||||
|
||||
gb_inline bool str_eq(String a, String b) { return are_strings_equal(a, b) != 0; }
|
||||
gb_inline bool str_eq(String a, String b) { return a.len == b.len ? gb_memcompare(a.text, b.text, a.len) == 0 : false; }
|
||||
gb_inline bool str_ne(String a, String b) { return !str_eq(a, b); }
|
||||
gb_inline bool str_lt(String a, String b) { return string_compare(a, b) < 0; }
|
||||
gb_inline bool str_gt(String a, String b) { return string_compare(a, b) > 0; }
|
||||
gb_inline bool str_le(String a, String b) { return string_compare(a, b) <= 0; }
|
||||
gb_inline bool str_ge(String a, String b) { return string_compare(a, b) >= 0; }
|
||||
|
||||
|
||||
gb_inline bool str_has_prefix(String s, String prefix) {
|
||||
isize i;
|
||||
if (prefix.len < s.len) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < prefix.len; i++) {
|
||||
if (s.text[i] != prefix.text[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_inline isize string_extension_position(String str) {
|
||||
isize dot_pos = -1;
|
||||
@@ -163,7 +157,8 @@ gb_inline bool string_has_extension(String str, String ext) {
|
||||
}
|
||||
|
||||
bool string_contains_char(String s, u8 c) {
|
||||
for (isize i = 0; i < s.len; i++) {
|
||||
isize i;
|
||||
for (i = 0; i < s.len; i++) {
|
||||
if (s.text[i] == c)
|
||||
return true;
|
||||
}
|
||||
@@ -172,20 +167,23 @@ bool string_contains_char(String s, u8 c) {
|
||||
|
||||
// TODO(bill): Make this non-windows specific
|
||||
String16 string_to_string16(gbAllocator a, String s) {
|
||||
int len, len1;
|
||||
wchar_t *text;
|
||||
|
||||
if (s.len < 1) {
|
||||
return make_string16(NULL, 0);
|
||||
}
|
||||
|
||||
int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||
cast(char *)s.text, s.len, NULL, 0);
|
||||
len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||
cast(char *)s.text, s.len, NULL, 0);
|
||||
if (len == 0) {
|
||||
return make_string16(NULL, 0);
|
||||
}
|
||||
|
||||
wchar_t *text = gb_alloc_array(a, wchar_t, len+1);
|
||||
text = gb_alloc_array(a, wchar_t, len+1);
|
||||
|
||||
int len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||
cast(char *)s.text, s.len, text, len);
|
||||
len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||
cast(char *)s.text, s.len, text, len);
|
||||
if (len1 == 0) {
|
||||
gb_free(a, text);
|
||||
return make_string16(NULL, 0);
|
||||
@@ -196,22 +194,23 @@ String16 string_to_string16(gbAllocator a, String s) {
|
||||
}
|
||||
|
||||
String string16_to_string(gbAllocator a, String16 s) {
|
||||
int len, len1;
|
||||
u8 *text;
|
||||
|
||||
if (s.len < 1) {
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
|
||||
int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
s.text, s.len, NULL, 0,
|
||||
NULL, NULL);
|
||||
len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
s.text, s.len, NULL, 0, NULL, NULL);
|
||||
if (len == 0) {
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
|
||||
u8 *text = gb_alloc_array(a, u8, len+1);
|
||||
text = gb_alloc_array(a, u8, len+1);
|
||||
|
||||
int len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
s.text, s.len, cast(char *)text, len,
|
||||
NULL, NULL);
|
||||
len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
s.text, s.len, cast(char *)text, len, NULL, NULL);
|
||||
if (len1 == 0) {
|
||||
gb_free(a, text);
|
||||
return make_string(NULL, 0);
|
||||
@@ -239,8 +238,10 @@ String string16_to_string(gbAllocator a, String16 s) {
|
||||
|
||||
|
||||
bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
|
||||
u8 c;
|
||||
|
||||
if (s.text[0] == quote &&
|
||||
(quote == '$' || quote == '"')) {
|
||||
(quote == '\'' || quote == '"')) {
|
||||
return false;
|
||||
} else if (s.text[0] >= 0x80) {
|
||||
Rune r = -1;
|
||||
@@ -258,7 +259,7 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
|
||||
if (s.len <= 1) {
|
||||
return false;
|
||||
}
|
||||
u8 c = s.text[1];
|
||||
c = s.text[1];
|
||||
s = make_string(s.text+2, s.len-2);
|
||||
|
||||
switch (c) {
|
||||
@@ -274,7 +275,7 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
|
||||
case '\\': *rune = '\\'; break;
|
||||
|
||||
|
||||
case '$':
|
||||
case '\'':
|
||||
case '"':
|
||||
if (c != quote) {
|
||||
return false;
|
||||
@@ -290,11 +291,12 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
|
||||
case '5':
|
||||
case '6':
|
||||
case '7': {
|
||||
isize i;
|
||||
i32 r = gb_digit_to_int(c);
|
||||
if (s.len < 2) {
|
||||
return false;
|
||||
}
|
||||
for (isize i = 0; i < 2; i++) {
|
||||
for (i = 0; i < 2; i++) {
|
||||
i32 d = gb_digit_to_int(s.text[i]);
|
||||
if (d < 0 || d > 7) {
|
||||
return false;
|
||||
@@ -311,18 +313,18 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
|
||||
case 'x':
|
||||
case 'u':
|
||||
case 'U': {
|
||||
isize count = 0;
|
||||
Rune r = 0;
|
||||
isize i, count = 0;
|
||||
switch (c) {
|
||||
case 'x': count = 2; break;
|
||||
case 'u': count = 4; break;
|
||||
case 'U': count = 8; break;
|
||||
}
|
||||
|
||||
Rune r = 0;
|
||||
if (s.len < count) {
|
||||
return false;
|
||||
}
|
||||
for (isize i = 0; i < count; i++) {
|
||||
for (i = 0; i < count; i++) {
|
||||
i32 d = gb_hex_digit_to_int(s.text[i]);
|
||||
if (d < 0) {
|
||||
return false;
|
||||
@@ -350,14 +352,16 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
|
||||
// 1 == original memory
|
||||
// 2 == new allocation
|
||||
i32 unquote_string(gbAllocator a, String *s_) {
|
||||
GB_ASSERT(s_ != NULL);
|
||||
String s = *s_;
|
||||
isize n = s.len;
|
||||
if (n < 2)
|
||||
u8 quote;
|
||||
if (n < 2) {
|
||||
return 0;
|
||||
u8 quote = s.text[0];
|
||||
if (quote != s.text[n-1])
|
||||
}
|
||||
quote = s.text[0];
|
||||
if (quote != s.text[n-1]) {
|
||||
return 0;
|
||||
}
|
||||
s.text += 1;
|
||||
s.len -= 2;
|
||||
|
||||
@@ -368,17 +372,19 @@ i32 unquote_string(gbAllocator a, String *s_) {
|
||||
*s_ = s;
|
||||
return 1;
|
||||
}
|
||||
if (quote != '"' && quote != '$')
|
||||
if (quote != '"' && quote != '\'') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (string_contains_char(s, '\n'))
|
||||
if (string_contains_char(s, '\n')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
|
||||
if (quote == '"') {
|
||||
*s_ = s;
|
||||
return 1;
|
||||
} else if (quote == '$') {
|
||||
} else if (quote == '\'') {
|
||||
Rune r = GB_RUNE_INVALID;
|
||||
isize size = gb_utf8_decode(s.text, s.len, &r);
|
||||
if ((size == s.len) && (r != -1 || size != 1)) {
|
||||
@@ -389,34 +395,36 @@ i32 unquote_string(gbAllocator a, String *s_) {
|
||||
}
|
||||
|
||||
|
||||
u8 rune_temp[4] = {0};
|
||||
isize buf_len = 3*s.len / 2;
|
||||
u8 *buf = gb_alloc_array(a, u8, buf_len);
|
||||
isize offset = 0;
|
||||
while (s.len > 0) {
|
||||
String tail_string = {0};
|
||||
Rune r = 0;
|
||||
bool multiple_bytes = false;
|
||||
bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
|
||||
if (!success) {
|
||||
gb_free(a, buf);
|
||||
return 0;
|
||||
}
|
||||
s = tail_string;
|
||||
{
|
||||
u8 rune_temp[4] = {0};
|
||||
isize buf_len = 3*s.len / 2;
|
||||
u8 *buf = gb_alloc_array(a, u8, buf_len);
|
||||
isize offset = 0;
|
||||
while (s.len > 0) {
|
||||
String tail_string = {0};
|
||||
Rune r = 0;
|
||||
bool multiple_bytes = false;
|
||||
bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
|
||||
if (!success) {
|
||||
gb_free(a, buf);
|
||||
return 0;
|
||||
}
|
||||
s = tail_string;
|
||||
|
||||
if (r < 0x80 || !multiple_bytes) {
|
||||
buf[offset++] = cast(u8)r;
|
||||
} else {
|
||||
isize size = gb_utf8_encode_rune(rune_temp, r);
|
||||
gb_memmove(buf+offset, rune_temp, size);
|
||||
offset += size;
|
||||
}
|
||||
if (r < 0x80 || !multiple_bytes) {
|
||||
buf[offset++] = cast(u8)r;
|
||||
} else {
|
||||
isize size = gb_utf8_encode_rune(rune_temp, r);
|
||||
gb_memmove(buf+offset, rune_temp, size);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
if (quote == '$' && s.len != 0) {
|
||||
gb_free(a, buf);
|
||||
return 0;
|
||||
if (quote == '\'' && s.len != 0) {
|
||||
gb_free(a, buf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*s_ = make_string(buf, offset);
|
||||
}
|
||||
*s_ = make_string(buf, offset);
|
||||
return 2;
|
||||
}
|
||||
|
||||
+4
-3
@@ -77,12 +77,13 @@ f64 time_stamp_as_ms(TimeStamp ts, u64 freq) {
|
||||
}
|
||||
|
||||
void timings_print_all(Timings *t) {
|
||||
char const SPACES[] = " ";
|
||||
isize max_len, i;
|
||||
|
||||
timings__stop_current_section(t);
|
||||
t->total.finish = time_stamp_time_now();
|
||||
|
||||
char const SPACES[] = " ";
|
||||
|
||||
isize max_len = t->total.label.len;
|
||||
max_len = t->total.label.len;
|
||||
for_array(i, t->sections) {
|
||||
TimeStamp ts = t->sections.e[i];
|
||||
max_len = gb_max(max_len, ts.label.len);
|
||||
|
||||
+239
-83
@@ -4,11 +4,11 @@
|
||||
TOKEN_KIND(Token_Comment, "Comment"), \
|
||||
\
|
||||
TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \
|
||||
TOKEN_KIND(Token_Identifier, "Identifier"), \
|
||||
TOKEN_KIND(Token_Integer, "Integer"), \
|
||||
TOKEN_KIND(Token_Float, "Float"), \
|
||||
TOKEN_KIND(Token_Rune, "Rune"), \
|
||||
TOKEN_KIND(Token_String, "String"), \
|
||||
TOKEN_KIND(Token_Ident, "identifier"), \
|
||||
TOKEN_KIND(Token_Integer, "integer"), \
|
||||
TOKEN_KIND(Token_Float, "float"), \
|
||||
TOKEN_KIND(Token_Rune, "rune"), \
|
||||
TOKEN_KIND(Token_String, "string"), \
|
||||
TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \
|
||||
\
|
||||
TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
|
||||
@@ -34,9 +34,6 @@ TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
|
||||
TOKEN_KIND(Token_transmute, "transmute"), \
|
||||
TOKEN_KIND(Token_down_cast, "down_cast"), \
|
||||
TOKEN_KIND(Token_union_cast, "union_cast"), \
|
||||
\
|
||||
TOKEN_KIND(Token_Prime, "'"), \
|
||||
TOKEN_KIND(Token_DoublePrime, "''"), \
|
||||
\
|
||||
TOKEN_KIND(Token_CmpAnd, "&&"), \
|
||||
TOKEN_KIND(Token_CmpOr, "||"), \
|
||||
@@ -56,8 +53,6 @@ TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \
|
||||
TOKEN_KIND(Token_CmpAndEq, "&&="), \
|
||||
TOKEN_KIND(Token_CmpOrEq, "||="), \
|
||||
TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \
|
||||
TOKEN_KIND(Token_Increment, "++"), \
|
||||
TOKEN_KIND(Token_Decrement, "--"), \
|
||||
TOKEN_KIND(Token_ArrowRight, "->"), \
|
||||
TOKEN_KIND(Token_ArrowLeft, "<-"), \
|
||||
\
|
||||
@@ -80,13 +75,14 @@ TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
|
||||
TOKEN_KIND(Token_Semicolon, ";"), \
|
||||
TOKEN_KIND(Token_Period, "."), \
|
||||
TOKEN_KIND(Token_Comma, ","), \
|
||||
TOKEN_KIND(Token_Ellipsis, ".."), \
|
||||
TOKEN_KIND(Token_RangeExclusive, "..<"), \
|
||||
TOKEN_KIND(Token_Ellipsis, "..."), \
|
||||
TOKEN_KIND(Token_Interval, "..<"), \
|
||||
TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
|
||||
\
|
||||
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
TOKEN_KIND(Token_type, "type"), \
|
||||
TOKEN_KIND(Token_proc, "proc"), \
|
||||
TOKEN_KIND(Token_macro, "macro"), \
|
||||
TOKEN_KIND(Token_match, "match"), \
|
||||
TOKEN_KIND(Token_break, "break"), \
|
||||
TOKEN_KIND(Token_continue, "continue"), \
|
||||
@@ -96,18 +92,20 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
TOKEN_KIND(Token_then, "then"), \
|
||||
TOKEN_KIND(Token_if, "if"), \
|
||||
TOKEN_KIND(Token_else, "else"), \
|
||||
TOKEN_KIND(Token_while, "while"), \
|
||||
TOKEN_KIND(Token_for, "for"), \
|
||||
TOKEN_KIND(Token_when, "when"), \
|
||||
TOKEN_KIND(Token_range, "range"), \
|
||||
TOKEN_KIND(Token_defer, "defer"), \
|
||||
TOKEN_KIND(Token_return, "return"), \
|
||||
TOKEN_KIND(Token_give, "give"), \
|
||||
TOKEN_KIND(Token_struct, "struct"), \
|
||||
TOKEN_KIND(Token_union, "union"), \
|
||||
TOKEN_KIND(Token_raw_union, "raw_union"), \
|
||||
TOKEN_KIND(Token_enum, "enum"), \
|
||||
TOKEN_KIND(Token_vector, "vector"), \
|
||||
TOKEN_KIND(Token_using, "using"), \
|
||||
TOKEN_KIND(Token_asm, "asm"), \
|
||||
TOKEN_KIND(Token_volatile, "volatile"), \
|
||||
TOKEN_KIND(Token_atomic, "atomic"), \
|
||||
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
|
||||
TOKEN_KIND(Token_push_context, "push_context"), \
|
||||
TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
|
||||
@@ -144,7 +142,7 @@ i32 token_pos_cmp(TokenPos a, TokenPos b) {
|
||||
return (a.line < b.line) ? -1 : +1;
|
||||
}
|
||||
|
||||
bool token_pos_are_equal(TokenPos a, TokenPos b) {
|
||||
bool token_pos_eq(TokenPos a, TokenPos b) {
|
||||
return token_pos_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
@@ -156,10 +154,10 @@ typedef struct Token {
|
||||
} Token;
|
||||
|
||||
Token empty_token = {Token_Invalid};
|
||||
Token blank_token = {Token_Identifier, {cast(u8 *)"_", 1}};
|
||||
Token blank_token = {Token_Ident, {cast(u8 *)"_", 1}};
|
||||
|
||||
Token make_token_ident(String s) {
|
||||
Token t = {Token_Identifier, s};
|
||||
Token t = {Token_Ident, s};
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -177,68 +175,71 @@ void init_global_error_collector(void) {
|
||||
gb_mutex_init(&global_error_collector.mutex);
|
||||
}
|
||||
|
||||
|
||||
void warning(Token token, char *fmt, ...) {
|
||||
void warning_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_lock(&global_error_collector.mutex);
|
||||
|
||||
global_error_collector.warning_count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
|
||||
va_list va;
|
||||
|
||||
if (!token_pos_eq(global_error_collector.prev, token.pos)) {
|
||||
global_error_collector.prev = token.pos;
|
||||
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) Warning: %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
gb_mutex_unlock(&global_error_collector.mutex);
|
||||
}
|
||||
|
||||
void error(Token token, char *fmt, ...) {
|
||||
void error_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_lock(&global_error_collector.mutex);
|
||||
|
||||
global_error_collector.count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
|
||||
va_list va;
|
||||
|
||||
if (!token_pos_eq(global_error_collector.prev, token.pos)) {
|
||||
global_error_collector.prev = token.pos;
|
||||
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
gb_mutex_unlock(&global_error_collector.mutex);
|
||||
}
|
||||
|
||||
void syntax_error(Token token, char *fmt, ...) {
|
||||
void syntax_error_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_lock(&global_error_collector.mutex);
|
||||
|
||||
global_error_collector.count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
|
||||
va_list va;
|
||||
|
||||
if (!token_pos_eq(global_error_collector.prev, token.pos)) {
|
||||
global_error_collector.prev = token.pos;
|
||||
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
gb_mutex_unlock(&global_error_collector.mutex);
|
||||
}
|
||||
|
||||
|
||||
void warning(Token token, char *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
warning_va(token, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void error(Token token, char *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
error_va(token, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void syntax_error(Token token, char *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
syntax_error_va(token, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
void compiler_error(char *fmt, ...) {
|
||||
va_list va;
|
||||
|
||||
@@ -284,6 +285,14 @@ typedef enum TokenizerInitError {
|
||||
} TokenizerInitError;
|
||||
|
||||
|
||||
typedef struct TokenizerState {
|
||||
Rune curr_rune; // current character
|
||||
u8 * curr; // character pos
|
||||
u8 * read_curr; // pos from start
|
||||
u8 * line; // current line pos
|
||||
isize line_count;
|
||||
} TokenizerState;
|
||||
|
||||
typedef struct Tokenizer {
|
||||
String fullpath;
|
||||
u8 *start;
|
||||
@@ -300,11 +309,31 @@ typedef struct Tokenizer {
|
||||
} Tokenizer;
|
||||
|
||||
|
||||
TokenizerState save_tokenizer_state(Tokenizer *t) {
|
||||
TokenizerState state = {0};
|
||||
state.curr_rune = t->curr_rune;
|
||||
state.curr = t->curr;
|
||||
state.read_curr = t->read_curr;
|
||||
state.line = t->line;
|
||||
state.line_count = t->line_count;
|
||||
return state;
|
||||
}
|
||||
|
||||
void restore_tokenizer_state(Tokenizer *t, TokenizerState *state) {
|
||||
t->curr_rune = state->curr_rune;
|
||||
t->curr = state->curr;
|
||||
t->read_curr = state->read_curr;
|
||||
t->line = state->line;
|
||||
t->line_count = state->line_count;
|
||||
}
|
||||
|
||||
|
||||
void tokenizer_err(Tokenizer *t, char *msg, ...) {
|
||||
va_list va;
|
||||
isize column = t->read_curr - t->line+1;
|
||||
if (column < 1)
|
||||
if (column < 1) {
|
||||
column = 1;
|
||||
}
|
||||
|
||||
gb_printf_err("%.*s(%td:%td) Syntax error: ", LIT(t->fullpath), t->line_count, column);
|
||||
|
||||
@@ -404,7 +433,10 @@ gb_inline void destroy_tokenizer(Tokenizer *t) {
|
||||
}
|
||||
|
||||
void tokenizer_skip_whitespace(Tokenizer *t) {
|
||||
while (rune_is_whitespace(t->curr_rune)) {
|
||||
while (t->curr_rune == ' ' ||
|
||||
t->curr_rune == '\t' ||
|
||||
t->curr_rune == '\n' ||
|
||||
t->curr_rune == '\r') {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
@@ -450,23 +482,27 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
|
||||
if (t->curr_rune == 'b') { // Binary
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 2);
|
||||
if (t->curr - prev <= 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);
|
||||
if (t->curr - prev <= 2)
|
||||
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);
|
||||
if (t->curr - prev <= 2)
|
||||
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);
|
||||
if (t->curr - prev <= 2)
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else {
|
||||
seen_decimal_point = false;
|
||||
scan_mantissa(t, 10);
|
||||
@@ -485,8 +521,15 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
|
||||
|
||||
fraction:
|
||||
if (t->curr_rune == '.') {
|
||||
token.kind = Token_Float;
|
||||
// HACK(bill): This may be inefficient
|
||||
TokenizerState state = save_tokenizer_state(t);
|
||||
advance_to_next_rune(t);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -500,6 +543,7 @@ exponent:
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
|
||||
end:
|
||||
token.string.len = t->curr - token.string.text;
|
||||
return token;
|
||||
}
|
||||
@@ -608,19 +652,63 @@ gb_inline TokenKind token_kind_dub_eq(Tokenizer *t, Rune sing_rune, TokenKind si
|
||||
return sing;
|
||||
}
|
||||
|
||||
Token tokenizer_get_token(Tokenizer *t) {
|
||||
Token token = {0};
|
||||
Rune curr_rune;
|
||||
void tokenizer__fle_update(Tokenizer *t) {
|
||||
t->curr_rune = '/';
|
||||
t->curr = t->curr-1;
|
||||
t->read_curr = t->curr+1;
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
|
||||
// NOTE(bill): needed if comment is straight after a "semicolon"
|
||||
bool tokenizer_find_line_end(Tokenizer *t) {
|
||||
while (t->curr_rune == '/' || t->curr_rune == '*') {
|
||||
if (t->curr_rune == '/') {
|
||||
tokenizer__fle_update(t);
|
||||
return true;
|
||||
}
|
||||
|
||||
advance_to_next_rune(t);
|
||||
while (t->curr_rune >= 0) {
|
||||
Rune r = t->curr_rune;
|
||||
if (r == '\n') {
|
||||
tokenizer__fle_update(t);
|
||||
return true;
|
||||
}
|
||||
advance_to_next_rune(t);
|
||||
if (r == '*' && t->curr_rune == '/') {
|
||||
advance_to_next_rune(t);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
tokenizer_skip_whitespace(t);
|
||||
if (t->curr_rune < 0 || t->curr_rune == '\n') {
|
||||
tokenizer__fle_update(t);
|
||||
return true;
|
||||
}
|
||||
if (t->curr_rune != '/') {
|
||||
tokenizer__fle_update(t);
|
||||
return false;
|
||||
}
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
|
||||
tokenizer__fle_update(t);
|
||||
return false;
|
||||
}
|
||||
|
||||
Token tokenizer_get_token(Tokenizer *t) {
|
||||
tokenizer_skip_whitespace(t);
|
||||
|
||||
Token token = {0};
|
||||
token.string = make_string(t->curr, 1);
|
||||
token.pos.file = t->fullpath;
|
||||
token.pos.line = t->line_count;
|
||||
token.pos.column = t->curr - t->line + 1;
|
||||
|
||||
curr_rune = t->curr_rune;
|
||||
Rune curr_rune = t->curr_rune;
|
||||
if (rune_is_letter(curr_rune)) {
|
||||
token.kind = Token_Identifier;
|
||||
token.kind = Token_Ident;
|
||||
while (rune_is_letter(t->curr_rune) || rune_is_digit(t->curr_rune)) {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
@@ -656,17 +744,50 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
token.kind = Token_EOF;
|
||||
break;
|
||||
|
||||
case '\'':
|
||||
token.kind = Token_Prime;
|
||||
if (t->curr_rune == '\'') {
|
||||
case '\'': // Rune Literal
|
||||
{
|
||||
token.kind = Token_Rune;
|
||||
Rune quote = curr_rune;
|
||||
bool valid = true;
|
||||
i32 n = 0, success;
|
||||
for (;;) {
|
||||
Rune r = t->curr_rune;
|
||||
if (r == '\n' || r < 0) {
|
||||
tokenizer_err(t, "Rune literal not terminated");
|
||||
break;
|
||||
}
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_DoublePrime;
|
||||
if (r == quote) {
|
||||
break;
|
||||
}
|
||||
n++;
|
||||
if (r == '\\') {
|
||||
if (!scan_escape(t, quote)) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// TODO(bill): Better Error Handling
|
||||
if (valid && n != 1) {
|
||||
tokenizer_err(t, "Invalid rune literal");
|
||||
}
|
||||
token.string.len = t->curr - token.string.text;
|
||||
success = unquote_string(heap_allocator(), &token.string);
|
||||
if (success > 0) {
|
||||
if (success == 2) {
|
||||
array_add(&t->allocated_strings, token.string);
|
||||
}
|
||||
return token;
|
||||
} else {
|
||||
tokenizer_err(t, "Invalid rune literal");
|
||||
}
|
||||
} break;
|
||||
|
||||
case '`': // Raw String Literal
|
||||
case '"': // String Literal
|
||||
{
|
||||
i32 success;
|
||||
Rune quote = curr_rune;
|
||||
token.kind = Token_String;
|
||||
if (curr_rune == '"') {
|
||||
@@ -677,10 +798,12 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
break;
|
||||
}
|
||||
advance_to_next_rune(t);
|
||||
if (r == quote)
|
||||
if (r == quote) {
|
||||
break;
|
||||
if (r == '\\')
|
||||
scan_escape(t, '"');
|
||||
}
|
||||
if (r == '\\') {
|
||||
scan_escape(t, quote);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (;;) {
|
||||
@@ -690,12 +813,13 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
break;
|
||||
}
|
||||
advance_to_next_rune(t);
|
||||
if (r == quote)
|
||||
if (r == quote) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
token.string.len = t->curr - token.string.text;
|
||||
i32 success = unquote_string(heap_allocator(), &token.string);
|
||||
success = unquote_string(heap_allocator(), &token.string);
|
||||
if (success > 0) {
|
||||
if (success == 2) {
|
||||
array_add(&t->allocated_strings, token.string);
|
||||
@@ -712,38 +836,70 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
token = scan_number_to_token(t, true);
|
||||
} else if (t->curr_rune == '.') { // Could be an ellipsis
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_Ellipsis;
|
||||
if (t->curr_rune == '<') {
|
||||
if (t->curr_rune == '.') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_RangeExclusive;
|
||||
token.kind = Token_Ellipsis;
|
||||
} else if (t->curr_rune == '<') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_Interval;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '#': token.kind = Token_Hash; break;
|
||||
case '@': token.kind = Token_At; break;
|
||||
case '^': token.kind = Token_Pointer; break;
|
||||
case '?': token.kind = Token_Maybe; break;
|
||||
case ';': token.kind = Token_Semicolon; break;
|
||||
case ',': token.kind = Token_Comma; 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_Colon; break;
|
||||
case '#':
|
||||
token.kind = Token_Hash;
|
||||
break;
|
||||
case '@':
|
||||
token.kind = Token_At;
|
||||
break;
|
||||
case '^':
|
||||
token.kind = Token_Pointer;
|
||||
break;
|
||||
case '?':
|
||||
token.kind = Token_Maybe;
|
||||
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_kind_variant2(t, Token_Mul, Token_MulEq); break;
|
||||
case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break;
|
||||
case '=': token.kind = token_kind_variant2(t, Token_Eq, Token_CmpEq); break;
|
||||
case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break;
|
||||
case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break;
|
||||
case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Increment); break;
|
||||
case '-': token.kind = token_kind_variant4(t, Token_Sub, Token_SubEq, '-', Token_Decrement, '>', Token_ArrowRight); break;
|
||||
case '+':
|
||||
token.kind = token_kind_variant2(t, Token_Add, Token_AddEq);
|
||||
break;
|
||||
case '-':
|
||||
token.kind = token_kind_variant3(t, Token_Sub, Token_SubEq, '>', Token_ArrowRight);
|
||||
break;
|
||||
case '/': {
|
||||
if (t->curr_rune == '/') {
|
||||
while (t->curr_rune != '\n') {
|
||||
while (t->curr_rune != '\n' && t->curr_rune != GB_RUNE_EOF) {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
token.kind = Token_Comment;
|
||||
|
||||
+1
-1
@@ -42,10 +42,10 @@ bool rune_is_whitespace(Rune r) {
|
||||
|
||||
|
||||
bool is_string_an_identifier(String s) {
|
||||
isize offset = 0;
|
||||
if (s.len < 1) {
|
||||
return false;
|
||||
}
|
||||
isize offset = 0;
|
||||
while (offset < s.len) {
|
||||
bool ok = false;
|
||||
Rune r = -1;
|
||||
|
||||
Reference in New Issue
Block a user