Compare commits

...

64 Commits

Author SHA1 Message Date
Ginger Bill b1e35b6da3 Fix array pointer as iterators; Remove stack allocations in startup_runtime 2017-01-06 15:47:07 +00:00
Ginger Bill fc1af0a04b Fix build error caused by invalid iterator types in for 2017-01-05 23:50:44 +00:00
Ginger Bill 4afb3f8fa4 Fix line comments at the end of file 2017-01-05 22:35:32 +00:00
Ginger Bill 207b252f23 Fix checking termination of a procedure 2017-01-05 22:32:19 +00:00
Ginger Bill 1356dfeec2 Fix SUBSYSTEM for link.exe 2017-01-05 21:58:24 +00:00
Ginger Bill d025791462 v0.0.5a 2017-01-05 21:46:09 +00:00
Ginger Bill b07ee9ec23 Fix problem with odin build 2017-01-05 21:43:36 +00:00
Ginger Bill 915b5cdab7 Rename llir -> ir 2017-01-04 11:24:32 +00:00
Ginger Bill c8f99b360f Fix init_arena_from_context 2017-01-03 20:35:47 +00:00
Ginger Bill b76f6a8c27 Removed capacity arguments from new_slice and slice_ptr 2017-01-03 20:31:14 +00:00
Ginger Bill cff1b3dff6 v0.0.5
Fix enumerations to so they work as integers in indices; Add llir_opt.c and llir_print.c
2017-01-03 20:07:46 +00:00
Ginger Bill 883dd0642c Change prev ssa to llir; 2017-01-03 19:34:06 +00:00
Ginger Bill 40f5dd56f7 Fix for interval upper bound check 2017-01-03 19:22:08 +00:00
Ginger Bill 70d4ca00df while; range is now for; remove ++ and -- 2017-01-03 19:11:12 +00:00
Ginger Bill a86896e4d3 Interval expressions in range 2017-01-03 18:02:13 +00:00
Ginger Bill a3883a178c range statement 2017-01-02 18:47:47 +00:00
Ginger Bill ce89a1428e Fix parameter/field lists and #import #include syntax 2017-01-02 00:26:28 +00:00
Ginger Bill 9202bd1b06 Nearly finished Jai-like declarations 2017-01-01 20:41:10 +00:00
Ginger Bill a48e0c7179 Begin transition to Jai-like syntax 2017-01-01 19:08:03 +00:00
Ginger Bill 3f1195cd03 More declaration differentiation in semantic stage e.g. make only variables and constants 2017-01-01 18:18:43 +00:00
Ginger Bill 311b5cb6e2 Add enum type info and fix enum casting 2017-01-01 16:58:38 +00:00
Ginger Bill 6fef74317c Bring back enum but using iota 2017-01-01 16:18:50 +00:00
Ginger Bill 0c6775ca14 Fix give expressions 2016-12-30 22:52:43 +00:00
Ginger Bill 6748f305db select to phi in if expression 2016-12-30 16:31:04 +00:00
Ginger Bill 2ecafda1d3 if expression 2016-12-30 16:21:45 +00:00
Ginger Bill 23d32f34e5 Block Expressions and give 2016-12-30 15:45:10 +00:00
Ginger Bill d714bece47 Handle calling conventions correctly 2016-12-22 23:06:31 +00:00
Ginger Bill 923b039cf6 Fix anonymous procedures and their dependencies 2016-12-21 15:20:33 +00:00
Ginger Bill d0e1efe622 Generic (grouped) declarations: var, let, const, type, import, include 2016-12-20 18:58:17 +00:00
Ginger Bill 478d63424f Remove enum for favour of Go-style enumerations 2016-12-19 14:03:59 +00:00
Ginger Bill ac1566762b Golang style enumerations with iota 2016-12-19 13:18:20 +00:00
Ginger Bill f5eeecaca5 Begin generic declarations for lists of specifications 2016-12-19 11:56:45 +00:00
Ginger Bill 77e219d442 Change var decl syntax
`var x int;` from `x: int;`
2016-12-18 22:32:18 +00:00
Ginger Bill 4c10fbdcd4 Change record field syntax 2016-12-18 22:23:34 +00:00
Ginger Bill e370337f97 var/const decl; remove : from parameter lists 2016-12-18 21:50:14 +00:00
Ginger Bill 5217eb55b4 Change of proc and type declaration syntax to "prefix" style
`proc name()` from `name :: proc()`
2016-12-18 20:34:55 +00:00
Ginger Bill 062a2c63e1 Very minor style changes 2016-12-18 16:17:45 +00:00
Ginger Bill b957365b0d Merge branch 'master' of https://github.com/gingerBill/Odin
# Conflicts:
#	src/ssa_print.c
2016-12-17 10:26:07 +00:00
Ginger Bill 625b98eac4 Fix issue with printing invalid IR for nested unions
(GitHub #4)
2016-12-17 10:23:28 +00:00
Ginger Bill 0f809989e3 Fix issue with printing invalid IR for nested unions
(GitHub #4)
2016-12-17 10:22:38 +00:00
Ginger Bill d4457e9fa4 Minor changes 2016-12-16 20:18:23 +00:00
Ginger Bill 9634b28b07 Add atomic.odin, sync.odin, sys/windows.odin 2016-12-16 17:21:39 +00:00
Ginger Bill f567983260 Semicolons mandatory again (and probably forever now...) 2016-12-16 11:31:08 +00:00
Ginger Bill ad84314143 Update README.md 2016-12-09 16:31:14 +00:00
Ginger Bill a6f8c9d6e0 v0.0.4 - odin build_dll, atomic.odin, sync.odin 2016-12-09 16:28:31 +00:00
Ginger Bill de9016b7d0 Fix DllMain and only call main on DLL_PROCESS_ATTACH 2016-12-09 00:43:50 +00:00
Ginger Bill e8b4228833 Fix procedure casting; SUBSYSTEM to CONSOLE in linker 2016-12-09 00:24:12 +00:00
Ginger Bill 0d69dfcde6 Custom entry points on Windows (DllMain; WinMain) 2016-12-09 00:07:08 +00:00
Ginger Bill fa89d2775a build_dll; Require an entry point procedure main 2016-12-08 17:33:30 +00:00
Ginger Bill 60b6538a7a Set :: as a single token 2016-12-07 10:20:25 +00:00
Ginger Bill d9bd770992 Fix enumeration constant expressions; Remove empty file error 2016-12-06 14:06:31 +00:00
Ginger Bill 517b34f798 Improve parsing with semicolon insertion 2016-12-06 00:05:59 +00:00
Ginger Bill a16bdb215a Go/BCPL style semicolon insertion during tokenizing stage 2016-12-05 23:39:26 +00:00
Ginger Bill 88aa74bbb9 Remove multiple messages for cyclic type errors. 2016-12-05 00:22:30 +00:00
Ginger Bill 8ec9811d7a Fix (Crude) cyclic type checking for arrays and vectors 2016-12-04 23:41:21 +00:00
Ginger Bill c71b547cde (Crude) Cyclic Type Checking 2016-12-04 23:25:52 +00:00
Ginger Bill 76e724718c Fix preload initialization ordering 2016-12-04 00:49:06 +00:00
Ginger Bill 0b87313f08 Change entity collection strategy 2016-12-03 00:16:51 +00:00
Ginger Bill 4bb45700a5 Semicolons are required; when condition for certain file scope declarations; #import syntax change 2016-12-01 22:44:00 +00:00
Ginger Bill be8b9bda2f Delay importing entities till all other entities are collected 2016-11-30 20:46:00 +00:00
Ginger Bill ab2ca7cf59 Fix illegal type declaration error 2016-11-30 20:07:23 +00:00
Ginger Bill b76c8abe73 error_node 2016-11-30 10:52:09 +00:00
Ginger Bill d9c686b53d when statement; Better entity collection system (for both local and global); Better parsing for record declarations 2016-11-29 23:57:06 +00:00
Ginger Bill b232b9d5ea Basic when statement - Compile time if statement
This is similar to an #if in C but handled during the semantic checking stage.
2016-11-29 22:08:48 +00:00
45 changed files with 15426 additions and 13233 deletions
+15 -19
View File
@@ -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
+13 -12
View File
@@ -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 -we4133 ^
-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
+67 -29
View File
@@ -1,32 +1,70 @@
#import "fmt.odin"
#import "utf8.odin"
#foreign_system_library "winmm" when ODIN_OS == "windows";
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
#import "fmt.odin";
main :: proc() {
MAX :: 64
buf: [MAX]rune
backing: [MAX]byte
offset: int
timeGetTime :: proc() -> u32 #foreign #dll_import
GetSystemTimeAsFileTime :: proc(SystemTimeAsFileTime : ^win32.FILETIME) #foreign #dll_import
msg := "Hello"
count := utf8.rune_count(msg)
assert(count <= MAX)
runes := buf[:count]
offset = 0
for i := 0; i < count; i++ {
s := msg[offset:]
r, len := utf8.decode_rune(s)
runes[count-i-1] = r
offset += len
}
offset = 0
for i := 0; i < count; i++ {
data, len := utf8.encode_rune(runes[i])
copy(backing[offset:], data[:len])
offset += len
}
reverse := backing[:offset] as string
fmt.println(reverse) // olleH
GetCommandLineArguments :: proc() -> []string {
argString := win32.GetCommandLineA();
fullArgString := to_odin_string(argString);
// Count Spaces
for r : fullArgString {
fmt.println(r);
}
}
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
View File
@@ -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 :: '💕'
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
View File
@@ -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
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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 -1
View File
@@ -1,4 +1,4 @@
#load "win32.odin"
#include "win32.odin"
assume :: proc(cond: bool) #foreign "llvm.assume"
+19 -21
View File
@@ -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
View File
@@ -28,7 +28,7 @@ thing :: proc() {
*/
/*
#load "fmt.odin"
#include "fmt.odin"
thing :: proc() {
println("Hello5!")
+179 -210
View File
@@ -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
View File
@@ -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
}
+101
View File
@@ -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;
}
+322 -332
View File
@@ -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, ' ') }
print_nl_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, '\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] = '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, '-')
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, '-')
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, '0')
return
bprint_rune(buffer, '0');
return;
}
if f < 0 {
print_rune_to_buffer(buffer, '-')
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, '.')
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_f32_to_buffer(buf, f)
case f64: print_f64_to_buffer(buf, f)
// 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 '0' <= r && r <= '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 - '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 != '%' {
continue
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 == '%' {
print_string_to_buffer(buf, "%")
i++
prev = i
continue
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, ' ')
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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
File diff suppressed because it is too large Load Diff
+1 -171
View File
@@ -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"
+272
View File
@@ -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;
}
+91
View File
@@ -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);
}
}
}
+555
View File
@@ -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
View File
@@ -1,17 +1,15 @@
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
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;
}
-495
View File
@@ -1,495 +0,0 @@
#foreign_system_library "user32"
#foreign_system_library "gdi32"
_:= compile_assert(ODIN_OS == "windows")
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 = '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,
}
+13 -16
View File
@@ -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)
+172
View File
@@ -0,0 +1,172 @@
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.5d");
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 ");
}
}
+541 -332
View File
File diff suppressed because it is too large Load Diff
+200 -151
View File
@@ -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
View File
@@ -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);
}
+878 -678
View File
File diff suppressed because it is too large Load Diff
+384 -213
View File
@@ -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,205 @@ 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);
gbString t = type_to_string(operand.type);
error_node(operand.expr, "Cannot iterate over `%s` of type `%s`", s, t);
gb_string_free(t);
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 +753,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 +822,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 +871,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 +890,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 +943,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 +960,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 +983,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 +1012,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 +1056,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 +1068,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 +1080,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 +1160,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 +1203,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
case_end;
default:
error(us->token, "Invalid AST: Using Statement");
break;
@@ -1098,33 +1227,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;
}
}
+363 -148
View File
@@ -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];
@@ -285,6 +277,27 @@ gb_global Type *t_type_info_union = NULL;
gb_global Type *t_type_info_raw_union = NULL;
gb_global Type *t_type_info_enum = NULL;
gb_global Type *t_type_info_named_ptr = NULL;
gb_global Type *t_type_info_integer_ptr = NULL;
gb_global Type *t_type_info_float_ptr = NULL;
gb_global Type *t_type_info_any_ptr = NULL;
gb_global Type *t_type_info_string_ptr = NULL;
gb_global Type *t_type_info_boolean_ptr = NULL;
gb_global Type *t_type_info_pointer_ptr = NULL;
gb_global Type *t_type_info_maybe_ptr = NULL;
gb_global Type *t_type_info_procedure_ptr = NULL;
gb_global Type *t_type_info_array_ptr = NULL;
gb_global Type *t_type_info_slice_ptr = NULL;
gb_global Type *t_type_info_vector_ptr = NULL;
gb_global Type *t_type_info_tuple_ptr = NULL;
gb_global Type *t_type_info_struct_ptr = NULL;
gb_global Type *t_type_info_union_ptr = NULL;
gb_global Type *t_type_info_raw_union_ptr = NULL;
gb_global Type *t_type_info_enum_ptr = NULL;
gb_global Type *t_allocator = NULL;
gb_global Type *t_allocator_ptr = NULL;
gb_global Type *t_context = NULL;
@@ -299,14 +312,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 +415,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 +430,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 +451,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 +467,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 +513,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 +529,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 +539,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 +633,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 +645,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 +672,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,7 +697,7 @@ 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;
@@ -694,8 +713,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)) {
@@ -748,10 +768,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
}
}
}
@@ -790,7 +808,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;
@@ -802,6 +821,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;
@@ -814,6 +836,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;
}
@@ -931,7 +1012,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);
@@ -946,14 +1026,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;
}
}
@@ -975,34 +1047,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);
@@ -1034,11 +1092,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;
@@ -1047,14 +1165,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);
@@ -1063,7 +1217,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;
}
@@ -1071,8 +1225,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) {
@@ -1081,23 +1243,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;
}
@@ -1107,15 +1287,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;
}
@@ -1123,7 +1307,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) {
@@ -1166,7 +1350,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: {
@@ -1186,52 +1373,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;
@@ -1242,46 +1443,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;
}
@@ -1371,7 +1577,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;
@@ -1432,11 +1638,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;
@@ -1473,6 +1674,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;
}
-65
View File
@@ -13,71 +13,6 @@ gb_global String global_module_path = {0};
gb_global bool global_module_path_set = false;
String get_module_dir() {
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;
}
i64 next_pow2(i64 n) {
if (n <= 0) {
return 0;
+3 -2
View File
@@ -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;
@@ -306,6 +306,7 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
case Token_Shr: c = a >> b; break;
default: goto error;
}
return make_exact_value_integer(c);
} break;
+1 -1
View File
@@ -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 (;;) {
+5806
View File
File diff suppressed because it is too large Load Diff
+118 -117
View File
@@ -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(&lt, root, 0, preorder);
i32 pre_num = ir_lt_depth_first_search(&lt, 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(&lt, v);
for (irBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) {
irBlock *u = ir_lt_eval(&lt, 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(&lt, v);
irBlock *v = w->preds.e[pred_index];
irBlock *u = ir_lt_eval(&lt, 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, lt.parent[w->index], w);
ir_lt_link(&lt, 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
View File
File diff suppressed because it is too large Load Diff
+93 -78
View File
@@ -1,22 +1,23 @@
#if defined(__cplusplus)
extern "C" {
#endif
#define VERSION_STRING "v0.0.3d"
#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,33 @@ 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";
} else {
link_settings = "/ENTRY:mainCRTStartup";
}
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 +271,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
+1175 -898
View File
File diff suppressed because it is too large Load Diff
-5411
View File
File diff suppressed because it is too large Load Diff
-1439
View File
File diff suppressed because it is too large Load Diff
-13
View File
@@ -108,19 +108,6 @@ 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 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; }
+197 -70
View File
@@ -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"), \
@@ -53,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, "<-"), \
\
@@ -77,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"), \
@@ -93,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"), \
@@ -141,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;
}
@@ -153,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;
}
@@ -174,68 +175,75 @@ 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);
} else if (token.pos.line == 0) {
gb_printf_err("Error: %s\n", gb_bprintf_va(fmt, 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);
} else if (token.pos.line == 0) {
gb_printf_err("Error: %s\n", gb_bprintf_va(fmt, 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;
@@ -281,6 +289,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;
@@ -297,11 +313,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);
@@ -401,7 +437,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);
}
}
@@ -447,23 +486,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);
@@ -482,8 +525,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);
}
@@ -497,6 +547,7 @@ exponent:
scan_mantissa(t, 10);
}
end:
token.string.len = t->curr - token.string.text;
return token;
}
@@ -605,19 +656,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);
}
@@ -745,38 +840,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;