Compare commits

..

63 Commits

Author SHA1 Message Date
Ginger Bill e1fdd675ce v0.1.0
Added:
 * Dynamic Arrays `[...]Type`
 * Dynamic Maps   `map[Key]Value`
 * Dynamic array and map literals
 * Custom struct alignemnt `struct #align 8 { bar: i8 }`
 * Allow `_` in numbers
 * Variadic `append`
 * fmt.sprint*
 * Entities prefixes with an underscore do not get exported on imports
 * Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps
 * enum types have an implict `names` field, a []string of all the names in that enum

Removed:
 * Maybe/option types
 * immutable variables
 * Remove `type` keyword and other "reserved" keywords
 * `compile_assert` and `assert`return the value of the condition for semantic reasons

Changed:
 * thread_local -> #thread_local
 * #include -> #load
 * Files only get checked if they are actually used
 * match x in y {} // For type match statements
 * Version numbering now starts from 0.1.0 and uses the convention:
 	- major.minor.patch

Fixes:
 * Many fmt.* fixes

To come very Soon™:
 * Linux and OS X builds (unofficial ones do exist already)
2017-02-11 21:20:57 +00:00
Ginger Bill 4306345ff1 Dynamic array syntax [...]Type; make entities private with a prefix of _; fix extension checking 2017-02-11 17:33:23 +00:00
Ginger Bill 346aa5f71c Only check files that have been truly imported. 2017-02-11 15:50:24 +00:00
Ginger Bill 73d6a55f5c Remove need for type keyword 2017-02-10 16:12:14 +00:00
Ginger Bill f18ae89931 Remove Maybe type; Enum names 2017-02-10 14:59:18 +00:00
Ginger Bill 454d0b5cf5 Fix global maps and initialize the preload types before 2017-02-07 18:13:37 +00:00
Ginger Bill 219ca0ac46 Map type info and fmt printing 2017-02-07 00:10:58 +00:00
Ginger Bill 5796c41357 map immutable fields: count, capacity, allocator 2017-02-06 22:53:48 +00:00
Ginger Bill 8cfae17535 map literals 2017-02-06 22:19:32 +00:00
Ginger Bill df78b8ad3e Make checking map key exists optional 2017-02-06 21:31:27 +00:00
Ginger Bill f11d73ffaa map string keys and for iterator 2017-02-06 20:54:51 +00:00
Ginger Bill c126339090 dynamic map insertion and lookup 2017-02-06 20:23:51 +00:00
Ginger Bill 9f2d9b596d Nearly implement dynamics map, missing insertion 2017-02-06 01:21:23 +00:00
Ginger Bill 00c7489157 Begin writing dynamic map procs and fix using bug in IR 2017-02-05 23:52:01 +00:00
Ginger Bill b1562edccf Add types.odin; Begin work on map 2017-02-05 18:17:55 +00:00
Ginger Bill 2a5b674d33 Custom struct alignment 2017-02-05 15:19:30 +00:00
Ginger Bill 7944b7714f Add build guards around compiling part of the code. 2017-02-01 21:00:32 +00:00
Ginger Bill 205f4664f8 Update code from OSX merge to be consistent with the rest of the code
Remove some dead code whilst I was here too :P
2017-02-01 20:59:14 +00:00
gingerBill c6133587d1 Merge pull request #16 from zhiayang/master
Basic, but sketchy, but somewhat usable, non-windows support
2017-02-01 20:31:57 +00:00
zhiayang 5516e80ab7 Merge branch 'master' of https://github.com/zhiayang/Odin 2017-02-02 04:21:42 +08:00
zhiayang 864310e3da oh boy, basic osx/unix support 2017-02-02 04:20:33 +08:00
Ginger Bill 4e7082a68d Change internals of context; Disable immutable 2017-02-01 17:52:55 +00:00
Ginger Bill 502e63b9c5 Remove dead code 2017-01-30 23:10:44 +00:00
Ginger Bill 34150385d8 Change vector memory layout and operations; for in vector. 2017-01-30 22:31:34 +00:00
Ginger Bill 0ca1b4612c Allow _ in floats 2017-01-29 23:13:50 +00:00
Ginger Bill 9e143a38ce sprint*, variadic append works correctly now. 2017-01-29 21:29:10 +00:00
Ginger Bill 43be91bca3 Variadic append 2017-01-29 20:48:08 +00:00
Ginger Bill 984e36a151 Dynamic arrays 2017-01-29 20:15:16 +00:00
Ginger Bill ec9c8fb8a4 Update README.md 2017-01-29 14:45:12 +00:00
Ginger Bill 3e79ec4aef Fix untyped to any assignments. Fixed crash when arguments with no value are passed 2017-01-29 14:27:55 +00:00
gingerBill 3e257ef8d0 Merge pull request #12 from thebirk/windows-odin-correction
Changed #foreign user32 to gdi32 where this was wrong.
2017-01-28 23:25:55 +00:00
thebirk 626f91f307 Changed #foreign user32 to gdi32 where this was wrong. 2017-01-28 23:23:02 +01:00
Ginger Bill e86c990b75 Overloaded free; 3 dotted ellipsis 2017-01-28 20:16:18 +00:00
Ginger Bill 31aacd5bf4 Fix parsing for block/if expression within if/for/etc. statements 2017-01-27 23:02:55 +00:00
Ginger Bill 92453369c5 Remove while loop and readd c-style for loops i.e. all loops are just for 2017-01-27 17:43:42 +00:00
Ginger Bill 832009f33a in keyword for for and match type 2017-01-27 16:34:58 +00:00
Ginger Bill d3d3bfd455 Fix utf8 stuff, Allow _ in numbers, Begin writing next demo code. 2017-01-27 12:43:01 +00:00
Ginger Bill ce3582fd89 Remove case sensitivity for libraries on windows 2017-01-26 20:06:22 +00:00
Ginger Bill e3e16f5d05 Library names - Only link with used foreign libraries 2017-01-26 20:00:16 +00:00
Ginger Bill f47f25f942 Fix pointer differences (issue #11); remove #dll_import 2017-01-26 17:39:44 +00:00
Ginger Bill e85458919c Basic float printing 2017-01-26 15:38:35 +00:00
Ginger Bill b59a052e32 Change casting syntax: cast(T)x transmute(T)x et al. 2017-01-25 19:19:25 +00:00
Ginger Bill 12498b2d39 Fix issue #8 - https://github.com/gingerBill/Odin/issues/8 2017-01-20 11:23:46 +00:00
Ginger Bill 6d93aa429f Fix issue #10 2017-01-20 00:21:40 +00:00
Ginger Bill 3f023509a7 using immutable thread_local on variable declarations 2017-01-19 20:03:10 +00:00
Ginger Bill 563b1e2b28 immutable field prefix 2017-01-19 19:02:44 +00:00
Ginger Bill 4603d2525e Closed range ... (both inclusive); Type comparisons with == and != 2017-01-19 11:29:15 +00:00
Ginger Bill 2af9fb79dc Change cast syntax, int(x), []byte(s), (^int)(p) 2017-01-17 23:36:07 +00:00
Ginger Bill 367d307dc4 Fix conversion of untyped integers to pointers 2017-01-17 20:27:14 +00:00
Ginger Bill cb59c1cf08 Comma for all field separators; Overloaded procedures follow exportation rules 2017-01-17 18:47:38 +00:00
Ginger Bill 383f5b55ad Best viable overloading procedure algorithm; no_alias; call expr style casts 2017-01-17 15:20:11 +00:00
Ginger Bill 6dc6b6f8aa Err on ambiguous overloaded calls 2017-01-15 20:43:28 +00:00
Ginger Bill ac736aa4ec Procedure overloading 2017-01-15 19:55:04 +00:00
Ginger Bill 6fe25badf0 Bug fix: comparisons with shifts 2017-01-15 12:00:13 +00:00
Ginger Bill c29d433e38 Handle enums correctly with printf 2017-01-08 23:19:50 +00:00
Ginger Bill ff473e8342 "Old style" enums
name and value information
`count`, `min_value`, `max_value` constants
2017-01-08 20:24:12 +00:00
Ginger Bill 659e5359b2 fmt.printf - Go style due to runtime type safety 2017-01-08 01:10:55 +00:00
Ginger Bill d9ce0b9da0 File reorganization for checker system. 2017-01-07 12:01:52 +00:00
Ginger Bill 703e1aa2bc Fix core library; Disable adding entity definitions for blank identifiers 2017-01-07 11:44:42 +00:00
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
42 changed files with 9881 additions and 5036 deletions
+18 -19
View File
@@ -2,7 +2,7 @@
# The Odin Programming Language
Odin is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals:
The Odin programming language is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals:
* simplicity
* high performance
* built for modern systems
@@ -18,6 +18,7 @@ Odin is fast, concise, readable, pragmatic and open sourced. It is designed with
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
## Requirements to build and run
@@ -30,27 +31,25 @@ 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 definitely not fixed
* Syntax is not fixed.
## Roadmap
Not in any particular order
Not in any particular order and not be implemented
* 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
* 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
* Documentation Generator for "Entities"
* Multiple Architecture support
* Debug Information
- pdb format too
* Command line tooling
* Compiler internals:
* Command Line Tooling
* Compiler Internals:
- Big numbers library
- Multithreading for performance increase
+2 -3
View File
@@ -4,8 +4,7 @@
set exe_name=odin.exe
:: Debug = 0, Release = 1
set release_mode=0
set release_mode=1
set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
if %release_mode% EQU 0 ( rem Debug
@@ -47,7 +46,7 @@ cl %compiler_settings% "src\main.c" ^
/link %linker_settings% -OUT:%exe_name% ^
&& odin run code/demo.odin
rem && odin build_dll code/example.odin ^
rem && odin run code/demo.odin
rem odin run code/demo.odin
rem pushd src\asm
rem nasm hellope.asm -fwin64 -o hellope.obj ^
+123 -1
View File
@@ -1,5 +1,127 @@
#import "fmt.odin";
main :: proc() {
fmt.println("Hellope!");
/*
Version 0.1.0
Added:
* Dynamic Arrays `[...]Type`
* Dynamic Maps `map[Key]Value`
* Dynamic array and map literals
* Custom struct alignemnt `struct #align 8 { bar: i8 }`
* Allow `_` in numbers
* Variadic `append`
* fmt.sprint*
* Entities prefixes with an underscore do not get exported on imports
* Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps
* enum types have an implict `names` field, a []string of all the names in that enum
Removed:
* Maybe/option types
* immutable variables
* Remove `type` keyword and other "reserved" keywords
* `compile_assert` and `assert`return the value of the condition for semantic reasons
Changed:
* thread_local -> #thread_local
* #include -> #load
* Files only get checked if they are actually used
* match x in y {} // For type match statements
* Version numbering now starts from 0.1.0 and uses the convention:
- major.minor.patch
Fixes:
* Many fmt.* fixes
To come very Soon:
* Linux and OS X builds (unofficial ones do exist already)
*/
{
Fruit :: enum {
APPLE,
BANANA,
COCONUT,
}
fmt.println(Fruit.names);
}
{
m: map[f32]int;
reserve(m, 16);
defer free(m);
m[1.0] = 1278;
m[2.0] = 7643;
m[3.0] = 564;
_, ok := m[3.0];
c := m[3.0];
assert(ok && c == 564);
fmt.print("map[");
i := 0;
for val, key in m {
if i > 0 {
fmt.print(", ");
}
fmt.printf("%v=%v", key, val);
i += 1;
}
fmt.println("]");
}
{
m := map[string]u32{
"a" = 56,
"b" = 13453,
"c" = 7654,
};
defer free(m);
c := m["c"];
_, ok := m["c"];
assert(ok && c == 7654);
fmt.println(m);
}
{
x: [...]f64;
reserve(x, 16);
defer free(x);
append(x, 2_000_000.500_000, 3, 5, 7);
for p, i in x {
if i > 0 { fmt.print(", "); }
fmt.print(p);
}
fmt.println();
}
{
x := [...]f64{2_000_000.500_000, 3, 5, 7};
defer free(x);
fmt.println(x);
}
{
Vec3 :: [vector 3]f32;
x := Vec3{1, 2, 3};
y := Vec3{4, 5, 6};
fmt.println(x < y);
fmt.println(x + y);
fmt.println(x - y);
fmt.println(x * y);
fmt.println(x / y);
for i in x {
fmt.println(i);
}
compile_assert(size_of([vector 7]bool) == size_of([7]bool));
compile_assert(size_of([vector 7]i32) == size_of([7]i32));
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
}
}
+86 -86
View File
@@ -1,10 +1,10 @@
#import "fmt.odin"
#import "fmt.odin";
#foreign_system_library "Ws2_32" when ODIN_OS == "windows"
#foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
SOCKET :: type uint
INVALID_SOCKET :: ~(0 as SOCKET)
SOCKET :: #type uint;
INVALID_SOCKET :: ~(cast(SOCKET)0);
AF :: enum i32 {
UNSPEC = 0, // unspecified
@@ -35,111 +35,111 @@ AF :: enum i32 {
SIP = 24, // Simple Internet Protocol
PIP = 25, // Help Identify PIP packets
MAX = 26,
}
};
SOCK_STREAM :: 1
SOCKET_ERROR :: -1
IPPROTO_TCP :: 6
AI_PASSIVE :: 0x0020
SOMAXCONN :: 128
SOCK_STREAM :: 1;
SOCKET_ERROR :: -1;
IPPROTO_TCP :: 6;
AI_PASSIVE :: 0x0020;
SOMAXCONN :: 128;
SD_RECEIVE :: 0
SD_SEND :: 1
SD_BOTH :: 2
SD_RECEIVE :: 0;
SD_SEND :: 1;
SD_BOTH :: 2;
WSADESCRIPTION_LEN :: 256
WSASYS_STATUS_LEN :: 128
WSADESCRIPTION_LEN :: 256;
WSASYS_STATUS_LEN :: 128;
WSADATA :: struct #ordered {
version: i16
high_version: i16
version: i16,
high_version: i16,
// NOTE(bill): This is x64 ordering
max_sockets: u16
max_udp_dg: u16
vendor_info: ^byte
description: [WSADESCRIPTION_LEN+1]byte
system_status: [WSASYS_STATUS_LEN+1]byte
max_sockets: u16,
max_udp_dg: u16,
vendor_info: ^byte,
description: [WSADESCRIPTION_LEN+1]byte,
system_status: [WSASYS_STATUS_LEN+1]byte,
}
addrinfo :: struct #ordered {
flags: i32
family: i32
socktype: i32
protocol: i32
addrlen: uint
canonname: ^u8
addr: ^sockaddr
next: ^addrinfo
flags: i32,
family: i32,
socktype: i32,
protocol: i32,
addrlen: uint,
canonname: ^u8,
addr: ^sockaddr,
next: ^addrinfo,
}
sockaddr :: struct #ordered {
family: u16
data: [14]byte
family: u16,
data: [14]byte,
}
WSAStartup :: proc(version_requested: i16, data: ^WSADATA) -> i32 #foreign #dll_import
WSACleanup :: proc() -> i32 #foreign #dll_import
getaddrinfo :: proc(node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32 #foreign #dll_import
freeaddrinfo :: proc(ai: ^addrinfo) #foreign #dll_import
socket :: proc(af, type_, protocol: i32) -> SOCKET #foreign #dll_import
closesocket :: proc(s: SOCKET) -> i32 #foreign #dll_import
bind :: proc(s: SOCKET, name: ^sockaddr, name_len: i32) -> i32 #foreign #dll_import
listen :: proc(s: SOCKET, back_log: i32) -> i32 #foreign #dll_import
accept :: proc(s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET #foreign #dll_import
recv :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign #dll_import
send :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign #dll_import
shutdown :: proc(s: SOCKET, how: i32) -> i32 #foreign #dll_import
WSAGetLastError :: proc() -> i32 #foreign #dll_import
WSAStartup :: proc(version_requested: i16, data: ^WSADATA) -> i32 #foreign ws2;
WSACleanup :: proc() -> i32 #foreign ws2;
getaddrinfo :: proc(node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32 #foreign ws2;
freeaddrinfo :: proc(ai: ^addrinfo) #foreign ws2;
socket :: proc(af, type_, protocol: i32) -> SOCKET #foreign ws2;
closesocket :: proc(s: SOCKET) -> i32 #foreign ws2;
bind :: proc(s: SOCKET, name: ^sockaddr, name_len: i32) -> i32 #foreign ws2;
listen :: proc(s: SOCKET, back_log: i32) -> i32 #foreign ws2;
accept :: proc(s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET #foreign ws2;
recv :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
send :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign ws2;
shutdown :: proc(s: SOCKET, how: i32) -> i32 #foreign ws2;
WSAGetLastError :: proc() -> i32 #foreign ws2;
to_c_string :: proc(s: string) -> ^byte {
c_str := new_slice(byte, s.count+1)
assert(c_str.data != nil)
copy(c_str, s as []byte)
c_str[s.count] = 0
return c_str.data
c_str := new_slice(byte, s.count+1);
assert(c_str.data != nil);
copy(c_str, cast([]byte)s);
c_str[s.count] = 0;
return c_str.data;
}
run :: proc() {
wsa: WSADATA
res: ^addrinfo = nil
hints: addrinfo
s, client: SOCKET
wsa: WSADATA;
res: ^addrinfo = nil;
hints: addrinfo;
s, client: SOCKET;
if WSAStartup(2 | (2 << 8), ^wsa) != 0 {
fmt.println("WSAStartup failed: ", WSAGetLastError())
return
fmt.println("WSAStartup failed: ", WSAGetLastError());
return;
}
defer WSACleanup()
defer WSACleanup();
hints.family = AF.INET as i32
hints.socktype = SOCK_STREAM
hints.protocol = IPPROTO_TCP
hints.flags = AI_PASSIVE
hints.family = cast(i32)AF.INET;
hints.socktype = SOCK_STREAM;
hints.protocol = IPPROTO_TCP;
hints.flags = AI_PASSIVE;
if getaddrinfo(nil, to_c_string("8080"), ^hints, ^res) != 0 {
fmt.println("getaddrinfo failed: ", WSAGetLastError())
return
fmt.println("getaddrinfo failed: ", WSAGetLastError());
return;
}
defer freeaddrinfo(res)
defer freeaddrinfo(res);
s = socket(res.family, res.socktype, res.protocol)
s = socket(res.family, res.socktype, res.protocol);
if s == INVALID_SOCKET {
fmt.println("socket failed: ", WSAGetLastError())
return
fmt.println("socket failed: ", WSAGetLastError());
return;
}
defer closesocket(s)
defer closesocket(s);
bind(s, res.addr, res.addrlen as i32)
listen(s, SOMAXCONN)
bind(s, res.addr, cast(i32)res.addrlen);
listen(s, SOMAXCONN);
client = accept(s, nil, 0)
client = accept(s, nil, 0);
if client == INVALID_SOCKET {
fmt.println("socket failed: ", WSAGetLastError())
return
fmt.println("socket failed: ", WSAGetLastError());
return;
}
defer closesocket(client)
defer closesocket(client);
html :=
`HTTP/1.1 200 OK
@@ -154,27 +154,27 @@ Content-type: text/html
<h1 style="color: orange;">Odin Server Demo</h1>
</body>
</html>
`
`;
buf: [1024]byte
buf: [1024]byte;
for {
bytes := recv(client, ^buf[0], buf.count as i32, 0)
bytes := recv(client, ^buf[0], cast(i32)buf.count, 0);
if bytes > 0 {
// fmt.println(buf[:bytes] as string)
bytes_sent := send(client, html.data, (html.count-1) as i32, 0)
bytes_sent := send(client, html.data, cast(i32)(html.count-1), 0);
if bytes_sent == SOCKET_ERROR {
fmt.println("send failed: ", WSAGetLastError())
return
fmt.println("send failed: ", WSAGetLastError());
return;
}
break
break;
} else if bytes == 0 {
fmt.println("Connection closing...")
break
fmt.println("Connection closing...");
break;
} else {
fmt.println("recv failed: ", WSAGetLastError())
return
fmt.println("recv failed: ", WSAGetLastError());
return;
}
}
shutdown(client, SD_SEND)
shutdown(client, SD_SEND);
}
+103 -100
View File
@@ -1,83 +1,86 @@
#import "fmt.odin"
#import "os.odin"
#import "mem.odin"
// #import "http_test.odin" as ht
// #import "game.odin" as game
// #import "punity.odin" as pn
#import "fmt.odin";
#import "os.odin";
#import "mem.odin";
// #import "http_test.odin" as ht;
// #import "game.odin" as game;
// #import "punity.odin" as pn;
main :: proc() {
// struct_padding()
// bounds_checking()
// type_introspection()
// any_type()
// crazy_introspection()
// namespaces_and_files()
// miscellany()
// ht.run()
// game.run()
// {
// init :: proc(c: ^pn.Core) {}
// step :: proc(c: ^pn.Core) {}
struct_padding();
bounds_checking();
type_introspection();
any_type();
crazy_introspection();
namespaces_and_files();
miscellany();
// pn.run(init, step)
// }
/*
ht.run();
game.run();
{
init :: proc(c: ^pn.Core) {}
step :: proc(c: ^pn.Core) {}
pn.run(init, step);
}
*/
}
struct_padding :: proc() {
{
A :: struct {
a: u8
b: u32
c: u16
a: u8,
b: u32,
c: u16,
}
B :: struct {
a: [7]u8
b: [3]u16
c: u8
d: u16
a: [7]u8,
b: [3]u16,
c: u8,
d: u16,
}
fmt.println("size_of(A):", size_of(A))
fmt.println("size_of(B):", size_of(B))
fmt.println("size_of(A):", size_of(A));
fmt.println("size_of(B):", size_of(B));
// n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html
}
{
A :: struct #ordered {
a: u8
b: u32
c: u16
a: u8,
b: u32,
c: u16,
}
B :: struct #ordered {
a: [7]u8
b: [3]u16
c: u8
d: u16
a: [7]u8,
b: [3]u16,
c: u8,
d: u16,
}
fmt.println("size_of(A):", size_of(A))
fmt.println("size_of(B):", size_of(B))
fmt.println("size_of(A):", size_of(A));
fmt.println("size_of(B):", size_of(B));
// C-style structure layout
}
{
A :: struct #packed {
a: u8
b: u32
c: u16
a: u8,
b: u32,
c: u16,
}
B :: struct #packed {
a: [7]u8
b: [3]u16
c: u8
d: u16
a: [7]u8,
b: [3]u16,
c: u8,
d: u16,
}
fmt.println("size_of(A):", size_of(A))
fmt.println("size_of(B):", size_of(B))
fmt.println("size_of(A):", size_of(A));
fmt.println("size_of(B):", size_of(B));
// Useful for explicit layout
}
@@ -119,7 +122,7 @@ struct_padding :: proc() {
}
bounds_checking :: proc() {
x: [4]int
x: [4]int;
// x[-1] = 0; // Compile Time
// x[4] = 0; // Compile Time
@@ -132,9 +135,9 @@ bounds_checking :: proc() {
// Works for arrays, strings, slices, and related procedures & operations
{
base: [10]int
s := base[2:6]
a, b := -1, 6
base: [10]int;
s := base[2:6];
a, b := -1, 6;
#no_bounds_check {
s[a] = 0;
@@ -154,69 +157,69 @@ bounds_checking :: proc() {
type_introspection :: proc() {
{
info: ^Type_Info
x: int
info: ^Type_Info;
x: int;
info = type_info(int) // by type
info = type_info_of_val(x) // by value
info = type_info(int); // by type
info = type_info_of_val(x); // by value
// See: runtime.odin
match type i : info {
match type i in info {
case Type_Info.Integer:
fmt.println("integer!")
fmt.println("integer!");
case Type_Info.Float:
fmt.println("float!")
fmt.println("float!");
default:
fmt.println("potato!")
fmt.println("potato!");
}
// Unsafe cast
integer_info := info as ^Type_Info.Integer
integer_info := cast(^Type_Info.Integer)info;
}
{
Vector2 :: struct { x, y: f32 }
Vector3 :: struct { x, y, z: f32 }
v1: Vector2
v2: Vector3
v3: Vector3
v1: Vector2;
v2: Vector3;
v3: Vector3;
t1 := type_info_of_val(v1)
t2 := type_info_of_val(v2)
t3 := type_info_of_val(v3)
t1 := type_info_of_val(v1);
t2 := type_info_of_val(v2);
t3 := type_info_of_val(v3);
fmt.println()
fmt.print("Type of v1 is:\n\t", t1)
fmt.println();
fmt.print("Type of v1 is:\n\t", t1);
fmt.println()
fmt.print("Type of v2 is:\n\t", t2)
fmt.println();
fmt.print("Type of v2 is:\n\t", t2);
fmt.println("\n")
fmt.println("t1 == t2:", t1 == t2)
fmt.println("t2 == t3:", t2 == t3)
fmt.println("\n");
fmt.println("t1 == t2:", t1 == t2);
fmt.println("t2 == t3:", t2 == t3);
}
}
any_type :: proc() {
a: any
a: any;
x: int = 123
y: f64 = 6.28
z: string = "Yo-Yo Ma"
x: int = 123;
y: f64 = 6.28;
z: string = "Yo-Yo Ma";
// All types can be implicit cast to `any`
a = x
a = y
a = z
a = a // This the "identity" type, it doesn't get converted
a = x;
a = y;
a = z;
a = a; // This the "identity" type, it doesn't get converted
a = 123 // Literals are copied onto the stack first
a = 123; // Literals are copied onto the stack first
// any has two members
// data - rawptr to the data
// type_info - pointer to the type info
fmt.println(x, y, z)
fmt.println(x, y, z);
// See: fmt.odin
// For variadic any procedures in action
}
@@ -232,15 +235,15 @@ crazy_introspection :: proc() {
TOMATO,
}
s: string
s = enum_to_string(Fruit.PEACH)
fmt.println(s)
s: string;
// s = enum_to_string(Fruit.PEACH);
fmt.println(s);
f := Fruit.GRAPE
s = enum_to_string(f)
fmt.println(s)
f := Fruit.GRAPE;
// s = enum_to_string(f);
fmt.println(s);
fmt.println(f)
fmt.println(f);
// See: runtime.odin
}
@@ -259,15 +262,15 @@ crazy_introspection :: proc() {
TOMATO,
}
fruit_ti := type_info(Fruit)
name := (fruit_ti as ^Type_Info.Named).name // Unsafe casts
info := type_info_base(fruit_ti) as ^Type_Info.Enum // Unsafe casts
fruit_ti := type_info(Fruit);
name := (cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
info := cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
fmt.printf("% :: enum % {\n", name, info.base);
for i := 0; i < info.values.count; i++ {
fmt.printf("\t%\t= %,\n", info.names[i], info.values[i])
for i := 0; i < info.values.count; i += 1 {
fmt.printf("\t%\t= %,\n", info.names[i], info.values[i]);
}
fmt.printf("}\n")
fmt.printf("}\n");
// NOTE(bill): look at that type-safe printf!
}
@@ -275,10 +278,10 @@ crazy_introspection :: proc() {
{
Vector3 :: struct {x, y, z: f32}
a := Vector3{x = 1, y = 4, z = 9}
fmt.println(a)
b := Vector3{x = 9, y = 3, z = 1}
fmt.println(b)
a := Vector3{x = 1, y = 4, z = 9};
fmt.println(a);
b := Vector3{x = 9, y = 3, z = 1};
fmt.println(b);
// NOTE(bill): See fmt.odin
}
+282
View File
@@ -0,0 +1,282 @@
#import "fmt.odin";
#import "utf8.odin";
// #import "atomic.odin";
// #import "hash.odin";
// #import "math.odin";
// #import "mem.odin";
// #import "opengl.odin";
// #import "os.odin";
// #import "sync.odin";
// #import win32 "sys/windows.odin";
main :: proc() {
// syntax();
procedure_overloading();
}
syntax :: proc() {
// Cyclic type checking
// Uncomment to see the error
// A :: struct {b: B};
// B :: struct {a: A};
x: int;
y := cast(f32)x;
z := transmute(u32)y;
// down_cast, union_cast are similar too
// Basic directives
fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure);
// NOTE: new and improved `printf`
// TODO: It does need accurate float printing
// record fields use the same syntax a procedure signatures
Thing1 :: struct {
x: f32,
y: int,
z: ^[]int,
};
Thing2 :: struct {x: f32, y: int, z: ^[]int};
// Slice interals are now just a `ptr+count`
slice: []int; compile_assert(size_of_val(slice) == 2*size_of(int));
// Helper type - Help the reader understand what it is quicker
My_Int :: type int;
My_Proc :: type proc(int) -> f32;
// All declarations with : are either variable or constant
// To make these declarations syntactically consistent
v_variable := 123;
c_constant :: 123;
c_type1 :: int;
c_type2 :: []int;
c_proc :: proc() { /* code here */ };
x += 1;
x -= 1;
// ++ and -- have been removed
// x++;
// x--;
// Question: Should they be added again?
// They were removed as they are redundant and statements, not expressions
// like in C/C++
// You can now build files as a `.dll`
// `odin build_dll demo.odin`
// New vector syntax
u, v: [vector 3]f32;
v[0] = 123;
v.x = 123; // valid for all vectors with count 1 to 4
// Next part
prefixes();
}
Prefix_Type :: struct {x: int, y: f32, z: rawptr};
thread_local my_tls: Prefix_Type;
prefixes :: proc() {
using var: Prefix_Type;
immutable const := Prefix_Type{1, 2, nil};
var.x = 123;
x = 123;
// const.x = 123; // const is immutable
foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
// int_ptr = nil; // Not valid
int_ptr^ = 123; // Is valid
}
// Same as C99's `restrict`
bar :: proc(no_alias a, b: ^int) {
// Assumes a never equals b so it can perform optimizations with that fact
}
when_statements();
}
when_statements :: proc() {
X :: 123 + 12;
Y :: X/5;
COND :: Y > 0;
when COND {
fmt.println("Y > 0");
} else {
fmt.println("Y <= 0");
}
when false {
this_code_does_not_exist(123, 321);
but_its_syntax_is_valid();
x :: ^^^^int;
}
foreign_procedures();
}
#foreign_system_library win32_user "user32.lib" when ODIN_OS == "windows";
// NOTE: This is done on purpose for two reasons:
// * Makes it clear where the platform specific stuff is
// * Removes the need to solve the travelling salesman problem when importing files :P
foreign_procedures :: proc() {
ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user;
show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user "ShowWindow";
// NOTE: If that library doesn't get used, it doesn't get linked with
// NOTE: There is not link checking yet to see if that procedure does come from that library
// See sys/windows.odin for more examples
special_expressions();
}
special_expressions :: proc() {
// Block expression
x := {
a: f32 = 123;
b := a-123;
c := b/a;
give c;
}; // semicolon is required as it's an expression
y := if x < 50 {
give x;
} else {
// TODO: Type cohesion is not yet finished
give 123;
}; // semicolon is required as it's an expression
// This is allows for inline blocks of code and will be a useful feature to have when
// macros will be implemented into the language
loops();
}
loops :: proc() {
// The C-style for loop
for i := 0; i < 123; i += 1 {
break;
}
for i := 0; i < 123; {
break;
}
for false {
break;
}
for {
break;
}
for i in 0..<123 { // 123 exclusive
}
for i in 0...122 { // 122 inclusive
}
for val, idx in 12..<16 {
fmt.println(val, idx);
}
primes := [...]int{2, 3, 5, 7, 11, 13, 17, 19};
for p in primes {
fmt.println(p);
}
// Pointers to arrays, slices, or strings are allowed
for _ in ^primes {
// ignore the value and just iterate across it
}
name := "你好,世界";
fmt.println(name);
for r in name {
compile_assert(type_of_val(r) == rune);
fmt.printf("%r\n", r);
}
when false {
for i, size := 0; i < name.count; i += size {
r: rune;
r, size = utf8.decode_rune(name[i:]);
fmt.printf("%r\n", r);
}
}
procedure_overloading();
}
procedure_overloading :: proc() {
THINGF :: 14451.1;
THINGI :: 14451;
foo :: proc() {
fmt.printf("Zero args\n");
}
foo :: proc(i: int) {
fmt.printf("int arg, i=%d\n", i);
}
foo :: proc(f: f64) {
i := cast(int)f;
fmt.printf("f64 arg, f=%d\n", i);
}
foo();
foo(THINGF);
// foo(THINGI); // 14451 is just a number so it could go to either procedures
foo(cast(int)THINGI);
foo :: proc(x: ^i32) -> (int, int) {
fmt.println("^int");
return 123, cast(int)(x^);
}
foo :: proc(x: rawptr) {
fmt.println("rawptr");
}
a: i32 = 123;
b: f32;
c: rawptr;
fmt.println(foo(^a));
foo(^b);
foo(c);
// foo(nil); // nil could go to numerous types thus the ambiguity
f: proc();
f = foo; // The correct `foo` to chosen
f();
// See math.odin and atomic.odin for more examples
}
+167 -160
View File
@@ -1,34 +1,35 @@
#import "win32.odin"
#import "fmt.odin"
#import "os.odin"
#import win32 "sys/windows.odin";
#import "fmt.odin";
#import "os.odin";
#import "mem.odin";
CANVAS_WIDTH :: 128
CANVAS_HEIGHT :: 128
CANVAS_SCALE :: 3
FRAME_TIME :: 1.0/30.0
WINDOW_TITLE :: "Punity\x00"
CANVAS_WIDTH :: 128;
CANVAS_HEIGHT :: 128;
CANVAS_SCALE :: 3;
FRAME_TIME :: 1.0/30.0;
WINDOW_TITLE :: "Punity\x00";
_ := compile_assert(CANVAS_WIDTH % 16 == 0)
_ := compile_assert(CANVAS_WIDTH % 16 == 0);
WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE
WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE
WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE;
WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE;
STACK_CAPACITY :: 1<<20
STORAGE_CAPACITY :: 1<<20
STACK_CAPACITY :: 1<<20;
STORAGE_CAPACITY :: 1<<20;
DRAW_LIST_RESERVE :: 128
DRAW_LIST_RESERVE :: 128;
MAX_KEYS :: 256
MAX_KEYS :: 256;
Core :: struct {
stack: ^Bank
storage: ^Bank
stack: ^Bank,
storage: ^Bank,
running: bool
key_modifiers: u32
key_states: [MAX_KEYS]byte
key_deltas: [MAX_KEYS]byte
running: bool,
key_modifiers: u32,
key_states: [MAX_KEYS]byte,
key_deltas: [MAX_KEYS]byte,
perf_frame,
perf_frame_inner,
@@ -36,70 +37,66 @@ Core :: struct {
perf_audio,
perf_blit,
perf_blit_cvt,
perf_blit_gdi: Perf_Span
perf_blit_gdi: Perf_Span,
frame: i64
frame: i64,
canvas: Canvas
draw_list: ^Draw_List
canvas: Canvas,
draw_list: ^Draw_List,
}
Perf_Span :: struct {
stamp: f64
delta: f32
stamp: f64,
delta: f32,
}
Bank :: struct {
memory: []byte
cursor: int
memory: []byte,
cursor: int,
}
Bank_State :: struct {
state: Bank
bank: ^Bank
state: Bank,
bank: ^Bank,
}
Color :: raw_union {
using channels: struct{ a, b, g, r: byte; }
rgba: u32
using channels: struct{a, b, g, r: byte},
rgba: u32,
}
Palette :: struct {
colors: [256]Color
colors_count: byte
colors: [256]Color,
colors_count: byte,
}
Rect :: raw_union {
using minmax: struct {
min_x, min_y, max_x, max_y: int
}
using pos: struct {
left, top, right, bottom: int
}
e: [4]int
using minmax: struct {min_x, min_y, max_x, max_y: int},
using pos: struct {left, top, right, bottom: int},
e: [4]int,
}
Bitmap :: struct {
pixels: []byte
width: int
height: int
pixels: []byte,
width: int,
height: int,
}
Font :: struct {
using bitmap: Bitmap
char_width: int
char_height: int
using bitmap: Bitmap,
char_width: int,
char_height: int,
}
Canvas :: struct {
using bitmap: ^Bitmap
palette: Palette
translate_x: int
translate_y: int
clip: Rect
font: ^Font
using bitmap: ^Bitmap,
palette: Palette,
translate_x: int,
translate_y: int,
clip: Rect,
font: ^Font,
}
DrawFlag :: enum {
@@ -109,12 +106,9 @@ DrawFlag :: enum {
MASK = 1<<2,
}
Draw_Item :: struct {}
Draw_List :: struct {
Item :: struct {
}
items: []Item
items: []Draw_Item,
}
Key :: enum {
@@ -268,112 +262,112 @@ Key :: enum {
BACKSLASH = 92, /* \ */
RIGHT_BRACKET = 93, /* ] */
GRAVE_ACCENT = 96, /* ` */
}
};
key_down :: proc(k: Key) -> bool {
return _core.key_states[k] != 0
return _core.key_states[k] != 0;
}
key_pressed :: proc(k: Key) -> bool {
return (_core.key_deltas[k] != 0) && key_down(k)
return (_core.key_deltas[k] != 0) && key_down(k);
}
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 := cast(f64)counter / cast(f64)win32_perf_count_freq;
return result;
}
_core: Core
_core: Core;
run :: proc(user_init, user_step: proc(c: ^Core)) {
using win32
using win32;
_core.running = true
_core.running = true;
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline #stdcall {
win32_proc :: proc(hwnd: win32.HWND, msg: u32, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT #no_inline #cc_c {
win32_app_key_mods :: proc() -> u32 {
mods: u32 = 0
mods: u32 = 0;
if is_key_down(Key_Code.SHIFT) {
mods |= Key.MOD_SHIFT as u32
mods |= cast(u32)Key.MOD_SHIFT;
}
if is_key_down(Key_Code.CONTROL) {
mods |= Key.MOD_CONTROL as u32
mods |= cast(u32)Key.MOD_CONTROL;
}
if is_key_down(Key_Code.MENU) {
mods |= Key.MOD_ALT as u32
mods |= cast(u32)Key.MOD_ALT;
}
if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
mods |= Key.MOD_SUPER as u32
mods |= cast(u32)Key.MOD_SUPER;
}
return mods
return mods;
}
match msg {
case WM_KEYDOWN:
_core.key_modifiers = win32_app_key_mods()
_core.key_modifiers = win32_app_key_mods();
if wparam < MAX_KEYS {
_core.key_states[wparam] = 1
_core.key_deltas[wparam] = 1
_core.key_states[wparam] = 1;
_core.key_deltas[wparam] = 1;
}
return 0
return 0;
case WM_KEYUP:
_core.key_modifiers = win32_app_key_mods()
_core.key_modifiers = win32_app_key_mods();
if wparam < MAX_KEYS {
_core.key_states[wparam] = 0
_core.key_deltas[wparam] = 1
_core.key_states[wparam] = 0;
_core.key_deltas[wparam] = 1;
}
return 0
return 0;
case WM_CLOSE:
PostQuitMessage(0)
_core.running = false
return 0
PostQuitMessage(0);
_core.running = false;
return 0;
}
return DefWindowProcA(hwnd, msg, wparam, lparam)
return DefWindowProcA(hwnd, msg, wparam, lparam);
}
window_class := WNDCLASSEXA{
class_name = ("Punity\x00" as string).data, // C-style string
size = size_of(WNDCLASSEXA) as u32,
class_name = (cast(string)"Punity\x00").data, // C-style string
size = size_of(WNDCLASSEXA),
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
instance = GetModuleHandleA(nil) as HINSTANCE,
instance = cast(HINSTANCE)GetModuleHandleA(nil),
wnd_proc = win32_proc,
// wnd_proc = DefWindowProcA,
background = GetStockObject(BLACK_BRUSH) as HBRUSH,
}
background = cast(HBRUSH)GetStockObject(BLACK_BRUSH),
};
if RegisterClassExA(^window_class) == 0 {
fmt.fprintln(os.stderr, "RegisterClassExA failed")
return
fmt.fprintln(os.stderr, "RegisterClassExA failed");
return;
}
screen_width := GetSystemMetrics(SM_CXSCREEN)
screen_height := GetSystemMetrics(SM_CYSCREEN)
screen_width := GetSystemMetrics(SM_CXSCREEN);
screen_height := GetSystemMetrics(SM_CYSCREEN);
rc: RECT
rc.left = (screen_width - WINDOW_WIDTH) / 2
rc.top = (screen_height - WINDOW_HEIGHT) / 2
rc.right = rc.left + WINDOW_WIDTH
rc.bottom = rc.top + WINDOW_HEIGHT
rc: RECT;
rc.left = (screen_width - WINDOW_WIDTH) / 2;
rc.top = (screen_height - WINDOW_HEIGHT) / 2;
rc.right = rc.left + WINDOW_WIDTH;
rc.bottom = rc.top + WINDOW_HEIGHT;
style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
assert(AdjustWindowRect(^rc, style, 0) != 0)
style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
assert(AdjustWindowRect(^rc, style, 0) != 0);
wt := WINDOW_TITLE
wt := WINDOW_TITLE;
win32_window := CreateWindowExA(0,
window_class.class_name,
@@ -382,101 +376,114 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
rc.left, rc.top,
rc.right-rc.left, rc.bottom-rc.top,
nil, nil, window_class.instance,
nil)
nil);
if win32_window == nil {
fmt.fprintln(os.stderr, "CreateWindowExA failed")
return
fmt.fprintln(os.stderr, "CreateWindowExA failed");
return;
}
window_bmi: BITMAPINFO
window_bmi.size = size_of(BITMAPINFO.HEADER) as u32
window_bmi.width = CANVAS_WIDTH
window_bmi.height = CANVAS_HEIGHT
window_bmi.planes = 1
window_bmi.bit_count = 32
window_bmi.compression = BI_RGB
window_bmi: BITMAPINFO;
window_bmi.size = size_of(BITMAPINFOHEADER);
window_bmi.width = CANVAS_WIDTH;
window_bmi.height = CANVAS_HEIGHT;
window_bmi.planes = 1;
window_bmi.bit_count = 32;
window_bmi.compression = BI_RGB;
user_init(^_core)
user_init(^_core);
ShowWindow(win32_window, SW_SHOW);
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
defer free(window_buffer);
ShowWindow(win32_window, SW_SHOW)
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++ {
window_buffer[i] = 0xff00ff
for i := 0; i < window_buffer.count; i += 1 {
window_buffer[i] = 0xff00ff;
}
prev_time, curr_time,dt: f64
prev_time = time_now()
curr_time = time_now()
total_time : f64 = 0
offset_x := 0
offset_y := 0
dt: f64;
prev_time := time_now();
curr_time := time_now();
total_time : f64 = 0;
offset_x := 0;
offset_y := 0;
message: MSG
message: MSG;
for _core.running {
curr_time = time_now()
dt = curr_time - prev_time
prev_time = curr_time
total_time += dt
curr_time = time_now();
dt = curr_time - prev_time;
prev_time = curr_time;
total_time += dt;
offset_x += 1
offset_y += 2
offset_x += 1;
offset_y += 2;
{
data: [128]byte
buf := data[:0]
fmt.bprintf(^buf, "Punity: % ms\x00", dt*1000)
win32.SetWindowTextA(win32_window, buf.data)
data: [128]byte;
buf: fmt.Buffer;
buf.data = data[:];
fmt.bprintf(^buf, "Punity: %.4f ms\x00", dt*1000);
win32.SetWindowTextA(win32_window, ^buf[0]);
}
for y := 0; y < CANVAS_HEIGHT; y++ {
for x := 0; x < CANVAS_WIDTH; x++ {
g := (x % 32) * 8
b := (y % 32) * 8
window_buffer[x + y*CANVAS_WIDTH] = (g << 8 | b) as u32
for y := 0; y < CANVAS_HEIGHT; y += 1 {
for x := 0; x < CANVAS_WIDTH; x += 1 {
g := (x % 32) * 8;
b := (y % 32) * 8;
window_buffer[x + y*CANVAS_WIDTH] = cast(u32)(g << 8 | b);
}
}
_core.key_deltas = nil
mem.zero(^_core.key_deltas[0], size_of_val(_core.key_deltas));
for PeekMessageA(^message, nil, 0, 0, PM_REMOVE) != 0 {
if message.message == WM_QUIT {
_core.running = false
_core.running = false;
}
TranslateMessage(^message)
DispatchMessageA(^message)
TranslateMessage(^message);
DispatchMessageA(^message);
}
user_step(^_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,
window_buffer.data,
^window_bmi,
DIB_RGB_COLORS,
SRCCOPY)
ReleaseDC(win32_window, dc)
SRCCOPY);
ReleaseDC(win32_window, dc);
{
delta := time_now() - prev_time
ms := ((FRAME_TIME - delta) * 1000) as i32
delta := time_now() - prev_time;
ms := cast(i32)((FRAME_TIME - delta) * 1000);
if ms > 0 {
win32.Sleep(ms)
win32.Sleep(ms);
}
}
_core.frame++
_core.frame += 1;
}
}
main :: proc() {
user_init :: proc(c: ^Core) {
}
user_step :: proc(c: ^Core) {
}
run(user_init, user_step);
}
+3 -3
View File
@@ -5,10 +5,10 @@ thing :: proc() {
}*/
#import "fmt.odin" as format
#import "fmt.odin";
thing :: proc() {
format.println("Hello2!")
main :: proc() {
fmt.println("hello, world!");
}
+449 -120
View File
@@ -4,6 +4,7 @@
#import "fmt.odin";
#import "mem.odin";
#import "utf8.odin";
#import "hash.odin";
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
// #shared_global_scope due to the internals of the compiler.
@@ -14,76 +15,101 @@
// IMPORTANT NOTE(bill): Do not change the order of any of this data
// The compiler relies upon this _exact_ order
Type_Info_Member :: struct #ordered {
name: string; // can be empty if tuple
type_info: ^Type_Info;
offset: int; // offsets are not used in tuples
name: string, // can be empty if tuple
type_info: ^Type_Info,
offset: int, // offsets may not be used in tuples
}
Type_Info_Record :: struct #ordered {
fields: []Type_Info_Member;
size: int; // in bytes
align: int; // in bytes
packed: bool;
ordered: bool;
fields: []Type_Info_Member,
size: int, // in bytes
align: int, // in bytes
packed: bool,
ordered: bool,
custom_align: bool,
}
Type_Info_Enum_Value :: raw_union {
f: f64,
i: i64,
}
// NOTE(bill): This much the same as the compiler's
Calling_Convention :: enum {
ODIN = 0,
C = 1,
STD = 2,
FAST = 3,
}
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
},
String: struct #ordered {},
Boolean: struct #ordered {},
Any: struct #ordered {},
Pointer: struct #ordered {
elem: ^Type_Info; // nil -> rawptr
};
Maybe: struct #ordered {
elem: ^Type_Info;
};
elem: ^Type_Info, // nil -> rawptr
},
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,
convention: Calling_Convention,
},
Array: struct #ordered {
elem: ^Type_Info;
elem_size: int;
count: int;
};
elem: ^Type_Info,
elem_size: int,
count: int,
},
Dynamic_Array: struct #ordered {
elem: ^Type_Info,
elem_size: 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: Type_Info_Record;
Struct: Type_Info_Record;
Union: Type_Info_Record;
Raw_Union: Type_Info_Record;
elem: ^Type_Info,
elem_size: int,
count: int,
align: int,
},
Tuple: Type_Info_Record, // Only really used for procedures
Struct: Type_Info_Record,
Union: Type_Info_Record,
Raw_Union: Type_Info_Record,
Enum: struct #ordered {
base: ^Type_Info;
names: []string;
// TODO(bill): store values some how. Maybe using a raw_union
};
base: ^Type_Info,
names: []string,
values: []Type_Info_Enum_Value,
},
Map: struct #ordered {
key: ^Type_Info,
value: ^Type_Info,
generated_struct: ^Type_Info,
count: int, // == 0 if dynamic
},
}
// // NOTE(bill): only the ones that are needed (not all types)
// // This will be set by the compiler
// immutable __type_infos: []Type_Info;
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
if info == nil {
return nil;
}
base := info;
match type i : base {
match i in base {
case Type_Info.Named:
base = i.base;
}
@@ -91,50 +117,56 @@ type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
}
type_info_base_without_enum :: proc(info: ^Type_Info) -> ^Type_Info {
if info == nil {
return nil;
}
base := info;
match i in base {
case Type_Info.Named:
base = i.base;
case Type_Info.Enum:
base = i.base;
}
return base;
}
assume :: proc(cond: bool) #foreign "llvm.assume"
__debug_trap :: proc() #foreign "llvm.debugtrap"
__trap :: proc() #foreign "llvm.trap"
read_cycle_counter :: proc() -> u64 #foreign "llvm.readcyclecounter"
bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16"
bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32"
bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64"
assume :: proc(cond: bool) #foreign __llvm_core "llvm.assume";
byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16"
byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32"
byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64"
fmuladd32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32"
fmuladd64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64"
__debug_trap :: proc() #foreign __llvm_core "llvm.debugtrap";
__trap :: proc() #foreign __llvm_core "llvm.trap";
read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter";
__cpuid :: proc(level: u32, sig: ^u32) -> i32 #foreign __llvm_core "__get_cpuid";
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
Allocator_Mode :: enum u8 {
ALLOC = iota,
ALLOC,
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_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;
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;
@@ -162,13 +194,21 @@ alloc_align :: proc(size, alignment: int) -> rawptr #inline {
return a.procedure(a.data, Allocator_Mode.ALLOC, size, alignment, nil, 0, 0);
}
free :: proc(ptr: rawptr) #inline {
__check_context();
a := context.allocator;
if ptr != nil {
a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0);
free_ptr_with_allocator :: proc(a: Allocator, ptr: rawptr) #inline {
if ptr == nil {
return;
}
if a.procedure == nil {
return;
}
a.procedure(a.data, Allocator_Mode.FREE, 0, 0, ptr, 0, 0);
}
free_ptr :: proc(ptr: rawptr) #inline {
__check_context();
free_ptr_with_allocator(context.allocator, ptr);
}
free_all :: proc() #inline {
__check_context();
a := context.allocator;
@@ -215,46 +255,21 @@ default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
using Allocator_Mode;
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);
match mode {
case ALLOC:
return os.heap_alloc(size);
case FREE:
os.heap_free(mem.allocation_header(old_memory));
return nil;
case FREE:
os.heap_free(old_memory);
return nil;
case FREE_ALL:
// NOTE(bill): Does nothing
case FREE_ALL:
// NOTE(bill): Does nothing
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:
os.heap_free(old_memory);
return nil;
case FREE_ALL:
// NOTE(bill): Does nothing
case RESIZE:
return os.heap_resize(old_memory, size);
}
case RESIZE:
ptr := os.heap_resize(old_memory, size);
assert(ptr != nil);
return ptr;
}
return nil;
@@ -284,11 +299,11 @@ __string_eq :: proc(a, b: string) -> bool {
if a.data == b.data {
return true;
}
return mem.compare(a.data, b.data, a.count) == 0;
return __string_cmp(a, b) == 0;
}
__string_cmp :: proc(a, b: string) -> int {
return mem.compare(a.data, b.data, min(a.count, b.count));
return mem.compare(cast([]byte)a, cast([]byte)b);
}
__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); }
@@ -299,7 +314,7 @@ __string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >=
__assert :: proc(file: string, line, column: int, msg: string) #inline {
fmt.fprintf(os.stderr, "%(%:%) Runtime assertion: %\n",
fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n",
file, line, column, msg);
__debug_trap();
}
@@ -308,7 +323,7 @@ __bounds_check_error :: proc(file: string, line, column: int, index, count: int)
if 0 <= index && index < count {
return;
}
fmt.fprintf(os.stderr, "%(%:%) Index % is out of bounds range [0, %)\n",
fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..<%d\n",
file, line, column, index, count);
__debug_trap();
}
@@ -317,7 +332,7 @@ __slice_expr_error :: proc(file: string, line, column: int, low, high: int) {
if 0 <= low && low <= high {
return;
}
fmt.fprintf(os.stderr, "%(%:%) Invalid slice indices: [%:%]\n",
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d:%d]\n",
file, line, column, low, high);
__debug_trap();
}
@@ -325,7 +340,7 @@ __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",
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d:%d]\n",
file, line, column, low, high);
__debug_trap();
}
@@ -334,3 +349,317 @@ __string_decode_rune :: proc(s: string) -> (rune, int) #inline {
return utf8.decode_rune(s);
}
Raw_Any :: struct #ordered {
type_info: ^Type_Info,
data: rawptr,
}
Raw_String :: struct #ordered {
data: ^byte,
count: int,
};
Raw_Slice :: struct #ordered {
data: rawptr,
count: int,
};
Raw_Dynamic_Array :: struct #ordered {
data: rawptr,
count: int,
capacity: int,
allocator: Allocator,
};
Raw_Dynamic_Map :: struct #ordered {
hashes: [...]int,
entries: Raw_Dynamic_Array,
};
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capacity: int) -> bool {
array := cast(^Raw_Dynamic_Array)array_;
if capacity <= array.capacity {
return true;
}
__check_context();
if array.allocator.procedure == nil {
array.allocator = context.allocator;
}
assert(array.allocator.procedure != nil);
old_size := array.capacity * elem_size;
new_size := capacity * elem_size;
allocator := array.allocator;
new_data := allocator.procedure(allocator.data, Allocator_Mode.RESIZE, new_size, elem_align, array.data, old_size, 0);
if new_data == nil {
return false;
}
array.data = new_data;
array.capacity = capacity;
return true;
}
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
items: rawptr, item_count: int) -> int {
array := cast(^Raw_Dynamic_Array)array_;
if item_count <= 0 || items == nil {
return array.count;
}
ok := true;
if array.capacity <= array.count+item_count {
capacity := 2 * array.capacity + max(8, item_count);
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity);
}
if !ok {
// TODO(bill): Better error handling for failed reservation
return array.count;
}
data := cast(^byte)array.data;
assert(data != nil);
mem.copy(data + (elem_size*array.count), items, elem_size * item_count);
array.count += item_count;
return array.count;
}
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int) -> int {
array := cast(^Raw_Dynamic_Array)array_;
ok := true;
if array.capacity <= array.count+1 {
capacity := 2 * array.capacity + max(8, 1);
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity);
}
if !ok {
// TODO(bill): Better error handling for failed reservation
return array.count;
}
data := cast(^byte)array.data;
assert(data != nil);
mem.zero(data + (elem_size*array.count), elem_size);
array.count += 1;
return array.count;
}
__default_hash :: proc(data: []byte) -> u64 {
return hash.fnv64a(data);
}
__default_hash_string :: proc(s: string) -> u64 {
return __default_hash(cast([]byte)s);
}
__Map_Key :: struct #ordered {
hash: u64,
str: string,
}
__Map_Find_Result :: struct #ordered {
hash_index: int,
entry_prev: int,
entry_index: int,
}
__Map_Entry_Header :: struct #ordered {
key: __Map_Key,
next: int,
/*
value: Value_Type,
*/
}
__Map_Header :: struct #ordered {
m: ^Raw_Dynamic_Map,
is_key_string: bool,
entry_size: int,
entry_align: int,
value_offset: int,
}
__dynamic_map_reserve :: proc(using header: __Map_Header, capacity: int) -> bool {
h := __dynamic_array_reserve(^m.hashes, size_of(int), align_of(int), capacity);
e := __dynamic_array_reserve(^m.entries, entry_size, entry_align, capacity);
return h && e;
}
__dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
new_header := header;
nm: Raw_Dynamic_Map;
new_header.m = ^nm;
reserve(nm.hashes, new_count);
nm.hashes.count = nm.hashes.capacity;
__dynamic_array_reserve(^nm.entries, entry_size, entry_align, m.entries.count);
for _, i in nm.hashes {
nm.hashes[i] = -1;
}
for i := 0; i < nm.entries.count; i += 1 {
entry_header := __dynamic_map_get_entry(new_header, i);
data := cast(^byte)entry_header;
if nm.hashes.count == 0 {
__dynamic_map_grow(new_header);
}
fr := __dynamic_map_find(new_header, entry_header.key);
j := __dynamic_map_add_entry(new_header, entry_header.key);
if fr.entry_prev < 0 {
nm.hashes[fr.hash_index] = j;
} else {
e := __dynamic_map_get_entry(new_header, fr.entry_prev);
e.next = j;
}
e := __dynamic_map_get_entry(new_header, j);
e.next = fr.entry_index;
ndata := cast(^byte)e;
mem.copy(ndata+value_offset, data+value_offset, entry_size-value_offset);
if __dynamic_map_full(new_header) {
__dynamic_map_grow(new_header);
}
}
free_ptr_with_allocator(header.m.hashes.allocator, header.m.hashes.data);
free_ptr_with_allocator(header.m.entries.allocator, header.m.entries.data);
header.m^ = nm;
}
__dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr {
index := __dynamic_map_find(h, key).entry_index;
if index >= 0 {
data := cast(^byte)__dynamic_map_get_entry(h, index);
val := data + h.value_offset;
return val;
}
return nil;
}
__dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr) {
index: int;
if m.hashes.count == 0 {
__dynamic_map_grow(h);
}
fr := __dynamic_map_find(h, key);
if fr.entry_index >= 0 {
index = fr.entry_index;
} else {
index = __dynamic_map_add_entry(h, key);
if fr.entry_prev >= 0 {
entry := __dynamic_map_get_entry(h, fr.entry_prev);
entry.next = index;
} else {
m.hashes[fr.hash_index] = index;
}
}
{
data := cast(^byte)__dynamic_map_get_entry(h, index);
val := data+value_offset;
mem.copy(val, value, entry_size-value_offset);
}
if __dynamic_map_full(h) {
__dynamic_map_grow(h);
}
}
__dynamic_map_grow :: proc(using h: __Map_Header) {
new_count := 2*m.entries.count + 8;
__dynamic_map_rehash(h, new_count);
}
__dynamic_map_full :: proc(using h: __Map_Header) -> bool {
return cast(int)(0.75 * cast(f64)m.hashes.count) <= m.entries.count;
}
__dynamic_map_hash_equal :: proc(h: __Map_Header, a, b: __Map_Key) -> bool {
if a.hash == b.hash {
if h.is_key_string {
return a.str == b.str;
}
return true;
}
return false;
}
__dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_Result {
fr := __Map_Find_Result{-1, -1, -1};
if m.hashes.count > 0 {
fr.hash_index = cast(int)(key.hash % cast(u64)m.hashes.count);
fr.entry_index = m.hashes[fr.hash_index];
for fr.entry_index >= 0 {
entry := __dynamic_map_get_entry(h, fr.entry_index);
if __dynamic_map_hash_equal(h, entry.key, key) {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = entry.next;
}
}
return fr;
}
__dynamic_map_add_entry :: proc(using h: __Map_Header, key: __Map_Key) -> int {
prev := m.entries.count;
c := __dynamic_array_append_nothing(^m.entries, entry_size, entry_align);
if c != prev {
end := __dynamic_map_get_entry(h, c-1);
end.key = key;
end.next = -1;
}
return prev;
}
__dynamic_map_remove :: proc(using h: __Map_Header, key: __Map_Key) {
fr := __dynamic_map_find(h, key);
if fr.entry_index >= 0 {
__dynamic_map_erase(h, fr);
}
}
__dynamic_map_get_entry :: proc(using h: __Map_Header, index: int) -> ^__Map_Entry_Header {
data := cast(^byte)m.entries.data + index*entry_size;
return cast(^__Map_Entry_Header)data;
}
__dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
if fr.entry_prev < 0 {
m.hashes[fr.hash_index] = __dynamic_map_get_entry(h, fr.entry_index).next;
} else {
__dynamic_map_get_entry(h, fr.entry_prev).next = __dynamic_map_get_entry(h, fr.entry_index).next;
}
if fr.entry_index == m.entries.count-1 {
m.entries.count -= 1;
}
mem.copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.count-1), entry_size);
last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key);
if last.entry_prev >= 0 {
__dynamic_map_get_entry(h, last.entry_prev).next = fr.entry_index;
} else {
m.hashes[last.hash_index] = fr.entry_index;
}
}
__print_ti_ptr :: proc(ti: ^Type_Info) {
fmt.println(ti);
match e in ti {
case Type_Info.Enum:
fmt.println(e.names);
}
}
+31 -32
View File
@@ -5,97 +5,96 @@
_ := compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
yield_thread :: proc() { win32._mm_pause(); }
yield_thread :: proc() { win32.mm_pause(); }
mfence :: proc() { win32.ReadWriteBarrier(); }
sfence :: proc() { win32.WriteBarrier(); }
lfence :: proc() { win32.ReadBarrier(); }
load32 :: proc(a: ^i32) -> i32 {
load :: proc(a: ^i32) -> i32 {
return a^;
}
store32 :: proc(a: ^i32, value: i32) {
store :: proc(a: ^i32, value: i32) {
a^ = value;
}
compare_exchange32 :: proc(a: ^i32, expected, desired: i32) -> i32 {
compare_exchange :: proc(a: ^i32, expected, desired: i32) -> i32 {
return win32.InterlockedCompareExchange(a, desired, expected);
}
exchanged32 :: proc(a: ^i32, desired: i32) -> i32 {
exchanged :: proc(a: ^i32, desired: i32) -> i32 {
return win32.InterlockedExchange(a, desired);
}
fetch_add32 :: proc(a: ^i32, operand: i32) -> i32 {
fetch_add :: proc(a: ^i32, operand: i32) -> i32 {
return win32.InterlockedExchangeAdd(a, operand);
}
fetch_and32 :: proc(a: ^i32, operand: i32) -> i32 {
fetch_and :: proc(a: ^i32, operand: i32) -> i32 {
return win32.InterlockedAnd(a, operand);
}
fetch_or32 :: proc(a: ^i32, operand: i32) -> i32 {
fetch_or :: 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);
spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
old_value := compare_exchange(a, 1, 0);
counter := 0;
while old_value != 0 && (time_out < 0 || counter < time_out) {
for old_value != 0 && (time_out < 0 || counter < time_out) {
counter += 1;
yield_thread();
old_value = compare_exchange32(a, 1, 0);
old_value = compare_exchange(a, 1, 0);
mfence();
}
return old_value == 0;
}
spin_unlock32 :: proc(a: ^i32) {
store32(a, 0);
spin_unlock :: proc(a: ^i32) {
store(a, 0);
mfence();
}
try_acquire_lock32 :: proc(a: ^i32) -> bool {
try_acquire_lock :: proc(a: ^i32) -> bool {
yield_thread();
old_value := compare_exchange32(a, 1, 0);
old_value := compare_exchange(a, 1, 0);
mfence();
return old_value == 0;
}
load64 :: proc(a: ^i64) -> i64 {
load :: proc(a: ^i64) -> i64 {
return a^;
}
store64 :: proc(a: ^i64, value: i64) {
store :: proc(a: ^i64, value: i64) {
a^ = value;
}
compare_exchange64 :: proc(a: ^i64, expected, desired: i64) -> i64 {
compare_exchange :: proc(a: ^i64, expected, desired: i64) -> i64 {
return win32.InterlockedCompareExchange64(a, desired, expected);
}
exchanged64 :: proc(a: ^i64, desired: i64) -> i64 {
exchanged :: proc(a: ^i64, desired: i64) -> i64 {
return win32.InterlockedExchange64(a, desired);
}
fetch_add64 :: proc(a: ^i64, operand: i64) -> i64 {
fetch_add :: proc(a: ^i64, operand: i64) -> i64 {
return win32.InterlockedExchangeAdd64(a, operand);
}
fetch_and64 :: proc(a: ^i64, operand: i64) -> i64 {
fetch_and :: proc(a: ^i64, operand: i64) -> i64 {
return win32.InterlockedAnd64(a, operand);
}
fetch_or64 :: proc(a: ^i64, operand: i64) -> i64 {
fetch_or :: 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);
spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
old_value := compare_exchange(a, 1, 0);
counter := 0;
while old_value != 0 && (time_out < 0 || counter < time_out) {
for old_value != 0 && (time_out < 0 || counter < time_out) {
counter += 1;
yield_thread();
old_value = compare_exchange64(a, 1, 0);
old_value = compare_exchange(a, 1, 0);
mfence();
}
return old_value == 0;
}
spin_unlock64 :: proc(a: ^i64) {
store64(a, 0);
spin_unlock :: proc(a: ^i64) {
store(a, 0);
mfence();
}
try_acquire_lock64 :: proc(a: ^i64) -> bool {
try_acquire_lock :: proc(a: ^i64) -> bool {
yield_thread();
old_value := compare_exchange64(a, 1, 0);
old_value := compare_exchange(a, 1, 0);
mfence();
return old_value == 0;
}
+941 -468
View File
File diff suppressed because it is too large Load Diff
+100 -62
View File
@@ -1,77 +1,112 @@
crc32 :: proc(data: rawptr, len: int) -> u32 {
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];
crc32 :: proc(data: []byte) -> u32 {
result := ~cast(u32)0;
for b in data {
result = result>>8 ~ __CRC32_TABLE[(result ~ cast(u32)b) & 0xff];
}
return ~result;
}
crc64 :: proc(data: rawptr, len: int) -> u64 {
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];
crc64 :: proc(data: []byte) -> u64 {
result := ~cast(u64)0;
for b in data {
result = result>>8 ~ __CRC64_TABLE[(result ~ cast(u64)b) & 0xff];
}
return ~result;
}
fnv32 :: proc(data: rawptr, len: int) -> u32 {
s := slice_ptr(data as ^u8, len);
fnv32 :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5;
for i : 0..<len {
h = (h * 0x01000193) ~ s[i] as u32;
for b in data {
h = (h * 0x01000193) ~ cast(u32)b;
}
return h;
}
fnv64 :: proc(data: rawptr, len: int) -> u64 {
s := slice_ptr(data as ^u8, len);
fnv64 :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325;
for i : 0..<len {
h = (h * 0x100000001b3) ~ s[i] as u64;
for b in data {
h = (h * 0x100000001b3) ~ cast(u64)b;
}
return h;
}
fnv32a :: proc(data: rawptr, len: int) -> u32 {
s := slice_ptr(data as ^u8, len);
fnv32a :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5;
for i : 0..<len {
h = (h ~ s[i] as u32) * 0x01000193;
for b in data {
h = (h ~ cast(u32)b) * 0x01000193;
}
return h;
}
fnv64a :: proc(data: rawptr, len: int) -> u64 {
s := slice_ptr(data as ^u8, len);
h :u64 = 0xcbf29ce484222325;
for i : 0..<len {
h = (h ~ s[i] as u64) * 0x100000001b3;
fnv64a :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325;
for b in data {
h = (h ~ cast(u64)b) * 0x100000001b3;
}
return h;
}
murmur32 :: proc(data: []byte) -> u32 {
c1_32: u32 : 0xcc9e2d51;
c2_32: u32 : 0x1b873593;
murmur64 :: proc(data_: rawptr, len: int) -> u64 {
h1: u32 = 0;
nblocks := data.count/4;
p := data.data;
p1 := p + 4*nblocks;
for ; p < p1; p += 4 {
k1 := (cast(^u32)p)^;
k1 *= c1_32;
k1 = (k1 << 15) | (k1 >> 17);
k1 *= c2_32;
h1 ~= k1;
h1 = (h1 << 13) | (h1 >> 19);
h1 = h1*5 + 0xe6546b64;
}
tail := data[nblocks*4:];
k1: u32;
match tail.count&3 {
case 3:
k1 ~= cast(u32)tail[2] << 16;
fallthrough;
case 2:
k1 ~= cast(u32)tail[2] << 8;
fallthrough;
case 1:
k1 ~= cast(u32)tail[0];
k1 *= c1_32;
k1 = (k1 << 15) | (k1 >> 17) ;
k1 *= c2_32;
h1 ~= k1;
}
h1 ~= cast(u32)data.count;
h1 ~= h1 >> 16;
h1 *= 0x85ebca6b;
h1 ~= h1 >> 13;
h1 *= 0xc2b2ae35;
h1 ~= h1 >> 16;
return h1;
}
murmur64 :: proc(data: []byte) -> u64 {
SEED :: 0x9747b28c;
when size_of(int) == 8 {
when false && size_of(int) == 8 {
m :: 0xc6a4a7935bd1e995;
r :: 47;
h: u64 = SEED ~ (len as u64 * m);
h: u64 = SEED ~ (cast(u64)data.count * m);
data64 := slice_ptr(cast(^u64)^data[0], data.count/size_of(u64));
data := slice_ptr(data_ as ^u64, len/size_of(u64));
data2 := slice_ptr(data_ as ^u8, len);
for i : 0 ..< data.count {
k := data[i];
for _, i in data64 {
k := data64[i];
k *= m;
k ~= k>>r;
@@ -81,15 +116,15 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
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;
match data.count&7 {
case 7: h ~= cast(u64)data[6] << 48; fallthrough;
case 6: h ~= cast(u64)data[5] << 40; fallthrough;
case 5: h ~= cast(u64)data[4] << 32; fallthrough;
case 4: h ~= cast(u64)data[3] << 24; fallthrough;
case 3: h ~= cast(u64)data[2] << 16; fallthrough;
case 2: h ~= cast(u64)data[1] << 8; fallthrough;
case 1:
h ~= data2[0] as u64;
h ~= cast(u64)data[0];
h *= m;
}
@@ -102,15 +137,16 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
m :: 0x5bd1e995;
r :: 24;
h1: u32 = SEED as u32 ~ len as u32;
h1: u32 = cast(u32)SEED ~ cast(u32)data.count;
h2: u32 = SEED >> 32;
data := slice_ptr(data_ as ^u32, len/size_of(u32));
data32 := slice_ptr(cast(^u32)^data[0], data.count/size_of(u32));
len := data.count;
i := 0;
while len >= 8 {
for len >= 8 {
k1, k2: u32;
k1 = data[i]; i += 1;
k1 = data32[i]; i += 1;
k1 *= m;
k1 ~= k1>>r;
k1 *= m;
@@ -118,7 +154,7 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
h1 ~= k1;
len -= 4;
k2 = data[i]; i += 1;
k2 = data32[i]; i += 1;
k2 *= m;
k2 ~= k2>>r;
k2 *= m;
@@ -129,7 +165,7 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
if len >= 4 {
k1: u32;
k1 = data[i]; i += 1;
k1 = data32[i]; i += 1;
k1 *= m;
k1 ~= k1>>r;
k1 *= m;
@@ -138,13 +174,16 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
len -= 4;
}
data8 := slice_ptr((data.data+i) as ^u8, 3); // NOTE(bill): This is unsafe
data8 := slice_to_bytes(data32[i:])[:3];
match len {
case 3: h2 ~= data8[2] as u32 << 16; fallthrough;
case 2: h2 ~= data8[1] as u32 << 8; fallthrough;
case 3:
h2 ~= cast(u32)data8[2] << 16;
fallthrough;
case 2:
h2 ~= cast(u32)data8[1] << 8;
fallthrough;
case 1:
h2 ~= data8[0] as u32;
h2 ~= cast(u32)data8[0];
h2 *= m;
}
@@ -157,13 +196,12 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
h2 ~= h1>>19;
h2 *= m;
h := (h1 as u64)<<32 | h2 as u64;
h := cast(u64)(h1)<<32 | cast(u64)(h2);
return h;
}
}
__CRC32_TABLE := [256]u32{
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+71 -65
View File
@@ -24,63 +24,69 @@ Mat2 :: [2]Vec2;
Mat3 :: [3]Vec3;
Mat4 :: [4]Vec4;
sqrt32 :: proc(x: f32) -> f32 #foreign "llvm.sqrt.f32"
sqrt64 :: proc(x: f64) -> f64 #foreign "llvm.sqrt.f64"
sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
sin32 :: proc(x: f32) -> f32 #foreign "llvm.sin.f32"
sin64 :: proc(x: f64) -> f64 #foreign "llvm.sin.f64"
sin :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
sin :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
cos32 :: proc(x: f32) -> f32 #foreign "llvm.cos.f32"
cos64 :: proc(x: f64) -> f64 #foreign "llvm.cos.f64"
cos :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
cos :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
tan32 :: proc(x: f32) -> f32 #inline { return sin32(x)/cos32(x); }
tan64 :: proc(x: f64) -> f64 #inline { return sin64(x)/cos64(x); }
tan :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); }
tan :: proc(x: f64) -> f64 #inline { return sin(x)/cos(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; }
lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
lerp :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; }
sign32 :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
sign64 :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; }
sign :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
sign :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; }
bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16";
bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32";
bit_reverse :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bitreverse.i64";
fmuladd :: proc(a, b, c: f32) -> f32 #foreign __llvm_core "llvm.fmuladd.f32";
fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
copy_sign32 :: proc(x, y: f32) -> f32 {
ix := x transmute u32;
iy := y transmute u32;
copy_sign :: proc(x, y: f32) -> f32 {
ix := transmute(u32)x;
iy := transmute(u32)y;
ix &= 0x7fffffff;
ix |= iy & 0x80000000;
return ix transmute f32;
return transmute(f32)ix;
}
round32 :: proc(x: f32) -> f32 {
round :: proc(x: f32) -> f32 {
if x >= 0 {
return floor32(x + 0.5);
return floor(x + 0.5);
}
return ceil32(x - 0.5);
return ceil(x - 0.5);
}
floor32 :: proc(x: f32) -> f32 {
floor :: proc(x: f32) -> f32 {
if x >= 0 {
return x as int as f32;
return cast(f32)cast(int)x;
}
return (x-0.5) as int as f32;
return cast(f32)cast(int)(x-0.5);
}
ceil32 :: proc(x: f32) -> f32 {
ceil :: proc(x: f32) -> f32 {
if x < 0 {
return x as int as f32;
return cast(f32)cast(int)x;
}
return ((x as int)+1) as f32;
return cast(f32)cast(int)(x+1);
}
remainder32 :: proc(x, y: f32) -> f32 {
return x - round32(x/y) * y;
return x - round(x/y) * y;
}
fmod32 :: proc(x, y: f32) -> f32 {
y = abs(y);
result := remainder32(abs(x), y);
if sign32(result) < 0 {
if sign(result) < 0 {
result += y;
}
return copy_sign32(result, x);
return copy_sign(result, x);
}
@@ -90,43 +96,43 @@ 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; }
dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; }
dot :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z; }
dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }
cross3 :: proc(x, y: Vec3) -> Vec3 {
cross :: 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;
}
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)); }
mag :: proc(v: Vec2) -> f32 { return sqrt(dot(v, v)); }
mag :: proc(v: Vec3) -> f32 { return sqrt(dot(v, v)); }
mag :: proc(v: Vec4) -> f32 { return sqrt(dot(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)}; }
norm :: proc(v: Vec2) -> Vec2 { return v / Vec2{mag(v)}; }
norm :: proc(v: Vec3) -> Vec3 { return v / Vec3{mag(v)}; }
norm :: proc(v: Vec4) -> Vec4 { return v / Vec4{mag(v)}; }
vec2_norm0 :: proc(v: Vec2) -> Vec2 {
m := vec2_mag(v);
norm0 :: proc(v: Vec2) -> Vec2 {
m := mag(v);
if m == 0 {
return Vec2{0};
}
return v / Vec2{m};
}
vec3_norm0 :: proc(v: Vec3) -> Vec3 {
m := vec3_mag(v);
norm0 :: proc(v: Vec3) -> Vec3 {
m := mag(v);
if m == 0 {
return Vec3{0};
}
return v / Vec3{m};
}
vec4_norm0 :: proc(v: Vec4) -> Vec4 {
m := vec4_mag(v);
norm0 :: proc(v: Vec4) -> Vec4 {
m := mag(v);
if m == 0 {
return Vec4{0};
}
@@ -145,18 +151,18 @@ mat4_identity :: proc() -> Mat4 {
}
mat4_transpose :: proc(m: Mat4) -> Mat4 {
for j : 0..<4 {
for i : 0..<4 {
for j in 0..<4 {
for i in 0..<4 {
m[i][j], m[j][i] = m[j][i], m[i][j];
}
}
return m;
}
mat4_mul :: proc(a, b: Mat4) -> Mat4 {
mul :: proc(a, b: Mat4) -> Mat4 {
c: Mat4;
for j : 0..<4 {
for i : 0..<4 {
for j in 0..<4 {
for i in 0..<4 {
c[j][i] = a[0][i]*b[j][0] +
a[1][i]*b[j][1] +
a[2][i]*b[j][2] +
@@ -166,7 +172,7 @@ mat4_mul :: proc(a, b: Mat4) -> Mat4 {
return c;
}
mat4_mul_vec4 :: proc(m: Mat4, v: Vec4) -> Vec4 {
mul :: proc(m: Mat4, v: Vec4) -> Vec4 {
return Vec4{
m[0][0]*v.x + m[1][0]*v.y + m[2][0]*v.z + m[3][0]*v.w,
m[0][1]*v.x + m[1][1]*v.y + m[2][1]*v.z + m[3][1]*v.w,
@@ -175,7 +181,7 @@ mat4_mul_vec4 :: proc(m: Mat4, v: Vec4) -> Vec4 {
};
}
mat4_inverse :: proc(m: Mat4) -> Mat4 {
inverse :: proc(m: Mat4) -> Mat4 {
o: Mat4;
sf00 := m[2][2] * m[3][3] - m[3][2] * m[2][3];
@@ -254,10 +260,10 @@ mat4_translate :: proc(v: Vec3) -> Mat4 {
}
mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
c := cos32(angle_radians);
s := sin32(angle_radians);
c := cos(angle_radians);
s := sin(angle_radians);
a := vec3_norm(v);
a := norm(v);
t := a * Vec3{1-c};
rot := mat4_identity();
@@ -280,14 +286,14 @@ mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
return rot;
}
mat4_scale :: proc(m: Mat4, v: Vec3) -> 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;
}
mat4_scalef :: proc(m: Mat4, s: f32) -> Mat4 {
scale :: proc(m: Mat4, s: f32) -> Mat4 {
m[0][0] *= s;
m[1][1] *= s;
m[2][2] *= s;
@@ -295,23 +301,23 @@ mat4_scalef :: proc(m: Mat4, s: f32) -> Mat4 {
}
mat4_look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
f := vec3_norm(centre - eye);
s := vec3_norm(cross3(f, up));
u := cross3(s, f);
look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
f := norm(centre - eye);
s := norm(cross(f, up));
u := cross(s, f);
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[3] = Vec4{dot(s, eye), dot(u, eye), dot(f, eye), 1};
return m;
}
mat4_perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
m: Mat4;
tan_half_fovy := tan32(0.5 * fovy);
tan_half_fovy := tan(0.5 * fovy);
m[0][0] = 1.0 / (aspect*tan_half_fovy);
m[1][1] = 1.0 / (tan_half_fovy);
m[2][2] = -(far + near) / (far - near);
@@ -321,7 +327,7 @@ mat4_perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
}
mat4_ortho3d :: proc(left, right, bottom, top, near, far: f32) -> 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);
+68 -88
View File
@@ -1,9 +1,14 @@
#import "fmt.odin";
#import "os.odin";
swap :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bswap.i16";
swap :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bswap.i32";
swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64";
set :: proc(data: rawptr, value: i32, len: int) -> rawptr #link_name "__mem_set" {
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign "llvm.memset.p0i8.i64"
llvm_memset_64bit(data, value as byte, len, 1, false);
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
llvm_memset_64bit(data, cast(byte)value, len, 1, false);
return data;
}
@@ -12,51 +17,29 @@ zero :: proc(data: rawptr, len: int) -> rawptr #link_name "__mem_zero" {
}
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"
// NOTE(bill): This _must_ be implemented like C's memmove
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
llvm_memmove_64bit(dst, src, len, 1, false);
return dst;
}
copy_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"
// NOTE(bill): This _must_ be implemented like C's memcpy
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
llvm_memcpy_64bit(dst, src, len, 1, false);
return dst;
}
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);
fast := n/size_of(int) + 1;
offset := (fast-1)*size_of(int);
curr_block := 0;
if n <= size_of(int) {
fast = 0;
}
la := slice_ptr(^a[0] as ^int, fast);
lb := slice_ptr(^b[0] as ^int, fast);
for _ : curr_block ..< fast {
if (la[curr_block] ~ lb[curr_block]) != 0 {
for pos : curr_block*size_of(int) ..< n {
if (a[pos] ~ b[pos]) != 0 {
return a[pos] as int - b[pos] as int;
}
}
}
}
for _ : offset ..< n {
if (a[offset] ~ b[offset]) != 0 {
return a[offset] as int - b[offset] as int;
compare :: proc(a, b: []byte) -> int #link_name "__mem_compare" {
n := min(a.count, b.count);
for i in 0..<n {
match {
case a[i] < b[i]:
return -1;
case a[i] > b[i]:
return +1;
}
}
return 0;
}
@@ -64,8 +47,8 @@ compare :: proc(dst, src: rawptr, n: int) -> int #link_name "__mem_compare" {
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; }
gigabytes :: proc(x: int) -> int #inline { return megabytes(x) * 1024; }
terabytes :: proc(x: int) -> int #inline { return gigabytes(x) * 1024; }
is_power_of_two :: proc(x: int) -> bool {
if x <= 0 {
@@ -77,36 +60,38 @@ is_power_of_two :: proc(x: int) -> bool {
align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
assert(is_power_of_two(align));
a := align as uint;
p := ptr as uint;
a := cast(uint)align;
p := cast(uint)ptr;
modulo := p & (a-1);
if modulo != 0 {
p += a - modulo;
}
return p as rawptr;
return cast(rawptr)p;
}
Allocation_Header :: struct {
size: int;
size: int,
}
allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: int) {
header.size = size;
ptr := (header+1) as ^int;
ptr := cast(^int)(header+1);
while i := 0; ptr as rawptr < data {
for i := 0; cast(rawptr)ptr < data; i += 1 {
(ptr+i)^ = -1;
i += 1;
}
}
allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
p := data as ^int;
while (p-1)^ == -1 {
if data == nil {
return nil;
}
p := cast(^int)data;
for (p-1)^ == -1 {
p = (p-1);
}
return (p as ^Allocation_Header)-1;
return cast(^Allocation_Header)p-1;
}
@@ -115,15 +100,15 @@ allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
// Custom allocators
Arena :: struct {
backing: Allocator;
offset: int;
memory: []byte;
temp_count: int;
backing: Allocator,
offset: int,
memory: []byte,
temp_count: int,
}
Arena_Temp_Memory :: struct {
arena: ^Arena;
original_count: int;
arena: ^Arena,
original_count: int,
}
@@ -145,8 +130,8 @@ init_arena_from_context :: proc(using a: ^Arena, size: int) {
free_arena :: proc(using a: ^Arena) {
if backing.procedure != nil {
push_allocator backing {
free(memory.data);
memory = memory[0:0];
free(memory);
memory = nil;
offset = 0;
}
}
@@ -163,7 +148,7 @@ 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;
arena := cast(^Arena)allocator_data;
match mode {
case ALLOC:
@@ -232,8 +217,7 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int {
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 {
match info in type_info {
case Named:
return align_of_type_info(info.base);
case Integer:
@@ -244,27 +228,35 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int {
return WORD_SIZE;
case Boolean:
return 1;
case Any:
return WORD_SIZE;
case Pointer:
return WORD_SIZE;
case Maybe:
return max(align_of_type_info(info.elem), 1);
case Procedure:
return WORD_SIZE;
case Array:
return align_of_type_info(info.elem);
case Dynamic_Array:
return WORD_SIZE;
case Slice:
return WORD_SIZE;
case Vector:
size := size_of_type_info(info.elem);
count := max(prev_pow2(info.count as i64), 1) as int;
count := cast(int)max(prev_pow2(cast(i64)info.count), 1);
total := size * count;
return clamp(total, 1, MAX_ALIGN);
case Tuple:
return info.align;
case Struct:
return info.align;
case Union:
return info.align;
case Raw_Union:
return info.align;
case Enum:
return align_of_type_info(info.base);
case Map:
return align_of_type_info(info.generated_struct);
}
return 0;
@@ -278,23 +270,21 @@ align_formula :: proc(size, align: int) -> int {
size_of_type_info :: proc(type_info: ^Type_Info) -> int {
WORD_SIZE :: size_of(int);
using Type_Info;
match type info : type_info {
match info in type_info {
case Named:
return size_of_type_info(info.base);
case Integer:
return info.size;
case Float:
return info.size;
case Any:
return 2*WORD_SIZE;
case String:
return 2*WORD_SIZE;
case Boolean:
return 1;
case Any:
return 2*WORD_SIZE;
case Pointer:
return WORD_SIZE;
case Maybe:
return size_of_type_info(info.elem) + 1;
case Procedure:
return WORD_SIZE;
case Array:
@@ -306,39 +296,29 @@ size_of_type_info :: proc(type_info: ^Type_Info) -> int {
align := align_of_type_info(info.elem);
alignment := align_formula(size, align);
return alignment*(count-1) + size;
case Dynamic_Array:
return size_of(rawptr) + 2*size_of(int) + size_of(Allocator);
case Slice:
return 3*WORD_SIZE;
return 2*WORD_SIZE;
case Vector:
is_bool :: proc(type_info: ^Type_Info) -> bool {
match type info : type_info {
case Named:
return is_bool(info.base);
case Boolean:
return true;
}
return false;
}
count := info.count;
if count == 0 {
return 0;
}
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;
}
total_size_in_bits := bit_size * count;
total_size := (total_size_in_bits+7)/8;
return total_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 Struct:
return info.size;
case Union:
return info.size;
case Raw_Union:
return info.size;
case Enum:
return size_of_type_info(info.base);
case Map:
return size_of_type_info(info.generated_struct);
}
return 0;
+27 -26
View File
@@ -1,36 +1,37 @@
#foreign_system_library "opengl32" when ODIN_OS == "windows";
#foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
#include "opengl_constants.odin";
#load "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"
Clear :: proc(mask: u32) #foreign lib "glClear";
ClearColor :: proc(r, g, b, a: f32) #foreign lib "glClearColor";
Begin :: proc(mode: i32) #foreign lib "glBegin";
End :: proc() #foreign lib "glEnd";
Finish :: proc() #foreign lib "glFinish";
BlendFunc :: proc(sfactor, dfactor: i32) #foreign lib "glBlendFunc";
Enable :: proc(cap: i32) #foreign lib "glEnable";
Disable :: proc(cap: i32) #foreign lib "glDisable";
GenTextures :: proc(count: i32, result: ^u32) #foreign lib "glGenTextures";
DeleteTextures:: proc(count: i32, result: ^u32) #foreign lib "glDeleteTextures";
TexParameteri :: proc(target, pname, param: i32) #foreign lib "glTexParameteri";
TexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign lib "glTexParameterf";
BindTexture :: proc(target: i32, texture: u32) #foreign lib "glBindTexture";
LoadIdentity :: proc() #foreign lib "glLoadIdentity";
Viewport :: proc(x, y, width, height: i32) #foreign lib "glViewport";
Ortho :: proc(left, right, bottom, top, near, far: f64) #foreign lib "glOrtho";
Color3f :: proc(r, g, b: f32) #foreign lib "glColor3f";
Vertex3f :: proc(x, y, z: f32) #foreign lib "glVertex3f";
TexImage2D :: proc(target, level, internal_format,
width, height, border,
format, _type: i32, pixels: rawptr) #foreign "glTexImage2D"
format, _type: i32, pixels: rawptr) #foreign lib "glTexImage2D";
GetError :: proc() -> i32 #foreign "glGetError"
GetString :: proc(name: i32) -> ^byte #foreign "glGetString"
GetIntegerv :: proc(name: i32, v: ^i32) #foreign "glGetIntegerv"
GetError :: proc() -> i32 #foreign lib "glGetError";
GetString :: proc(name: i32) -> ^byte #foreign lib "glGetString";
GetIntegerv :: proc(name: i32, v: ^i32) #foreign lib "glGetIntegerv";
string_data :: proc(s: string) -> ^u8 #inline { return ^s[0]; }
_libgl := win32.LoadLibraryA(("opengl32.dll\x00" as string).data);
_libgl := win32.LoadLibraryA(string_data("opengl32.dll\x00"));
GetProcAddress :: proc(name: string) -> proc() #cc_c {
assert(name[name.count-1] == 0);
@@ -100,7 +101,7 @@ UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c
GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c;
init :: proc() {
set_proc_address :: proc(p: rawptr, name: string) #inline { (p as ^(proc() #cc_c))^ = GetProcAddress(name); }
set_proc_address :: proc(p: rawptr, name: string) #inline { (cast(^(proc() #cc_c))p)^ = GetProcAddress(name); }
set_proc_address(^GenBuffers, "glGenBuffers\x00");
set_proc_address(^GenVertexArrays, "glGenVertexArrays\x00");
+2 -1
View File
@@ -1,2 +1,3 @@
#include "os_windows.odin" when ODIN_OS == "windows"
#load "os_windows.odin" when ODIN_OS == "windows";
#load "os_x.odin" when ODIN_OS == "osx";
+84 -73
View File
@@ -2,11 +2,11 @@
#import "fmt.odin";
Handle :: uint;
Handle :: int;
File_Time :: u64;
Error :: int;
Errno :: int;
INVALID_HANDLE: Handle : ~(0 as Handle);
INVALID_HANDLE: Handle : -1;
O_RDONLY :: 0x00000;
@@ -22,37 +22,37 @@ 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;
ERROR_NONE: Errno : 0;
ERROR_FILE_NOT_FOUND: Errno : 2;
ERROR_PATH_NOT_FOUND: Errno : 3;
ERROR_ACCESS_DENIED: Errno : 5;
ERROR_NO_MORE_FILES: Errno : 18;
ERROR_HANDLE_EOF: Errno : 38;
ERROR_NETNAME_DELETED: Errno : 64;
ERROR_FILE_EXISTS: Errno : 80;
ERROR_BROKEN_PIPE: Errno : 109;
ERROR_BUFFER_OVERFLOW: Errno : 111;
ERROR_INSUFFICIENT_BUFFER: Errno : 122;
ERROR_MOD_NOT_FOUND: Errno : 126;
ERROR_PROC_NOT_FOUND: Errno : 127;
ERROR_DIR_NOT_EMPTY: Errno : 145;
ERROR_ALREADY_EXISTS: Errno : 183;
ERROR_ENVVAR_NOT_FOUND: Errno : 203;
ERROR_MORE_DATA: Errno : 234;
ERROR_OPERATION_ABORTED: Errno : 995;
ERROR_IO_PENDING: Errno : 997;
ERROR_NOT_FOUND: Errno : 1168;
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
WSAEACCES: Errno : 10013;
WSAECONNRESET: Errno : 10054;
// Windows reserves errors >= 1<<29 for application use
ERROR_FILE_IS_PIPE: Error : 1<<29 + 0;
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Error) {
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
using win32;
if path.count == 0 {
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
@@ -73,7 +73,7 @@ open :: proc(path: string, mode: int, perm: u32) -> (Handle, Error) {
access |= FILE_APPEND_DATA;
}
share_mode := (FILE_SHARE_READ|FILE_SHARE_WRITE) as u32;
share_mode := cast(u32)(FILE_SHARE_READ|FILE_SHARE_WRITE);
sa: ^SECURITY_ATTRIBUTES = nil;
sa_inherit := SECURITY_ATTRIBUTES{length = size_of(SECURITY_ATTRIBUTES), inherit_handle = 1};
if mode&O_CLOEXEC == 0 {
@@ -95,40 +95,41 @@ open :: proc(path: string, mode: int, perm: u32) -> (Handle, Error) {
}
buf: [300]byte;
copy(buf[:], path as []byte);
copy(buf[:], cast([]byte)path);
handle := CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil) as Handle;
if handle == INVALID_HANDLE {
handle := cast(Handle)CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil);
if handle != INVALID_HANDLE {
return handle, ERROR_NONE;
}
err := GetLastError();
return INVALID_HANDLE, err as Error;
return INVALID_HANDLE, cast(Errno)err;
}
close :: proc(fd: Handle) {
win32.CloseHandle(fd as win32.HANDLE);
win32.CloseHandle(cast(win32.HANDLE)fd);
}
write :: proc(fd: Handle, data: []byte) -> (int, Error) {
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
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 {
e := win32.WriteFile(cast(win32.HANDLE)fd, data.data, cast(i32)data.count, ^bytes_written, nil);
if e == win32.FALSE {
err := win32.GetLastError();
return 0, err as Error;
return 0, cast(Errno)err;
}
return bytes_read as int, ERROR_NONE;
return cast(int)bytes_written, ERROR_NONE;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
bytes_read: i32;
e := win32.ReadFile(cast(win32.HANDLE)fd, data.data, cast(u32)data.count, ^bytes_read, nil);
if e == win32.FALSE {
err := win32.GetLastError();
return 0, cast(Errno)err;
}
return cast(int)bytes_read, ERROR_NONE;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
using win32;
w: u32;
match whence {
@@ -136,18 +137,18 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
case 1: w = FILE_CURRENT;
case 2: w = FILE_END;
}
hi := (offset>>32) as i32;
lo := offset as i32;
ft := GetFileType(fd as HANDLE);
hi := cast(i32)(offset>>32);
lo := cast(i32)(offset);
ft := GetFileType(cast(HANDLE)fd);
if ft == FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE;
}
dw_ptr := SetFilePointer(fd as HANDLE, lo, ^hi, w);
dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w);
if dw_ptr == INVALID_SET_FILE_POINTER {
err := GetLastError();
return 0, err as Error;
return 0, cast(Errno)err;
}
return (hi as i64)<<32 + (dw_ptr as i64), ERROR_NONE;
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
}
@@ -158,9 +159,9 @@ stderr := get_std_handle(win32.STD_ERROR_HANDLE);
get_std_handle :: proc(h: int) -> Handle {
fd := win32.GetStdHandle(h as i32);
fd := win32.GetStdHandle(cast(i32)h);
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
return fd as Handle;
return cast(Handle)fd;
}
@@ -170,9 +171,9 @@ get_std_handle :: proc(h: int) -> 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;
win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info);
lo := cast(File_Time)file_info.last_write_time.lo;
hi := cast(File_Time)file_info.last_write_time.hi;
return lo | hi << 32;
}
@@ -183,14 +184,14 @@ last_write_time_by_name :: proc(name: string) -> File_Time {
assert(buf.count > name.count);
copy(buf[:], name as []byte);
copy(buf[:], cast([]byte)name);
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;
l := cast(File_Time)last_write_time.lo;
h := cast(File_Time)last_write_time.hi;
return l | h << 32;
}
@@ -200,7 +201,7 @@ last_write_time_by_name :: proc(name: string) -> File_Time {
read_entire_file :: proc(name: string) -> ([]byte, bool) {
buf: [300]byte;
copy(buf[:], name as []byte);
copy(buf[:], cast([]byte)name);
fd, err := open(name, O_RDONLY, 0);
if err != ERROR_NONE {
@@ -209,7 +210,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
defer close(fd);
length: i64;
file_size_ok := win32.GetFileSizeEx(fd as win32.HANDLE, ^length) != 0;
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
if !file_size_ok {
return nil, false;
}
@@ -222,23 +223,23 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
single_read_length: i32;
total_read: i64;
while total_read < length {
for total_read < length {
remaining := length - total_read;
to_read: u32;
MAX :: 1<<32-1;
if remaining <= MAX {
to_read = remaining as u32;
to_read = cast(u32)remaining;
} else {
to_read = MAX;
}
win32.ReadFile(fd as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil);
win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil);
if single_read_length <= 0 {
free(data.data);
free(data);
return nil, false;
}
total_read += single_read_length as i64;
total_read += cast(i64)single_read_length;
}
return data, true;
@@ -247,25 +248,35 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
heap_alloc :: proc(size: int) -> rawptr {
assert(size > 0);
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
if new_size == 0 {
heap_free(ptr);
return nil;
}
if ptr == nil {
return heap_alloc(new_size);
}
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
}
heap_free :: proc(ptr: rawptr) {
if ptr == nil {
return;
}
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
}
exit :: proc(code: int) {
win32.ExitProcess(code as u32);
win32.ExitProcess(cast(u32)code);
}
current_thread_id :: proc() -> int {
GetCurrentThreadId :: proc() -> u32 #foreign #dll_import
return GetCurrentThreadId() as int;
return cast(int)win32.GetCurrentThreadId();
}
+230
View File
@@ -0,0 +1,230 @@
#import "fmt.odin";
Handle :: i32;
File_Time :: u64;
Errno :: int;
// INVALID_HANDLE: Handle : -1;
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
O_RDWR :: 0x00002;
O_CREAT :: 0x00040;
O_EXCL :: 0x00080;
O_NOCTTY :: 0x00100;
O_TRUNC :: 0x00200;
O_NONBLOCK :: 0x00800;
O_APPEND :: 0x00400;
O_SYNC :: 0x01000;
O_ASYNC :: 0x02000;
O_CLOEXEC :: 0x80000;
// ERROR_NONE: Errno : 0;
// ERROR_FILE_NOT_FOUND: Errno : 2;
// ERROR_PATH_NOT_FOUND: Errno : 3;
// ERROR_ACCESS_DENIED: Errno : 5;
// ERROR_NO_MORE_FILES: Errno : 18;
// ERROR_HANDLE_EOF: Errno : 38;
// ERROR_NETNAME_DELETED: Errno : 64;
// ERROR_FILE_EXISTS: Errno : 80;
// ERROR_BROKEN_PIPE: Errno : 109;
// ERROR_BUFFER_OVERFLOW: Errno : 111;
// ERROR_INSUFFICIENT_BUFFER: Errno : 122;
// ERROR_MOD_NOT_FOUND: Errno : 126;
// ERROR_PROC_NOT_FOUND: Errno : 127;
// ERROR_DIR_NOT_EMPTY: Errno : 145;
// ERROR_ALREADY_EXISTS: Errno : 183;
// ERROR_ENVVAR_NOT_FOUND: Errno : 203;
// ERROR_MORE_DATA: Errno : 234;
// ERROR_OPERATION_ABORTED: Errno : 995;
// ERROR_IO_PENDING: Errno : 997;
// ERROR_NOT_FOUND: Errno : 1168;
// ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
// WSAEACCES: Errno : 10013;
// WSAECONNRESET: Errno : 10054;
// Windows reserves errors >= 1<<29 for application use
// ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
#foreign_system_library libc "c";
unix_open :: proc(path: ^u8, mode: int, perm: u32) -> Handle #foreign libc "open";
unix_close :: proc(handle: Handle) #foreign libc "close";
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "read";
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "write";
unix_gettid :: proc() -> u64 #foreign libc "gettid";
unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
unix_free :: proc(ptr: rawptr) #foreign libc "free";
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
unix_exit :: proc(status: int) #foreign libc "exit";
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
return unix_open(path.data, mode, perm), 0;
}
close :: proc(fd: Handle) {
unix_close(fd);
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return unix_write(fd, data.data, data.count), 0;
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return unix_read(fd, data.data, data.count), 0;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
/*
using win32;
w: u32;
match whence {
case 0: w = FILE_BEGIN;
case 1: w = FILE_CURRENT;
case 2: w = FILE_END;
}
hi := cast(i32)(offset>>32);
lo := cast(i32)(offset);
ft := GetFileType(cast(HANDLE)fd);
if ft == FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE;
}
dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w);
if dw_ptr == INVALID_SET_FILE_POINTER {
err := GetLastError();
return 0, cast(Errno)err;
}
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
*/
return 0, 0;
}
// NOTE(bill): Uses startup to initialize it
stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE);
stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE);
stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE);
/*
get_std_handle :: proc(h: int) -> Handle {
fd := win32.GetStdHandle(cast(i32)h);
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
return cast(Handle)fd;
}
last_write_time :: proc(fd: Handle) -> File_Time {
file_info: win32.BY_HANDLE_FILE_INFORMATION;
win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info);
lo := cast(File_Time)file_info.last_write_time.lo;
hi := cast(File_Time)file_info.last_write_time.hi;
return lo | hi << 32;
}
last_write_time_by_name :: proc(name: string) -> File_Time {
last_write_time: win32.FILETIME;
data: win32.FILE_ATTRIBUTE_DATA;
buf: [1024]byte;
assert(buf.count > name.count);
copy(buf[:], cast([]byte)name);
if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
last_write_time = data.last_write_time;
}
l := cast(File_Time)last_write_time.lo;
h := cast(File_Time)last_write_time.hi;
return l | h << 32;
}
read_entire_file :: proc(name: string) -> ([]byte, bool) {
buf: [300]byte;
copy(buf[:], cast([]byte)name);
fd, err := open(name, O_RDONLY, 0);
if err != ERROR_NONE {
return nil, false;
}
defer close(fd);
length: i64;
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^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 = cast(u32)remaining;
} else {
to_read = MAX;
}
win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil);
if single_read_length <= 0 {
free(data);
return nil, false;
}
total_read += cast(i64)single_read_length;
}
return data, true;
}
*/
heap_alloc :: proc(size: int) -> rawptr {
assert(size > 0);
return unix_malloc(size);
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
return unix_realloc(ptr, new_size);
}
heap_free :: proc(ptr: rawptr) {
unix_free(ptr);
}
exit :: proc(code: int) {
unix_exit(code);
}
current_thread_id :: proc() -> int {
// return cast(int) unix_gettid();
return 0;
}
+20 -20
View File
@@ -2,18 +2,18 @@
#import "atomic.odin";
Semaphore :: struct {
handle: win32.HANDLE;
handle: win32.HANDLE,
}
Mutex :: struct {
semaphore: Semaphore;
counter: i32;
owner: i32;
recursion: i32;
semaphore: Semaphore,
counter: i32,
owner: i32,
recursion: i32,
}
current_thread_id :: proc() -> i32 {
return win32.GetCurrentThreadId() as i32;
return cast(i32)win32.GetCurrentThreadId();
}
semaphore_init :: proc(s: ^Semaphore) {
@@ -25,7 +25,7 @@ semaphore_destroy :: proc(s: ^Semaphore) {
}
semaphore_post :: proc(s: ^Semaphore, count: int) {
win32.ReleaseSemaphore(s.handle, count as i32, nil);
win32.ReleaseSemaphore(s.handle, cast(i32)count, nil);
}
semaphore_release :: proc(s: ^Semaphore) #inline { semaphore_post(s, 1); }
@@ -36,8 +36,8 @@ semaphore_wait :: proc(s: ^Semaphore) {
mutex_init :: proc(m: ^Mutex) {
atomic.store32(^m.counter, 0);
atomic.store32(^m.owner, current_thread_id());
atomic.store(^m.counter, 0);
atomic.store(^m.owner, current_thread_id());
semaphore_init(^m.semaphore);
m.recursion = 0;
}
@@ -46,27 +46,27 @@ mutex_destroy :: proc(m: ^Mutex) {
}
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) {
if atomic.fetch_add(^m.counter, 1) > 0 {
if thread_id != atomic.load(^m.owner) {
semaphore_wait(^m.semaphore);
}
}
atomic.store32(^m.owner, thread_id);
atomic.store(^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);
if atomic.load(^m.owner) == thread_id {
atomic.fetch_add(^m.counter, 1);
} else {
expected: i32 = 0;
if atomic.load32(^m.counter) != 0 {
if atomic.load(^m.counter) != 0 {
return false;
}
if atomic.compare_exchange32(^m.counter, expected, 1) == 0 {
if atomic.compare_exchange(^m.counter, expected, 1) == 0 {
return false;
}
atomic.store32(^m.owner, thread_id);
atomic.store(^m.owner, thread_id);
}
m.recursion += 1;
return true;
@@ -74,15 +74,15 @@ mutex_try_lock :: proc(m: ^Mutex) -> bool {
mutex_unlock :: proc(m: ^Mutex) {
recursion: i32;
thread_id := current_thread_id();
assert(thread_id == atomic.load32(^m.owner));
assert(thread_id == atomic.load(^m.owner));
m.recursion -= 1;
recursion = m.recursion;
if recursion == 0 {
atomic.store32(^m.owner, thread_id);
atomic.store(^m.owner, thread_id);
}
if atomic.fetch_add32(^m.counter, -1) > 1 {
if atomic.fetch_add(^m.counter, -1) > 1 {
if recursion == 0 {
semaphore_release(^m.semaphore);
}
+144 -133
View File
@@ -1,5 +1,8 @@
#foreign_system_library "user32" when ODIN_OS == "windows";
#foreign_system_library "gdi32" when ODIN_OS == "windows";
#foreign_system_library "kernel32.lib" when ODIN_OS == "windows";
#foreign_system_library "user32.lib" when ODIN_OS == "windows";
#foreign_system_library "gdi32.lib" when ODIN_OS == "windows";
#foreign_system_library "winmm.lib" when ODIN_OS == "windows";
#foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
HANDLE :: rawptr;
HWND :: HANDLE;
@@ -16,10 +19,10 @@ LPARAM :: int;
LRESULT :: int;
ATOM :: i16;
BOOL :: i32;
WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT;
WNDPROC :: #type proc(HWND, u32, WPARAM, LPARAM) -> LRESULT #cc_c;
INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE;
INVALID_HANDLE_VALUE :: cast(HANDLE)~cast(int)0;
FALSE: BOOL : 0;
TRUE: BOOL : 1;
@@ -46,7 +49,7 @@ WM_KEYUP :: 0x0101;
PM_REMOVE :: 1;
COLOR_BACKGROUND :: 1 as HBRUSH;
COLOR_BACKGROUND :: cast(HBRUSH)(cast(int)1);
BLACK_BRUSH :: 4;
SM_CXSCREEN :: 0;
@@ -56,61 +59,67 @@ SW_SHOW :: 5;
POINT :: struct #ordered {
x, y: i32;
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;
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;
hwnd: HWND,
message: u32,
wparam: WPARAM,
lparam: LPARAM,
time: u32,
pt: POINT,
}
RECT :: struct #ordered {
left: i32;
top: i32;
right: i32;
bottom: i32;
left: i32,
top: i32,
right: i32,
bottom: i32,
}
FILETIME :: struct #ordered {
lo, hi: u32;
lo, hi: u32,
}
SYSTEMTIME :: struct #ordered {
year, month: u16,
day_of_week, day: u16,
hour, minute, second, millisecond: u16,
}
BY_HANDLE_FILE_INFORMATION :: struct #ordered {
file_attributes: u32;
file_attributes: u32,
creation_time,
last_access_time,
last_write_time: FILETIME;
last_write_time: FILETIME,
volume_serial_number,
file_size_high,
file_size_low,
number_of_links,
file_index_high,
file_index_low: u32;
file_index_low: u32,
}
FILE_ATTRIBUTE_DATA :: struct #ordered {
file_attributes: u32;
file_attributes: u32,
creation_time,
last_access_time,
last_write_time: FILETIME;
last_write_time: FILETIME,
file_size_high,
file_size_low: u32;
file_size_low: u32,
}
GET_FILEEX_INFO_LEVELS :: i32;
@@ -118,43 +127,43 @@ 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
GetLastError :: proc() -> i32 #foreign kernel32;
ExitProcess :: proc(exit_code: u32) #foreign kernel32;
GetDesktopWindow :: proc() -> HWND #foreign user32;
GetCursorPos :: proc(p: ^POINT) -> i32 #foreign user32;
ScreenToClient :: proc(h: HWND, p: ^POINT) -> i32 #foreign user32;
GetModuleHandleA :: proc(module_name: ^u8) -> HINSTANCE #foreign kernel32;
GetStockObject :: proc(fn_object: i32) -> HGDIOBJ #foreign gdi32;
PostQuitMessage :: proc(exit_code: i32) #foreign user32;
SetWindowTextA :: proc(hwnd: HWND, c_string: ^u8) -> BOOL #foreign user32;
QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign #dll_import
QueryPerformanceCounter :: proc(result: ^i64) -> i32 #foreign #dll_import
QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign kernel32;
QueryPerformanceCounter :: proc(result: ^i64) -> i32 #foreign kernel32;
Sleep :: proc(ms: i32) -> i32 #foreign #dll_import
Sleep :: proc(ms: i32) -> i32 #foreign kernel32;
OutputDebugStringA :: proc(c_str: ^u8) #foreign #dll_import
OutputDebugStringA :: proc(c_str: ^u8) #foreign kernel32;
RegisterClassExA :: proc(wc: ^WNDCLASSEXA) -> ATOM #foreign #dll_import
RegisterClassExA :: proc(wc: ^WNDCLASSEXA) -> ATOM #foreign user32;
CreateWindowExA :: proc(ex_style: u32,
class_name, title: ^u8,
style: u32,
x, y, w, h: i32,
parent: HWND, menu: HMENU, instance: HINSTANCE,
param: rawptr) -> HWND #foreign #dll_import
param: rawptr) -> HWND #foreign user32;
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
ShowWindow :: proc(hwnd: HWND, cmd_show: i32) -> BOOL #foreign user32;
TranslateMessage :: proc(msg: ^MSG) -> BOOL #foreign user32;
DispatchMessageA :: proc(msg: ^MSG) -> LRESULT #foreign user32;
UpdateWindow :: proc(hwnd: HWND) -> BOOL #foreign user32;
PeekMessageA :: proc(msg: ^MSG, hwnd: HWND,
msg_filter_min, msg_filter_max, remove_msg: u32) -> BOOL #foreign #dll_import
msg_filter_min, msg_filter_max, remove_msg: u32) -> BOOL #foreign user32;
DefWindowProcA :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #foreign #dll_import
DefWindowProcA :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #foreign user32;
AdjustWindowRect :: proc(rect: ^RECT, style: u32, menu: BOOL) -> BOOL #foreign #dll_import
GetActiveWindow :: proc() -> HWND #foreign #dll_import
AdjustWindowRect :: proc(rect: ^RECT, style: u32, menu: BOOL) -> BOOL #foreign user32;
GetActiveWindow :: proc() -> HWND #foreign user32;
GetQueryPerformanceFrequency :: proc() -> i64 {
@@ -163,28 +172,34 @@ GetQueryPerformanceFrequency :: proc() -> i64 {
return r;
}
GetCommandLineA :: proc() -> ^u8 #foreign #dll_import
GetSystemMetrics :: proc(index: i32) -> i32 #foreign #dll_import
GetCurrentThreadId :: proc() -> u32 #foreign #dll_import
GetCommandLineA :: proc() -> ^u8 #foreign kernel32;
GetSystemMetrics :: proc(index: i32) -> i32 #foreign kernel32;
GetCurrentThreadId :: proc() -> u32 #foreign kernel32;
timeGetTime :: proc() -> u32 #foreign winmm;
GetSystemTimeAsFileTime :: proc(system_time_as_file_time: ^FILETIME) #foreign kernel32;
FileTimeToLocalFileTime :: proc(file_time: ^FILETIME, local_file_time: ^FILETIME) -> BOOL #foreign kernel32;
FileTimeToSystemTime :: proc(file_time: ^FILETIME, system_time: ^SYSTEMTIME) -> BOOL #foreign kernel32;
SystemTimeToFileTime :: proc(system_time: ^SYSTEMTIME, file_time: ^FILETIME) -> BOOL #foreign kernel32;
// File Stuff
CloseHandle :: proc(h: HANDLE) -> i32 #foreign #dll_import
GetStdHandle :: proc(h: i32) -> HANDLE #foreign #dll_import
CloseHandle :: proc(h: HANDLE) -> i32 #foreign kernel32;
GetStdHandle :: proc(h: i32) -> HANDLE #foreign kernel32;
CreateFileA :: proc(filename: ^u8, desired_access, share_mode: u32,
security: rawptr,
creation, flags_and_attribs: u32, template_file: HANDLE) -> HANDLE #foreign #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
creation, flags_and_attribs: u32, template_file: HANDLE) -> HANDLE #foreign kernel32;
ReadFile :: proc(h: HANDLE, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> BOOL #foreign kernel32;
WriteFile :: proc(h: HANDLE, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> BOOL #foreign kernel32;
GetFileSizeEx :: proc(file_handle: HANDLE, file_size: ^i64) -> BOOL #foreign #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
GetFileSizeEx :: proc(file_handle: HANDLE, file_size: ^i64) -> BOOL #foreign kernel32;
GetFileAttributesExA :: proc(filename: ^u8, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: rawptr) -> BOOL #foreign kernel32;
GetFileInformationByHandle :: proc(file_handle: HANDLE, file_info: ^BY_HANDLE_FILE_INFORMATION) -> BOOL #foreign kernel32;
GetFileType :: proc(file_handle: HANDLE) -> u32 #foreign #dll_import
SetFilePointer :: proc(file_handle: HANDLE, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign #dll_import
GetFileType :: proc(file_handle: HANDLE) -> u32 #foreign kernel32;
SetFilePointer :: proc(file_handle: HANDLE, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign kernel32;
SetHandleInformation :: proc(obj: HANDLE, mask, flags: u32) -> BOOL #foreign #dll_import
SetHandleInformation :: proc(obj: HANDLE, mask, flags: u32) -> BOOL #foreign kernel32;
HANDLE_FLAG_INHERIT :: 1;
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2;
@@ -233,15 +248,15 @@ FILE_TYPE_DISK :: 0x0001;
FILE_TYPE_CHAR :: 0x0002;
FILE_TYPE_PIPE :: 0x0003;
INVALID_SET_FILE_POINTER :: ~(0 as u32);
INVALID_SET_FILE_POINTER :: ~cast(u32)0;
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
HeapAlloc :: proc (h: HANDLE, flags: u32, bytes: int) -> rawptr #foreign kernel32;
HeapReAlloc :: proc (h: HANDLE, flags: u32, memory: rawptr, bytes: int) -> rawptr #foreign kernel32;
HeapFree :: proc (h: HANDLE, flags: u32, memory: rawptr) -> BOOL #foreign kernel32;
GetProcessHeap :: proc () -> HANDLE #foreign kernel32;
HEAP_ZERO_MEMORY :: 0x00000008;
@@ -249,57 +264,55 @@ HEAP_ZERO_MEMORY :: 0x00000008;
// Synchronization
SECURITY_ATTRIBUTES :: struct #ordered {
length: u32;
security_descriptor: rawptr;
inherit_handle: BOOL;
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
CreateSemaphoreA :: proc(attributes: ^SECURITY_ATTRIBUTES, initial_count, maximum_count: i32, name: ^byte) -> HANDLE #foreign kernel32;
ReleaseSemaphore :: proc(semaphore: HANDLE, release_count: i32, previous_count: ^i32) -> BOOL #foreign kernel32;
WaitForSingleObject :: proc(handle: HANDLE, milliseconds: u32) -> u32 #foreign kernel32;
InterlockedCompareExchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 #foreign
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
InterlockedCompareExchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 #foreign kernel32;
InterlockedExchange :: proc(dst: ^i32, desired: i32) -> i32 #foreign kernel32;
InterlockedExchangeAdd :: proc(dst: ^i32, desired: i32) -> i32 #foreign kernel32;
InterlockedAnd :: proc(dst: ^i32, desired: i32) -> i32 #foreign kernel32;
InterlockedOr :: proc(dst: ^i32, desired: i32) -> i32 #foreign kernel32;
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
InterlockedCompareExchange64 :: proc(dst: ^i64, exchange, comparand: i64) -> i64 #foreign kernel32;
InterlockedExchange64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32;
InterlockedExchangeAdd64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32;
InterlockedAnd64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32;
InterlockedOr64 :: proc(dst: ^i64, desired: i64) -> i64 #foreign kernel32;
mm_pause :: proc() #foreign kernel32 "_mm_pause";
ReadWriteBarrier :: proc() #foreign kernel32;
WriteBarrier :: proc() #foreign kernel32;
ReadBarrier :: proc() #foreign kernel32;
_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;
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;
using header: BITMAPINFOHEADER,
colors: [1]RGBQUAD,
}
RGBQUAD :: struct #ordered {
blue, green, red, reserved: byte;
}
RGBQUAD :: struct #ordered { blue, green, red, reserved: byte }
BI_RGB :: 0;
DIB_RGB_COLORS :: 0x00;
@@ -311,17 +324,15 @@ StretchDIBits :: proc (hdc: HDC,
x_src, y_src, width_src, header_src: i32,
bits: rawptr, bits_info: ^BITMAPINFO,
usage: u32,
rop: u32) -> i32 #foreign #dll_import
rop: u32) -> i32 #foreign gdi32;
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
LoadLibraryA :: proc (c_str: ^u8) -> HMODULE #foreign kernel32;
FreeLibrary :: proc (h: HMODULE) #foreign kernel32;
GetProcAddress :: proc (h: HMODULE, c_str: ^u8) -> PROC #foreign kernel32;
GetClientRect :: proc(hwnd: HWND, rect: ^RECT) -> BOOL #foreign user32;
// Windows OpenGL
PFD_TYPE_RGBA :: 0;
@@ -347,14 +358,14 @@ PFD_DOUBLEBUFFER_DONTCARE :: 0x40000000;
PFD_STEREO_DONTCARE :: 0x80000000;
HGLRC :: HANDLE;
PROC :: type proc() #cc_c;
wglCreateContextAttribsARBType :: proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC;
PROC :: #type proc() #cc_c;
wglCreateContextAttribsARBType :: #type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC;
PIXELFORMATDESCRIPTOR :: struct #ordered {
size,
version,
flags: u32;
flags: u32,
pixel_type,
color_bits,
@@ -375,18 +386,18 @@ PIXELFORMATDESCRIPTOR :: struct #ordered {
stencil_bits,
aux_buffers,
layer_type,
reserved: byte;
reserved: byte,
layer_mask,
visible_mask,
damage_mask: u32;
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
GetDC :: proc(h: HANDLE) -> HDC #foreign user32;
SetPixelFormat :: proc(hdc: HDC, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR ) -> BOOL #foreign gdi32;
ChoosePixelFormat :: proc(hdc: HDC, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign gdi32;
SwapBuffers :: proc(hdc: HDC) -> BOOL #foreign gdi32;
ReleaseDC :: proc(wnd: HWND, hdc: HDC) -> i32 #foreign user32;
WGL_CONTEXT_MAJOR_VERSION_ARB :: 0x2091;
WGL_CONTEXT_MINOR_VERSION_ARB :: 0x2092;
@@ -394,17 +405,17 @@ 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
wglCreateContext :: proc(hdc: HDC) -> HGLRC #foreign opengl32;
wglMakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign opengl32;
wglGetProcAddress :: proc(c_str: ^u8) -> PROC #foreign opengl32;
wglDeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign opengl32;
GetKeyState :: proc(v_key: i32) -> i16 #foreign #dll_import
GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign #dll_import
GetKeyState :: proc(v_key: i32) -> i16 #foreign user32;
GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign user32;
is_key_down :: proc(key: Key_Code) -> bool #inline { return GetAsyncKeyState(key as i32) < 0; }
is_key_down :: proc(key: Key_Code) -> bool #inline { return GetAsyncKeyState(cast(i32)key) < 0; }
Key_Code :: enum i32 {
LBUTTON = 0x01,
+146
View File
@@ -0,0 +1,146 @@
is_signed :: proc(info: ^Type_Info) -> bool {
if is_integer(info) {
i := cast(^Type_Info.Integer)info;
return i.signed;
}
if is_float(info) {
return true;
}
return false;
}
is_integer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Integer: return true;
}
return false;
}
is_float :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Float: return true;
}
return false;
}
is_any :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Any: return true;
}
return false;
}
is_string :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.String: return true;
}
return false;
}
is_boolean :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Boolean: return true;
}
return false;
}
is_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Pointer: return true;
}
return false;
}
is_procedure :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Procedure: return true;
}
return false;
}
is_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Array: return true;
}
return false;
}
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Dynamic_Array: return true;
}
return false;
}
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Map: return i.count == 0;
}
return false;
}
is_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Slice: return true;
}
return false;
}
is_vector :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Vector: return true;
}
return false;
}
is_tuple :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Tuple: return true;
}
return false;
}
is_struct :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Struct: return true;
}
return false;
}
is_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Union: return true;
}
return false;
}
is_raw_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Raw_Union: return true;
}
return false;
}
is_enum :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Enum: return true;
}
return false;
}
+54 -44
View File
@@ -1,16 +1,34 @@
RUNE_ERROR :: '\ufffd';
RUNE_SELF :: 0x80;
RUNE_BOM :: 0xfeff;
RUNE_EOF :: ~(0 as rune);
RUNE_EOF :: ~cast(rune)0;
MAX_RUNE :: '\U0010ffff';
UTF_MAX :: 4;
SURROGATE_MIN :: 0xd800;
SURROGATE_MAX :: 0xdfff;
Accept_Range :: struct {
lo, hi: u8;
}
T1 :: 0b0000_0000;
TX :: 0b1000_0000;
T2 :: 0b1100_0000;
T3 :: 0b1110_0000;
T4 :: 0b1111_0000;
T5 :: 0b1111_1000;
MASKX :: 0b0011_1111;
MASK2 :: 0b0001_1111;
MASK3 :: 0b0000_1111;
MASK4 :: 0b0000_0111;
RUNE1_MAX :: 1<<7 - 1;
RUNE2_MAX :: 1<<11 - 1;
RUNE3_MAX :: 1<<16 - 1;
// The default lowest and highest continuation byte.
LOCB :: 0b1000_0000;
HICB :: 0b1011_1111;
Accept_Range :: struct { lo, hi: u8 }
accept_ranges := [5]Accept_Range{
{0x80, 0xbf},
@@ -42,15 +60,15 @@ accept_sizes := [256]byte{
encode_rune :: proc(r: rune) -> ([4]byte, int) {
buf: [4]byte;
i := r as u32;
i := cast(u32)r;
mask: byte : 0x3f;
if i <= 1<<7-1 {
buf[0] = r as byte;
buf[0] = cast(byte)r;
return buf, 1;
}
if i <= 1<<11-1 {
buf[0] = 0xc0 | (r>>6) as byte;
buf[1] = 0x80 | (r) as byte & mask;
buf[0] = 0xc0 | cast(byte)(r>>6);
buf[1] = 0x80 | cast(byte)r & mask;
return buf, 2;
}
@@ -61,16 +79,16 @@ encode_rune :: proc(r: rune) -> ([4]byte, int) {
}
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;
buf[0] = 0xe0 | cast(byte)(r>>12);
buf[1] = 0x80 | cast(byte)(r>>6) & mask;
buf[2] = 0x80 | cast(byte)r & 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;
buf[0] = 0xf0 | cast(byte)(r>>18);
buf[1] = 0x80 | cast(byte)(r>>12) & mask;
buf[2] = 0x80 | cast(byte)(r>>6) & mask;
buf[3] = 0x80 | cast(byte)r & mask;
return buf, 4;
}
@@ -79,43 +97,36 @@ decode_rune :: proc(s: string) -> (rune, int) {
if n < 1 {
return RUNE_ERROR, 0;
}
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;
s0 := s[0];
x := accept_sizes[s0];
if x >= 0xF0 {
mask := cast(rune)(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
return cast(rune)(s[0])&~mask | RUNE_ERROR&mask, 1;
}
size := x & 7;
ar := accept_ranges[x>>4];
if n < size as int {
sz := x & 7;
accept := accept_ranges[x>>4];
if n < cast(int)sz {
return RUNE_ERROR, 1;
}
b1 := s[1];
if b1 < ar.lo || ar.hi < b1 {
if b1 < accept.lo || accept.hi < b1 {
return RUNE_ERROR, 1;
}
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;
if sz == 2 {
return cast(rune)(s0&MASK2)<<6 | cast(rune)(b1&MASKX), 2;
}
b2 := s[2];
if b2 < 0x80 || 0xbf < b2 {
if b2 < LOCB || HICB < b2 {
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;
if sz == 3 {
return cast(rune)(s0&MASK3)<<12 | cast(rune)(b1&MASKX)<<6 | cast(rune)(b2&MASKX), 3;
}
b3 := s[3];
if b3 < 0x80 || 0xbf < b3 {
if b3 < LOCB || HICB < b3 {
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 cast(rune)(s0&MASK4)<<18 | cast(rune)(b1&MASKX)<<12 | cast(rune)(b2&MASKX)<<6 | cast(rune)(b3&MASKX), 4;
}
@@ -132,8 +143,7 @@ valid_rune :: proc(r: rune) -> bool {
valid_string :: proc(s: string) -> bool {
n := s.count;
i := 0;
while i < n {
for i := 0; i < n; {
si := s[i];
if si < RUNE_SELF { // ascii
i += 1;
@@ -143,7 +153,7 @@ valid_string :: proc(s: string) -> bool {
if x == 0xf1 {
return false;
}
size := (x & 7) as int;
size := cast(int)(x & 7);
if i+size > n {
return false;
}
@@ -167,8 +177,8 @@ valid_string :: proc(s: string) -> bool {
rune_count :: proc(s: string) -> int {
count := 0;
n := s.count;
i := 0;
while i < n {
for i := 0; i < n; {
defer count += 1;
si := s[i];
if si < RUNE_SELF { // ascii
@@ -180,7 +190,7 @@ rune_count :: proc(s: string) -> int {
i += 1;
continue;
}
size := (x & 7) as int;
size := cast(int)(x & 7);
if i+size > n {
i += 1;
continue;
+96 -22
View File
@@ -1,6 +1,7 @@
typedef struct BuildContext {
String ODIN_OS; // target operating system
String ODIN_ARCH; // target architecture
String ODIN_ENDIAN; // target endian
String ODIN_VENDOR; // compiler vendor
String ODIN_VERSION; // compiler version
String ODIN_ROOT; // Odin ROOT
@@ -22,6 +23,7 @@ typedef struct BuildContext {
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
#if defined(GB_SYSTEM_WINDOWS)
String odin_root_dir(void) {
String path = global_module_path;
Array(wchar_t) path_buf;
@@ -71,6 +73,83 @@ String odin_root_dir(void) {
return path;
}
#elif defined(GB_SYSTEM_OSX)
#include <mach-o/dyld.h>
String odin_root_dir(void) {
String path = global_module_path;
Array(char) 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 (;;) {
int sz = path_buf.count;
int res = _NSGetExecutablePath(&path_buf.e[0], &sz);
if(res == 0) {
len = sz;
break;
} else {
array_resize(&path_buf, sz + 1);
}
}
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
gb_memmove(text, &path_buf.e[0], len);
path = make_string(text, len);
for (i = path.len-1; i >= 0; i--) {
u8 c = path.text[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
gb_temp_arena_memory_end(tmp);
// array_free(&path_buf);
return path;
}
#else
#error Implement system
#endif
#if defined(GB_SYSTEM_WINDOWS)
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);
@@ -86,6 +165,17 @@ String path_to_fullpath(gbAllocator a, String s) {
gb_temp_arena_memory_end(tmp);
return result;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
String path_to_fullpath(gbAllocator a, String s) {
char* p = realpath(s.text, 0);
// GB_ASSERT(p && "file does not exist");
if(!p) return make_string_c("");
return make_string(p, strlen(p));
}
#else
#error Implement system
#endif
String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
@@ -124,37 +214,21 @@ String get_fullpath_core(gbAllocator a, String path) {
}
String get_filepath_extension(String path) {
isize dot = 0;
bool seen_slash = false;
for (isize i = path.len-1; i >= 0; i--) {
u8 c = path.text[i];
if (c == '/' || c == '\\') {
seen_slash = true;
}
if (c == '.') {
if (seen_slash) {
return str_lit("");
}
dot = i;
break;
}
}
return make_string(path.text, dot);
}
void init_build_context(BuildContext *bc) {
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = str_lit("0.0.5a");
bc->ODIN_VERSION = str_lit("0.1.0");
bc->ODIN_ROOT = odin_root_dir();
#if defined(GB_SYSTEM_WINDOWS)
bc->ODIN_OS = str_lit("windows");
bc->ODIN_ARCH = str_lit("amd64");
bc->ODIN_ENDIAN = str_lit("little");
#elif defined(GB_SYSTEM_OSX)
bc->ODIN_OS = str_lit("osx");
bc->ODIN_ARCH = str_lit("amd64");
bc->ODIN_ENDIAN = str_lit("little");
#else
#error Implement system
#endif
+46 -102
View File
@@ -40,6 +40,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
}
t = default_type(t);
}
GB_ASSERT(is_type_typed(t));
e->type = t;
}
@@ -60,23 +61,9 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
// an extra allocation
Array(Operand) operands;
ArrayOperand operands = {0};
array_init_reserve(&operands, c->tmp_allocator, 2*lhs_count);
for_array(i, inits) {
AstNode *rhs = inits.e[i];
Operand o = {0};
check_multi_expr(c, &o, rhs);
if (o.type->kind != Type_Tuple) {
array_add(&operands, o);
} else {
TypeTuple *tuple = &o.type->Tuple;
for (isize j = 0; j < tuple->variable_count; j++) {
o.type = tuple->variables[j]->type;
array_add(&operands, o);
}
}
}
check_unpack_arguments(c, lhs_count, &operands, inits, true);
isize rhs_count = operands.count;
for_array(i, operands) {
@@ -107,78 +94,6 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
gb_temp_arena_memory_end(tmp);
}
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);
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;
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) {
if (entities[i] != NULL) {
add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
}
}
}
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
@@ -249,9 +164,6 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
}
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)) {
@@ -259,7 +171,6 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
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;
@@ -270,9 +181,6 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
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;
@@ -282,10 +190,10 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
}
check_init_constant(c, e, &operand);
c->context.iota = (ExactValue){0};
if (operand.mode == Addressing_Invalid) {
error(e->token, "Illegal cyclic declaration");
if (operand.mode == Addressing_Invalid ||
base_type(operand.type) == t_invalid) {
error(e->token, "Invalid declaration type");
}
}
@@ -379,10 +287,11 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
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;
}
// TODO(bill): Is this the best option? What about passing to external shit?!
// if (proc_type->Proc.calling_convention != ProcCC_Odin) {
// error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention");
// proc_type->Proc.calling_convention = ProcCC_Odin;
// }
d->scope = c->context.scope;
@@ -397,6 +306,29 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
name = pd->foreign_name;
}
AstNode *foreign_library = d->proc_lit->ProcLit.foreign_library;
if (foreign_library == NULL) {
error(e->token, "#foreign procedures must declare which library they are from");
} else if (foreign_library->kind != AstNode_Ident) {
error_node(foreign_library, "#foreign library names must be an identifier");
} else {
String name = foreign_library->Ident.string;
Entity *found = scope_lookup_entity(c->context.scope, name);
if (found == NULL) {
if (str_eq(name, str_lit("_"))) {
error_node(foreign_library, "`_` cannot be used as a value type");
} else {
error_node(foreign_library, "Undeclared name: %.*s", LIT(name));
}
} else if (found->kind != Entity_LibraryName) {
error_node(foreign_library, "`_` cannot be used as a library name");
} else {
// TODO(bill): Extra stuff to do with library names?
e->Procedure.foreign_library = found;
add_entity_use(c, foreign_library, found);
}
}
e->Procedure.is_foreign = true;
e->Procedure.foreign_name = name;
@@ -532,9 +464,19 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
GB_ASSERT(body->kind == AstNode_BlockStmt);
String proc_name = {0};
if (token.kind == Token_Ident) {
proc_name = token.string;
} else {
// TODO(bill): Better name
proc_name = str_lit("(anonymous-procedure)");
}
CheckerContext old_context = c->context;
c->context.scope = decl->scope;
c->context.decl = decl;
c->context.proc_name = proc_name;
GB_ASSERT(type->kind == Type_Proc);
if (type->Proc.param_count > 0) {
@@ -545,6 +487,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
if (!(e->flags & EntityFlag_Anonymous)) {
continue;
}
bool is_immutable = e->Variable.is_immutable;
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
@@ -554,6 +497,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
uvar->Variable.is_immutable = is_immutable;
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
File diff suppressed because it is too large Load Diff
+244 -139
View File
@@ -3,7 +3,7 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
return;
}
check_scope_decls(c, stmts, 1.2*stmts.count, NULL);
check_scope_decls(c, stmts, 1.2*stmts.count);
bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
flags &= ~Stmt_FallthroughAllowed;
@@ -127,15 +127,15 @@ bool check_is_terminating(AstNode *node) {
}
case_end;
case_ast_node(ws, WhileStmt, node);
if (ws->cond == NULL && !check_has_break(ws->body, true)) {
return true;
case_ast_node(fs, ForStmt, node);
if (!check_has_break(fs->body, true)) {
return check_is_terminating(fs->body);
}
case_end;
case_ast_node(rs, ForStmt, node);
case_ast_node(rs, RangeStmt, node);
if (!check_has_break(rs->body, true)) {
return true;
return check_is_terminating(rs->body);
}
case_end;
@@ -184,7 +184,7 @@ bool check_is_terminating(AstNode *node) {
Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
if (op_a->mode == Addressing_Invalid ||
op_a->type == t_invalid) {
(op_a->type == t_invalid && op_a->mode != Addressing_Overload)) {
return NULL;
}
@@ -195,37 +195,73 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
str_eq(node->Ident.string, str_lit("_"))) {
add_entity_definition(&c->info, node, NULL);
check_assignment(c, op_a, NULL, str_lit("assignment to `_` identifier"));
if (op_a->mode == Addressing_Invalid)
if (op_a->mode == Addressing_Invalid) {
return NULL;
}
return op_a->type;
}
Entity *e = NULL;
bool used = false;
if (node->kind == AstNode_Ident) {
ast_node(i, Ident, node);
e = scope_lookup_entity(c->context.scope, i->string);
if (e != NULL && e->kind == Entity_Variable) {
used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case
}
}
Operand op_b = {Addressing_Invalid};
check_expr(c, &op_b, lhs);
if (e) {
e->flags |= EntityFlag_Used*used;
}
check_expr(c, &op_b, lhs);
if (op_b.mode == Addressing_Invalid ||
op_b.type == t_invalid) {
return NULL;
}
if (op_a->mode == Addressing_Overload) {
isize overload_count = op_a->overload_count;
Entity **procs = op_a->overload_entities;
GB_ASSERT(procs != NULL && overload_count > 0);
// NOTE(bill): These should be done
for (isize i = 0; i < overload_count; i++) {
Type *t = base_type(procs[i]->type);
if (t == t_invalid) {
continue;
}
Operand x = {0};
x.mode = Addressing_Value;
x.type = t;
if (check_is_assignable_to(c, &x, op_b.type)) {
e = procs[i];
add_entity_use(c, op_a->expr, e);
break;
}
}
if (e != NULL) {
// HACK TODO(bill): Should the entities be freed as it's technically a leak
op_a->mode = Addressing_Value;
op_a->type = e->type;
op_a->overload_count = 0;
op_a->overload_entities = NULL;
}
} else {
if (node->kind == AstNode_Ident) {
ast_node(i, Ident, node);
e = scope_lookup_entity(c->context.scope, i->string);
if (e != NULL && e->kind == Entity_Variable) {
used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case
}
}
}
if (e != NULL && used) {
e->flags |= EntityFlag_Used;
}
Type *assignment_type = op_b.type;
switch (op_b.mode) {
case Addressing_Invalid:
return NULL;
case Addressing_Variable:
case Addressing_MapIndex:
break;
default: {
if (op_b.expr->kind == AstNode_SelectorExpr) {
@@ -233,22 +269,25 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
Operand op_c = {Addressing_Invalid};
ast_node(se, SelectorExpr, op_b.expr);
check_expr(c, &op_c, se->expr);
if (op_c.mode == Addressing_MapIndex) {
gbString str = expr_to_string(op_b.expr);
error_node(op_b.expr, "Cannot assign to record field `%s` in map", str);
gb_string_free(str);
return NULL;
}
}
gbString str = expr_to_string(op_b.expr);
switch (op_b.mode) {
case Addressing_Value:
if (e != NULL && e->kind == Entity_Variable && e->Variable.is_immutable) {
error_node(op_b.expr, "Cannot assign to an immutable: `%s`", str);
} else {
error_node(op_b.expr, "Cannot assign to `%s`", str);
break;
default:
error_node(op_b.expr, "Cannot assign to `%s`", str);
break;
}
gb_string_free(str);
} break;
}
check_assignment(c, op_a, op_b.type, str_lit("assignment"));
check_assignment(c, op_a, assignment_type, str_lit("assignment"));
if (op_a->mode == Addressing_Invalid) {
return NULL;
}
@@ -276,12 +315,13 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
u32 in = node->stmt_state_flags;
u32 out = c->context.stmt_state_flags;
if (in & StmtStateFlag_bounds_check) {
out |= StmtStateFlag_bounds_check;
out &= ~StmtStateFlag_no_bounds_check;
} else if (in & StmtStateFlag_no_bounds_check) {
if (in & StmtStateFlag_no_bounds_check) {
out |= StmtStateFlag_no_bounds_check;
out &= ~StmtStateFlag_bounds_check;
} else {
// if (in & StmtStateFlag_bounds_check) {
out |= StmtStateFlag_bounds_check;
out &= ~StmtStateFlag_no_bounds_check;
}
c->context.stmt_state_flags = out;
@@ -302,7 +342,7 @@ typedef struct TypeAndToken {
#define MAP_TYPE TypeAndToken
#define MAP_PROC map_type_and_token_
#define MAP_NAME MapTypeAndToken
#include "../map.c"
#include "map.c"
void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
Operand operand = {Addressing_Invalid};
@@ -495,7 +535,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(rs, ReturnStmt, node);
GB_ASSERT(c->proc_stack.count > 0);
if (c->in_defer) {
if (c->context.in_defer) {
error(rs->token, "You cannot `return` within a defer statement");
// TODO(bill): Should I break here?
break;
@@ -517,35 +557,50 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
if (rs->results.count == 0) {
error_node(node, "Expected %td return values, got 0", result_count);
} else {
// TokenPos pos = rs->token.pos;
// if (pos.line == 10) {
// gb_printf_err("%s\n", type_to_string(variables[0]->type));
// }
check_init_variables(c, variables, result_count,
rs->results, str_lit("return statement"));
// if (pos.line == 10) {
// AstNode *x = rs->results.e[0];
// gb_printf_err("%s\n", expr_to_string(x));
// gb_printf_err("%s\n", type_to_string(type_of_expr(&c->info, x)));
// }
}
} else if (rs->results.count > 0) {
error_node(rs->results.e[0], "No return values expected");
}
case_end;
case_ast_node(ws, WhileStmt, node);
case_ast_node(fs, ForStmt, node);
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(c, node);
if (ws->init != NULL) {
check_stmt(c, ws->init, 0);
if (fs->init != NULL) {
check_stmt(c, fs->init, 0);
}
if (ws->cond) {
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, ws->cond);
if (operand.mode != Addressing_Invalid &&
!is_type_boolean(operand.type)) {
error_node(ws->cond, "Non-boolean condition in `while` statement");
if (fs->cond != NULL) {
Operand o = {Addressing_Invalid};
check_expr(c, &o, fs->cond);
if (o.mode != Addressing_Invalid && !is_type_boolean(o.type)) {
error_node(fs->cond, "Non-boolean condition in `for` statement");
}
}
check_stmt(c, ws->body, new_flags);
if (fs->post != NULL) {
check_stmt(c, fs->post, 0);
if (fs->post->kind != AstNode_AssignStmt) {
error_node(fs->post, "`for` statement post statement must be an assignment");
}
}
check_stmt(c, fs->body, new_flags);
check_close_scope(c);
case_end;
case_ast_node(rs, ForStmt, node);
case_ast_node(rs, RangeStmt, node);
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(c, node);
@@ -602,9 +657,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
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)) {
if (!is_type_integer(type) && !is_type_float(type) && !is_type_pointer(type)) {
error(ie->op, "Only numerical and pointer types are allowed within interval expressions");
goto skip_expr;
}
@@ -616,6 +669,12 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
GB_ASSERT(are_types_identical(x.type, y.type));
TokenKind op = Token_Lt;
switch (ie->op.kind) {
case Token_HalfOpenRange: op = Token_Lt; break;
case Token_Ellipsis: op = Token_LtEq; break;
default: error(ie->op, "Invalid range operator"); break;
}
bool ok = compare_exact_values(Token_Lt, a, b);
if (!ok) {
// TODO(bill): Better error message
@@ -645,21 +704,39 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
val = t->Array.elem;
idx = t_int;
break;
case Type_Slice:
val = t->Array.elem;
case Type_DynamicArray:
val = t->DynamicArray.elem;
idx = t_int;
break;
case Type_Slice:
val = t->Slice.elem;
idx = t_int;
break;
case Type_Vector:
val = t->Vector.elem;
idx = t_int;
break;
case Type_Map:
val = t->Map.value;
idx = t->Map.key;
break;
}
}
if (val == NULL) {
gbString s = expr_to_string(operand.expr);
error_node(node, "Cannot iterate over %s", s);
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);
}
}
skip_expr:
skip_expr:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
AstNode *lhs[2] = {rs->value, rs->index};
Type * rhs[2] = {val, idx};
@@ -680,8 +757,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
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;
entity = make_entity_variable(c->allocator, c->context.scope, token, type, true);
add_entity_definition(&c->info, name, entity);
} else {
TokenPos pos = found->token.pos;
@@ -736,7 +812,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
Token token = {0};
token.pos = ast_node_token(ms->body).pos;
token.string = str_lit("true");
x.expr = make_ident(c->curr_ast_file, token);
x.expr = ast_ident(c->curr_ast_file, token);
}
// NOTE(bill): Check for multiple defaults
@@ -782,21 +858,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
for_array(j, cc->list) {
AstNode *expr = cc->list.e[j];
Operand y = {0};
Operand z = {0};
Token eq = {Token_CmpEq};
check_expr(c, &y, expr);
if (x.mode == Addressing_Invalid ||
y.mode == Addressing_Invalid) {
continue;
}
convert_to_typed(c, &y, x.type, 0);
if (y.mode == Addressing_Invalid) {
continue;
}
z = y;
check_comparison(c, &z, &x, eq);
// NOTE(bill): the ordering here matters
Operand z = y;
check_comparison(c, &z, &x, Token_CmpEq);
if (z.mode == Addressing_Invalid) {
continue;
}
@@ -804,6 +880,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
continue;
}
if (y.value.kind != ExactValue_Invalid) {
HashKey key = hash_exact_value(y.value);
TypeAndToken *found = map_type_and_token_get(&seen, key);
@@ -865,7 +942,25 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
bool is_union_ptr = false;
bool is_any = false;
check_expr(c, &x, ms->tag);
if (ms->tag->kind != AstNode_AssignStmt) {
error_node(ms->tag, "Expected an `in` assignment for this type match statement");
break;
}
ast_node(as, AssignStmt, ms->tag);
Token as_token = ast_node_token(ms->tag);
if (as->lhs.count != 1) {
syntax_error(as_token, "Expected 1 name before `in`");
break;
}
if (as->rhs.count != 1) {
syntax_error(as_token, "Expected 1 expression after `in`");
break;
}
AstNode *lhs = as->lhs.e[0];
AstNode *rhs = as->rhs.e[0];
check_expr(c, &x, rhs);
check_assignment(c, &x, NULL, str_lit("type match expression"));
if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
gbString str = type_to_string(x.type);
@@ -903,7 +998,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
}
if (ms->var->kind != AstNode_Ident) {
if (lhs->kind != AstNode_Ident) {
error_node(rhs, "Expected an identifier, got `%.*s`", LIT(ast_node_strings[rhs->kind]));
break;
}
@@ -970,20 +1067,27 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
check_open_scope(c, stmt);
if (case_type != NULL) {
add_type_info_type(c, case_type);
if (case_type == NULL) {
if (is_union_ptr) {
case_type = type_deref(x.type);
} else {
case_type = x.type;
}
}
add_type_info_type(c, case_type);
{
// NOTE(bill): Dummy type
Type *tt = case_type;
if (is_union_ptr) {
tt = make_type_pointer(c->allocator, case_type);
add_type_info_type(c, tt);
}
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, ms->var->Ident, tt);
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, tt, true);
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);
add_entity(c, c->context.scope, lhs, tag_var);
add_entity_use(c, lhs, tag_var);
}
check_stmt_list(c, cc->stmts, mod_flags);
check_close_scope(c);
@@ -998,10 +1102,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
if (is_ast_node_decl(ds->stmt)) {
error(ds->token, "You cannot defer a declaration");
} else {
bool out_in_defer = c->in_defer;
c->in_defer = true;
bool out_in_defer = c->context.in_defer;
c->context.in_defer = true;
check_stmt(c, ds->stmt, 0);
c->in_defer = out_in_defer;
c->context.in_defer = out_in_defer;
}
case_end;
@@ -1031,6 +1135,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(us, UsingStmt, node);
switch (us->node->kind) {
default:
// TODO(bill): Better error message for invalid using statement
error(us->token, "Invalid `using` statement");
break;
case_ast_node(es, ExprStmt, us->node);
// TODO(bill): Allow for just a LHS expression list rather than this silly code
Entity *e = NULL;
@@ -1042,7 +1150,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
e = scope_lookup_entity(c->context.scope, name);
} else if (expr->kind == AstNode_SelectorExpr) {
Operand o = {0};
e = check_selector(c, &o, expr);
e = check_selector(c, &o, expr, NULL);
is_selector = true;
}
@@ -1141,10 +1249,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
error(us->token, "`using` cannot be applied to a procedure");
break;
case Entity_ImplicitValue:
error(us->token, "`using` cannot be applied to an implicit value");
break;
case Entity_Nil:
error(us->token, "`using` cannot be applied to `nil`");
break;
@@ -1158,52 +1262,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
case_end;
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, 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);
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
error(us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
return;
}
}
}
} else {
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
return;
}
}
case_end;
default:
error(us->token, "Invalid AST: Using Statement");
break;
}
case_end;
@@ -1226,15 +1284,22 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(vd, ValueDecl, node);
GB_ASSERT(!c->context.scope->is_file);
if (vd->is_var) {
isize entity_count = vd->names.count;
isize entity_index = 0;
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
isize entity_count = 0;
if (vd->flags & VarDeclFlag_thread_local) {
vd->flags &= ~VarDeclFlag_thread_local;
error_node(node, "`thread_local` may only be applied to a variable declaration");
}
for_array(i, vd->names) {
AstNode *name = vd->names.e[i];
Entity *entity = NULL;
if (name->kind == AstNode_Ident) {
if (name->kind != AstNode_Ident) {
error_node(name, "A variable declaration must be an identifier");
} else {
Token token = name->Ident;
String str = token.string;
Entity *found = NULL;
@@ -1243,8 +1308,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
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);
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL, vd->flags&VarDeclFlag_immutable);
entity->identifier = name;
} else {
TokenPos pos = found->token.pos;
error(token,
@@ -1253,13 +1318,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
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;
entities[entity_count++] = entity;
}
Type *init_type = NULL;
@@ -1279,18 +1342,60 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
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) {
if (entities[i] != NULL) {
add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
}
}
check_arity_match(c, vd);
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
for (isize i = 0; i < entity_count; i++) {
add_entity(c, c->context.scope, entities[i]->identifier, entities[i]);
}
if ((vd->flags & VarDeclFlag_using) != 0) {
Token token = ast_node_token(node);
if (vd->type != NULL && entity_count > 1) {
error(token, "`using` can only be applied to one variable of the same type");
// TODO(bill): Should a `continue` happen here?
}
for (isize entity_index = 0; entity_index < entity_count; entity_index++) {
Entity *e = entities[entity_index];
if (e == NULL) {
continue;
}
if (e->kind != Entity_Variable) {
continue;
}
bool is_immutable = e->Variable.is_immutable;
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
uvar->Variable.is_immutable = is_immutable;
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
return;
}
}
}
} else {
// NOTE(bill): skip the rest to remove extra errors
error(token, "`using` can only be applied to variables of type struct or raw_union");
return;
}
}
}
} else {
// NOTE(bill): Handled elsewhere
}
File diff suppressed because it is too large Load Diff
+12
View File
@@ -6,12 +6,24 @@ gbAllocator heap_allocator(void) {
return gb_heap_allocator();
}
#include "unicode.c"
#include "string.c"
#include "array.c"
gb_global String global_module_path = {0};
gb_global bool global_module_path_set = false;
gb_global gbScratchMemory scratch_memory = {0};
void init_scratch_memory(isize size) {
void *memory = gb_alloc(heap_allocator(), size);
gb_scratch_memory_init(&scratch_memory, memory, size);
}
gbAllocator scratch_allocator(void) {
return gb_scratch_allocator(&scratch_memory);
}
i64 next_pow2(i64 n) {
if (n <= 0) {
+67 -30
View File
@@ -1,8 +1,8 @@
typedef struct Scope Scope;
typedef struct Checker Checker;
typedef struct Type Type;
typedef enum BuiltinProcId BuiltinProcId;
typedef enum ImplicitValueId ImplicitValueId;
// typedef enum BuiltinProcId BuiltinProcId;
#define ENTITY_KINDS \
ENTITY_KIND(Invalid) \
@@ -12,8 +12,8 @@ typedef enum ImplicitValueId ImplicitValueId;
ENTITY_KIND(Procedure) \
ENTITY_KIND(Builtin) \
ENTITY_KIND(ImportName) \
ENTITY_KIND(LibraryName) \
ENTITY_KIND(Nil) \
ENTITY_KIND(ImplicitValue) \
ENTITY_KIND(Count)
typedef enum EntityKind {
@@ -35,8 +35,17 @@ typedef enum EntityFlag {
EntityFlag_Field = 1<<3,
EntityFlag_Param = 1<<4,
EntityFlag_VectorElem = 1<<5,
EntityFlag_Ellipsis = 1<<6,
EntityFlag_NoAlias = 1<<7,
EntityFlag_TypeField = 1<<8,
} EntityFlag;
typedef enum OverloadKind {
Overload_No = -1,
Overload_Unknown = 0,
Overload_Yes = +1,
} OverloadKind;
typedef struct Entity Entity;
struct Entity {
EntityKind kind;
@@ -58,16 +67,19 @@ struct Entity {
i32 field_index;
i32 field_src_index;
bool is_immutable;
bool is_thread_local;
} Variable;
i32 TypeName;
struct {
bool is_foreign;
String foreign_name;
String link_name;
u64 tags;
bool is_foreign;
String foreign_name;
Entity * foreign_library;
String link_name;
u64 tags;
OverloadKind overload_kind;
} Procedure;
struct {
BuiltinProcId id;
i32 id;
} Builtin;
struct {
String path;
@@ -75,17 +87,40 @@ struct Entity {
Scope *scope;
bool used;
} ImportName;
i32 Nil;
struct {
// TODO(bill): Should this be a user-level construct rather than compiler-level?
ImplicitValueId id;
Entity * backing;
} ImplicitValue;
String path;
String name;
bool used;
} LibraryName;
i32 Nil;
};
};
gb_global Entity *e_context = NULL;
Entity *e_iota = NULL;
bool is_entity_kind_exported(EntityKind kind) {
switch (kind) {
case Entity_Builtin:
case Entity_ImportName:
case Entity_LibraryName:
case Entity_Nil:
return false;
}
return true;
}
bool is_entity_exported(Entity *e) {
GB_ASSERT(e != NULL);
if (!is_entity_kind_exported(e->kind)) {
return false;
}
String name = e->token.string;
if (name.len == 0) {
return false;
}
return name.text[0] != '_';
}
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
@@ -97,13 +132,15 @@ Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token,
return entity;
}
Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type) {
Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type, bool is_immutable) {
Entity *entity = alloc_entity(a, Entity_Variable, scope, token, type);
entity->Variable.is_immutable = is_immutable;
return entity;
}
Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, Type *type) {
GB_ASSERT(parent != NULL);
token.pos = parent->token.pos;
Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
entity->using_parent = parent;
entity->flags |= EntityFlag_Anonymous;
@@ -122,16 +159,16 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty
return entity;
}
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous) {
Entity *entity = make_entity_variable(a, scope, token, type);
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, bool is_immutable) {
Entity *entity = make_entity_variable(a, scope, token, type, is_immutable);
entity->flags |= EntityFlag_Used;
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
if (anonymous) entity->flags |= EntityFlag_Anonymous;
entity->flags |= EntityFlag_Param;
return entity;
}
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type);
Entity *entity = make_entity_variable(a, scope, token, type, false);
entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index;
entity->flags |= EntityFlag_Field;
@@ -140,7 +177,7 @@ Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type,
}
Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *type, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type);
Entity *entity = make_entity_variable(a, scope, token, type, false);
entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index;
entity->flags |= EntityFlag_Field;
@@ -154,7 +191,7 @@ Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *si
return entity;
}
Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, BuiltinProcId id) {
Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, i32 id) {
Entity *entity = alloc_entity(a, Entity_Builtin, scope, token, type);
entity->Builtin.id = id;
return entity;
@@ -169,22 +206,22 @@ Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *
return entity;
}
Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type *type,
String path, String name) {
Entity *entity = alloc_entity(a, Entity_LibraryName, scope, token, type);
entity->LibraryName.path = path;
entity->LibraryName.name = name;
return entity;
}
Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
Token token = make_token_ident(name);
Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
return entity;
}
Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, ImplicitValueId id) {
Token token = make_token_ident(name);
Entity *entity = alloc_entity(a, Entity_ImplicitValue, NULL, token, type);
entity->ImplicitValue.id = id;
return entity;
}
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
token.string = str_lit("_");
return make_entity_variable(a, scope, token, NULL);
return make_entity_variable(a, scope, token, NULL, false);
}
+118 -25
View File
@@ -54,37 +54,12 @@ ExactValue make_exact_value_string(String string) {
return result;
}
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.len > 2 && string.text[0] == '0') {
switch (string.text[1]) {
case 'b': base = 2; break;
case 'o': base = 8; break;
case 'd': base = 10; break;
case 'x': base = 16; break;
}
}
result.value_integer = gb_str_to_i64(cast(char *)string.text, NULL, base);
return result;
}
ExactValue make_exact_value_integer(i64 i) {
ExactValue result = {ExactValue_Integer};
result.value_integer = i;
return result;
}
ExactValue make_exact_value_float_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
ExactValue result = {ExactValue_Float};
result.value_float = gb_str_to_f64(cast(char *)string.text, NULL);
return result;
}
ExactValue make_exact_value_float(f64 f) {
ExactValue result = {ExactValue_Float};
result.value_float = f;
@@ -97,6 +72,123 @@ ExactValue make_exact_value_pointer(i64 ptr) {
return result;
}
ExactValue make_exact_value_integer_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
i32 base = 10;
bool has_prefix = false;
if (string.len > 2 && string.text[0] == '0') {
switch (string.text[1]) {
case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break;
}
}
u8 *text = string.text;
isize len = string.len;
if (has_prefix) {
text += 2;
len -= 2;
}
i64 result = 0;
for (isize i = 0; i < len; i++) {
Rune r = cast(Rune)text[i];
if (r == '_') {
continue;
}
i64 v = 0;
if (gb_char_is_digit(r)) {
v = r - '0';
} else if (gb_char_is_hex_digit(r)) {
v = gb_hex_digit_to_int(r);
} else {
break;
}
result *= base;
result += v;
}
return make_exact_value_integer(result);
}
ExactValue make_exact_value_float_from_string(String string) {
isize i = 0;
u8 *str = string.text;
isize len = string.len;
f64 sign = 1.0;
if (str[i] == '-') {
sign = -1.0;
i++;
} else if (*str == '+') {
i++;
}
f64 value = 0.0;
for (; i < len; i++) {
Rune r = cast(Rune)str[i];
if (r == '_') {
continue;
}
if (!gb_char_is_digit(r)) {
break;
}
i64 v = r - '0';
value *= 10.0;
value += v;
}
if (str[i] == '.') {
f64 pow10 = 10.0;
i++;
for (; i < string.len; i++) {
Rune r = cast(Rune)str[i];
if (r == '_') {
continue;
}
if (!gb_char_is_digit(r)) {
break;
}
value += (r-'0')/pow10;
pow10 *= 10.0;
}
}
f64 frac = 0;
f64 scale = 1.0;
if ((str[i] == 'e') || (str[i] == 'E')) {
i++;
if (str[i] == '-') {
frac = 1;
i++;
} else if (str[i] == '+') {
i++;
}
u32 exp;
for (exp = 0; gb_char_is_digit(str[i]); i++) {
exp = exp * 10 + (str[i]-'0');
}
if (exp > 308) exp = 308;
while (exp >= 50) { scale *= 1e50; exp -= 50; }
while (exp >= 8) { scale *= 1e8; exp -= 8; }
while (exp > 0) { scale *= 10.0; exp -= 1; }
}
f64 result = sign * (frac ? (value / scale) : (value * scale));
return make_exact_value_float(result);
}
ExactValue make_exact_value_from_basic_literal(Token token) {
switch (token.kind) {
case Token_String: return make_exact_value_string(token.string);
@@ -324,6 +416,7 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
}
error:
; // MSVC accepts this??? apparently you cannot declare variables immediately after labels...
ExactValue error_value = {0};
// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op));
return error_value;
+117 -22
View File
@@ -1,4 +1,4 @@
/* gb.h - v0.26d - Ginger Bill's C Helper Library - public domain
/* gb.h - v0.27 - Ginger Bill's C Helper Library - public domain
- no warranty implied; use at your own risk
This is a single header file with a bunch of useful stuff
@@ -58,6 +58,7 @@ TODOS
- More date & time functions
VERSION HISTORY
0.27 - OSX fixes and Linux gbAffinity
0.26d - Minor changes to how gbFile works
0.26c - gb_str_to_f* fix
0.26b - Minor fixes
@@ -276,6 +277,8 @@ extern "C" {
#include <stdarg.h>
#include <stddef.h>
#if defined(GB_SYSTEM_WINDOWS)
#if !defined(GB_NO_WINDOWS_H)
#define NOMINMAX 1
@@ -296,6 +299,9 @@ extern "C" {
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#ifndef _IOSC11_SOURCE
#define _IOSC11_SOURCE
#endif
#include <stdlib.h> // NOTE(bill): malloc on linux
#include <sys/mman.h>
#if !defined(GB_SYSTEM_OSX)
@@ -309,18 +315,18 @@ extern "C" {
#endif
#if defined(GB_SYSTEM_OSX)
#include <mach/mach.h>
#include <mach/mach_init.h>
#include <mach/mach_time.h>
#include <mach/thread_act.h>
#include <mach/thread_policy.h>
#include <sys/sysctl.h>
#include <copyfile.h>
#include <mach/clock.h>
#include <mach/mach.h>
#include <mach/mach_init.h>
#include <mach/mach_time.h>
#include <mach/thread_act.h>
#include <mach/thread_policy.h>
#include <sys/sysctl.h>
#include <copyfile.h>
#include <mach/clock.h>
#endif
#if defined(GB_SYSTEM_UNIX)
#include <semaphore.h>
#include <semaphore.h>
#endif
@@ -1003,7 +1009,12 @@ typedef struct gbAffinity {
} gbAffinity;
#elif defined(GB_SYSTEM_LINUX)
#error TODO(bill): Implement gbAffinity for linux
typedef struct gbAffinity {
b32 is_accurate;
isize core_count;
isize thread_count;
isize threads_per_core;
} gbAffinity;
#else
#error TODO(bill): Unknown system
#endif
@@ -4819,18 +4830,15 @@ GB_ALLOCATOR_PROC(gb_heap_allocator_proc) {
#else
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
gbAllocationHeader *header;
isize total_size = size + alignment + gb_size_of(gbAllocationHeader);
ptr = malloc(total_size);
header = cast(gbAllocationHeader *)ptr;
ptr = gb_align_forward(header+1, alignment);
gb_allocation_header_fill(header, ptr, size);
if (flags & gbAllocatorFlag_ClearToZero)
posix_memalign(&ptr, alignment, size);
if (flags & gbAllocatorFlag_ClearToZero) {
gb_zero_size(ptr, size);
}
} break;
case gbAllocation_Free: {
free(gb_allocation_header(old_memory));
free(old_memory);
} break;
case gbAllocation_Resize: {
@@ -4927,7 +4935,7 @@ isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
void gb_affinity_init(gbAffinity *a) {
usize count, count_size = gb_size_of(count);
a->is_accurate = false;
a->is_accurate = false;
a->thread_count = 1;
a->core_count = 1;
a->threads_per_core = 1;
@@ -4977,7 +4985,83 @@ isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
}
#elif defined(GB_SYSTEM_LINUX)
#error TODO(bill): Implement gbAffinity for linux
// IMPORTANT TODO(bill): This gbAffinity stuff for linux needs be improved a lot!
// NOTE(zangent): I have to read /proc/cpuinfo to get the number of threads per core.
#include <stdio.h>
void gb_affinity_init(gbAffinity *a) {
b32 accurate = true;
isize threads = 0;
a->thread_count = 1;
a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
a->threads_per_core = 1;
if(a->core_count <= 0) {
a->core_count = 1;
accurate = false;
}
// Parsing /proc/cpuinfo to get the number of threads per core.
// NOTE(zangent): This calls the CPU's threads "cores", although the wording
// is kind of weird. This should be right, though.
if (fopen("/proc/cpuinfo", "r") != NULL) {
for (;;) {
// The 'temporary char'. Everything goes into this char,
// so that we can check against EOF at the end of this loop.
char c;
#define AF__CHECK(letter) ((c = getc(cpu_info)) == letter)
if (AF__CHECK('c') && AF__CHECK('p') && AF__CHECK('u') && AF__CHECK(' ') &&
AF__CHECK('c') && AF__CHECK('o') && AF__CHECK('r') && AF__CHECK('e') && AF__CHECK('s')) {
// We're on a CPU info line.
while (!AF__CHECK(EOF)) {
if (c == '\n') {
break;
} else if (c < '0' || '9' > c) {
continue;
}
threads = threads * 10 + (c - '0');
}
break;
} else {
while (!AF__CHECK('\n')) {
if (c==EOF) {
break;
}
}
}
if (c == EOF) {
break;
}
#undef AF__CHECK
}
}
if (threads == 0) {
threads = 1;
accurate = false;
}
a->threads_per_core = threads;
a->thread_count = a->threads_per_core * a->core_count;
a->is_accurate = accurate;
}
void gb_affinity_destroy(gbAffinity *a) {
gb_unused(a);
}
b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
return true;
}
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
GB_ASSERT(0 <= core && core < a->core_count);
return a->threads_per_core;
}
#else
#error TODO(bill): Unknown system
#endif
@@ -7758,7 +7842,18 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
return gb_alloc_str_len(a, buf, len+1);
#else
// TODO(bill): Make work on *nix, etc.
return gb_alloc_str_len(a, path, gb_strlen(path));
char* p = realpath(path, 0);
GB_ASSERT(p && "file does not exist");
isize len = gb_strlen(p);
// bill... gb_alloc_str_len refused to work for this...
char* ret = gb_alloc(a, sizeof(char) * len + 1);
gb_memmove(ret, p, len);
ret[len] = 0;
free(p);
return ret;
#endif
}
+1503 -698
View File
File diff suppressed because it is too large Load Diff
+14 -17
View File
@@ -27,9 +27,6 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
array_add(ops, i->PtrOffset.address);
array_add(ops, i->PtrOffset.offset);
break;
case irInstr_ArrayExtractValue:
array_add(ops, i->ArrayExtractValue.address);
break;
case irInstr_StructExtractValue:
array_add(ops, i->StructExtractValue.address);
break;
@@ -69,18 +66,18 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
array_add(ops, i->Call.args[j]);
}
break;
case irInstr_VectorExtractElement:
array_add(ops, i->VectorExtractElement.vector);
array_add(ops, i->VectorExtractElement.index);
break;
case irInstr_VectorInsertElement:
array_add(ops, i->VectorInsertElement.vector);
array_add(ops, i->VectorInsertElement.elem);
array_add(ops, i->VectorInsertElement.index);
break;
case irInstr_VectorShuffle:
array_add(ops, i->VectorShuffle.vector);
break;
// case irInstr_VectorExtractElement:
// array_add(ops, i->VectorExtractElement.vector);
// array_add(ops, i->VectorExtractElement.index);
// break;
// case irInstr_VectorInsertElement:
// array_add(ops, i->VectorInsertElement.vector);
// array_add(ops, i->VectorInsertElement.elem);
// array_add(ops, i->VectorInsertElement.index);
// break;
// case irInstr_VectorShuffle:
// array_add(ops, i->VectorShuffle.vector);
// break;
case irInstr_StartupRuntime:
break;
case irInstr_BoundsCheck:
@@ -261,7 +258,7 @@ void ir_opt_blocks(irProcedure *proc) {
if (b == NULL) {
continue;
}
GB_ASSERT(b->index == i);
GB_ASSERT_MSG(b->index == i, "%d, %td", b->index, i);
if (ir_opt_block_fusion(proc, b)) {
changed = true;
@@ -470,7 +467,7 @@ void ir_opt_tree(irGen *s) {
}
ir_opt_blocks(proc);
#if 1
#if 0
ir_opt_build_referrers(proc);
ir_opt_build_dom_tree(proc);
+149 -123
View File
@@ -141,58 +141,66 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
i64 word_bits = 8*s.word_size;
GB_ASSERT_NOT_NULL(t);
t = default_type(t);
GB_ASSERT(is_type_typed(t));
switch (t->kind) {
case Type_Basic:
switch (t->Basic.kind) {
case Basic_bool: ir_fprintf(f, "i1"); break;
case Basic_i8: ir_fprintf(f, "i8"); break;
case Basic_u8: ir_fprintf(f, "i8"); break;
case Basic_i16: ir_fprintf(f, "i16"); break;
case Basic_u16: ir_fprintf(f, "i16"); break;
case Basic_i32: ir_fprintf(f, "i32"); break;
case Basic_u32: ir_fprintf(f, "i32"); break;
case Basic_i64: ir_fprintf(f, "i64"); break;
case Basic_u64: ir_fprintf(f, "i64"); break;
// case Basic_i128: ir_fprintf(f, "i128"); break;
// case Basic_u128: ir_fprintf(f, "i128"); break;
// case Basic_f16: ir_fprintf(f, "half"); break;
case Basic_f32: ir_fprintf(f, "float"); break;
case Basic_f64: ir_fprintf(f, "double"); break;
// case Basic_f128: ir_fprintf(f, "fp128"); break;
case Basic_rawptr: ir_fprintf(f, "%%..rawptr"); break;
case Basic_string: ir_fprintf(f, "%%..string"); break;
case Basic_uint: ir_fprintf(f, "i%lld", word_bits); break;
case Basic_int: ir_fprintf(f, "i%lld", word_bits); break;
case Basic_any: ir_fprintf(f, "%%..any"); break;
case Basic_bool: ir_fprintf(f, "i1"); return;
case Basic_i8: ir_fprintf(f, "i8"); return;
case Basic_u8: ir_fprintf(f, "i8"); return;
case Basic_i16: ir_fprintf(f, "i16"); return;
case Basic_u16: ir_fprintf(f, "i16"); return;
case Basic_i32: ir_fprintf(f, "i32"); return;
case Basic_u32: ir_fprintf(f, "i32"); return;
case Basic_i64: ir_fprintf(f, "i64"); return;
case Basic_u64: ir_fprintf(f, "i64"); return;
// case Basic_i128: ir_fprintf(f, "i128"); return;
// case Basic_u128: ir_fprintf(f, "i128"); return;
// case Basic_f16: ir_fprintf(f, "half"); return;
case Basic_f32: ir_fprintf(f, "float"); return;
case Basic_f64: ir_fprintf(f, "double"); return;
// case Basic_f128: ir_fprintf(f, "fp128"); return;
case Basic_rawptr: ir_fprintf(f, "%%..rawptr"); return;
case Basic_string: ir_fprintf(f, "%%..string"); return;
case Basic_uint: ir_fprintf(f, "i%lld", word_bits); return;
case Basic_int: ir_fprintf(f, "i%lld", word_bits); return;
case Basic_any: ir_fprintf(f, "%%..any"); return;
}
break;
case Type_Pointer:
ir_print_type(f, m, t->Pointer.elem);
ir_fprintf(f, "*");
break;
case Type_Maybe:
ir_fprintf(f, "{");
ir_print_type(f, m, t->Maybe.elem);
ir_fprintf(f, ", ");
ir_print_type(f, m, t_bool);
ir_fprintf(f, "}");
break;
return;
case Type_Array:
ir_fprintf(f, "[%lld x ", t->Array.count);
ir_print_type(f, m, t->Array.elem);
ir_fprintf(f, "]");
break;
case Type_Vector:
ir_fprintf(f, "<%lld x ", t->Vector.count);
return;
case Type_Vector: {
i64 align = type_align_of(s, heap_allocator(), t);
i64 count = t->Vector.count;
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x ", align, count);
ir_print_type(f, m, t->Vector.elem);
ir_fprintf(f, "]}");
return;
}
/* ir_fprintf(f, "<%lld x ", t->Vector.count);
ir_print_type(f, m, t->Vector.elem);
ir_fprintf(f, ">");
break;
return; */
case Type_Slice:
ir_fprintf(f, "{");
ir_print_type(f, m, t->Slice.elem);
ir_fprintf(f, "*, i%lld, i%lld}", word_bits, word_bits);
break;
return;
case Type_DynamicArray:
ir_fprintf(f, "{");
ir_print_type(f, m, t->Slice.elem);
ir_fprintf(f, "*, i%lld, i%lld,", word_bits, word_bits);
ir_print_type(f, m, t_allocator);
ir_fprintf(f, "}");
return;
case Type_Record: {
switch (t->Record.kind) {
case TypeRecord_Struct:
@@ -200,6 +208,12 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
ir_fprintf(f, "<");
}
ir_fprintf(f, "{");
if (t->Record.custom_align > 0) {
ir_fprintf(f, "[0 x <%lld x i8>]", t->Record.custom_align);
if (t->Record.field_count > 0) {
ir_fprintf(f, ", ");
}
}
for (isize i = 0; i < t->Record.field_count; i++) {
if (i > 0) {
ir_fprintf(f, ", ");
@@ -210,24 +224,24 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
if (t->Record.struct_is_packed) {
ir_fprintf(f, ">");
}
break;
return;
case TypeRecord_Union: {
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
// LLVM takes the first element's alignment as the entire alignment (like C)
i64 size_of_union = type_size_of(s, heap_allocator(), t) - s.word_size;
i64 align_of_union = type_align_of(s, heap_allocator(), t);
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align_of_union, size_of_union, word_bits);
} break;
} return;
case TypeRecord_RawUnion: {
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
// LLVM takes the first element's alignment as the entire alignment (like C)
i64 size_of_union = type_size_of(s, heap_allocator(), t);
i64 align_of_union = type_align_of(s, heap_allocator(), t);
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8]}", align_of_union, size_of_union);
} break;
} return;
case TypeRecord_Enum:
ir_print_type(f, m, base_enum_type(t));
break;
return;
}
} break;
@@ -240,7 +254,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
} else {
ir_print_type(f, m, base_type(t));
}
break;
return;
case Type_Tuple:
if (t->Tuple.variable_count == 1) {
ir_print_type(f, m, t->Tuple.variables[0]->type);
@@ -254,7 +268,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
}
ir_fprintf(f, "}");
}
break;
return;
case Type_Proc: {
if (t->Proc.result_count == 0) {
ir_fprintf(f, "void");
@@ -270,6 +284,11 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
ir_print_type(f, m, params->variables[i]->type);
}
ir_fprintf(f, ")*");
} return;
case Type_Map: {
GB_ASSERT(t->Map.generated_struct_type != NULL);
ir_print_type(f, m, t->Map.generated_struct_type);
} break;
}
}
@@ -280,25 +299,11 @@ void ir_print_compound_element(irFileBuffer *f, irModule *m, ExactValue v, Type
ir_print_type(f, m, elem_type);
ir_fprintf(f, " ");
if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) {
Type *t = base_type(elem_type)->Maybe.elem;
ir_fprintf(f, "{");
ir_print_type(f, m, t);
ir_fprintf(f, " ");
}
if (v.kind == ExactValue_Invalid || base_type(elem_type) == t_any) {
ir_fprintf(f, "zeroinitializer");
} else {
ir_print_exact_value(f, m, v, elem_type);
}
if (v.kind != ExactValue_Invalid && is_type_maybe(elem_type)) {
ir_fprintf(f, ", ");
ir_print_type(f, m, t_bool);
ir_fprintf(f, " ");
ir_fprintf(f, "true}");
}
}
void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type) {
@@ -450,9 +455,14 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
break;
}
ir_fprintf(f, "<");
i64 align = type_align_of(m->sizes, m->allocator, type);
i64 count = type->Vector.count;
Type *elem_type = type->Vector.elem;
ir_fprintf(f, "{[0 x <%lld x i8>] zeroinitializer, [%lld x ", align, count);
ir_print_type(f, m, elem_type);
ir_fprintf(f, "][");
if (elem_count == 1 && type->Vector.count > 1) {
TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[0]);
GB_ASSERT(tav != NULL);
@@ -474,7 +484,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
}
}
ir_fprintf(f, ">");
ir_fprintf(f, "]}");
} else if (is_type_struct(type)) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
@@ -684,7 +694,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
} break;
case irInstr_Store: {
Type *type = ir_type(instr->Store.value);
Type *type = type_deref(ir_type(instr->Store.address));
ir_fprintf(f, "store ");
ir_print_type(f, m, type);
ir_fprintf(f, " ");
@@ -719,6 +729,10 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_fprintf(f, ", ");
ir_print_type(f, m, t_int);
ir_fprintf(f, " 0, ");
if (is_type_vector(type_deref(et))) {
ir_print_type(f, m, t_i32);
ir_fprintf(f, " 1, ");
}
irValue *index =instr->ArrayElementPtr.elem_index;
Type *t = ir_type(index);
@@ -731,6 +745,13 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_StructElementPtr: {
Type *et = ir_type(instr->StructElementPtr.address);
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
i32 index = instr->StructElementPtr.elem_index;
Type *st = base_type(type_deref(et));
if (is_type_struct(st)) {
if (st->Record.custom_align > 0) {
index += 1;
}
}
ir_print_type(f, m, type_deref(et));
ir_fprintf(f, ", ");
@@ -741,7 +762,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_print_type(f, m, t_int);
ir_fprintf(f, " 0, ");
ir_print_type(f, m, t_i32);
ir_fprintf(f, " %d", instr->StructElementPtr.elem_index);
ir_fprintf(f, " %d", index);
ir_fprintf(f, "\n");
} break;
@@ -789,24 +810,22 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_fprintf(f, "\n");
} break;
case irInstr_ArrayExtractValue: {
Type *et = ir_type(instr->ArrayExtractValue.address);
ir_fprintf(f, "%%%d = extractvalue ", value->index);
ir_print_type(f, m, et);
ir_fprintf(f, " ");
ir_print_value(f, m, instr->ArrayExtractValue.address, et);
ir_fprintf(f, ", %d\n", instr->ArrayExtractValue.index);
} break;
case irInstr_StructExtractValue: {
Type *et = ir_type(instr->StructExtractValue.address);
ir_fprintf(f, "%%%d = extractvalue ", value->index);
i32 index = instr->StructExtractValue.index;
Type *st = base_type(et);
if (is_type_struct(st)) {
if (st->Record.custom_align > 0) {
index += 1;
}
}
ir_print_type(f, m, et);
ir_fprintf(f, " ");
ir_print_value(f, m, instr->StructExtractValue.address, et);
ir_fprintf(f, ", %d\n", instr->StructExtractValue.index);
ir_fprintf(f, ", %d\n", index);
} break;
case irInstr_UnionTagPtr: {
@@ -938,6 +957,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
irInstrBinaryOp *bo = &value->Instr.BinaryOp;
Type *type = base_type(ir_type(bo->left));
Type *elem_type = type;
GB_ASSERT(!is_type_vector(elem_type));
while (elem_type->kind == Type_Vector) {
elem_type = base_type(elem_type->Vector.elem);
}
@@ -1078,6 +1098,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, t);
if (e->flags&EntityFlag_NoAlias) {
ir_fprintf(f, " noalias");
}
ir_fprintf(f, " ");
irValue *arg = call->args[i];
ir_print_value(f, m, arg, t);
@@ -1101,68 +1124,68 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_fprintf(f, "\n");
} break;
case irInstr_VectorExtractElement: {
Type *vt = ir_type(instr->VectorExtractElement.vector);
Type *it = ir_type(instr->VectorExtractElement.index);
ir_fprintf(f, "%%%d = extractelement ", value->index);
// case irInstr_VectorExtractElement: {
// Type *vt = ir_type(instr->VectorExtractElement.vector);
// Type *it = ir_type(instr->VectorExtractElement.index);
// ir_fprintf(f, "%%%d = extractelement ", value->index);
ir_print_type(f, m, vt);
ir_fprintf(f, " ");
ir_print_value(f, m, instr->VectorExtractElement.vector, vt);
ir_fprintf(f, ", ");
ir_print_type(f, m, it);
ir_fprintf(f, " ");
ir_print_value(f, m, instr->VectorExtractElement.index, it);
ir_fprintf(f, "\n");
} break;
// ir_print_type(f, m, vt);
// ir_fprintf(f, " ");
// ir_print_value(f, m, instr->VectorExtractElement.vector, vt);
// ir_fprintf(f, ", ");
// ir_print_type(f, m, it);
// ir_fprintf(f, " ");
// ir_print_value(f, m, instr->VectorExtractElement.index, it);
// ir_fprintf(f, "\n");
// } break;
case irInstr_VectorInsertElement: {
irInstrVectorInsertElement *ie = &instr->VectorInsertElement;
Type *vt = ir_type(ie->vector);
ir_fprintf(f, "%%%d = insertelement ", value->index);
// case irInstr_VectorInsertElement: {
// irInstrVectorInsertElement *ie = &instr->VectorInsertElement;
// Type *vt = ir_type(ie->vector);
// ir_fprintf(f, "%%%d = insertelement ", value->index);
ir_print_type(f, m, vt);
ir_fprintf(f, " ");
ir_print_value(f, m, ie->vector, vt);
ir_fprintf(f, ", ");
// ir_print_type(f, m, vt);
// ir_fprintf(f, " ");
// ir_print_value(f, m, ie->vector, vt);
// ir_fprintf(f, ", ");
ir_print_type(f, m, ir_type(ie->elem));
ir_fprintf(f, " ");
ir_print_value(f, m, ie->elem, ir_type(ie->elem));
ir_fprintf(f, ", ");
// ir_print_type(f, m, ir_type(ie->elem));
// ir_fprintf(f, " ");
// ir_print_value(f, m, ie->elem, ir_type(ie->elem));
// ir_fprintf(f, ", ");
ir_print_type(f, m, ir_type(ie->index));
ir_fprintf(f, " ");
ir_print_value(f, m, ie->index, ir_type(ie->index));
// ir_print_type(f, m, ir_type(ie->index));
// ir_fprintf(f, " ");
// ir_print_value(f, m, ie->index, ir_type(ie->index));
ir_fprintf(f, "\n");
} break;
// ir_fprintf(f, "\n");
// } break;
case irInstr_VectorShuffle: {
irInstrVectorShuffle *sv = &instr->VectorShuffle;
Type *vt = ir_type(sv->vector);
ir_fprintf(f, "%%%d = shufflevector ", value->index);
// case irInstr_VectorShuffle: {
// irInstrVectorShuffle *sv = &instr->VectorShuffle;
// Type *vt = ir_type(sv->vector);
// ir_fprintf(f, "%%%d = shufflevector ", value->index);
ir_print_type(f, m, vt);
ir_fprintf(f, " ");
ir_print_value(f, m, sv->vector, vt);
ir_fprintf(f, ", ");
// ir_print_type(f, m, vt);
// ir_fprintf(f, " ");
// ir_print_value(f, m, sv->vector, vt);
// ir_fprintf(f, ", ");
ir_print_type(f, m, vt);
ir_fprintf(f, " ");
ir_print_value(f, m, sv->vector, vt);
ir_fprintf(f, ", ");
// ir_print_type(f, m, vt);
// ir_fprintf(f, " ");
// ir_print_value(f, m, sv->vector, vt);
// ir_fprintf(f, ", ");
ir_fprintf(f, "<%td x i32> <", sv->index_count);
for (isize i = 0; i < sv->index_count; i++) {
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_fprintf(f, "i32 %d", sv->indices[i]);
}
ir_fprintf(f, ">");
ir_fprintf(f, "\n");
} break;
// ir_fprintf(f, "<%td x i32> <", sv->index_count);
// for (isize i = 0; i < sv->index_count; i++) {
// if (i > 0) {
// ir_fprintf(f, ", ");
// }
// ir_fprintf(f, "i32 %d", sv->indices[i]);
// }
// ir_fprintf(f, ">");
// ir_fprintf(f, "\n");
// } break;
case irInstr_BoundsCheck: {
irInstrBoundsCheck *bc = &instr->BoundsCheck;
@@ -1241,9 +1264,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
if (proc->body == NULL) {
ir_fprintf(f, "declare ");
if (proc->tags & ProcTag_dll_import) {
ir_fprintf(f, "dllimport ");
}
// if (proc->tags & ProcTag_dll_import) {
// ir_fprintf(f, "dllimport ");
// }
} else {
ir_fprintf(f, "\n");
ir_fprintf(f, "define ");
@@ -1277,6 +1300,9 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, e->type);
if (e->flags&EntityFlag_NoAlias) {
ir_fprintf(f, " noalias");
}
if (proc->body != NULL) {
if (!str_eq(e->token.string, str_lit("")) &&
!str_eq(e->token.string, str_lit("_"))) {
+71 -17
View File
@@ -4,20 +4,20 @@ extern "C" {
#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 "checker.c"
// #include "ssa.c"
#include "ir.c"
#include "ir_opt.c"
#include "ir_print.c"
// #include "vm.c"
#if defined(GB_SYSTEM_WINDOWS)
// NOTE(bill): `name` is used in debugging and profiling modes
i32 win32_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
i32 system_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,6 +59,54 @@ i32 win32_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
gb_temp_arena_memory_end(tmp);
return exit_code;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
char cmd_line[4096] = {0};
isize cmd_len;
va_list va;
String cmd;
i32 exit_code = 0;
va_start(va, fmt);
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
va_end(va);
cmd = make_string(cast(u8 *)&cmd_line, cmd_len-1);
exit_code = system(&cmd_line[0]);
// pid_t pid = fork();
// int status = 0;
// if(pid == 0) {
// // in child, pid == 0.
// int ret = execvp(cmd.text, (char* const*) cmd.text);
// if(ret == -1) {
// gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
// // we're in the child, so returning won't do us any good -- just quit.
// exit(-1);
// }
// // unreachable
// abort();
// } else {
// // wait for child to finish, then we can continue cleanup
// int s = 0;
// waitpid(pid, &s, 0);
// status = WEXITSTATUS(s);
// }
// exit_code = status;
}
#endif
void print_usage_line(i32 indent, char *fmt, ...) {
while (indent --> 0) {
@@ -92,6 +140,7 @@ int main(int argc, char **argv) {
timings_init(&timings, str_lit("Total Time"), 128);
// defer (timings_destroy(&timings));
init_string_buffer_memory();
init_scratch_memory(gb_megabytes(10));
init_global_error_collector();
#if 1
@@ -189,7 +238,7 @@ int main(int argc, char **argv) {
// prof_print_all();
#if 1
#if 1
timings_start_section(&timings, str_lit("llvm-opt"));
char const *output_name = ir_gen.output_file.filename;
@@ -201,8 +250,8 @@ 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", false,
"%.*sbin/opt %s -o %.*s.bc "
exit_code = system_exec_command_line_app("llvm-opt", false,
"\"%.*sbin/opt\" \"%s\" -o \"%.*s\".bc "
"-mem2reg "
"-memcpyopt "
"-die "
@@ -216,11 +265,11 @@ int main(int argc, char **argv) {
return exit_code;
}
#if 1
#if defined(GB_SYSTEM_WINDOWS)
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", false,
"%.*sbin/llc %.*s.bc -filetype=obj -O%d "
exit_code = system_exec_command_line_app("llvm-llc", false,
"\"%.*sbin/llc\" \"%.*s.bc\" -filetype=obj -O%d "
"%.*s "
// "-debug-pass=Arguments "
"",
@@ -234,13 +283,14 @@ int main(int argc, char **argv) {
timings_start_section(&timings, str_lit("msvc-link"));
gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib");
gbString lib_str = gb_string_make(heap_allocator(), "");
// defer (gb_string_free(lib_str));
char lib_str_buf[1024] = {0};
for_array(i, checker.info.foreign_libraries) {
String lib = checker.info.foreign_libraries.e[i];
for_array(i, ir_gen.module.foreign_library_paths) {
String lib = ir_gen.module.foreign_library_paths.e[i];
// gb_printf_err("Linking lib: %.*s\n", LIT(lib));
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" %.*s.lib", LIT(lib));
" \"%.*s\"", LIT(lib));
lib_str = gb_string_appendc(lib_str, lib_str_buf);
}
@@ -249,10 +299,12 @@ int main(int argc, char **argv) {
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 "
exit_code = system_exec_command_line_app("msvc-link", true,
"link \"%.*s\".obj -OUT:\"%.*s.%s\" %s "
"/defaultlib:libcmt "
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
" %.*s "
@@ -269,14 +321,16 @@ int main(int argc, char **argv) {
// timings_print_all(&timings);
if (run_output) {
win32_exec_command_line_app("odin run", false, "%.*s.exe", cast(int)base_name_len, output_name);
system_exec_command_line_app("odin run", false, "%.*s.exe", cast(int)base_name_len, output_name);
}
#else
#error Implement build stuff for this platform
#endif
#endif
#endif
#endif
return 0;
}
+2
View File
@@ -320,6 +320,7 @@ void _J2(MAP_PROC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
if (h->hashes.count == 0) {
_J2(MAP_PROC,grow)(h);
}
// Make
fr = _J2(MAP_PROC,_find)(h, key);
i = _J2(MAP_PROC,_add_entry)(h, key);
if (fr.entry_prev < 0) {
@@ -329,6 +330,7 @@ void _J2(MAP_PROC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
}
h->entries.e[i].next = fr.entry_index;
h->entries.e[i].value = value;
// Grow if needed
if (_J2(MAP_PROC,_full)(h)) {
_J2(MAP_PROC,grow)(h);
}
+1024 -683
View File
File diff suppressed because it is too large Load Diff
+75 -17
View File
@@ -15,6 +15,8 @@ typedef struct String {
} String;
// NOTE(bill): used for printf style arguments
#define LIT(x) ((int)(x).len), (x).text
#define STR_LIT(c_str) {cast(u8 *)c_str, gb_size_of(c_str)-1}
#define str_lit(c_str) (String){cast(u8 *)c_str, gb_size_of(c_str)-1}
typedef struct String16 {
@@ -46,7 +48,6 @@ gb_inline String make_string_c(char *text) {
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
}
#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
@@ -144,16 +145,37 @@ gb_inline isize string_extension_position(String str) {
return dot_pos;
}
String string_trim_whitespace(String str) {
while (str.len > 0 && rune_is_whitespace(str.text[str.len-1])) {
str.len--;
}
while (str.len > 0 && rune_is_whitespace(str.text[0])) {
str.text++;
str.len--;
}
return str;
}
gb_inline bool string_has_extension(String str, String ext) {
if (str.len > ext.len+1) {
u8 *s = str.text+str.len - ext.len-1;
if (s[0] == '.') {
s++;
return gb_memcompare(s, ext.text, ext.len) == 0;
}
str = string_trim_whitespace(str);
if (str.len <= ext.len+1) {
return false;
}
return false;
isize len = str.len;
for (isize i = len-1; i >= 0; i--) {
if (str.text[i] == '.') {
break;
}
len--;
}
if (len == 0) {
return false;
}
u8 *s = str.text + len;
return gb_memcompare(s, ext.text, ext.len) == 0;
}
bool string_contains_char(String s, u8 c) {
@@ -165,6 +187,47 @@ bool string_contains_char(String s, u8 c) {
return false;
}
#if defined(GB_SYSTEM_WINDOWS)
int convert_multibyte_to_widechar(char *multibyte_input, int input_length, wchar_t *output, int output_size) {
return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, multibyte_input, input_length, output, output_size);
}
int convert_widechar_to_multibyte(wchar_t *widechar_input, int input_length, char *output, int output_size) {
return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, NULL, NULL);
}
#elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX)
#include <iconv.h>
int convert_multibyte_to_widechar(char *multibyte_input, int input_length, wchar_t *output, int output_size) {
iconv_t conv = iconv_open("WCHAR_T", "UTF-8");
size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size);
iconv_close(conv);
return (int) result;
}
int convert_widechar_to_multibyte(wchar_t* widechar_input, int input_length, char* output, int output_size) {
iconv_t conv = iconv_open("UTF-8", "WCHAR_T");
size_t result = iconv(conv, (char**) &widechar_input, &input_length, (char**) &output, &output_size);
iconv_close(conv);
return (int) result;
}
#else
#error Implement system
#endif
// TODO(bill): Make this non-windows specific
String16 string_to_string16(gbAllocator a, String s) {
int len, len1;
@@ -174,16 +237,14 @@ String16 string_to_string16(gbAllocator a, String s) {
return make_string16(NULL, 0);
}
len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
cast(char *)s.text, s.len, NULL, 0);
len = convert_multibyte_to_widechar(cast(char *)s.text, s.len, NULL, 0);
if (len == 0) {
return make_string16(NULL, 0);
}
text = gb_alloc_array(a, wchar_t, len+1);
len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
cast(char *)s.text, s.len, text, len);
len1 = convert_multibyte_to_widechar(cast(char *)s.text, s.len, text, len);
if (len1 == 0) {
gb_free(a, text);
return make_string16(NULL, 0);
@@ -201,16 +262,14 @@ String string16_to_string(gbAllocator a, String16 s) {
return make_string(NULL, 0);
}
len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
s.text, s.len, NULL, 0, NULL, NULL);
len = convert_widechar_to_multibyte(s.text, s.len, NULL, 0);
if (len == 0) {
return make_string(NULL, 0);
}
text = gb_alloc_array(a, u8, len+1);
len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
s.text, s.len, cast(char *)text, len, NULL, NULL);
len1 = convert_widechar_to_multibyte(s.text, s.len, cast(char *)text, len);
if (len1 == 0) {
gb_free(a, text);
return make_string(NULL, 0);
@@ -236,7 +295,6 @@ String string16_to_string(gbAllocator a, String16 s) {
bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
u8 c;
+38
View File
@@ -11,6 +11,7 @@ typedef struct Timings {
} Timings;
#if defined(GB_SYSTEM_WINDOWS)
u64 win32_time_stamp_time_now(void) {
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
@@ -27,9 +28,44 @@ u64 win32_time_stamp__freq(void) {
return win32_perf_count_freq.QuadPart;
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
#include <time.h>
u64 unix_time_stamp_time_now(void) {
struct timespec ts;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
return (ts.tv_sec * 1000000000) + ts.tv_nsec;
}
u64 unix_time_stamp__freq(void) {
gb_local_persist u64 freq = 0;
if (freq == 0) {
struct timespec ts;
clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts);
// that would be an absurd resolution (or lack thereof)
GB_ASSERT(ts.tv_sec == 0);
freq = cast(u64) ((1.0 / ts.tv_nsec) * 1000000000.0);
GB_ASSERT(freq != 0);
}
return freq;
}
#else
#error Implement system
#endif
u64 time_stamp_time_now(void) {
#if defined(GB_SYSTEM_WINDOWS)
return win32_time_stamp_time_now();
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
return unix_time_stamp_time_now();
#else
#error time_stamp_time_now
#endif
@@ -38,6 +74,8 @@ u64 time_stamp_time_now(void) {
u64 time_stamp__freq(void) {
#if defined(GB_SYSTEM_WINDOWS)
return win32_time_stamp__freq();
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
return unix_time_stamp__freq();
#else
#error time_stamp__freq
#endif
+156 -192
View File
@@ -1,113 +1,115 @@
#define TOKEN_KINDS \
TOKEN_KIND(Token_Invalid, "Invalid"), \
TOKEN_KIND(Token_EOF, "EOF"), \
TOKEN_KIND(Token_EOF, "EOF"), \
TOKEN_KIND(Token_Comment, "Comment"), \
\
TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \
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_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"), \
TOKEN_KIND(Token_Eq, "="), \
TOKEN_KIND(Token_Not, "!"), \
TOKEN_KIND(Token_Hash, "#"), \
TOKEN_KIND(Token_At, "@"), \
TOKEN_KIND(Token_Pointer, "^"), \
TOKEN_KIND(Token_Maybe, "?"), \
TOKEN_KIND(Token_Add, "+"), \
TOKEN_KIND(Token_Sub, "-"), \
TOKEN_KIND(Token_Mul, "*"), \
TOKEN_KIND(Token_Quo, "/"), \
TOKEN_KIND(Token_Mod, "%"), \
TOKEN_KIND(Token_And, "&"), \
TOKEN_KIND(Token_Or, "|"), \
TOKEN_KIND(Token_Xor, "~"), \
TOKEN_KIND(Token_AndNot, "&~"), \
TOKEN_KIND(Token_Shl, "<<"), \
TOKEN_KIND(Token_Shr, ">>"), \
\
TOKEN_KIND(Token_as, "as"), \
TOKEN_KIND(Token_transmute, "transmute"), \
TOKEN_KIND(Token_down_cast, "down_cast"), \
TOKEN_KIND(Token_union_cast, "union_cast"), \
TOKEN_KIND(Token_Eq, "="), \
TOKEN_KIND(Token_Not, "!"), \
TOKEN_KIND(Token_Hash, "#"), \
TOKEN_KIND(Token_At, "@"), \
TOKEN_KIND(Token_Dollar, "$"), \
TOKEN_KIND(Token_Pointer, "^"), \
TOKEN_KIND(Token_Question, "?"), \
TOKEN_KIND(Token_Add, "+"), \
TOKEN_KIND(Token_Sub, "-"), \
TOKEN_KIND(Token_Mul, "*"), \
TOKEN_KIND(Token_Quo, "/"), \
TOKEN_KIND(Token_Mod, "%"), \
TOKEN_KIND(Token_And, "&"), \
TOKEN_KIND(Token_Or, "|"), \
TOKEN_KIND(Token_Xor, "~"), \
TOKEN_KIND(Token_AndNot, "&~"), \
TOKEN_KIND(Token_Shl, "<<"), \
TOKEN_KIND(Token_Shr, ">>"), \
\
TOKEN_KIND(Token_CmpAnd, "&&"), \
TOKEN_KIND(Token_CmpOr, "||"), \
TOKEN_KIND(Token_CmpOr, "||"), \
\
TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \
TOKEN_KIND(Token_AddEq, "+="), \
TOKEN_KIND(Token_SubEq, "-="), \
TOKEN_KIND(Token_MulEq, "*="), \
TOKEN_KIND(Token_QuoEq, "/="), \
TOKEN_KIND(Token_ModEq, "%="), \
TOKEN_KIND(Token_AndEq, "&="), \
TOKEN_KIND(Token_OrEq, "|="), \
TOKEN_KIND(Token_XorEq, "~="), \
TOKEN_KIND(Token_AndNotEq, "&~="), \
TOKEN_KIND(Token_ShlEq, "<<="), \
TOKEN_KIND(Token_ShrEq, ">>="), \
TOKEN_KIND(Token_CmpAndEq, "&&="), \
TOKEN_KIND(Token_CmpOrEq, "||="), \
TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \
TOKEN_KIND(Token_AddEq, "+="), \
TOKEN_KIND(Token_SubEq, "-="), \
TOKEN_KIND(Token_MulEq, "*="), \
TOKEN_KIND(Token_QuoEq, "/="), \
TOKEN_KIND(Token_ModEq, "%="), \
TOKEN_KIND(Token_AndEq, "&="), \
TOKEN_KIND(Token_OrEq, "|="), \
TOKEN_KIND(Token_XorEq, "~="), \
TOKEN_KIND(Token_AndNotEq, "&~="), \
TOKEN_KIND(Token_ShlEq, "<<="), \
TOKEN_KIND(Token_ShrEq, ">>="), \
TOKEN_KIND(Token_CmpAndEq, "&&="), \
TOKEN_KIND(Token_CmpOrEq, "||="), \
TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \
TOKEN_KIND(Token_ArrowRight, "->"), \
TOKEN_KIND(Token_ArrowLeft, "<-"), \
TOKEN_KIND(Token_ArrowLeft, "<-"), \
TOKEN_KIND(Token_Increment, "++"), \
TOKEN_KIND(Token_Decrement, "--"), \
\
TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
TOKEN_KIND(Token_CmpEq, "=="), \
TOKEN_KIND(Token_NotEq, "!="), \
TOKEN_KIND(Token_Lt, "<"), \
TOKEN_KIND(Token_Gt, ">"), \
TOKEN_KIND(Token_LtEq, "<="), \
TOKEN_KIND(Token_GtEq, ">="), \
TOKEN_KIND(Token_Lt, "<"), \
TOKEN_KIND(Token_Gt, ">"), \
TOKEN_KIND(Token_LtEq, "<="), \
TOKEN_KIND(Token_GtEq, ">="), \
TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
\
TOKEN_KIND(Token_OpenParen, "("), \
TOKEN_KIND(Token_CloseParen, ")"), \
TOKEN_KIND(Token_OpenBracket, "["), \
TOKEN_KIND(Token_CloseBracket, "]"), \
TOKEN_KIND(Token_OpenBrace, "{"), \
TOKEN_KIND(Token_CloseBrace, "}"), \
TOKEN_KIND(Token_Colon, ":"), \
TOKEN_KIND(Token_Semicolon, ";"), \
TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, "..."), \
TOKEN_KIND(Token_Interval, "..<"), \
TOKEN_KIND(Token_OpenParen, "("), \
TOKEN_KIND(Token_CloseParen, ")"), \
TOKEN_KIND(Token_OpenBracket, "["), \
TOKEN_KIND(Token_CloseBracket, "]"), \
TOKEN_KIND(Token_OpenBrace, "{"), \
TOKEN_KIND(Token_CloseBrace, "}"), \
TOKEN_KIND(Token_Colon, ":"), \
TOKEN_KIND(Token_Semicolon, ";"), \
TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, "..."), \
TOKEN_KIND(Token_HalfOpenRange, "..<"), \
TOKEN_KIND(Token__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"), \
TOKEN_KIND(Token_fallthrough, "fallthrough"), \
TOKEN_KIND(Token_case, "case"), \
TOKEN_KIND(Token_default, "default"), \
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_push_allocator, "push_allocator"), \
TOKEN_KIND(Token_push_context, "push_context"), \
TOKEN_KIND(Token_when, "when"), \
TOKEN_KIND(Token_if, "if"), \
TOKEN_KIND(Token_else, "else"), \
TOKEN_KIND(Token_for, "for"), \
TOKEN_KIND(Token_in, "in"), \
TOKEN_KIND(Token_match, "match"), \
TOKEN_KIND(Token_default, "default"), \
TOKEN_KIND(Token_case, "case"), \
TOKEN_KIND(Token_break, "break"), \
TOKEN_KIND(Token_continue, "continue"), \
TOKEN_KIND(Token_fallthrough, "fallthrough"), \
TOKEN_KIND(Token_defer, "defer"), \
TOKEN_KIND(Token_return, "return"), \
TOKEN_KIND(Token_give, "give"), \
TOKEN_KIND(Token_proc, "proc"), \
TOKEN_KIND(Token_macro, "macro"), \
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_map, "map"), \
TOKEN_KIND(Token_using, "using"), \
TOKEN_KIND(Token_no_alias, "no_alias"), \
TOKEN_KIND(Token_cast, "cast"), \
TOKEN_KIND(Token_transmute, "transmute"), \
TOKEN_KIND(Token_down_cast, "down_cast"), \
TOKEN_KIND(Token_union_cast, "union_cast"), \
TOKEN_KIND(Token_context, "context"), \
TOKEN_KIND(Token_push_context, "push_context"), \
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
TOKEN_KIND(Token_asm, "asm"), \
TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
TOKEN_KIND(Token_Count, "")
@@ -146,7 +148,6 @@ bool token_pos_eq(TokenPos a, TokenPos b) {
return token_pos_cmp(a, b) == 0;
}
// NOTE(bill): Text is UTF-8, thus why u8 and not char
typedef struct Token {
TokenKind kind;
String string;
@@ -198,6 +199,8 @@ void error_va(Token token, char *fmt, va_list va) {
gb_printf_err("%.*s(%td:%td) %s\n",
LIT(token.pos.file), token.pos.line, token.pos.column,
gb_bprintf_va(fmt, 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);
@@ -212,6 +215,8 @@ void syntax_error_va(Token token, char *fmt, va_list va) {
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));
} else if (token.pos.line == 0) {
gb_printf_err("Error: %s\n", gb_bprintf_va(fmt, va));
}
gb_mutex_unlock(&global_error_collector.mutex);
@@ -254,20 +259,20 @@ void compiler_error(char *fmt, ...) {
gb_inline bool token_is_literal(Token t) {
return gb_is_between(t.kind, Token__LiteralBegin+1, Token__LiteralEnd-1);
gb_inline bool token_is_literal(TokenKind t) {
return gb_is_between(t, Token__LiteralBegin+1, Token__LiteralEnd-1);
}
gb_inline bool token_is_operator(Token t) {
return gb_is_between(t.kind, Token__OperatorBegin+1, Token__OperatorEnd-1);
gb_inline bool token_is_operator(TokenKind t) {
return gb_is_between(t, Token__OperatorBegin+1, Token__OperatorEnd-1);
}
gb_inline bool token_is_keyword(Token t) {
return gb_is_between(t.kind, Token__KeywordBegin+1, Token__KeywordEnd-1);
gb_inline bool token_is_keyword(TokenKind t) {
return gb_is_between(t, Token__KeywordBegin+1, Token__KeywordEnd-1);
}
gb_inline bool token_is_comparison(Token t) {
return gb_is_between(t.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1);
gb_inline bool token_is_comparison(TokenKind t) {
return gb_is_between(t, Token__ComparisonBegin+1, Token__ComparisonEnd-1);
}
gb_inline bool token_is_shift(Token t) {
return t.kind == Token_Shl || t.kind == Token_Shr;
gb_inline bool token_is_shift(TokenKind t) {
return t == Token_Shl || t == Token_Shr;
}
gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }
@@ -452,16 +457,18 @@ gb_inline i32 digit_value(Rune r) {
return 16; // NOTE(bill): Larger than highest possible
}
gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
// TODO(bill): Allow for underscores in numbers as a number separator
// TODO(bill): Is this a good idea?
// while (digit_value(t->curr_rune) < base || t->curr_rune == '_')
while (digit_value(t->curr_rune) < base) {
advance_to_next_rune(t);
gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) {
if (allow_underscore) {
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
advance_to_next_rune(t);
}
} else {
while (digit_value(t->curr_rune) < base) {
advance_to_next_rune(t);
}
}
}
Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
Token token = {0};
token.kind = Token_Integer;
@@ -472,7 +479,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
if (seen_decimal_point) {
token.kind = Token_Float;
scan_mantissa(t, 10);
scan_mantissa(t, 10, true);
goto exponent;
}
@@ -481,31 +488,31 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
advance_to_next_rune(t);
if (t->curr_rune == 'b') { // Binary
advance_to_next_rune(t);
scan_mantissa(t, 2);
scan_mantissa(t, 2, true);
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);
scan_mantissa(t, 8, true);
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);
scan_mantissa(t, 10, true);
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);
scan_mantissa(t, 16, true);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else {
seen_decimal_point = false;
scan_mantissa(t, 10);
scan_mantissa(t, 10, true);
if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
seen_decimal_point = true;
@@ -517,20 +524,20 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
return token;
}
scan_mantissa(t, 10);
scan_mantissa(t, 10, true);
fraction:
if (t->curr_rune == '.') {
// HACK(bill): This may be inefficient
TokenizerState state = save_tokenizer_state(t);
advance_to_next_rune(t);
if (t->curr_rune == '.') {
if (digit_value(t->curr_rune) >= 10) {
// TODO(bill): Clean up this shit
restore_tokenizer_state(t, &state);
goto end;
}
token.kind = Token_Float;
scan_mantissa(t, 10);
scan_mantissa(t, 10, true);
}
exponent:
@@ -540,7 +547,7 @@ exponent:
if (t->curr_rune == '-' || t->curr_rune == '+') {
advance_to_next_rune(t);
}
scan_mantissa(t, 10);
scan_mantissa(t, 10, false);
}
end:
@@ -717,20 +724,10 @@ Token tokenizer_get_token(Tokenizer *t) {
// NOTE(bill): All keywords are > 1
if (token.string.len > 1) {
if (str_eq(token.string, token_strings[Token_as])) {
token.kind = Token_as;
} else if (str_eq(token.string, token_strings[Token_transmute])) {
token.kind = Token_transmute;
} else if (str_eq(token.string, token_strings[Token_down_cast])) {
token.kind = Token_down_cast;
} else if (str_eq(token.string, token_strings[Token_union_cast])) {
token.kind = Token_union_cast;
} else {
for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) {
if (str_eq(token.string, token_strings[k])) {
token.kind = cast(TokenKind)k;
break;
}
for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) {
if (str_eq(token.string, token_strings[k])) {
token.kind = cast(TokenKind)k;
break;
}
}
}
@@ -832,74 +829,43 @@ Token tokenizer_get_token(Tokenizer *t) {
case '.':
token.kind = Token_Period; // Default
if (gb_is_between(t->curr_rune, '0', '9')) { // Might be a number
token = scan_number_to_token(t, true);
} else if (t->curr_rune == '.') { // Could be an ellipsis
if (t->curr_rune == '.') { // Could be an ellipsis
advance_to_next_rune(t);
if (t->curr_rune == '.') {
if (t->curr_rune == '<') {
advance_to_next_rune(t);
token.kind = Token_HalfOpenRange;
} else if (t->curr_rune == '.') {
advance_to_next_rune(t);
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_Colon;
break;
case '(':
token.kind = Token_OpenParen;
break;
case ')':
token.kind = Token_CloseParen;
break;
case '[':
token.kind = Token_OpenBracket;
break;
case ']':
token.kind = Token_CloseBracket;
break;
case '{':
token.kind = Token_OpenBrace;
break;
case '}':
token.kind = Token_CloseBrace;
break;
case '#': token.kind = Token_Hash; break;
case '@': token.kind = Token_At; break;
case '$': token.kind = Token_Dollar; break;
case '?': token.kind = Token_Question; break;
case '^': token.kind = Token_Pointer; break;
case ';': token.kind = Token_Semicolon; break;
case ',': token.kind = Token_Comma; break;
case ':': token.kind = Token_Colon; break;
case '(': token.kind = Token_OpenParen; break;
case ')': token.kind = Token_CloseParen; break;
case '[': token.kind = Token_OpenBracket; break;
case ']': token.kind = Token_CloseBracket; break;
case '{': token.kind = Token_OpenBrace; break;
case '}': token.kind = Token_CloseBrace; break;
case '*': token.kind = token_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_variant2(t, Token_Add, Token_AddEq);
break;
case '-':
token.kind = token_kind_variant3(t, Token_Sub, Token_SubEq, '>', Token_ArrowRight);
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 '/': {
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;
@@ -936,9 +902,7 @@ Token tokenizer_get_token(Tokenizer *t) {
token.kind = token_kind_dub_eq(t, '<', Token_Lt, Token_LtEq, Token_Shl, Token_ShlEq);
}
break;
case '>':
token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq);
break;
case '>': token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq); break;
case '&':
token.kind = Token_And;
File diff suppressed because it is too large Load Diff
-25
View File
@@ -39,28 +39,3 @@ bool rune_is_whitespace(Rune r) {
}
return false;
}
bool is_string_an_identifier(String s) {
isize offset = 0;
if (s.len < 1) {
return false;
}
while (offset < s.len) {
bool ok = false;
Rune r = -1;
isize size = gb_utf8_decode(s.text+offset, s.len-offset, &r);
if (offset == 0) {
ok = rune_is_letter(r);
} else {
ok = rune_is_letter(r) || rune_is_digit(r);
}
if (!ok) {
return false;
}
offset += size;
}
return offset == s.len;
}