Compare commits

...

132 Commits

Author SHA1 Message Date
Ginger Bill 7057034b75 v0.2.0 2017-04-30 16:28:13 +01:00
Ginger Bill 1430ca30a3 Fix subtype polymorphism implicit conversion 2017-04-30 16:22:24 +01:00
Ginger Bill e63393e394 Add type assertion for any 2017-04-30 15:29:46 +01:00
Ginger Bill 784f3ecf7e Syntax change: cast(T)x => T(x); union_cast(T)x => x.(T); transmute(T)x => transmute(T, x); y:=^x => y:=&x;
Sorry for all the code breaking in this commit :(
2017-04-30 15:09:36 +01:00
Ginger Bill 54ea70df98 Fix issues #50 and #55 2017-04-29 20:06:29 +01:00
Ginger Bill c7575164cc Revert to previous demo 2017-04-28 11:03:19 +01:00
Ginger Bill 99125dc743 Fix issue #51; begin work on atomic types 2017-04-28 11:01:46 +01:00
Ginger Bill b78e970698 Fix issue #48 dependency issue 2017-04-26 23:51:13 +01:00
Ginger Bill 5b8be25938 fmt.String_Buffer, Fix issue #44, Tweak overloading rules 2017-04-26 19:43:17 +01:00
Ginger Bill 29efdc5fc1 Fix initialization of global any types 2017-04-25 15:02:35 +01:00
Ginger Bill a80872b60d Fix checking if a procedure terminates for for loops. 2017-04-25 09:46:30 +01:00
Ginger Bill 822bb51b55 Swap memory layout of any 2017-04-23 18:03:29 +01:00
Ginger Bill c2fa79012e Fix find_using_index_expr 2017-04-23 11:04:22 +01:00
Ginger Bill 3fd37c6dc5 Internal change: IntervalExpr is now a BinaryExpr 2017-04-22 10:10:49 +01:00
Ginger Bill 0ea815db49 Fix constant bounds checking for slicing 2017-04-22 09:40:32 +01:00
Ginger Bill 91ed51ff5c Continue work on custom SSA; Fix double declaration in when statements 2017-04-21 17:56:29 +01:00
Ginger Bill 4d0afc55c3 Making slicing a little more robust 2017-04-21 10:03:27 +01:00
Ginger Bill 9a1566d665 Interval expressions for match statements 2017-04-21 00:13:20 +01:00
Ginger Bill a713e33007 Change interval syntax: .. open range, ..< half-closed range 2017-04-20 23:22:45 +01:00
Ginger Bill c5411a25a9 Change Union representation for LLVM IR; fix dynamic array size 2017-04-19 18:58:23 +01:00
Ginger Bill 95692fda52 Fix bug with union literal checking crashing the compiler 2017-04-18 21:20:41 +01:00
Ginger Bill 813a028ed0 Fix procedure calls from non-regular addressing modes 2017-04-17 22:17:16 +01:00
Ginger Bill 0c22081e5f Fix error printing for basic directives 2017-04-17 19:58:43 +01:00
Ginger Bill 6d9fadf351 Make the ABI changes only affect windows
TODO: decide upon rules for *nix systems
2017-04-17 12:01:04 +01:00
Ginger Bill a213061f33 Change tag checking order 2017-04-16 23:08:48 +01:00
Ginger Bill d1a0a46141 Fix issue #37 for procedure literal scopes 2017-04-16 22:48:29 +01:00
Ginger Bill 187b186112 Add #require_results for procedures 2017-04-16 22:30:48 +01:00
Ginger Bill 5041a35b95 Fix ir printing of constant slices 2017-04-16 22:07:26 +01:00
Ginger Bill 92d4fcedee Update ir type aggregate rules for transmute 2017-04-16 16:44:45 +01:00
Ginger Bill c69df7cd3a Exit program if there were syntax errors 2017-04-16 16:38:05 +01:00
Ginger Bill 67d8f48553 Calling convention, change from bitcast to transmute 2017-04-16 16:28:39 +01:00
Ginger Bill b4a339f2e3 Call convention, pass by pointer: pointers are 16 byte aligned 2017-04-16 10:54:05 +01:00
Ginger Bill 0d7bf58b60 Revert to the old demo 2017-04-16 10:40:24 +01:00
Ginger Bill abb9930725 IR emit C ABI compatible types for calling conventions (Only for x86/amd64 like processors at the moment) 2017-04-16 10:38:42 +01:00
Ginger Bill 169310a9f6 Fix non-ascii function parameters in LLVM IR 2017-04-15 23:14:14 +01:00
Ginger Bill 23a0a6de4b Add parse_int; Fix union bugs with size, alignment, and recursive definition checking 2017-04-14 21:47:59 +01:00
Ginger Bill 0d2dbee84e Fix addressing mode rules for match in statements 2017-04-13 22:42:36 +01:00
Ginger Bill d8d22e34dd Fix fmt for type; remove dead stuff 2017-04-13 19:29:17 +01:00
Ginger Bill 627ee002e8 Fix: map key not getting transferred on rehash 2017-04-11 23:11:05 +01:00
Ginger Bill 8e73d1ce1f Fix map bug which removed N values from the beginning 2017-04-11 22:43:33 +01:00
Ginger Bill b53d16d1d5 Remove debug text 2017-04-11 21:24:10 +01:00
Ginger Bill f5819eafa9 Fix map assignment bug due to growth 2017-04-11 21:13:21 +01:00
Ginger Bill 5916e71d4f Fix slicing bug on dynamic arrays 2017-04-11 16:00:49 +01:00
Ginger Bill 913b9b6447 Remove odin.exe 2017-04-10 22:30:38 +01:00
Ginger Bill 8e55bb2a6c Fix append crash when pointer is passed 2017-04-10 21:09:04 +01:00
root 98d493504b Fix segfault with heap allocation 2017-04-10 20:48:56 +01:00
Ginger Bill 3a3202fbc6 Change code to match original MSVC 2017-04-10 13:27:09 +01:00
Ginger Bill aaf355e750 Basic Linux Build! 2017-04-09 22:33:32 +01:00
gingerBill 0683d2b4f4 Merge pull request #33 from zangent/master
Base of *nix port
2017-04-09 22:01:22 +01:00
Ginger Bill d7fdd3d7b8 Add raw.odin
Forgot to do this in the previous commit, whoops :P
2017-04-09 11:45:41 +01:00
Ginger Bill 83ebb24015 Move to Raw_* types to raw.odin; Add size and align members to Type_Info 2017-04-07 14:05:28 +01:00
Ginger Bill 70f9cacdce Fix cast to any of untyped constants 2017-04-07 09:55:19 +01:00
Zachary Pierson 6b33b254e9 Merged from upstream, fixed 'args' name colission 2017-04-06 18:14:42 -05:00
Zachary Pierson c0019cc305 Merge https://github.com/gingerBill/Odin 2017-04-06 17:50:23 -05:00
Ginger Bill c067a1f0ec Fix ir bugs: global variable names, untyped to any assignment 2017-04-06 11:12:11 +01:00
Zachary Pierson 63345cd0d8 Bridged a bugfix from os_windows to other os's. 2017-04-04 18:51:36 -05:00
Zachary Pierson e41d6261c2 Merge https://github.com/gingerBill/Odin 2017-04-04 18:46:05 -05:00
Ginger Bill 3e80411d37 Fix issue #31; Removed down_cast 2017-04-04 21:54:55 +01:00
Zachary Pierson f952c7c747 Merge https://github.com/gingerBill/Odin 2017-04-03 00:08:00 -05:00
Zachary Pierson 642256f9ba I accidentally left debug stuff (like abs paths) in! Whoops! 2017-04-02 18:46:31 -05:00
Zachary Pierson c9c82da1f3 It's terrible, but I added _some_ form of launch args support for Linux/macOS 2017-04-02 18:42:58 -05:00
Ginger Bill 382a5ca6a2 Update and regression test old demos 2017-04-02 22:03:52 +01:00
Ginger Bill 96e8bb5b6f Add website to README.md 2017-04-02 20:20:14 +01:00
Ginger Bill 22afac2b90 Update README.md with latest demo 2017-04-02 20:10:56 +01:00
Ginger Bill 01da0d1377 Fix make for dynamic arrays 2017-04-02 18:28:45 +01:00
Ginger Bill 8ce58573df len, cap, make; remove .count, .capacity, new_slice 2017-04-02 18:16:45 +01:00
Zachary Pierson ce0d874efd Merge https://github.com/gingerBill/Odin 2017-04-02 03:29:51 -05:00
Ginger Bill 2c8b99337b Fix conj 2017-04-01 22:55:33 +01:00
Ginger Bill 5008e2c88b Add Quaternions: quaternion128, quaternion256 2017-04-01 22:41:23 +01:00
Ginger Bill 90fc9abeae Fix constant conversion for complex numbers from integers 2017-04-01 12:12:08 +01:00
Ginger Bill dc303cde21 Complex numbers: complex64 complex128 2017-04-01 12:07:41 +01:00
Zachary Pierson 24b33374b7 Reverted the main proc changed, after a chat with Bill about better solutions. 2017-03-31 05:31:45 -05:00
Zachary Pierson 3315dc7f25 Literally just a commit to revert a previous one. 2017-03-31 05:30:09 -05:00
Zachary Pierson 77b3295de5 Added checking for params and return values in main 2017-03-30 01:21:05 -05:00
Zachary Pierson 1349aa6f2c Merge https://github.com/gingerBill/Odin, cleaned up a bit, fixed the object file version message on macOS 2017-03-30 00:26:46 -05:00
Ginger Bill a75ccb6fbc v0.1.3 2017-03-27 20:32:36 +01:00
Zachary Pierson 7a28827602 Forgot to include stdio.h since Win32 won't resolve it otherwise. 2017-03-21 19:30:54 -05:00
Zachary Pierson c61015b1fe Updated shell.bat for Visual Studio 2017 2017-03-21 19:17:41 -05:00
Zac Pierson e935f8e2ff Fixed os_linux and os_x read_entire_file function not null-terminating data. 2017-03-21 16:00:11 -05:00
Zac Pierson 690c682847 Remember kids, always test your code. There was a variable name colission in dlsym D: 2017-03-21 14:57:09 -05:00
Zac Pierson f541dd40db Fixed some memory leaks and made os_* use strings.odin 2017-03-21 14:54:29 -05:00
Zac Pierson c7bb861d3c Merge https://github.com/gingerBill/Odin
"Fixed" a proc overload bug. Still needs a *real* fix.
2017-03-21 14:16:42 -05:00
Ginger Bill 188b290dd5 Update version number 2017-03-19 21:03:56 +00:00
Ginger Bill c6ff961088 Add base 12 in strconv.odin 2017-03-19 21:03:29 +00:00
Ginger Bill c26990c22d Multiple type cases for match in 2017-03-19 20:55:39 +00:00
Ginger Bill c34d839f9f Add named branches for match statements 2017-03-19 17:36:08 +00:00
Ginger Bill 5562364a98 Add branch labels for loops; using list 2017-03-19 16:59:11 +00:00
Ginger Bill 32150e401e Update gb.h 2017-03-17 12:30:59 +00:00
Ginger Bill aaec8bf423 windows.odin TYPE_NAME to Type_Name; More SSA work and SSA printing for debugging 2017-03-12 16:42:51 +00:00
Ginger Bill 0fcbda951a Finally fix signed integer conversion and printing 2017-03-10 10:34:25 +00:00
Ginger Bill e2734a2dc6 Begin work on the custom backend 2017-03-05 21:22:33 +00:00
Ginger Bill 5adfbec847 Refactoring of code: remove make prefix on many procedures 2017-03-05 15:03:01 +00:00
Ginger Bill 4ef4605d6d Move files to misc 2017-03-03 11:20:22 +00:00
Ginger Bill 2aa402f462 Cleanup root directory 2017-03-03 11:19:12 +00:00
Ginger Bill 00f6bee454 Update README.md 2017-03-03 11:15:34 +00:00
Zac Pierson d890731716 Merge https://github.com/gingerBill/Odin 2017-03-02 15:41:19 -06:00
Zachary Pierson 231ea8b026 Merge https://github.com/gingerBill/Odin 2017-02-27 23:25:47 -06:00
Zachary Pierson 5bbdb3a3a3 Merge https://github.com/gingerBill/Odin 2017-02-25 02:07:58 -06:00
Zachary Pierson 27aa07307b Merge https://github.com/gingerBill/Odin 2017-02-24 15:53:56 -06:00
Zac Pierson 20b9f1ff59 Added getenv to the *nix stdlib. 2017-02-23 15:29:41 -06:00
Zac Pierson 561c583b3f Merge https://github.com/gingerBill/Odin 2017-02-22 10:57:30 -06:00
Zac Pierson 8d5896ab7e Merge https://github.com/gingerBill/Odin 2017-02-20 10:14:52 -06:00
Zac Pierson 802b1a70f8 Fixed an error in function naming in os_linux 2017-02-15 11:20:11 -06:00
Zac Pierson aaa4dd5c36 Merge https://github.com/gingerBill/odin 2017-02-15 10:21:38 -06:00
Zachary Pierson 9d19ee7e4c Changed standard libraries for MacOS and Linux to be closer to os_windows. 2017-02-12 18:25:58 -06:00
Zachary Pierson 8df3175f10 Updated Linux standard library to convert c strs 2017-02-12 17:22:27 -06:00
Zachary Pierson ebb10e5597 One of the warning flags was misspelled. Oops! 2017-02-12 16:09:21 -06:00
Zachary Pierson 047f883078 Updated warning removal list, and made system_exec_command_line_app in main.c return the exit code. 2017-02-12 16:08:09 -06:00
Zachary Pierson 320c22e08a Merge https://github.com/gingerBill/Odin 2017-02-12 16:04:13 -06:00
Zachary Pierson a9398bf30f Tested MacOS. If a commit doesn't follow in 15 minutes, Linux works too! 2017-02-12 00:21:25 -06:00
Zachary Pierson 7829421085 Fixed Windows (updated gb.h) | Need to test on MacOS and Linux now! 2017-02-11 23:52:56 -06:00
Zachary Pierson c50aabd916 Merging from gingerBill's master 2017-02-11 23:35:07 -06:00
Zachary Pierson 3f3122bccc Temporary fix for an Odin bug. 2017-02-11 18:54:54 -06:00
Zachary Pierson fc1a006de1 Added support for reading files on MacOS and Linux 2017-02-11 17:24:47 -06:00
Zachary Pierson 754b368140 Added dynamic library loading to Linux and MacOS's standard libraries. 2017-02-11 15:09:53 -06:00
Zachary Pierson a49e888ce6 Merge https://github.com/gingerBill/Odin 2017-02-11 13:48:16 -06:00
Zac Pierson 99c663d9f3 Questioning whether MacOS libraries should be .dylib or .so 2017-02-11 01:10:03 -06:00
Zachary Pierson afac95e092 Oh, I left math.odin open when I merged gingerBill's changes. Oops. Updated to his version. 2017-02-11 00:33:12 -06:00
Zachary Pierson 05486f9fa3 I'm not sure what I changed here, to be honest. I've ctrl-z'd everything, but git's still complaining. 2017-02-11 00:30:04 -06:00
Zachary Pierson cad46ae51c Merge https://github.com/gingerBill/Odin 2017-02-10 23:41:23 -06:00
Zachary Pierson 3424b2badd Added ability to use -framework on MacOS 2017-02-10 23:33:30 -06:00
Zachary Pierson 3445a28c4a Code quality upkeep. Fixed a broken thread finding assembly instruction in gb.h 2017-02-09 01:40:45 -06:00
Zac Pierson 7f6b83d50c Fixed gb.h - the file handle for /proc/cpuinfo is needed to read chars. 2017-02-08 11:59:54 -06:00
Zac Pierson 72d4bfb32a Merge https://github.com/gingerBill/Odin 2017-02-08 11:50:33 -06:00
Zachary Pierson 37f7630a9e Updated README.md to reflect Linux's dependancy on clang for now. 2017-02-07 23:33:36 -06:00
Zachary Pierson 73c5c5d5d3 Linker on MacOS and GNU/Linux now includes foreign_system_libraries. Fixed foreign_system_library not respecting 'when' condition. 2017-02-07 23:21:52 -06:00
Zac Pierson 584869730a Linux can build now! Woo! 2017-02-07 15:07:20 -06:00
Zachary Pierson 90ab448bca Modified the test program to see where the compiler inserted the code. 2017-02-07 12:26:15 -06:00
Zachary Pierson 8becbdc1b2 Added a very basic Linux standard library shamelessly stolen from the MacOS one.
Made Linux (almost) work. The generated binaries segfault, but it's so close I can almost taste it.
2017-02-07 00:28:21 -06:00
Zachary Pierson eeeb90c441 MacOS is able to run Hello World! 2017-02-06 21:47:58 -06:00
Zac Pierson 6efd400c98 Updated build script in an attempt to track down a segfault. It's not helping, though. 2017-02-06 15:45:51 -06:00
Zac Pierson 5cfa4ba580 Added Linux functions throughout the code, but it segfaults. 2017-02-06 12:26:41 -06:00
58 changed files with 11195 additions and 4573 deletions
+9 -4
View File
@@ -251,8 +251,13 @@ paket-files/
# Project Specific # Project Specific
# - Windows
*.sln *.sln
!misc/llvm-bim/lli.exe builds/
!misc/llvm-bim/opt.exe bin/
builds *.exe
bin
# - Linux/MacOS
odin
odin.dSYM
+22 -6
View File
@@ -1,4 +1,4 @@
<img src="logo-slim.png" alt="Odin logo" height="74"> <img src="misc/logo-slim.png" alt="Odin logo" height="74">
# The Odin Programming Language # The Odin Programming Language
@@ -10,6 +10,8 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
* metaprogramming * metaprogramming
* designed for good programmers * designed for good programmers
Website: [https://odin.handmade.network/](https://odin.handmade.network/)
## Demonstrations: ## Demonstrations:
* First Talk & Demo * First Talk & Demo
- [Talk](https://youtu.be/TMCkT-uASaE?t=338) - [Talk](https://youtu.be/TMCkT-uASaE?t=338)
@@ -19,14 +21,28 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s) * [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) * [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8) * [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
## Requirements to build and run ## Requirements to build and run
* Windows - Windows
* x86-64 * x86-64
* MSVC 2015 installed (C99 support) * MSVC 2015 installed (C99 support)
* Requires MSVC's link.exe as the linker * [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe`
- run `vcvarsall.bat` to setup the path * Requires MSVC's link.exe as the linker
* run `vcvarsall.bat` to setup the path
- MacOS
* x86-64
* LLVM explicitly installed (`brew install llvm`)
* XCode installed (for the linker)
- GNU/Linux
* x86-64
* Build tools (ld)
* LLVM installed
* Clang installed (temporary - this is calling the linker for now)
## Warnings ## Warnings
+4 -8
View File
@@ -45,15 +45,11 @@ del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.c" ^ cl %compiler_settings% "src\main.c" ^
/link %linker_settings% -OUT:%exe_name% ^ /link %linker_settings% -OUT:%exe_name% ^
&& odin run code/demo.odin && odin run code/demo.odin
rem && odin build code/Jaze/src/main.odin rem && odin build code/metagen.odin ^
rem && odin build_dll code/example.odin ^ rem && call "code\metagen.exe" "src\ast_nodes.metagen"
rem odin run code/demo.odin rem && odin run code/Jaze/src/main.odin
rem pushd src\asm del *.obj > NUL 2> NUL
rem nasm hellope.asm -fwin64 -o hellope.obj ^
rem && cl /nologo hellope.obj /link kernel32.lib /entry:main ^
rem && hellope.exe
rem popd
:end_of_build :end_of_build
Executable
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
release_mode=0
warnings_to_disable="-std=c11 -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined"
libraries="-pthread -ldl -lm"
other_args=""
compiler="clang"
if [ "$release_mode" -eq "0" ]; then
other_args="${other_args} -g -fno-inline-functions"
fi
if [[ "$(uname)" == "Darwin" ]]; then
# Set compiler to clang on MacOS
# MacOS provides a symlink to clang called gcc, but it's nice to be explicit here.
compiler="clang"
other_args="${other_args} -liconv"
fi
${compiler} src/main.c ${warnings_to_disable} ${libraries} ${other_args} -o odin
./odin run code/demo.odin
+10 -159
View File
@@ -1,167 +1,18 @@
#import "fmt.odin"; #import "fmt.odin";
#import "atomic.odin";
#import "hash.odin";
#import "math.odin";
#import "mem.odin";
#import "opengl.odin";
#import "os.odin";
#import "strconv.odin";
#import "sync.odin";
main :: proc() { main :: proc() {
// buf: [64]byte; immutable program := "+ + * - /";
// // len := strconv.generic_ftoa(buf[..], 123.5431, 'f', 4, 64); accumulator := 0;
// x := 624.123;
// s := strconv.format_float(buf[..], x, 'f', 6, 64);
// fmt.println(s);
// fmt.printf("%3d\n", 102);
s := new_slice(int, 0, 10); for token in program {
append(s, 1, 2, 6, 3, 6, 5, 5, 5, 5, 1, 2); match token {
fmt.println(s); case '+': accumulator += 1;
case '-': accumulator -= 1;
case '*': accumulator *= 2;
when false { case '/': accumulator /= 2;
/* default: // Ignore everything else
Version 0.1.1
Added:
* Dynamic Arrays `[dynamic]Type`
* Dynamic Maps `map[Key]Value`
* Dynamic array and map literals
* Custom struct alignemnt `struct #align 8 { bar: i8 }`
* Allow `_` in numbers
* Variadic `append`
* fmt.sprint*
* Entities prefixes with an underscore do not get exported on imports
* Overloaded `free` for pointers, slices, strings, dynamic arrays, and dynamic maps
* enum types have an implict `names` field, a []string of all the names in that enum
* immutable variables are "completely immutable" - rules need a full explanation
* `slice_to_bytes` - convert any slice to a slice of bytes
* `union_cast` allows for optional ok check
* Record type field `names` (struct/raw_union/enum)
* ?: ternary operator
* Unions with variants and common fields
* New built-in procedures
- `delete` to delete map entries `delete(m, key)`
- `clear` to clear dynamic maps and arrays `clear(map_or_array)`
- `reserve` to reserve space for the dynamic maps and arrays `reserve(map_or_array)`
* Unexported entities and fields using an underscore prefix
Removed:
* Maybe/option types
* Remove `type` keyword and other "reserved" keywords
* `compile_assert` and `assert`return the value of the condition for semantic reasons
Changed:
* thread_local -> #thread_local
* #include -> #load
* Files only get checked if they are actually used
* match x in y {} // For type match statements
* Version numbering now starts from 0.1.0 and uses the convention:
- major.minor.patch
* Core library additions to Windows specific stuff
Fixes:
* Many fmt.* fixes
* Overloading bug due to comparison of named types
* Overloading bug due to `#import .` collision
* disallow a `cast` from pointers of unions
* Minor bugs in generated IR code for slices
To come very Soon:
* Linux and OS X builds (unofficial ones do exist already)
*/
{
}
{
Fruit :: enum {
APPLE,
BANANA,
COCONUT,
} }
fmt.println(Fruit.names);
} }
{ fmt.printf("The program \"%s\" calculates the value %d\n", program, accumulator);
m: map[f32]int;
reserve(m, 16);
defer free(m);
m[1.0] = 1278;
m[2.0] = 7643;
m[3.0] = 564;
_, ok := m[3.0];
c := m[3.0];
assert(ok && c == 564);
fmt.print("map[");
i := 0;
for val, key in m {
if i > 0 {
fmt.print(", ");
}
fmt.printf("%v=%v", key, val);
i += 1;
}
fmt.println("]");
}
{
m := map[string]u32{
"a" = 56,
"b" = 13453,
"c" = 7654,
};
defer free(m);
c := m["c"];
_, ok := m["c"];
assert(ok && c == 7654);
fmt.println(m);
}
{
x: [dynamic]f64;
reserve(x, 16);
defer free(x);
append(x, 2_000_000.500_000, 3, 5, 7);
for p, i in x {
if i > 0 { fmt.print(", "); }
fmt.print(p);
}
fmt.println();
}
{
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
defer free(x);
fmt.println(x);
}
{
Vec3 :: [vector 3]f32;
x := Vec3{1, 2, 3};
y := Vec3{4, 5, 6};
fmt.println(x < y);
fmt.println(x + y);
fmt.println(x - y);
fmt.println(x * y);
fmt.println(x / y);
for i in x {
fmt.println(i);
}
compile_assert(size_of([vector 7]bool) == size_of([7]bool));
compile_assert(size_of([vector 7]i32) == size_of([7]i32));
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
}
} }
}
+46 -39
View File
@@ -1,4 +1,5 @@
#import "win32.odin" when ODIN_OS == "windows"; #import win32 "sys/windows.odin" when ODIN_OS == "windows";
#import wgl "sys/wgl.odin" when ODIN_OS == "windows";
#import "fmt.odin"; #import "fmt.odin";
#import "math.odin"; #import "math.odin";
#import "os.odin"; #import "os.odin";
@@ -12,11 +13,11 @@ time_now :: proc() -> f64 {
counter: i64; counter: i64;
win32.QueryPerformanceCounter(^counter); win32.QueryPerformanceCounter(^counter);
result := counter as f64 / win32_perf_count_freq as f64; result := cast(f64)counter / cast(f64)win32_perf_count_freq;
return result; return result;
} }
win32_print_last_error :: proc() { win32_print_last_error :: proc() {
err_code := win32.GetLastError() as int; err_code := cast(int)win32.GetLastError();
if err_code != 0 { if err_code != 0 {
fmt.println("GetLastError: %", err_code); fmt.println("GetLastError: %", err_code);
} }
@@ -24,42 +25,42 @@ win32_print_last_error :: proc() {
// Yuk! // Yuk!
to_c_string :: proc(s: string) -> []u8 { to_c_string :: proc(s: string) -> []u8 {
c_str := new_slice(u8, s.count+1); c_str := make([]u8, len(s)+1);
copy(c_str, s as []byte); copy(c_str, cast([]byte)s);
c_str[s.count] = 0; c_str[len(s)] = 0;
return c_str; return c_str;
} }
Window :: struct { Window :: struct {
width, height: int; width, height: int,
wc: win32.WNDCLASSEXA; wc: win32.WndClassExA,
dc: win32.HDC; dc: win32.Hdc,
hwnd: win32.HWND; hwnd: win32.Hwnd,
opengl_context, rc: win32.HGLRC; opengl_context, rc: wgl.Hglrc,
c_title: []u8; c_title: []u8,
} }
make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC) -> (Window, bool) { make_window :: proc(title: string, msg, height: int, window_proc: win32.Wnd_Proc) -> (Window, bool) {
using win32; using win32;
w: Window; w: Window;
w.width, w.height = msg, height; w.width, w.height = msg, height;
class_name := "Win32-Odin-Window\x00"; class_name := "Win32-Odin-Window\x00";
c_class_name := class_name.data; c_class_name := ^class_name[0];
if title[title.count-1] != 0 { if title[len(title)-1] != 0 {
w.c_title = to_c_string(title); w.c_title = to_c_string(title);
} else { } else {
w.c_title = title as []u8; w.c_title = cast([]u8)title;
} }
instance := GetModuleHandleA(nil); instance := GetModuleHandleA(nil);
w.wc = WNDCLASSEXA{ w.wc = WndClassExA{
size = size_of(WNDCLASSEXA) as u32, size = size_of(WndClassExA),
style = CS_VREDRAW | CS_HREDRAW, style = CS_VREDRAW | CS_HREDRAW,
instance = instance as HINSTANCE, instance = cast(Hinstance)instance,
class_name = c_class_name, class_name = c_class_name,
wnd_proc = window_proc, wnd_proc = window_proc,
}; };
@@ -70,10 +71,10 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
} }
w.hwnd = CreateWindowExA(0, w.hwnd = CreateWindowExA(0,
c_class_name, w.c_title.data, c_class_name, ^w.c_title[0],
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
w.width as i32, w.height as i32, cast(i32)w.width, cast(i32)w.height,
nil, nil, instance, nil); nil, nil, instance, nil);
if w.hwnd == nil { if w.hwnd == nil {
@@ -85,7 +86,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
{ {
pfd := PIXELFORMATDESCRIPTOR{ pfd := PIXELFORMATDESCRIPTOR{
size = size_of(PIXELFORMATDESCRIPTOR) as u32, size = size_of(PIXELFORMATDESCRIPTOR),
version = 1, version = 1,
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
pixel_type = PFD_TYPE_RGBA, pixel_type = PFD_TYPE_RGBA,
@@ -97,19 +98,20 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
}; };
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil); SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil);
w.opengl_context = wglCreateContext(w.dc); w.opengl_context = wgl.CreateContext(w.dc);
wglMakeCurrent(w.dc, w.opengl_context); wgl.MakeCurrent(w.dc, w.opengl_context);
attribs := [8]i32{ attribs := [8]i32{
WGL_CONTEXT_MAJOR_VERSION_ARB, 2, wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
WGL_CONTEXT_MINOR_VERSION_ARB, 1, wgl.CONTEXT_MINOR_VERSION_ARB, 1,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
0, // NOTE(bill): tells the proc that this is the end of attribs 0, // NOTE(bill): tells the proc that this is the end of attribs
}; };
wglCreateContextAttribsARB := wglGetProcAddress(("wglCreateContextAttribsARB\x00" as string).data) as wglCreateContextAttribsARBType; wgl_str := "wglCreateContextAttribsARB\x00";
w.rc = wglCreateContextAttribsARB(w.dc, 0, ^attribs[0]); wglCreateContextAttribsARB := cast(wgl.Create_Context_Attribs_ARB_Type)wgl.GetProcAddress(^wgl_str[0]);
wglMakeCurrent(w.dc, w.rc); w.rc = wglCreateContextAttribsARB(w.dc, nil, ^attribs[0]);
wgl.MakeCurrent(w.dc, w.rc);
SwapBuffers(w.dc); SwapBuffers(w.dc);
} }
@@ -117,7 +119,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
} }
destroy_window :: proc(w: ^Window) { destroy_window :: proc(w: ^Window) {
free(w.c_title.data); free(w.c_title);
} }
display_window :: proc(w: ^Window) { display_window :: proc(w: ^Window) {
@@ -129,7 +131,7 @@ run :: proc() {
using win32; using win32;
using math; using math;
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline { win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT { if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
os.exit(0); os.exit(0);
return 0; return 0;
@@ -137,7 +139,7 @@ run :: proc() {
return DefWindowProcA(hwnd, msg, wparam, lparam); return DefWindowProcA(hwnd, msg, wparam, lparam);
} }
window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc); window, window_success := make_window("Odin Language Demo", 854, 480, cast(Wnd_Proc)win32_proc);
if !window_success { if !window_success {
return; return;
} }
@@ -153,10 +155,10 @@ run :: proc() {
for running { for running {
curr_time := time_now(); curr_time := time_now();
dt := (curr_time - prev_time) as f32; dt := cast(f32)(curr_time - prev_time);
prev_time = curr_time; prev_time = curr_time;
msg: MSG; msg: Msg;
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 { for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
if msg.message == WM_QUIT { if msg.message == WM_QUIT {
running = false; running = false;
@@ -178,7 +180,7 @@ run :: proc() {
if is_key_down(Key_Code.UP) { v[1] += 1; } if is_key_down(Key_Code.UP) { v[1] += 1; }
if is_key_down(Key_Code.DOWN) { v[1] -= 1; } if is_key_down(Key_Code.DOWN) { v[1] -= 1; }
v = vec2_norm0(v); v = norm(v);
pos += v * Vec2{SPEED * dt}; pos += v * Vec2{SPEED * dt};
} }
@@ -188,8 +190,8 @@ run :: proc() {
gl.Clear(gl.COLOR_BUFFER_BIT); gl.Clear(gl.COLOR_BUFFER_BIT);
gl.LoadIdentity(); gl.LoadIdentity();
gl.Ortho(0, window.width as f64, gl.Ortho(0, cast(f64)window.width,
0, window.height as f64, 0, 1); 0, cast(f64)window.height, 0, 1);
draw_rect :: proc(x, y, w, h: f32) { draw_rect :: proc(x, y, w, h: f32) {
gl.Begin(gl.TRIANGLES); gl.Begin(gl.TRIANGLES);
@@ -207,9 +209,14 @@ run :: proc() {
draw_rect(pos.x, pos.y, 50, 50); draw_rect(pos.x, pos.y, 50, 50);
display_window(^window); display_window(^window);
ms_to_sleep := (16 - 1000*dt) as i32; ms_to_sleep := cast(i32)(16 - 1000*dt);
if ms_to_sleep > 0 { if ms_to_sleep > 0 {
win32.Sleep(ms_to_sleep); win32.Sleep(ms_to_sleep);
} }
} }
} }
main :: proc() {
run();
}
+8 -8
View File
@@ -136,7 +136,7 @@ bounds_checking :: proc() {
{ {
base: [10]int; base: [10]int;
s := base[2:6]; s := base[2..6];
a, b := -1, 6; a, b := -1, 6;
#no_bounds_check { #no_bounds_check {
@@ -164,7 +164,7 @@ type_introspection :: proc() {
info = type_info_of_val(x); // by value info = type_info_of_val(x); // by value
// See: runtime.odin // See: runtime.odin
match type i in info { match i in info {
case Type_Info.Integer: case Type_Info.Integer:
fmt.println("integer!"); fmt.println("integer!");
case Type_Info.Float: case Type_Info.Float:
@@ -174,7 +174,7 @@ type_introspection :: proc() {
} }
// Unsafe cast // Unsafe cast
integer_info := cast(^Type_Info.Integer)info; integer_info := cast(^Type_Info.Integer)cast(rawptr)info;
} }
{ {
@@ -263,12 +263,12 @@ crazy_introspection :: proc() {
} }
fruit_ti := type_info(Fruit); fruit_ti := type_info(Fruit);
name := (cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts name := (union_cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
info := cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts info, _ := union_cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
fmt.printf("% :: enum % {\n", name, info.base); fmt.printf("%s :: enum %T {\n", name, info.base);
for i := 0; i < info.values.count; i += 1 { for i := 0; i < len(info.values); i++ {
fmt.printf("\t%\t= %,\n", info.names[i], info.values[i]); fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
} }
fmt.printf("}\n"); fmt.printf("}\n");
File diff suppressed because it is too large Load Diff
+17 -17
View File
@@ -1,14 +1,14 @@
#import "fmt.odin" #import "fmt.odin";
#import "utf8.odin" #import "utf8.odin";
#import "hash.odin" #import "hash.odin";
#import "mem.odin" #import "mem.odin";
main :: proc() { main :: proc() {
{ // New Standard Library stuff { // New Standard Library stuff
s := "Hello" s := "Hello";
fmt.println(s, fmt.println(s,
utf8.valid_string(s), utf8.valid_string(s),
hash.murmur64(s.data, s.count)) hash.murmur64(cast([]byte)s));
// utf8.odin // utf8.odin
// hash.odin // hash.odin
@@ -19,15 +19,15 @@ main :: proc() {
} }
{ {
arena: mem.Arena arena: mem.Arena;
mem.init_arena_from_context(^arena, mem.megabytes(16)) // Uses default allocator mem.init_arena_from_context(^arena, mem.megabytes(16)); // Uses default allocator
defer mem.free_arena(^arena) defer mem.free_arena(^arena);
push_allocator mem.arena_allocator(^arena) { push_allocator mem.arena_allocator(^arena) {
x := new(int) x := new(int);
x^ = 1337 x^ = 1337;
fmt.println(x^) fmt.println(x^);
} }
/* /*
@@ -48,14 +48,14 @@ main :: proc() {
// You can also "push" a context // You can also "push" a context
c := current_context() // Create copy of the allocator c := context; // Create copy of the allocator
c.allocator = mem.arena_allocator(^arena) c.allocator = mem.arena_allocator(^arena);
push_context c { push_context c {
x := new(int) x := new(int);
x^ = 365 x^ = 365;
fmt.println(x^) fmt.println(x^);
} }
} }
+15 -13
View File
@@ -42,12 +42,12 @@ syntax :: proc() {
}; };
Thing2 :: struct {x: f32, y: int, z: ^[]int}; Thing2 :: struct {x: f32, y: int, z: ^[]int};
// Slice interals are now just a `ptr+count` // Slice interals are now just a `ptr+len+cap`
slice: []int; compile_assert(size_of_val(slice) == 2*size_of(int)); slice: []int; compile_assert(size_of_val(slice) == 3*size_of(int));
// Helper type - Help the reader understand what it is quicker // Helper type - Help the reader understand what it is quicker
My_Int :: type int; My_Int :: #type int;
My_Proc :: type proc(int) -> f32; My_Proc :: #type proc(int) -> f32;
// All declarations with : are either variable or constant // All declarations with : are either variable or constant
@@ -59,6 +59,7 @@ syntax :: proc() {
c_proc :: proc() { /* code here */ }; c_proc :: proc() { /* code here */ };
/*
x += 1; x += 1;
x -= 1; x -= 1;
// ++ and -- have been removed // ++ and -- have been removed
@@ -67,7 +68,7 @@ syntax :: proc() {
// Question: Should they be added again? // Question: Should they be added again?
// They were removed as they are redundant and statements, not expressions // They were removed as they are redundant and statements, not expressions
// like in C/C++ // like in C/C++
*/
// You can now build files as a `.dll` // You can now build files as a `.dll`
// `odin build_dll demo.odin` // `odin build_dll demo.odin`
@@ -85,7 +86,7 @@ syntax :: proc() {
Prefix_Type :: struct {x: int, y: f32, z: rawptr}; Prefix_Type :: struct {x: int, y: f32, z: rawptr};
thread_local my_tls: Prefix_Type; #thread_local my_tls: Prefix_Type;
prefixes :: proc() { prefixes :: proc() {
using var: Prefix_Type; using var: Prefix_Type;
@@ -98,7 +99,7 @@ prefixes :: proc() {
foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) { foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
// int_ptr = nil; // Not valid // int_ptr = nil; // Not valid
int_ptr^ = 123; // Is valid // int_ptr^ = 123; // Not valid
} }
@@ -154,6 +155,7 @@ foreign_procedures :: proc() {
} }
special_expressions :: proc() { special_expressions :: proc() {
/*
// Block expression // Block expression
x := { x := {
a: f32 = 123; a: f32 = 123;
@@ -168,7 +170,7 @@ special_expressions :: proc() {
// TODO: Type cohesion is not yet finished // TODO: Type cohesion is not yet finished
give 123; give 123;
}; // semicolon is required as it's an expression }; // 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 // This is allows for inline blocks of code and will be a useful feature to have when
// macros will be implemented into the language // macros will be implemented into the language
@@ -191,17 +193,17 @@ loops :: proc() {
break; break;
} }
for i in 0..<123 { // 123 exclusive for i in 0..123 { // 123 exclusive
} }
for i in 0...122 { // 122 inclusive for i in 0..123-1 { // 122 inclusive
} }
for val, idx in 12..<16 { for val, idx in 12..16 {
fmt.println(val, idx); fmt.println(val, idx);
} }
primes := [...]int{2, 3, 5, 7, 11, 13, 17, 19}; primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19};
for p in primes { for p in primes {
fmt.println(p); fmt.println(p);
@@ -224,7 +226,7 @@ loops :: proc() {
when false { when false {
for i, size := 0; i < name.count; i += size { for i, size := 0; i < name.count; i += size {
r: rune; r: rune;
r, size = utf8.decode_rune(name[i:]); r, size = utf8.decode_rune(name[i..]);
fmt.printf("%r\n", r); fmt.printf("%r\n", r);
} }
} }
+211 -124
View File
@@ -2,9 +2,8 @@
#import "os.odin"; #import "os.odin";
#import "fmt.odin"; #import "fmt.odin";
#import "mem.odin";
#import "utf8.odin"; #import "utf8.odin";
#import "hash.odin"; #import "raw.odin";
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a // IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
// #shared_global_scope due to the internals of the compiler. // #shared_global_scope due to the internals of the compiler.
@@ -29,24 +28,29 @@ Calling_Convention :: enum {
Type_Info_Record :: struct #ordered { Type_Info_Record :: struct #ordered {
types: []^Type_Info, types: []^Type_Info,
names: []string, names: []string,
offsets: []int, // offsets may not be used in tuples offsets: []int, // offsets may not be used in tuples
size: int, // in bytes usings: []bool, // usings may not be used in tuples
align: int, // in bytes
packed: bool, packed: bool,
ordered: bool, ordered: bool,
custom_align: bool, custom_align: bool,
} }
Type_Info :: union { Type_Info :: union {
size: int,
align: int,
Named{name: string, base: ^Type_Info}, Named{name: string, base: ^Type_Info},
Integer{size: int, signed: bool}, Integer{signed: bool},
Float{size: int}, Float{},
Complex{},
Quaternion{},
String{}, String{},
Boolean{}, Boolean{},
Any{}, Any{},
Pointer{ Pointer{
elem: ^Type_Info, // nil -> rawptr elem: ^Type_Info, // nil -> rawptr
}, },
Atomic{elem: ^Type_Info},
Procedure{ Procedure{
params: ^Type_Info, // Type_Info.Tuple params: ^Type_Info, // Type_Info.Tuple
results: ^Type_Info, // Type_Info.Tuple results: ^Type_Info, // Type_Info.Tuple
@@ -60,20 +64,18 @@ Type_Info :: union {
}, },
Dynamic_Array{elem: ^Type_Info, elem_size: int}, Dynamic_Array{elem: ^Type_Info, elem_size: int},
Slice {elem: ^Type_Info, elem_size: int}, Slice {elem: ^Type_Info, elem_size: int},
Vector {elem: ^Type_Info, elem_size, count, align: int}, Vector {elem: ^Type_Info, elem_size, count: int},
Tuple {using record: Type_Info_Record}, // Only really used for procedures Tuple {using record: Type_Info_Record}, // Only really used for procedures
Struct {using record: Type_Info_Record}, Struct {using record: Type_Info_Record},
Raw_Union {using record: Type_Info_Record}, Raw_Union {using record: Type_Info_Record},
Union{ Union{
common_fields: struct { common_fields: struct {
types: []^Type_Info, types: []^Type_Info,
names: []string, names: []string,
offsets: []int, // offsets may not be used in tuples offsets: []int, // offsets may not be used in tuples
}, },
variant_names: []string, variant_names: []string,
variant_types: []^Type_Info, variant_types: []^Type_Info,
size: int,
align: int,
}, },
Enum{ Enum{
base: ^Type_Info, base: ^Type_Info,
@@ -93,6 +95,9 @@ Type_Info :: union {
// This will be set by the compiler // This will be set by the compiler
__type_table: []Type_Info; __type_table: []Type_Info;
__argv__: ^^byte;
__argc__: i32;
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info { type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
if info == nil { if info == nil {
return nil; return nil;
@@ -128,11 +133,6 @@ __debug_trap :: proc() #foreign __llvm_core "llvm.debugtrap";
__trap :: proc() #foreign __llvm_core "llvm.trap"; __trap :: proc() #foreign __llvm_core "llvm.trap";
read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter"; 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) // IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
Allocator_Mode :: enum u8 { Allocator_Mode :: enum u8 {
@@ -149,6 +149,7 @@ Allocator :: struct #ordered {
data: rawptr, data: rawptr,
} }
Context :: struct #ordered { Context :: struct #ordered {
thread_id: int, thread_id: int,
@@ -165,7 +166,7 @@ DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
__check_context :: proc() { __check_context :: proc() {
c := ^__context; c := &__context;
if c.allocator.procedure == nil { if c.allocator.procedure == nil {
c.allocator = default_allocator(); c.allocator = default_allocator();
@@ -233,7 +234,7 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
return nil; return nil;
} }
mem.copy(new_memory, old_memory, min(old_size, new_size));; __mem_copy(new_memory, old_memory, min(old_size, new_size));;
free(old_memory); free(old_memory);
return new_memory; return new_memory;
} }
@@ -279,20 +280,21 @@ default_allocator :: proc() -> Allocator {
__string_eq :: proc(a, b: string) -> bool { __string_eq :: proc(a, b: string) -> bool {
if a.count != b.count { if len(a) != len(b) {
return false; return false;
} }
if a.data == b.data { if len(a) == 0 {
return true;
}
if &a[0] == &b[0] {
return true; return true;
} }
return __string_cmp(a, b) == 0; return __string_cmp(a, b) == 0;
} }
__string_cmp :: proc(a, b: string) -> int { __string_cmp :: proc(a, b: string) -> int {
return mem.compare(cast([]byte)a, cast([]byte)b); return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
} }
__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); } __string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); }
@@ -302,6 +304,26 @@ __string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <=
__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; } __string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; }
__complex64_eq :: proc(a, b: complex64) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
__complex64_ne :: proc(a, b: complex64) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
__complex128_eq :: proc(a, b: complex128) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
__complex128_ne :: proc(a, b: complex128) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
__quaternion128_eq :: proc(a, b: quaternion128) -> bool #inline {
return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
}
__quaternion128_ne :: proc(a, b: quaternion128) -> bool #inline {
return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
}
__quaternion256_eq :: proc(a, b: quaternion256) -> bool #inline {
return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
}
__quaternion256_ne :: proc(a, b: quaternion256) -> bool #inline {
return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
}
__assert :: proc(file: string, line, column: int, msg: string) #inline { __assert :: proc(file: string, line, column: int, msg: string) #inline {
fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n", fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n",
file, line, column, msg); file, line, column, msg);
@@ -316,7 +338,7 @@ __bounds_check_error :: proc(file: string, line, column: int, index, count: int)
if 0 <= index && index < count { if 0 <= index && index < count {
return; return;
} }
fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..%d\n", fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..<%d\n",
file, line, column, index, count); file, line, column, index, count);
__debug_trap(); __debug_trap();
} }
@@ -325,21 +347,22 @@ __slice_expr_error :: proc(file: string, line, column: int, low, high, max: int)
if 0 <= low && low <= high && high <= max { if 0 <= low && low <= high && high <= max {
return; return;
} }
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..%d..%d]\n", fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..<%d..<%d]\n",
file, line, column, low, high, max); file, line, column, low, high, max);
__debug_trap(); __debug_trap();
} }
__substring_expr_error :: proc(file: string, line, column: int, low, high: int) { __substring_expr_error :: proc(file: string, line, column: int, low, high: int) {
if 0 <= low && low <= high { if 0 <= low && low <= high {
return; return;
} }
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d..%d]\n", fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d..<%d]\n",
file, line, column, low, high); file, line, column, low, high);
__debug_trap(); __debug_trap();
} }
__union_cast_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) { __type_assertion_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) {
if !ok { if !ok {
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid `union_cast` from %T to %T\n", fmt.fprintf(os.stderr, "%s(%d:%d) Invalid type_assertion from %T to %T\n",
file, line, column, from, to); file, line, column, from, to);
__debug_trap(); __debug_trap();
} }
@@ -350,40 +373,77 @@ __string_decode_rune :: proc(s: string) -> (rune, int) #inline {
} }
Raw_Any :: struct #ordered { __mem_set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
type_info: ^Type_Info, llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
data: rawptr, llvm_memset_64bit(data, byte(value), len, 1, false);
return data;
}
__mem_zero :: proc(data: rawptr, len: int) -> rawptr {
return __mem_set(data, 0, len);
}
__mem_copy :: proc(dst, src: rawptr, len: int) -> rawptr {
// NOTE(bill): This _must_ be implemented like C's memmove
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
llvm_memmove_64bit(dst, src, len, 1, false);
return dst;
}
__mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
// NOTE(bill): This _must_ be implemented like C's memcpy
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
llvm_memcpy_64bit(dst, src, len, 1, false);
return dst;
} }
Raw_String :: struct #ordered { __mem_compare :: proc(a, b: ^byte, n: int) -> int {
data: ^byte, for i in 0..<n {
count: int, match {
}; case (a+i)^ < (b+i)^:
return -1;
case (a+i)^ > (b+i)^:
return +1;
}
}
return 0;
}
Raw_Slice :: struct #ordered { __sqrt_f32 :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
data: rawptr, __sqrt_f64 :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
count: int, __abs_complex64 :: proc(x: complex64) -> f32 #inline {
capacity: int, r, i := real(x), imag(x);
}; return __sqrt_f32(r*r + i*i);
}
Raw_Dynamic_Array :: struct #ordered { __abs_complex128 :: proc(x: complex128) -> f64 #inline {
data: rawptr, r, i := real(x), imag(x);
count: int, return __sqrt_f64(r*r + i*i);
capacity: int, }
allocator: Allocator, __abs_quaternion128 :: proc(x: quaternion128) -> f32 #inline {
}; r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return __sqrt_f32(r*r + i*i + j*j + k*k);
Raw_Dynamic_Map :: struct #ordered { }
hashes: [dynamic]int, __abs_quaternion256 :: proc(x: quaternion256) -> f64 #inline {
entries: Raw_Dynamic_Array, r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
}; return __sqrt_f64(r*r + i*i + j*j + k*k);
}
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capacity: int) -> bool {
array := cast(^Raw_Dynamic_Array)array_;
if capacity <= array.capacity { __dynamic_array_make :: proc(array_: rawptr, elem_size, elem_align: int, len, cap: int) {
array := ^raw.Dynamic_Array(array_);
__check_context();
array.allocator = context.allocator;
assert(array.allocator.procedure != nil);
if cap > 0 {
__dynamic_array_reserve(array_, elem_size, elem_align, cap);
array.len = len;
}
}
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, cap: int) -> bool {
array := ^raw.Dynamic_Array(array_);
if cap <= array.cap {
return true; return true;
} }
@@ -393,8 +453,8 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capa
} }
assert(array.allocator.procedure != nil); assert(array.allocator.procedure != nil);
old_size := array.capacity * elem_size; old_size := array.cap * elem_size;
new_size := capacity * elem_size; new_size := cap * elem_size;
allocator := array.allocator; allocator := array.allocator;
new_data := allocator.procedure(allocator.data, Allocator_Mode.RESIZE, new_size, elem_align, array.data, old_size, 0); new_data := allocator.procedure(allocator.data, Allocator_Mode.RESIZE, new_size, elem_align, array.data, old_size, 0);
@@ -403,83 +463,102 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capa
} }
array.data = new_data; array.data = new_data;
array.capacity = capacity; array.cap = cap;
return true; return true;
} }
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int) -> bool {
array := ^raw.Dynamic_Array(array_);
ok := __dynamic_array_reserve(array_, elem_size, elem_align, len);
if ok {
array.len = len;
}
return ok;
}
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int, __dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
items: rawptr, item_count: int) -> int { items: rawptr, item_count: int) -> int {
array := cast(^Raw_Dynamic_Array)array_; array := ^raw.Dynamic_Array(array_);
if item_count <= 0 || items == nil { if item_count <= 0 || items == nil {
return array.count; return array.len;
} }
ok := true; ok := true;
if array.capacity <= array.count+item_count { if array.cap <= array.len+item_count {
capacity := 2 * array.capacity + max(8, item_count); cap := 2 * array.cap + max(8, item_count);
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity); ok = __dynamic_array_reserve(array, elem_size, elem_align, cap);
} }
if !ok { if !ok {
// TODO(bill): Better error handling for failed reservation // TODO(bill): Better error handling for failed reservation
return array.count; return array.len;
} }
data := cast(^byte)array.data; data := ^byte(array.data);
assert(data != nil); assert(data != nil);
mem.copy(data + (elem_size*array.count), items, elem_size * item_count); __mem_copy(data + (elem_size*array.len), items, elem_size * item_count);
array.count += item_count; array.len += item_count;
return array.count; return array.len;
} }
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int) -> int { __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int) -> int {
array := cast(^Raw_Dynamic_Array)array_; array := ^raw.Dynamic_Array(array_);
ok := true; ok := true;
if array.capacity <= array.count+1 { if array.cap <= array.len+1 {
capacity := 2 * array.capacity + max(8, 1); cap := 2 * array.cap + max(8, 1);
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity); ok = __dynamic_array_reserve(array, elem_size, elem_align, cap);
} }
if !ok { if !ok {
// TODO(bill): Better error handling for failed reservation // TODO(bill): Better error handling for failed reservation
return array.count; return array.len;
} }
data := cast(^byte)array.data; data := ^byte(array.data);
assert(data != nil); assert(data != nil);
mem.zero(data + (elem_size*array.count), elem_size); __mem_zero(data + (elem_size*array.len), elem_size);
array.count++; array.len++;
return array.count; return array.len;
} }
__slice_append :: proc(slice_: rawptr, elem_size, elem_align: int, __slice_append :: proc(slice_: rawptr, elem_size, elem_align: int,
items: rawptr, item_count: int) -> int { items: rawptr, item_count: int) -> int {
slice := cast(^Raw_Slice)slice_; slice := ^raw.Slice(slice_);
if item_count <= 0 || items == nil { if item_count <= 0 || items == nil {
return slice.count; return slice.len;
} }
item_count = min(slice.capacity-slice.count, item_count); item_count = min(slice.cap-slice.len, item_count);
if item_count > 0 { if item_count > 0 {
data := cast(^byte)slice.data; data := ^byte(slice.data);
assert(data != nil); assert(data != nil);
mem.copy(data + (elem_size*slice.count), items, elem_size * item_count); __mem_copy(data + (elem_size*slice.len), items, elem_size * item_count);
slice.count += item_count; slice.len += item_count;
} }
return slice.count; return slice.len;
} }
// Map stuff // Map stuff
__default_hash :: proc(data: []byte) -> u64 { __default_hash :: proc(data: []byte) -> u64 {
return hash.fnv64a(data); fnv64a :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325;
for b in data {
h = (h ~ u64(b)) * 0x100000001b3;
}
return h;
}
return fnv64a(data);
} }
__default_hash_string :: proc(s: string) -> u64 { __default_hash_string :: proc(s: string) -> u64 {
return __default_hash(cast([]byte)s); return __default_hash([]byte(s));
} }
__INITIAL_MAP_CAP :: 16;
__Map_Key :: struct #ordered { __Map_Key :: struct #ordered {
hash: u64, hash: u64,
str: string, str: string,
@@ -500,39 +579,41 @@ __Map_Entry_Header :: struct #ordered {
} }
__Map_Header :: struct #ordered { __Map_Header :: struct #ordered {
m: ^Raw_Dynamic_Map, m: ^raw.Dynamic_Map,
is_key_string: bool, is_key_string: bool,
entry_size: int, entry_size: int,
entry_align: int, entry_align: int,
value_offset: int, value_offset: int,
value_size: int,
} }
__dynamic_map_reserve :: proc(using header: __Map_Header, capacity: int) -> bool { __dynamic_map_reserve :: proc(using header: __Map_Header, cap: int) {
h := __dynamic_array_reserve(^m.hashes, size_of(int), align_of(int), capacity); __dynamic_array_reserve(&m.hashes, size_of(int), align_of(int), cap);
e := __dynamic_array_reserve(^m.entries, entry_size, entry_align, capacity); __dynamic_array_reserve(&m.entries, entry_size, entry_align, cap);
return h && e;
} }
__dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) { __dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
new_header := header; new_header: __Map_Header = header;
nm: Raw_Dynamic_Map; nm: raw.Dynamic_Map;
new_header.m = ^nm; new_header.m = &nm;
reserve(nm.hashes, new_count); header_hashes := ^raw.Dynamic_Array(&header.m.hashes);
nm.hashes.count = nm.hashes.capacity; nm_hashes := ^raw.Dynamic_Array(&nm.hashes);
__dynamic_array_reserve(^nm.entries, entry_size, entry_align, m.entries.count);
for _, i in nm.hashes { __dynamic_array_resize(nm_hashes, size_of(int), align_of(int), new_count);
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len);
for i in 0..<new_count {
nm.hashes[i] = -1; nm.hashes[i] = -1;
} }
for i := 0; i < nm.entries.count; i++ { for i := 0; i < m.entries.len; i++ {
entry_header := __dynamic_map_get_entry(new_header, i); if len(nm.hashes) == 0 {
data := cast(^byte)entry_header;
if nm.hashes.count == 0 {
__dynamic_map_grow(new_header); __dynamic_map_grow(new_header);
} }
entry_header := __dynamic_map_get_entry(header, i);
data := ^byte(entry_header);
fr := __dynamic_map_find(new_header, entry_header.key); fr := __dynamic_map_find(new_header, entry_header.key);
j := __dynamic_map_add_entry(new_header, entry_header.key); j := __dynamic_map_add_entry(new_header, entry_header.key);
if fr.entry_prev < 0 { if fr.entry_prev < 0 {
@@ -544,13 +625,14 @@ __dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
e := __dynamic_map_get_entry(new_header, j); e := __dynamic_map_get_entry(new_header, j);
e.next = fr.entry_index; e.next = fr.entry_index;
ndata := cast(^byte)e; ndata := ^byte(e);
mem.copy(ndata+value_offset, data+value_offset, entry_size-value_offset); __mem_copy(ndata+value_offset, data+value_offset, value_size);
if __dynamic_map_full(new_header) { if __dynamic_map_full(new_header) {
__dynamic_map_grow(new_header); __dynamic_map_grow(new_header);
} }
} }
free_ptr_with_allocator(header.m.hashes.allocator, header.m.hashes.data); free_ptr_with_allocator(header_hashes.allocator, header_hashes.data);
free_ptr_with_allocator(header.m.entries.allocator, header.m.entries.data); free_ptr_with_allocator(header.m.entries.allocator, header.m.entries.data);
header.m^ = nm; header.m^ = nm;
} }
@@ -558,7 +640,7 @@ __dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
__dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr { __dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr {
index := __dynamic_map_find(h, key).entry_index; index := __dynamic_map_find(h, key).entry_index;
if index >= 0 { if index >= 0 {
data := cast(^byte)__dynamic_map_get_entry(h, index); data := ^byte(__dynamic_map_get_entry(h, index));
val := data + h.value_offset; val := data + h.value_offset;
return val; return val;
} }
@@ -567,10 +649,14 @@ __dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr {
__dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr) { __dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr) {
index: int; index: int;
assert(value != nil);
if m.hashes.count == 0 {
if len(m.hashes) == 0 {
__dynamic_map_reserve(h, __INITIAL_MAP_CAP);
__dynamic_map_grow(h); __dynamic_map_grow(h);
} }
fr := __dynamic_map_find(h, key); fr := __dynamic_map_find(h, key);
if fr.entry_index >= 0 { if fr.entry_index >= 0 {
index = fr.entry_index; index = fr.entry_index;
@@ -584,9 +670,10 @@ __dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr)
} }
} }
{ {
data := cast(^byte)__dynamic_map_get_entry(h, index); e := __dynamic_map_get_entry(h, index);
val := data+value_offset; e.key = key;
mem.copy(val, value, entry_size-value_offset); val := ^byte(e) + value_offset;
__mem_copy(val, value, value_size);
} }
if __dynamic_map_full(h) { if __dynamic_map_full(h) {
@@ -596,12 +683,12 @@ __dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr)
__dynamic_map_grow :: proc(using h: __Map_Header) { __dynamic_map_grow :: proc(using h: __Map_Header) {
new_count := 2*m.entries.count + 8; new_count := max(2*m.entries.cap + 8, __INITIAL_MAP_CAP);
__dynamic_map_rehash(h, new_count); __dynamic_map_rehash(h, new_count);
} }
__dynamic_map_full :: proc(using h: __Map_Header) -> bool { __dynamic_map_full :: proc(using h: __Map_Header) -> bool {
return cast(int)(0.75 * cast(f64)m.hashes.count) <= m.entries.count; return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
} }
@@ -617,8 +704,8 @@ __dynamic_map_hash_equal :: proc(h: __Map_Header, a, b: __Map_Key) -> bool {
__dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_Result { __dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_Result {
fr := __Map_Find_Result{-1, -1, -1}; fr := __Map_Find_Result{-1, -1, -1};
if m.hashes.count > 0 { if len(m.hashes) > 0 {
fr.hash_index = cast(int)(key.hash % cast(u64)m.hashes.count); fr.hash_index = int(key.hash % u64(len(m.hashes)));
fr.entry_index = m.hashes[fr.hash_index]; fr.entry_index = m.hashes[fr.hash_index];
for fr.entry_index >= 0 { for fr.entry_index >= 0 {
entry := __dynamic_map_get_entry(h, fr.entry_index); entry := __dynamic_map_get_entry(h, fr.entry_index);
@@ -633,8 +720,8 @@ __dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_
} }
__dynamic_map_add_entry :: proc(using h: __Map_Header, key: __Map_Key) -> int { __dynamic_map_add_entry :: proc(using h: __Map_Header, key: __Map_Key) -> int {
prev := m.entries.count; prev := m.entries.len;
c := __dynamic_array_append_nothing(^m.entries, entry_size, entry_align); c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align);
if c != prev { if c != prev {
end := __dynamic_map_get_entry(h, c-1); end := __dynamic_map_get_entry(h, c-1);
end.key = key; end.key = key;
@@ -652,8 +739,8 @@ __dynamic_map_delete :: proc(using h: __Map_Header, key: __Map_Key) {
} }
__dynamic_map_get_entry :: proc(using h: __Map_Header, index: int) -> ^__Map_Entry_Header { __dynamic_map_get_entry :: proc(using h: __Map_Header, index: int) -> ^__Map_Entry_Header {
data := cast(^byte)m.entries.data + index*entry_size; data := ^byte(m.entries.data) + index*entry_size;
return cast(^__Map_Entry_Header)data; return ^__Map_Entry_Header(data);
} }
__dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) { __dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
@@ -663,10 +750,10 @@ __dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
__dynamic_map_get_entry(h, fr.entry_prev).next = __dynamic_map_get_entry(h, fr.entry_index).next; __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 { if fr.entry_index == m.entries.len-1 {
m.entries.count--; m.entries.len--;
} }
mem.copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.count-1), entry_size); __mem_copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.len-1), entry_size);
last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key); last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key);
if last.entry_prev >= 0 { if last.entry_prev >= 0 {
__dynamic_map_get_entry(h, last.entry_prev).next = fr.entry_index; __dynamic_map_get_entry(h, last.entry_prev).next = fr.entry_index;
-100
View File
@@ -1,100 +0,0 @@
// TODO(bill): Use assembly instead here to implement atomics
// Inline vs external file?
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
_ := compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
yield_thread :: proc() { win32.mm_pause(); }
mfence :: proc() { win32.ReadWriteBarrier(); }
sfence :: proc() { win32.WriteBarrier(); }
lfence :: proc() { win32.ReadBarrier(); }
load :: proc(a: ^i32) -> i32 {
return a^;
}
store :: proc(a: ^i32, value: i32) {
a^ = value;
}
compare_exchange :: proc(a: ^i32, expected, desired: i32) -> i32 {
return win32.InterlockedCompareExchange(a, desired, expected);
}
exchanged :: proc(a: ^i32, desired: i32) -> i32 {
return win32.InterlockedExchange(a, desired);
}
fetch_add :: proc(a: ^i32, operand: i32) -> i32 {
return win32.InterlockedExchangeAdd(a, operand);
}
fetch_and :: proc(a: ^i32, operand: i32) -> i32 {
return win32.InterlockedAnd(a, operand);
}
fetch_or :: proc(a: ^i32, operand: i32) -> i32 {
return win32.InterlockedOr(a, operand);
}
spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
old_value := compare_exchange(a, 1, 0);
counter := 0;
for old_value != 0 && (time_out < 0 || counter < time_out) {
counter++;
yield_thread();
old_value = compare_exchange(a, 1, 0);
mfence();
}
return old_value == 0;
}
spin_unlock :: proc(a: ^i32) {
store(a, 0);
mfence();
}
try_acquire_lock :: proc(a: ^i32) -> bool {
yield_thread();
old_value := compare_exchange(a, 1, 0);
mfence();
return old_value == 0;
}
load :: proc(a: ^i64) -> i64 {
return a^;
}
store :: proc(a: ^i64, value: i64) {
a^ = value;
}
compare_exchange :: proc(a: ^i64, expected, desired: i64) -> i64 {
return win32.InterlockedCompareExchange64(a, desired, expected);
}
exchanged :: proc(a: ^i64, desired: i64) -> i64 {
return win32.InterlockedExchange64(a, desired);
}
fetch_add :: proc(a: ^i64, operand: i64) -> i64 {
return win32.InterlockedExchangeAdd64(a, operand);
}
fetch_and :: proc(a: ^i64, operand: i64) -> i64 {
return win32.InterlockedAnd64(a, operand);
}
fetch_or :: proc(a: ^i64, operand: i64) -> i64 {
return win32.InterlockedOr64(a, operand);
}
spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
old_value := compare_exchange(a, 1, 0);
counter := 0;
for old_value != 0 && (time_out < 0 || counter < time_out) {
counter++;
yield_thread();
old_value = compare_exchange(a, 1, 0);
mfence();
}
return old_value == 0;
}
spin_unlock :: proc(a: ^i64) {
store(a, 0);
mfence();
}
try_acquire_lock :: proc(a: ^i64) -> bool {
yield_thread();
old_value := compare_exchange(a, 1, 0);
mfence();
return old_value == 0;
}
+27 -27
View File
@@ -14,37 +14,37 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
for _, i in buf { for _, i in buf {
buf[i] = '0'; buf[i] = '0';
} }
return buf.count; return len(buf);
} }
n := 10 + a.count + abs(a.decimal_point); n := 10 + a.count + abs(a.decimal_point);
// TODO(bill): make this work with a buffer that's not big enough // TODO(bill): make this work with a buffer that's not big enough
assert(buf.count >= n); assert(len(buf) >= n);
buf = buf[..n]; buf = buf[0..<n];
if a.count == 0 { if a.count == 0 {
buf[0] = '0'; buf[0] = '0';
return cast(string)buf[0..1]; return string(buf[0..<1]);
} }
w := 0; w := 0;
if a.decimal_point <= 0 { if a.decimal_point <= 0 {
buf[w] = '0'; w++; buf[w] = '0'; w++;
buf[w] = '.'; w++; buf[w] = '.'; w++;
w += digit_zero(buf[w .. w-a.decimal_point]); w += digit_zero(buf[w ..< w-a.decimal_point]);
w += copy(buf[w..], a.digits[0..a.count]); w += copy(buf[w..], a.digits[0..<a.count]);
} else if a.decimal_point < a.count { } else if a.decimal_point < a.count {
w += copy(buf[w..], a.digits[0..a.decimal_point]); w += copy(buf[w..], a.digits[0..<a.decimal_point]);
buf[w] = '.'; w++; buf[w] = '.'; w++;
w += copy(buf[w..], a.digits[a.decimal_point .. a.count]); w += copy(buf[w..], a.digits[a.decimal_point ..< a.count]);
} else { } else {
w += copy(buf[w..], a.digits[0..a.count]); w += copy(buf[w..], a.digits[0..<a.count]);
w += digit_zero(buf[w .. w+a.decimal_point-a.count]); w += digit_zero(buf[w ..< w+a.decimal_point-a.count]);
} }
return cast(string)buf[0..w]; return string(buf[0..<w]);
} }
// trim trailing zeros // trim trailing zeros
@@ -64,7 +64,7 @@ assign :: proc(a: ^Decimal, i: u64) {
for i > 0 { for i > 0 {
j := i/10; j := i/10;
i -= 10*j; i -= 10*j;
buf[n] = cast(byte)('0'+i); buf[n] = byte('0'+i);
n++; n++;
i = j; i = j;
} }
@@ -99,7 +99,7 @@ shift_right :: proc(a: ^Decimal, k: uint) {
} }
break; break;
} }
c := cast(uint)a.digits[r]; c := uint(a.digits[r]);
n = n*10 + c - '0'; n = n*10 + c - '0';
} }
a.decimal_point -= r-1; a.decimal_point -= r-1;
@@ -107,10 +107,10 @@ shift_right :: proc(a: ^Decimal, k: uint) {
mask: uint = (1<<k) - 1; mask: uint = (1<<k) - 1;
for ; r < a.count; r++ { for ; r < a.count; r++ {
c := cast(uint)a.digits[r]; c := uint(a.digits[r]);
dig := n>>k; dig := n>>k;
n &= mask; n &= mask;
a.digits[w] = cast(byte)('0' + dig); a.digits[w] = byte('0' + dig);
w++; w++;
n = n*10 + c - '0'; n = n*10 + c - '0';
} }
@@ -118,8 +118,8 @@ shift_right :: proc(a: ^Decimal, k: uint) {
for n > 0 { for n > 0 {
dig := n>>k; dig := n>>k;
n &= mask; n &= mask;
if w < a.digits.count { if w < len(a.digits) {
a.digits[w] = cast(byte)('0' + dig); a.digits[w] = byte('0' + dig);
w++; w++;
} else if dig > 0 { } else if dig > 0 {
a.trunc = true; a.trunc = true;
@@ -133,19 +133,19 @@ shift_right :: proc(a: ^Decimal, k: uint) {
} }
shift_left :: proc(a: ^Decimal, k: uint) { shift_left :: proc(a: ^Decimal, k: uint) {
delta := cast(int)(k/4); delta := int(k/4);
r := a.count; // read index r := a.count; // read index
w := a.count+delta; // write index w := a.count+delta; // write index
n: uint; n: uint;
for r--; r >= 0; r-- { for r--; r >= 0; r-- {
n += (cast(uint)a.digits[r] - '0') << k; n += (uint(a.digits[r]) - '0') << k;
quo := n/10; quo := n/10;
rem := n - 10*quo; rem := n - 10*quo;
w--; w--;
if w < a.digits.count { if w < len(a.digits) {
a.digits[w] = cast(byte)('0' + rem); a.digits[w] = byte('0' + rem);
} else if rem != 0 { } else if rem != 0 {
a.trunc = true; a.trunc = true;
} }
@@ -156,8 +156,8 @@ shift_left :: proc(a: ^Decimal, k: uint) {
quo := n/10; quo := n/10;
rem := n - 10*quo; rem := n - 10*quo;
w--; w--;
if w < a.digits.count { if 0 <= w && w < len(a.digits) {
a.digits[w] = cast(byte)('0' + rem); a.digits[w] = byte('0' + rem);
} else if rem != 0 { } else if rem != 0 {
a.trunc = true; a.trunc = true;
} }
@@ -165,7 +165,7 @@ shift_left :: proc(a: ^Decimal, k: uint) {
} }
a.count += delta; a.count += delta;
a.count = min(a.count, a.digits.count); a.count = min(a.count, len(a.digits));
a.decimal_point += delta; a.decimal_point += delta;
trim(a); trim(a);
} }
@@ -179,7 +179,7 @@ shift :: proc(a: ^Decimal, k: int) {
shift_left(a, max_shift); shift_left(a, max_shift);
k -= max_shift; k -= max_shift;
} }
shift_left(a, cast(uint)k); shift_left(a, uint(k));
case k < 0: case k < 0:
@@ -187,7 +187,7 @@ shift :: proc(a: ^Decimal, k: int) {
shift_right(a, max_shift); shift_right(a, max_shift);
k += max_shift; k += max_shift;
} }
shift_right(a, cast(uint)-k); shift_right(a, uint(-k));
} }
} }
@@ -245,7 +245,7 @@ rounded_integer :: proc(a: ^Decimal) -> u64 {
n: u64 = 0; n: u64 = 0;
m := min(a.decimal_point, a.count); m := min(a.decimal_point, a.count);
for i = 0; i < m; i++ { for i = 0; i < m; i++ {
n = n*10 + cast(u64)(a.digits[i]-'0'); n = n*10 + u64(a.digits[i]-'0');
} }
for ; i < a.decimal_point; i++ { for ; i < a.decimal_point; i++ {
n *= 10; n *= 10;
+405 -227
View File
File diff suppressed because it is too large Load Diff
+36 -35
View File
@@ -1,14 +1,14 @@
crc32 :: proc(data: []byte) -> u32 { crc32 :: proc(data: []byte) -> u32 {
result := ~cast(u32)0; result := ~u32(0);
for b in data { for b in data {
result = result>>8 ~ _crc32_table[(result ~ cast(u32)b) & 0xff]; result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
} }
return ~result; return ~result;
} }
crc64 :: proc(data: []byte) -> u64 { crc64 :: proc(data: []byte) -> u64 {
result := ~cast(u64)0; result := ~u64(0);
for b in data { for b in data {
result = result>>8 ~ _crc64_table[(result ~ cast(u64)b) & 0xff]; result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
} }
return ~result; return ~result;
} }
@@ -16,7 +16,7 @@ crc64 :: proc(data: []byte) -> u64 {
fnv32 :: proc(data: []byte) -> u32 { fnv32 :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5; h: u32 = 0x811c9dc5;
for b in data { for b in data {
h = (h * 0x01000193) ~ cast(u32)b; h = (h * 0x01000193) ~ u32(b);
} }
return h; return h;
} }
@@ -24,7 +24,7 @@ fnv32 :: proc(data: []byte) -> u32 {
fnv64 :: proc(data: []byte) -> u64 { fnv64 :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325; h: u64 = 0xcbf29ce484222325;
for b in data { for b in data {
h = (h * 0x100000001b3) ~ cast(u64)b; h = (h * 0x100000001b3) ~ u64(b);
} }
return h; return h;
} }
@@ -32,7 +32,7 @@ fnv64 :: proc(data: []byte) -> u64 {
fnv32a :: proc(data: []byte) -> u32 { fnv32a :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5; h: u32 = 0x811c9dc5;
for b in data { for b in data {
h = (h ~ cast(u32)b) * 0x01000193; h = (h ~ u32(b)) * 0x01000193;
} }
return h; return h;
} }
@@ -40,7 +40,7 @@ fnv32a :: proc(data: []byte) -> u32 {
fnv64a :: proc(data: []byte) -> u64 { fnv64a :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325; h: u64 = 0xcbf29ce484222325;
for b in data { for b in data {
h = (h ~ cast(u64)b) * 0x100000001b3; h = (h ~ u64(b)) * 0x100000001b3;
} }
return h; return h;
} }
@@ -50,12 +50,12 @@ murmur32 :: proc(data: []byte) -> u32 {
c2_32: u32 : 0x1b873593; c2_32: u32 : 0x1b873593;
h1: u32 = 0; h1: u32 = 0;
nblocks := data.count/4; nblocks := len(data)/4;
p := data.data; p := &data[0];
p1 := p + 4*nblocks; p1 := p + 4*nblocks;
for ; p < p1; p += 4 { for ; p < p1; p += 4 {
k1 := (cast(^u32)p)^; k1 := ^u32(p)^;
k1 *= c1_32; k1 *= c1_32;
k1 = (k1 << 15) | (k1 >> 17); k1 = (k1 << 15) | (k1 >> 17);
@@ -69,22 +69,22 @@ murmur32 :: proc(data: []byte) -> u32 {
tail := data[nblocks*4 ..]; tail := data[nblocks*4 ..];
k1: u32; k1: u32;
match tail.count&3 { match len(tail)&3 {
case 3: case 3:
k1 ~= cast(u32)tail[2] << 16; k1 ~= u32(tail[2]) << 16;
fallthrough; fallthrough;
case 2: case 2:
k1 ~= cast(u32)tail[2] << 8; k1 ~= u32(tail[2]) << 8;
fallthrough; fallthrough;
case 1: case 1:
k1 ~= cast(u32)tail[0]; k1 ~= u32(tail[0]);
k1 *= c1_32; k1 *= c1_32;
k1 = (k1 << 15) | (k1 >> 17) ; k1 = (k1 << 15) | (k1 >> 17) ;
k1 *= c2_32; k1 *= c2_32;
h1 ~= k1; h1 ~= k1;
} }
h1 ~= cast(u32)data.count; h1 ~= u32(len(data));
h1 ~= h1 >> 16; h1 ~= h1 >> 16;
h1 *= 0x85ebca6b; h1 *= 0x85ebca6b;
@@ -98,12 +98,12 @@ murmur32 :: proc(data: []byte) -> u32 {
murmur64 :: proc(data: []byte) -> u64 { murmur64 :: proc(data: []byte) -> u64 {
SEED :: 0x9747b28c; SEED :: 0x9747b28c;
when false && size_of(int) == 8 { when size_of(int) == 8 {
m :: 0xc6a4a7935bd1e995; m :: 0xc6a4a7935bd1e995;
r :: 47; r :: 47;
h: u64 = SEED ~ (cast(u64)data.count * m); h: u64 = SEED ~ (u64(len(data)) * m);
data64 := slice_ptr(cast(^u64)^data[0], data.count/size_of(u64)); data64 := slice_ptr(^u64(&data[0]), len(data)/size_of(u64));
for _, i in data64 { for _, i in data64 {
k := data64[i]; k := data64[i];
@@ -116,15 +116,15 @@ murmur64 :: proc(data: []byte) -> u64 {
h *= m; h *= m;
} }
match data.count&7 { match len(data)&7 {
case 7: h ~= cast(u64)data[6] << 48; fallthrough; case 7: h ~= u64(data[6]) << 48; fallthrough;
case 6: h ~= cast(u64)data[5] << 40; fallthrough; case 6: h ~= u64(data[5]) << 40; fallthrough;
case 5: h ~= cast(u64)data[4] << 32; fallthrough; case 5: h ~= u64(data[4]) << 32; fallthrough;
case 4: h ~= cast(u64)data[3] << 24; fallthrough; case 4: h ~= u64(data[3]) << 24; fallthrough;
case 3: h ~= cast(u64)data[2] << 16; fallthrough; case 3: h ~= u64(data[2]) << 16; fallthrough;
case 2: h ~= cast(u64)data[1] << 8; fallthrough; case 2: h ~= u64(data[1]) << 8; fallthrough;
case 1: case 1:
h ~= cast(u64)data[0]; h ~= u64(data[0]);
h *= m; h *= m;
} }
@@ -137,11 +137,11 @@ murmur64 :: proc(data: []byte) -> u64 {
m :: 0x5bd1e995; m :: 0x5bd1e995;
r :: 24; r :: 24;
h1: u32 = cast(u32)SEED ~ cast(u32)data.count; h1 := u32(SEED) ~ u32(len(data));
h2: u32 = SEED >> 32; h2 := u32(SEED) >> 32;
data32 := slice_ptr(cast(^u32)^data[0], data.count/size_of(u32)); data32 := slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
len := data.count; len := len(data);
i := 0; i := 0;
for len >= 8 { for len >= 8 {
@@ -174,16 +174,17 @@ murmur64 :: proc(data: []byte) -> u64 {
len -= 4; len -= 4;
} }
data8 := slice_to_bytes(data32[i..])[..3]; // TODO(bill): Fix this
#no_bounds_check data8 := slice_to_bytes(data32[i..])[0..<3];
match len { match len {
case 3: case 3:
h2 ~= cast(u32)data8[2] << 16; h2 ~= u32(data8[2]) << 16;
fallthrough; fallthrough;
case 2: case 2:
h2 ~= cast(u32)data8[1] << 8; h2 ~= u32(data8[1]) << 8;
fallthrough; fallthrough;
case 1: case 1:
h2 ~= cast(u32)data8[0]; h2 ~= u32(data8[0]);
h2 *= m; h2 *= m;
} }
+49 -43
View File
@@ -27,20 +27,24 @@ Mat4 :: [4]Vec4;
sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32"; sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64"; sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
sin :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32"; sin :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
sin :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64"; sin :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
cos :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32"; cos :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
cos :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64"; cos :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
tan :: proc(θ: f32) -> f32 #inline { return sin(θ)/cos(θ); }
tan :: proc(θ: f64) -> f64 #inline { return sin(θ)/cos(θ); }
pow :: proc(x, power: f32) -> f32 #foreign __llvm_core "llvm.pow.f32";
pow :: proc(x, power: f64) -> f64 #foreign __llvm_core "llvm.pow.f64";
tan :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); }
tan :: proc(x: f64) -> f64 #inline { return sin(x)/cos(x); }
lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; } lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
lerp :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; } lerp :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; }
sign :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; } sign :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; }
sign :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; } sign :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; }
bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16"; bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16";
bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32"; bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32";
@@ -51,38 +55,44 @@ fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
copy_sign :: proc(x, y: f32) -> f32 { copy_sign :: proc(x, y: f32) -> f32 {
ix := transmute(u32)x; ix := transmute(u32, x);
iy := transmute(u32)y; iy := transmute(u32, y);
ix &= 0x7fffffff; ix &= 0x7fff_ffff;
ix |= iy & 0x80000000; ix |= iy & 0x8000_0000;
return transmute(f32)ix; return transmute(f32, ix);
}
round :: proc(x: f32) -> f32 {
if x >= 0 {
return floor(x + 0.5);
}
return ceil(x - 0.5);
}
floor :: proc(x: f32) -> f32 {
if x >= 0 {
return cast(f32)cast(int)x;
}
return cast(f32)cast(int)(x-0.5);
}
ceil :: proc(x: f32) -> f32 {
if x < 0 {
return cast(f32)cast(int)x;
}
return cast(f32)cast(int)(x+1);
} }
remainder32 :: proc(x, y: f32) -> f32 { copy_sign :: proc(x, y: f64) -> f64 {
return x - round(x/y) * y; ix := transmute(u64, x);
iy := transmute(u64, y);
ix &= 0x7fff_ffff_ffff_ff;
ix |= iy & 0x8000_0000_0000_0000;
return transmute(f64, ix);
} }
fmod32 :: proc(x, y: f32) -> f32 { round :: proc(x: f32) -> f32 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
round :: proc(x: f64) -> f64 { return x >= 0 ? floor(x + 0.5) : ceil(x - 0.5); }
floor :: proc(x: f32) -> f32 { return x >= 0 ? f32(i64(x)) : f32(i64(x-0.5)); } // TODO: Get accurate versions
floor :: proc(x: f64) -> f64 { return x >= 0 ? f64(i64(x)) : f64(i64(x-0.5)); } // TODO: Get accurate versions
ceil :: proc(x: f32) -> f32 { return x < 0 ? f32(i64(x)) : f32(i64(x+1)); } // TODO: Get accurate versions
ceil :: proc(x: f64) -> f64 { return x < 0 ? f64(i64(x)) : f64(i64(x+1)); } // TODO: Get accurate versions
remainder :: proc(x, y: f32) -> f32 { return x - round(x/y) * y; }
remainder :: proc(x, y: f64) -> f64 { return x - round(x/y) * y; }
mod :: proc(x, y: f32) -> f32 {
y = abs(y); y = abs(y);
result := remainder32(abs(x), y); result := remainder(abs(x), y);
if sign(result) < 0 {
result += y;
}
return copy_sign(result, x);
}
mod :: proc(x, y: f64) -> f64 {
y = abs(y);
result := remainder(abs(x), y);
if sign(result) < 0 { if sign(result) < 0 {
result += y; result += y;
} }
@@ -95,7 +105,6 @@ to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; } dot :: proc(a, b: 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: 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; } dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }
@@ -151,8 +160,8 @@ mat4_identity :: proc() -> Mat4 {
} }
mat4_transpose :: proc(m: Mat4) -> Mat4 { mat4_transpose :: proc(m: Mat4) -> Mat4 {
for j in 0..4 { for j in 0..<4 {
for i in 0..4 { for i in 0..<4 {
m[i][j], m[j][i] = m[j][i], m[i][j]; m[i][j], m[j][i] = m[j][i], m[i][j];
} }
} }
@@ -161,8 +170,8 @@ mat4_transpose :: proc(m: Mat4) -> Mat4 {
mul :: proc(a, b: Mat4) -> Mat4 { mul :: proc(a, b: Mat4) -> Mat4 {
c: Mat4; c: Mat4;
for j in 0..4 { for j in 0..<4 {
for i in 0..4 { for i in 0..<4 {
c[j][i] = a[0][i]*b[j][0] + c[j][i] = a[0][i]*b[j][0] +
a[1][i]*b[j][1] + a[1][i]*b[j][1] +
a[2][i]*b[j][2] + a[2][i]*b[j][2] +
@@ -367,6 +376,3 @@ F64_MIN_10_EXP :: -307; // min decimal exponent
F64_MIN_EXP :: -1021; // min binary exponent F64_MIN_EXP :: -1021; // min binary exponent
F64_RADIX :: 2; // exponent radix F64_RADIX :: 2; // exponent radix
F64_ROUNDS :: 1; // addition rounding: near F64_ROUNDS :: 1; // addition rounding: near
+26 -47
View File
@@ -6,41 +6,20 @@ swap :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bswap.i32";
swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64"; swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64";
set :: proc(data: rawptr, value: i32, len: int) -> rawptr #link_name "__mem_set" { set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64"; return __mem_set(data, value, len);
llvm_memset_64bit(data, cast(byte)value, len, 1, false);
return data;
} }
zero :: proc(data: rawptr, len: int) -> rawptr {
zero :: proc(data: rawptr, len: int) -> rawptr #link_name "__mem_zero" { return __mem_zero(data, len);
return set(data, 0, len);
} }
copy :: proc(dst, src: rawptr, len: int) -> rawptr {
copy :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy" { return __mem_copy(dst, src, len);
// 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 {
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy_non_overlapping" { return __mem_copy_non_overlapping(dst, src, len);
// 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(a, b: []byte) -> int {
compare :: proc(a, b: []byte) -> int #link_name "__mem_compare" { return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
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;
} }
@@ -60,13 +39,13 @@ is_power_of_two :: proc(x: int) -> bool {
align_forward :: proc(ptr: rawptr, align: int) -> rawptr { align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
assert(is_power_of_two(align)); assert(is_power_of_two(align));
a := cast(uint)align; a := uint(align);
p := cast(uint)ptr; p := uint(ptr);
modulo := p & (a-1); modulo := p & (a-1);
if modulo != 0 { if modulo != 0 {
p += a - modulo; p += a - modulo;
} }
return cast(rawptr)p; return rawptr(p);
} }
@@ -77,9 +56,9 @@ Allocation_Header :: struct {
allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: int) { allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: int) {
header.size = size; header.size = size;
ptr := cast(^int)(header+1); ptr := ^int(header+1);
for i := 0; cast(rawptr)ptr < data; i++ { for i := 0; rawptr(ptr) < data; i++ {
(ptr+i)^ = -1; (ptr+i)^ = -1;
} }
} }
@@ -87,11 +66,11 @@ allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
if data == nil { if data == nil {
return nil; return nil;
} }
p := cast(^int)data; p := ^int(data);
for (p-1)^ == -1 { for (p-1)^ == -1 {
p = (p-1); p = (p-1);
} }
return cast(^Allocation_Header)p-1; return ^Allocation_Header(p-1);
} }
@@ -117,13 +96,13 @@ Arena_Temp_Memory :: struct {
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) { init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
backing = Allocator{}; backing = Allocator{};
memory = data[..0]; memory = data[0..<0];
temp_count = 0; temp_count = 0;
} }
init_arena_from_context :: proc(using a: ^Arena, size: int) { init_arena_from_context :: proc(using a: ^Arena, size: int) {
backing = context.allocator; backing = context.allocator;
memory = new_slice(byte, size); memory = make([]byte, size);
temp_count = 0; temp_count = 0;
} }
@@ -148,18 +127,18 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int, size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64) -> rawptr { old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
using Allocator_Mode; using Allocator_Mode;
arena := cast(^Arena)allocator_data; arena := ^Arena(allocator_data);
match mode { match mode {
case ALLOC: case ALLOC:
total_size := size + alignment; total_size := size + alignment;
if arena.offset + total_size > arena.memory.count { if arena.offset + total_size > len(arena.memory) {
fmt.fprintln(os.stderr, "Arena out of memory"); fmt.fprintln(os.stderr, "Arena out of memory");
return nil; return nil;
} }
#no_bounds_check end := ^arena.memory[arena.offset]; #no_bounds_check end := &arena.memory[arena.offset];
ptr := align_forward(end, alignment); ptr := align_forward(end, alignment);
arena.offset += total_size; arena.offset += total_size;
@@ -182,15 +161,15 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory { begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
tmp: Arena_Temp_Memory; tmp: Arena_Temp_Memory;
tmp.arena = a; tmp.arena = a;
tmp.original_count = a.memory.count; tmp.original_count = len(a.memory);
a.temp_count++; a.temp_count++;
return tmp; return tmp;
} }
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) { end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
assert(arena.memory.count >= original_count); assert(len(arena.memory) >= original_count);
assert(arena.temp_count > 0); assert(arena.temp_count > 0);
arena.memory.count = original_count; arena.memory = arena.memory[0..<original_count];
arena.temp_count--; arena.temp_count--;
} }
@@ -242,7 +221,7 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int {
return WORD_SIZE; return WORD_SIZE;
case Vector: case Vector:
size := size_of_type_info(info.elem); size := size_of_type_info(info.elem);
count := cast(int)max(prev_pow2(cast(i64)info.count), 1); count := int(max(prev_pow2(i64(info.count)), 1));
total := size * count; total := size * count;
return clamp(total, 1, MAX_ALIGN); return clamp(total, 1, MAX_ALIGN);
case Tuple: case Tuple:
+55 -45
View File
@@ -1,4 +1,5 @@
#foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows"; #foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
#foreign_system_library lib "gl" when ODIN_OS == "linux";
#import win32 "sys/windows.odin" when ODIN_OS == "windows"; #import win32 "sys/windows.odin" when ODIN_OS == "windows";
#import "sys/wgl.odin" when ODIN_OS == "windows"; #import "sys/wgl.odin" when ODIN_OS == "windows";
#load "opengl_constants.odin"; #load "opengl_constants.odin";
@@ -30,15 +31,19 @@ TexImage2D :: proc(target, level, internal_format,
format, type: i32, pixels: rawptr) #foreign lib "glTexImage2D"; format, type: i32, pixels: rawptr) #foreign lib "glTexImage2D";
string_data :: proc(s: string) -> ^u8 #inline { return ^s[0]; } _string_data :: proc(s: string) -> ^u8 #inline { return &s[0]; }
_libgl := win32.LoadLibraryA(string_data("opengl32.dll\x00")); _libgl := win32.LoadLibraryA(_string_data("opengl32.dll\x00"));
GetProcAddress :: proc(name: string) -> proc() #cc_c { GetProcAddress :: proc(name: string) -> proc() #cc_c {
assert(name[name.count-1] == 0); if name[len(name)-1] == 0 {
res := wgl.GetProcAddress(name.data); name = name[0..<len(name)-1];
}
// NOTE(bill): null terminated
assert((&name[0] + len(name))^ == 0);
res := wgl.GetProcAddress(&name[0]);
if res == nil { if res == nil {
res = win32.GetProcAddress(_libgl, name.data); res = win32.GetProcAddress(_libgl, &name[0]);
} }
return res; return res;
} }
@@ -46,6 +51,7 @@ GetProcAddress :: proc(name: string) -> proc() #cc_c {
GenBuffers: proc(count: i32, buffers: ^u32) #cc_c; GenBuffers: proc(count: i32, buffers: ^u32) #cc_c;
GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c; GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c;
GenSamplers: proc(count: i32, buffers: ^u32) #cc_c; GenSamplers: proc(count: i32, buffers: ^u32) #cc_c;
DeleteBuffers: proc(count: i32, buffers: ^u32) #cc_c;
BindBuffer: proc(target: i32, buffer: u32) #cc_c; BindBuffer: proc(target: i32, buffer: u32) #cc_c;
BindVertexArray: proc(buffer: u32) #cc_c; BindVertexArray: proc(buffer: u32) #cc_c;
BindSampler: proc(position: i32, sampler: u32) #cc_c; BindSampler: proc(position: i32, sampler: u32) #cc_c;
@@ -102,55 +108,59 @@ UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32
GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c; GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c;
init :: proc() { init :: proc() {
set_proc_address :: proc(p: rawptr, name: string) #inline { (cast(^(proc() #cc_c))p)^ = GetProcAddress(name); } set_proc_address :: proc(p: rawptr, name: string) #inline {
x := ^(proc() #cc_c)(p);
x^ = GetProcAddress(name);
}
set_proc_address(^GenBuffers, "glGenBuffers\x00"); set_proc_address(&GenBuffers, "glGenBuffers\x00");
set_proc_address(^GenVertexArrays, "glGenVertexArrays\x00"); set_proc_address(&GenVertexArrays, "glGenVertexArrays\x00");
set_proc_address(^GenSamplers, "glGenSamplers\x00"); set_proc_address(&GenSamplers, "glGenSamplers\x00");
set_proc_address(^BindBuffer, "glBindBuffer\x00"); set_proc_address(&DeleteBuffers, "glDeleteBuffers\x00");
set_proc_address(^BindSampler, "glBindSampler\x00"); set_proc_address(&BindBuffer, "glBindBuffer\x00");
set_proc_address(^BindVertexArray, "glBindVertexArray\x00"); set_proc_address(&BindSampler, "glBindSampler\x00");
set_proc_address(^BufferData, "glBufferData\x00"); set_proc_address(&BindVertexArray, "glBindVertexArray\x00");
set_proc_address(^BufferSubData, "glBufferSubData\x00"); set_proc_address(&BufferData, "glBufferData\x00");
set_proc_address(&BufferSubData, "glBufferSubData\x00");
set_proc_address(^DrawArrays, "glDrawArrays\x00"); set_proc_address(&DrawArrays, "glDrawArrays\x00");
set_proc_address(^DrawElements, "glDrawElements\x00"); set_proc_address(&DrawElements, "glDrawElements\x00");
set_proc_address(^MapBuffer, "glMapBuffer\x00"); set_proc_address(&MapBuffer, "glMapBuffer\x00");
set_proc_address(^UnmapBuffer, "glUnmapBuffer\x00"); set_proc_address(&UnmapBuffer, "glUnmapBuffer\x00");
set_proc_address(^VertexAttribPointer, "glVertexAttribPointer\x00"); set_proc_address(&VertexAttribPointer, "glVertexAttribPointer\x00");
set_proc_address(^EnableVertexAttribArray, "glEnableVertexAttribArray\x00"); set_proc_address(&EnableVertexAttribArray, "glEnableVertexAttribArray\x00");
set_proc_address(^CreateShader, "glCreateShader\x00"); set_proc_address(&CreateShader, "glCreateShader\x00");
set_proc_address(^ShaderSource, "glShaderSource\x00"); set_proc_address(&ShaderSource, "glShaderSource\x00");
set_proc_address(^CompileShader, "glCompileShader\x00"); set_proc_address(&CompileShader, "glCompileShader\x00");
set_proc_address(^CreateProgram, "glCreateProgram\x00"); set_proc_address(&CreateProgram, "glCreateProgram\x00");
set_proc_address(^AttachShader, "glAttachShader\x00"); set_proc_address(&AttachShader, "glAttachShader\x00");
set_proc_address(^DetachShader, "glDetachShader\x00"); set_proc_address(&DetachShader, "glDetachShader\x00");
set_proc_address(^DeleteShader, "glDeleteShader\x00"); set_proc_address(&DeleteShader, "glDeleteShader\x00");
set_proc_address(^LinkProgram, "glLinkProgram\x00"); set_proc_address(&LinkProgram, "glLinkProgram\x00");
set_proc_address(^UseProgram, "glUseProgram\x00"); set_proc_address(&UseProgram, "glUseProgram\x00");
set_proc_address(^DeleteProgram, "glDeleteProgram\x00"); set_proc_address(&DeleteProgram, "glDeleteProgram\x00");
set_proc_address(^GetShaderiv, "glGetShaderiv\x00"); set_proc_address(&GetShaderiv, "glGetShaderiv\x00");
set_proc_address(^GetProgramiv, "glGetProgramiv\x00"); set_proc_address(&GetProgramiv, "glGetProgramiv\x00");
set_proc_address(^GetShaderInfoLog, "glGetShaderInfoLog\x00"); set_proc_address(&GetShaderInfoLog, "glGetShaderInfoLog\x00");
set_proc_address(^GetProgramInfoLog, "glGetProgramInfoLog\x00"); set_proc_address(&GetProgramInfoLog, "glGetProgramInfoLog\x00");
set_proc_address(^ActiveTexture, "glActiveTexture\x00"); set_proc_address(&ActiveTexture, "glActiveTexture\x00");
set_proc_address(^GenerateMipmap, "glGenerateMipmap\x00"); set_proc_address(&GenerateMipmap, "glGenerateMipmap\x00");
set_proc_address(^Uniform1i, "glUniform1i\x00"); set_proc_address(&Uniform1i, "glUniform1i\x00");
set_proc_address(^UniformMatrix4fv, "glUniformMatrix4fv\x00"); set_proc_address(&UniformMatrix4fv, "glUniformMatrix4fv\x00");
set_proc_address(^GetUniformLocation, "glGetUniformLocation\x00"); set_proc_address(&GetUniformLocation, "glGetUniformLocation\x00");
set_proc_address(^SamplerParameteri, "glSamplerParameteri\x00"); set_proc_address(&SamplerParameteri, "glSamplerParameteri\x00");
set_proc_address(^SamplerParameterf, "glSamplerParameterf\x00"); set_proc_address(&SamplerParameterf, "glSamplerParameterf\x00");
set_proc_address(^SamplerParameteriv, "glSamplerParameteriv\x00"); set_proc_address(&SamplerParameteriv, "glSamplerParameteriv\x00");
set_proc_address(^SamplerParameterfv, "glSamplerParameterfv\x00"); set_proc_address(&SamplerParameterfv, "glSamplerParameterfv\x00");
set_proc_address(^SamplerParameterIiv, "glSamplerParameterIiv\x00"); set_proc_address(&SamplerParameterIiv, "glSamplerParameterIiv\x00");
set_proc_address(^SamplerParameterIuiv, "glSamplerParameterIuiv\x00"); set_proc_address(&SamplerParameterIuiv, "glSamplerParameterIuiv\x00");
} }
+1 -1
View File
@@ -1,3 +1,3 @@
#load "os_windows.odin" when ODIN_OS == "windows"; #load "os_windows.odin" when ODIN_OS == "windows";
#load "os_x.odin" when ODIN_OS == "osx"; #load "os_x.odin" when ODIN_OS == "osx";
#load "os_linux.odin" when ODIN_OS == "linux";
+303
View File
@@ -0,0 +1,303 @@
#import "fmt.odin";
#import "strings.odin";
Handle :: i32;
File_Time :: u64;
Errno :: i32;
// INVALID_HANDLE: Handle : -1;
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
O_RDWR :: 0x00002;
O_CREAT :: 0x00040;
O_EXCL :: 0x00080;
O_NOCTTY :: 0x00100;
O_TRUNC :: 0x00200;
O_NONBLOCK :: 0x00800;
O_APPEND :: 0x00400;
O_SYNC :: 0x01000;
O_ASYNC :: 0x02000;
O_CLOEXEC :: 0x80000;
SEEK_SET :: 0;
SEEK_CUR :: 1;
SEEK_END :: 2;
SEEK_DATA :: 3;
SEEK_HOLE :: 4;
SEEK_MAX :: SEEK_HOLE;
// NOTE(zangent): These are OS specific!
// Do not mix these up!
RTLD_LAZY :: 0x001;
RTLD_NOW :: 0x002;
RTLD_BINDING_MASK :: 0x3;
RTLD_GLOBAL :: 0x100;
// "Argv" arguments converted to Odin strings
immutable args := _alloc_command_line_arguments();
_File_Time :: struct #ordered {
seconds: i64,
nanoseconds: i32,
reserved: i32,
}
// Translated from
// https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6/+/jb-dev/sysroot/usr/include/bits/stat.h
// Validity is not guaranteed.
Stat :: struct #ordered {
device_id: u64, // ID of device containing file
serial: u64, // File serial number
nlink: u32, // Number of hard links
mode: u32, // Mode of the file
uid: u32, // User ID of the file's owner
gid: u32, // Group ID of the file's group
_padding: i32, // 32 bits of padding
rdev: u64, // Device ID, if device
size: i64, // Size of the file, in bytes
block_size: i64, // Optimal bllocksize for I/O
blocks: i64, // Number of 512-byte blocks allocated
last_access: _File_Time, // Time of last access
modified: _File_Time, // Time of last modification
status_change: _File_Time, // Time of last status change
_reserve1,
_reserve2,
_reserve3: i64,
serial_numbe: u64, // File serial number...? Maybe.
_reserve4: i64,
};
// File type
S_IFMT :: 0170000; // Type of file mask
S_IFIFO :: 0010000; // Named pipe (fifo)
S_IFCHR :: 0020000; // Character special
S_IFDIR :: 0040000; // Directory
S_IFBLK :: 0060000; // Block special
S_IFREG :: 0100000; // Regular
S_IFLNK :: 0120000; // Symbolic link
S_IFSOCK :: 0140000; // Socket
// File mode
// Read, write, execute/search by owner
S_IRWXU :: 0000700; // RWX mask for owner
S_IRUSR :: 0000400; // R for owner
S_IWUSR :: 0000200; // W for owner
S_IXUSR :: 0000100; // X for owner
// Read, write, execute/search by group
S_IRWXG :: 0000070; // RWX mask for group
S_IRGRP :: 0000040; // R for group
S_IWGRP :: 0000020; // W for group
S_IXGRP :: 0000010; // X for group
// Read, write, execute/search by others
S_IRWXO :: 0000007; // RWX mask for other
S_IROTH :: 0000004; // R for other
S_IWOTH :: 0000002; // W for other
S_IXOTH :: 0000001; // X for other
S_ISUID :: 0004000; // Set user id on execution
S_ISGID :: 0002000; // Set group id on execution
S_ISVTX :: 0001000; // Directory restrcted delete
S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; }
S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; }
S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; }
S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; }
S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; }
S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; }
S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;}
R_OK :: 4; // Test for read permission
W_OK :: 2; // Test for write permission
X_OK :: 1; // Test for execute permission
F_OK :: 0; // Test for file existance
#foreign_system_library dl "dl";
#foreign_system_library libc "c";
_unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open";
_unix_close :: proc(fd: Handle) -> i32 #foreign libc "close";
_unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int #foreign libc "read";
_unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int #foreign libc "write";
_unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 #foreign libc "lseek64";
_unix_gettid :: proc() -> u64 #foreign libc "gettid";
_unix_stat :: proc(path: ^u8, stat: ^Stat) -> i32 #foreign libc "stat";
_unix_access :: proc(path: ^u8, mask: int) -> i32 #foreign libc "access";
_unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
_unix_free :: proc(ptr: rawptr) #foreign libc "free";
_unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
_unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv";
_unix_exit :: proc(status: int) #foreign libc "exit";
_unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen";
_unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym";
_unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose";
_unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror";
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
cstr := strings.new_c_string(path);
handle := _unix_open(cstr, mode);
free(cstr);
if(handle == -1) {
return 0, 1;
}
return handle, 0;
}
// NOTE(zangent): This is here for compatability reasons. Should this be here?
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
return open_simple(path, mode);
}
close :: proc(fd: Handle) {
_unix_close(fd);
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
sz := _unix_read(fd, &data[0], len(data));
return sz, 0;
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
sz := _unix_write(fd, &data[0], len(data));
return sz, 0;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
res := _unix_seek(fd, offset, i32(whence));
return res, 0;
}
// NOTE(bill): Uses startup to initialize it
stdin: Handle = 0;
stdout: Handle = 1;
stderr: Handle = 2;
/* TODO(zangent): Implement these!
last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {}
*/
stat :: proc(path: string) -> (Stat, int) #inline {
s: Stat;
cstr := strings.new_c_string(path);
defer free(cstr);
ret_int := _unix_stat(cstr, &s);
return s, int(ret_int);
}
access :: proc(path: string, mask: int) -> bool #inline {
cstr := strings.new_c_string(path);
defer free(cstr);
return _unix_access(cstr, mask) == 0;
}
read_entire_file :: proc(name: string) -> ([]byte, bool) {
fd: Handle;
err: Errno;
size: i64;
fd, err = open_simple(name, O_RDONLY);
if(err != 0) {
fmt.println("Failed to open file.");
return nil, false;
}
defer close(fd);
// We have a file
size, err = seek(fd, 0, SEEK_END);
if(err != 0) {
fmt.println("Failed to seek to end of file.");
return nil, false;
}
_, err = seek(fd, 0, SEEK_SET);
if(err != 0) {
fmt.println("Failed to seek to beginning of file.");
return nil, false;
}
// We have a file size!
data := make([]u8, size+1);
if data == nil {
fmt.println("Failed to allocate file buffer.");
return nil, false;
}
read(fd, data);
data[size] = 0;
return data, true;
}
heap_alloc :: proc(size: int) -> rawptr {
assert(size > 0);
return _unix_malloc(size);
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
return _unix_realloc(ptr, new_size);
}
heap_free :: proc(ptr: rawptr) {
_unix_free(ptr);
}
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.new_c_string(name);
cstr: ^u8 = _unix_getenv(path_str);
free(path_str);
if(cstr == nil) {
return "", false;
}
return strings.to_odin_string(cstr), true;
}
exit :: proc(code: int) {
_unix_exit(code);
}
current_thread_id :: proc() -> int {
// return int(_unix_gettid());
return 0;
}
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
cstr := strings.new_c_string(filename);
handle := _unix_dlopen(cstr, flags);
free(cstr);
return handle;
}
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
assert(handle != nil);
cstr := strings.new_c_string(symbol);
proc_handle := _unix_dlsym(handle, cstr);
free(cstr);
return proc_handle;
}
dlclose :: proc(handle: rawptr) -> bool #inline {
assert(handle != nil);
return _unix_dlclose(handle) == 0;
}
dlerror :: proc() -> string {
return strings.to_odin_string(_unix_dlerror());
}
_alloc_command_line_arguments :: proc() -> []string {
// TODO(bill):
return nil;
}
+140 -70
View File
@@ -1,6 +1,5 @@
#import win32 "sys/windows.odin"; #import win32 "sys/windows.odin";
#import "fmt.odin"; #import fmt "fmt.odin";
Handle :: int; Handle :: int;
File_Time :: u64; File_Time :: u64;
@@ -50,105 +49,114 @@ WSAECONNRESET: Errno : 10054;
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0; ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
// "Argv" arguments converted to Odin strings
immutable args := _alloc_command_line_arguments();
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) { open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
using win32; if len(path) == 0 {
if path.count == 0 {
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND; return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
} }
access: u32; access: u32;
match mode & (O_RDONLY|O_WRONLY|O_RDWR) { match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY: access = FILE_GENERIC_READ; case O_RDONLY: access = win32.FILE_GENERIC_READ;
case O_WRONLY: access = FILE_GENERIC_WRITE; case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
case O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE; case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
} }
if mode&O_CREAT != 0 { if mode&O_CREAT != 0 {
access |= FILE_GENERIC_WRITE; access |= win32.FILE_GENERIC_WRITE;
} }
if mode&O_APPEND != 0 { if mode&O_APPEND != 0 {
access &~= FILE_GENERIC_WRITE; access &~= win32.FILE_GENERIC_WRITE;
access |= FILE_APPEND_DATA; access |= win32.FILE_APPEND_DATA;
} }
share_mode := cast(u32)(FILE_SHARE_READ|FILE_SHARE_WRITE); share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
sa: ^SECURITY_ATTRIBUTES = nil; sa: ^win32.Security_Attributes = nil;
sa_inherit := SECURITY_ATTRIBUTES{length = size_of(SECURITY_ATTRIBUTES), inherit_handle = 1}; sa_inherit := win32.Security_Attributes{length = size_of(win32.Security_Attributes), inherit_handle = 1};
if mode&O_CLOEXEC == 0 { if mode&O_CLOEXEC == 0 {
sa = ^sa_inherit; sa = &sa_inherit;
} }
create_mode: u32; create_mode: u32;
match { match {
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL): case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
create_mode = CREATE_NEW; create_mode = win32.CREATE_NEW;
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC): case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
create_mode = CREATE_ALWAYS; create_mode = win32.CREATE_ALWAYS;
case mode&O_CREAT == O_CREAT: case mode&O_CREAT == O_CREAT:
create_mode = OPEN_ALWAYS; create_mode = win32.OPEN_ALWAYS;
case mode&O_TRUNC == O_TRUNC: case mode&O_TRUNC == O_TRUNC:
create_mode = TRUNCATE_EXISTING; create_mode = win32.TRUNCATE_EXISTING;
default: default:
create_mode = OPEN_EXISTING; create_mode = win32.OPEN_EXISTING;
} }
buf: [300]byte; buf: [300]byte;
copy(buf[..], cast([]byte)path); copy(buf[..], []byte(path));
handle := cast(Handle)CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil); handle := Handle(win32.CreateFileA(&buf[0], access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
if handle != INVALID_HANDLE { if handle != INVALID_HANDLE {
return handle, ERROR_NONE; return handle, ERROR_NONE;
} }
err := GetLastError(); err := win32.GetLastError();
return INVALID_HANDLE, cast(Errno)err; return INVALID_HANDLE, Errno(err);
} }
close :: proc(fd: Handle) { close :: proc(fd: Handle) {
win32.CloseHandle(cast(win32.HANDLE)fd); win32.CloseHandle(win32.Handle(fd));
} }
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
return write(fd, []byte(str));
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE;
}
bytes_written: i32; bytes_written: i32;
e := win32.WriteFile(cast(win32.HANDLE)fd, data.data, cast(i32)data.count, ^bytes_written, nil); e := win32.WriteFile(win32.Handle(fd), &data[0], i32(len(data)), &bytes_written, nil);
if e == win32.FALSE { if e == win32.FALSE {
err := win32.GetLastError(); err := win32.GetLastError();
return 0, cast(Errno)err; return 0, Errno(err);
} }
return cast(int)bytes_written, ERROR_NONE; return int(bytes_written), ERROR_NONE;
} }
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if len(data) == 0 {
return 0, ERROR_NONE;
}
bytes_read: i32; bytes_read: i32;
e := win32.ReadFile(cast(win32.HANDLE)fd, data.data, cast(u32)data.count, ^bytes_read, nil); e := win32.ReadFile(win32.Handle(fd), &data[0], u32(len(data)), &bytes_read, nil);
if e == win32.FALSE { if e == win32.FALSE {
err := win32.GetLastError(); err := win32.GetLastError();
return 0, cast(Errno)err; return 0, Errno(err);
} }
return cast(int)bytes_read, ERROR_NONE; return int(bytes_read), ERROR_NONE;
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
using win32;
w: u32; w: u32;
match whence { match whence {
case 0: w = FILE_BEGIN; case 0: w = win32.FILE_BEGIN;
case 1: w = FILE_CURRENT; case 1: w = win32.FILE_CURRENT;
case 2: w = FILE_END; case 2: w = win32.FILE_END;
} }
hi := cast(i32)(offset>>32); hi := i32(offset>>32);
lo := cast(i32)(offset); lo := i32(offset);
ft := GetFileType(cast(HANDLE)fd); ft := win32.GetFileType(win32.Handle(fd));
if ft == FILE_TYPE_PIPE { if ft == win32.FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE; return 0, ERROR_FILE_IS_PIPE;
} }
dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w); dw_ptr := win32.SetFilePointer(win32.Handle(fd), lo, &hi, w);
if dw_ptr == INVALID_SET_FILE_POINTER { if dw_ptr == win32.INVALID_SET_FILE_POINTER {
err := GetLastError(); err := win32.GetLastError();
return 0, cast(Errno)err; return 0, Errno(err);
} }
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE; return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
} }
@@ -159,9 +167,9 @@ stderr := get_std_handle(win32.STD_ERROR_HANDLE);
get_std_handle :: proc(h: int) -> Handle { get_std_handle :: proc(h: int) -> Handle {
fd := win32.GetStdHandle(cast(i32)h); fd := win32.GetStdHandle(i32(h));
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0); win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
return cast(Handle)fd; return Handle(fd);
} }
@@ -170,38 +178,35 @@ get_std_handle :: proc(h: int) -> Handle {
last_write_time :: proc(fd: Handle) -> File_Time { last_write_time :: proc(fd: Handle) -> File_Time {
file_info: win32.BY_HANDLE_FILE_INFORMATION; file_info: win32.By_Handle_File_Information;
win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info); win32.GetFileInformationByHandle(win32.Handle(fd), &file_info);
lo := cast(File_Time)file_info.last_write_time.lo; lo := File_Time(file_info.last_write_time.lo);
hi := cast(File_Time)file_info.last_write_time.hi; hi := File_Time(file_info.last_write_time.hi);
return lo | hi << 32; return lo | hi << 32;
} }
last_write_time_by_name :: proc(name: string) -> File_Time { last_write_time_by_name :: proc(name: string) -> File_Time {
last_write_time: win32.FILETIME; last_write_time: win32.Filetime;
data: win32.FILE_ATTRIBUTE_DATA; data: win32.File_Attribute_Data;
buf: [1024]byte; buf: [1024]byte;
assert(buf.count > name.count); assert(len(buf) > len(name));
copy(buf[..], cast([]byte)name); copy(buf[..], []byte(name));
if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 { if win32.GetFileAttributesExA(&buf[0], win32.GetFileExInfoStandard, &data) != 0 {
last_write_time = data.last_write_time; last_write_time = data.last_write_time;
} }
l := cast(File_Time)last_write_time.lo; l := File_Time(last_write_time.lo);
h := cast(File_Time)last_write_time.hi; h := File_Time(last_write_time.hi);
return l | h << 32; return l | h << 32;
} }
read_entire_file :: proc(name: string) -> ([]byte, bool) { read_entire_file :: proc(name: string) -> ([]byte, bool) {
buf: [300]byte; buf: [300]byte;
copy(buf[..], cast([]byte)name); copy(buf[..], []byte(name));
fd, err := open(name, O_RDONLY, 0); fd, err := open(name, O_RDONLY, 0);
if err != ERROR_NONE { if err != ERROR_NONE {
@@ -210,13 +215,16 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
defer close(fd); defer close(fd);
length: i64; length: i64;
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0; if ok := win32.GetFileSizeEx(win32.Handle(fd), &length) != 0; !ok {
if !file_size_ok {
return nil, false; return nil, false;
} }
data := new_slice(u8, length); if length == 0 {
if data.data == nil { return nil, true;
}
data := make([]byte, length);
if data == nil {
return nil, false; return nil, false;
} }
@@ -228,18 +236,18 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
to_read: u32; to_read: u32;
MAX :: 1<<32-1; MAX :: 1<<32-1;
if remaining <= MAX { if remaining <= MAX {
to_read = cast(u32)remaining; to_read = u32(remaining);
} else { } else {
to_read = MAX; to_read = MAX;
} }
win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil); win32.ReadFile(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil);
if single_read_length <= 0 { if single_read_length <= 0 {
free(data); free(data);
return nil, false; return nil, false;
} }
total_read += cast(i64)single_read_length; total_read += i64(single_read_length);
} }
return data, true; return data, true;
@@ -248,7 +256,6 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
heap_alloc :: proc(size: int) -> rawptr { heap_alloc :: proc(size: int) -> rawptr {
assert(size > 0);
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size); return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
} }
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
@@ -270,14 +277,77 @@ heap_free :: proc(ptr: rawptr) {
exit :: proc(code: int) { exit :: proc(code: int) {
win32.ExitProcess(cast(u32)code); win32.ExitProcess(u32(code));
} }
current_thread_id :: proc() -> int { current_thread_id :: proc() -> int {
return cast(int)win32.GetCurrentThreadId(); return int(win32.GetCurrentThreadId());
} }
_alloc_command_line_arguments :: proc() -> []string {
alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string {
wstr_len := 0;
for (wstr+wstr_len)^ != 0 {
wstr_len++;
}
len := 2*wstr_len-1;
buf := make([]byte, len+1);
str := slice_ptr(wstr, wstr_len+1);
i, j := 0, 0;
for str[j] != 0 {
match {
case str[j] < 0x80:
if i+1 > len {
return "";
}
buf[i] = byte(str[j]); i++;
j++;
case str[j] < 0x800:
if i+2 > len {
return "";
}
buf[i] = byte(0xc0 + (str[j]>>6)); i++;
buf[i] = byte(0x80 + (str[j]&0x3f)); i++;
j++;
case 0xd800 <= str[j] && str[j] < 0xdc00:
if i+4 > len {
return "";
}
c := rune((str[j] - 0xd800) << 10) + rune((str[j+1]) - 0xdc00) + 0x10000;
buf[i] = byte(0xf0 + (c >> 18)); i++;
buf[i] = byte(0x80 + ((c >> 12) & 0x3f)); i++;
buf[i] = byte(0x80 + ((c >> 6) & 0x3f)); i++;
buf[i] = byte(0x80 + ((c ) & 0x3f)); i++;
j += 2;
case 0xdc00 <= str[j] && str[j] < 0xe000:
return "";
default:
if i+3 > len {
return "";
}
buf[i] = 0xe0 + byte (str[j] >> 12); i++;
buf[i] = 0x80 + byte((str[j] >> 6) & 0x3f); i++;
buf[i] = 0x80 + byte((str[j] ) & 0x3f); i++;
j++;
}
}
return string(buf[0..<i]);
}
arg_count: i32;
arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count);
arg_list := make([]string, arg_count);
for _, i in arg_list {
arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^);
}
return arg_list;
}
+221 -139
View File
@@ -1,11 +1,14 @@
#import "fmt.odin"; #import "fmt.odin";
#import "strings.odin";
Handle :: i32; Handle :: i32;
File_Time :: u64; File_Time :: u64;
Errno :: int; Errno :: int;
// INVALID_HANDLE: Handle : -1; // TODO(zangent): Find out how to make this work on x64 and x32.
AddressSize :: i64;
// INVALID_HANDLE: Handle : -1;
O_RDONLY :: 0x00000; O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001; O_WRONLY :: 0x00001;
@@ -19,91 +22,178 @@ O_APPEND :: 0x00400;
O_SYNC :: 0x01000; O_SYNC :: 0x01000;
O_ASYNC :: 0x02000; O_ASYNC :: 0x02000;
O_CLOEXEC :: 0x80000; O_CLOEXEC :: 0x80000;
SEEK_SET :: 0;
SEEK_CUR :: 1;
SEEK_END :: 2;
SEEK_DATA :: 3;
SEEK_HOLE :: 4;
SEEK_MAX :: SEEK_HOLE;
// ERROR_NONE: Errno : 0; // NOTE(zangent): These are OS specific!
// ERROR_FILE_NOT_FOUND: Errno : 2; // Do not mix these up!
// ERROR_PATH_NOT_FOUND: Errno : 3; RTLD_LAZY :: 0x1;
// ERROR_ACCESS_DENIED: Errno : 5; RTLD_NOW :: 0x2;
// ERROR_NO_MORE_FILES: Errno : 18; RTLD_LOCAL :: 0x4;
// ERROR_HANDLE_EOF: Errno : 38; RTLD_GLOBAL :: 0x8;
// ERROR_NETNAME_DELETED: Errno : 64; RTLD_NODELETE :: 0x80;
// ERROR_FILE_EXISTS: Errno : 80; RTLD_NOLOAD :: 0x10;
// ERROR_BROKEN_PIPE: Errno : 109; RTLD_FIRST :: 0x100;
// 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 args: [dynamic]string;
// ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
FileTime :: struct #ordered {
seconds: i64,
nanoseconds: i64
}
Stat :: struct #ordered {
device_id : i32, // ID of device containing file
mode : u16, // Mode of the file
nlink : u16, // Number of hard links
serial : u64, // File serial number
uid : u32, // User ID of the file's owner
gid : u32, // Group ID of the file's group
rdev : i32, // Device ID, if device
last_access : FileTime, // Time of last access
modified : FileTime, // Time of last modification
status_change : FileTime, // Time of last status change
created : FileTime, // Time of creation
size : i64, // Size of the file, in bytes
blocks : i64, // Number of blocks allocated for the file
block_size: i32, // Optimal blocksize for I/O
flags : u32, // User-defined flags for the file
gen_num : u32, // File generation number ...?
_spare : i32, // RESERVED
_reserve1,
_reserve2 : i64, // RESERVED
};
// File type
S_IFMT :: 0170000; // Type of file mask
S_IFIFO :: 0010000; // Named pipe (fifo)
S_IFCHR :: 0020000; // Character special
S_IFDIR :: 0040000; // Directory
S_IFBLK :: 0060000; // Block special
S_IFREG :: 0100000; // Regular
S_IFLNK :: 0120000; // Symbolic link
S_IFSOCK :: 0140000; // Socket
// File mode
// Read, write, execute/search by owner
S_IRWXU :: 0000700; // RWX mask for owner
S_IRUSR :: 0000400; // R for owner
S_IWUSR :: 0000200; // W for owner
S_IXUSR :: 0000100; // X for owner
// Read, write, execute/search by group
S_IRWXG :: 0000070; // RWX mask for group
S_IRGRP :: 0000040; // R for group
S_IWGRP :: 0000020; // W for group
S_IXGRP :: 0000010; // X for group
// Read, write, execute/search by others
S_IRWXO :: 0000007; // RWX mask for other
S_IROTH :: 0000004; // R for other
S_IWOTH :: 0000002; // W for other
S_IXOTH :: 0000001; // X for other
S_ISUID :: 0004000; // Set user id on execution
S_ISGID :: 0002000; // Set group id on execution
S_ISVTX :: 0001000; // Directory restrcted delete
S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; }
S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; }
S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; }
S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; }
S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; }
S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; }
S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;}
R_OK :: 4; // Test for read permission
W_OK :: 2; // Test for write permission
X_OK :: 1; // Test for execute permission
F_OK :: 0; // Test for file existance
#foreign_system_library dl "dl";
#foreign_system_library libc "c"; #foreign_system_library libc "c";
unix_open :: proc(path: ^u8, mode: int, perm: u32) -> Handle #foreign libc "open"; unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open";
unix_close :: proc(handle: Handle) #foreign libc "close"; unix_close :: proc(handle: Handle) #foreign libc "close";
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "read"; unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "read";
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "write"; unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "write";
unix_gettid :: proc() -> u64 #foreign libc "gettid"; unix_lseek :: proc(fs: Handle, offset: AddressSize, whence: int) -> AddressSize #foreign libc "lseek";
unix_gettid :: proc() -> u64 #foreign libc "gettid";
unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #foreign libc "stat";
unix_access :: proc(path: ^u8, mask: int) -> int #foreign libc "access";
unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc"; unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
unix_free :: proc(ptr: rawptr) #foreign libc "free"; unix_free :: proc(ptr: rawptr) #foreign libc "free";
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc"; unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv";
unix_exit :: proc(status: int) #foreign libc "exit"; unix_exit :: proc(status: int) #foreign libc "exit";
unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen";
unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym";
unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose";
unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror";
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
cstr := strings.new_c_string(path);
handle := unix_open(cstr, mode);
free(cstr);
if(handle == -1) {
return 0, 1;
}
return handle, 0;
}
// NOTE(zangent): This is here for compatability reasons. Should this be here?
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) { open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
return unix_open(path.data, mode, perm), 0; return open_simple(path, mode);
} }
close :: proc(fd: Handle) { close :: proc(fd: Handle) {
unix_close(fd); unix_close(fd);
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) {
return unix_write(fd, data.data, data.count), 0; assert(fd != -1);
bytes_written := unix_write(fd, &data[0], len(data));
if(bytes_written == -1) {
return 0, 1;
}
return bytes_written, 0;
} }
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) {
return unix_read(fd, data.data, data.count), 0; assert(fd != -1);
bytes_read := unix_read(fd, &data[0], len(data));
if(bytes_read == -1) {
return 0, 1;
}
return bytes_read, 0;
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: AddressSize, whence: int) -> (AddressSize, Errno) {
/* assert(fd != -1);
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;
*/ final_offset := unix_lseek(fd, offset, whence);
return 0, 0; if(final_offset == -1) {
return 0, 1;
}
return final_offset, 0;
} }
@@ -112,111 +202,85 @@ stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE);
stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE); stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE);
stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE); stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE);
/* TODO(zangent): Implement these!
last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {}
*/
/* stat :: proc(path: string) -> (Stat, bool) #inline {
get_std_handle :: proc(h: int) -> Handle { s: Stat;
fd := win32.GetStdHandle(cast(i32)h); cstr := strings.new_c_string(path);
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0); defer free(cstr);
return cast(Handle)fd; ret_int := unix_stat(cstr, &s);
return s, ret_int==0;
} }
access :: proc(path: string, mask: int) -> bool #inline {
cstr := strings.new_c_string(path);
defer free(cstr);
return unix_access(cstr, mask) == 0;
last_write_time :: 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) { read_entire_file :: proc(name: string) -> ([]byte, bool) {
buf: [300]byte;
copy(buf[:], cast([]byte)name);
fd, err := open(name, O_RDONLY, 0); handle, err := open_simple(name, O_RDONLY);
if err != ERROR_NONE { if(err != 0) {
fmt.println("Failed to open file.");
return nil, false; return nil, false;
} }
defer close(fd); defer(close(handle));
length: i64; // We have a file!
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
if !file_size_ok { size: AddressSize;
size, err = seek(handle, 0, SEEK_END);
if(err != 0) {
fmt.println("Failed to seek to end of file.");
return nil, false; return nil, false;
} }
data := new_slice(u8, length); _, err = seek(handle, 0, SEEK_SET);
if data.data == nil { if(err != 0) {
fmt.println("Failed to seek to beginning of file.");
return nil, false; return nil, false;
} }
single_read_length: i32; // We have a file size!
total_read: i64;
for total_read < length { data := make([]u8, size+1);
remaining := length - total_read; if data == nil {
to_read: u32; fmt.println("Failed to allocate file buffer.");
MAX :: 1<<32-1; return nil, false;
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;
} }
read(handle, data);
data[size] = 0;
return data, true; return data, true;
} }
heap_alloc :: proc(size: int) -> rawptr #inline {
*/
heap_alloc :: proc(size: int) -> rawptr {
assert(size > 0); assert(size > 0);
return unix_malloc(size); return unix_malloc(size);
} }
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr #inline {
return unix_realloc(ptr, new_size); return unix_realloc(ptr, new_size);
} }
heap_free :: proc(ptr: rawptr) { heap_free :: proc(ptr: rawptr) #inline {
unix_free(ptr); unix_free(ptr);
} }
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.new_c_string(name);
cstr: ^u8 = unix_getenv(path_str);
free(path_str);
if(cstr == nil) {
return "", false;
}
return strings.to_odin_string(cstr), true;
}
exit :: proc(code: int) { exit :: proc(code: int) #inline {
unix_exit(code); unix_exit(code);
} }
@@ -226,5 +290,23 @@ current_thread_id :: proc() -> int {
return 0; return 0;
} }
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
cstr := strings.new_c_string(filename);
handle := unix_dlopen(cstr, flags);
free(cstr);
return handle;
}
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
assert(handle != nil);
cstr := strings.new_c_string(symbol);
proc_handle := unix_dlsym(handle, cstr);
free(cstr);
return proc_handle;
}
dlclose :: proc(handle: rawptr) -> bool #inline {
assert(handle != nil);
return unix_dlclose(handle) == 0;
}
dlerror :: proc() -> string {
return strings.to_odin_string(unix_dlerror());
}
+27
View File
@@ -0,0 +1,27 @@
Any :: struct #ordered {
data: rawptr,
type_info: ^Type_Info,
}
String :: struct #ordered {
data: ^byte,
len: int,
};
Slice :: struct #ordered {
data: rawptr,
len: int,
cap: int,
};
Dynamic_Array :: struct #ordered {
data: rawptr,
len: int,
cap: int,
allocator: Allocator,
};
Dynamic_Map :: struct #ordered {
hashes: [dynamic]int,
entries: Dynamic_Array,
};
+119 -75
View File
@@ -18,24 +18,65 @@ parse_bool :: proc(s: string) -> (result: bool, ok: bool) {
return false, false; return false, false;
} }
_digit_value :: proc(r: rune) -> (int) {
ri := int(r);
v: int = 16;
match r {
case '0'..'9': v = ri-'0';
case 'a'..'z': v = ri-'a'+10;
case 'A'..'Z': v = ri-'A'+10;
}
return v;
}
parse_i64 :: proc(s: string, base: int) -> i64 {
result: i64;
for r in s {
v := _digit_value(r);
if v >= base {
break;
}
result *= i64(base);
result += i64(v);
}
return result;
}
parse_u64 :: proc(s: string, base: int) -> u64 {
result: u64;
for r in s {
v := _digit_value(r);
if v >= base {
break;
}
result *= u64(base);
result += u64(v);
}
return result;
}
parse_int :: proc(s: string, base: int) -> int {
return int(parse_i64(s, base));
}
parse_uint :: proc(s: string, base: int) -> uint {
return uint(parse_u64(s, base));
}
append_bool :: proc(buf: []byte, b: bool) -> string { append_bool :: proc(buf: []byte, b: bool) -> string {
s := b ? "true" : "false"; s := b ? "true" : "false";
append(buf, ..cast([]byte)s); append(buf, ..[]byte(s));
return cast(string)buf; return string(buf);
} }
append_uint :: proc(buf: []byte, u: u64, base: int) -> string { append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
return append_bits(buf, u, base, false, digits, 0); return append_bits(buf, u, base, false, 8*size_of(uint), digits, 0);
} }
append_int :: proc(buf: []byte, i: i64, base: int) -> string { append_int :: proc(buf: []byte, i: i64, base: int) -> string {
return append_bits(buf, cast(u64)i, base, i < 0, digits, 0); return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, 0);
}
itoa :: proc(buf: []byte, i: int) -> string {
return append_int(buf, cast(i64)i, 10);
} }
itoa :: proc(buf: []byte, i: int) -> string { return append_int(buf, i64(i), 10); }
append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string {
return cast(string)generic_ftoa(buf, f, fmt, prec, bit_size); return string(generic_ftoa(buf, f, fmt, prec, bit_size));
} }
@@ -63,18 +104,18 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
flt: ^Float_Info; flt: ^Float_Info;
match bit_size { match bit_size {
case 32: case 32:
bits = cast(u64)transmute(u32)cast(f32)val; bits = u64(transmute(u32, f32(val)));
flt = ^f32_info; flt = &f32_info;
case 64: case 64:
bits = transmute(u64)val; bits = transmute(u64, val);
flt = ^f64_info; flt = &f64_info;
default: default:
panic("strconv: invalid bit_size"); panic("strconv: invalid bit_size");
} }
neg := bits>>(flt.expbits+flt.mantbits) != 0; neg := bits>>(flt.expbits+flt.mantbits) != 0;
exp := cast(int)(bits>>flt.mantbits) & (1<<flt.expbits - 1); exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1);
mant := bits & (cast(u64)1 << flt.mantbits - 1); mant := bits & (u64(1) << flt.mantbits - 1);
match exp { match exp {
case 1<<flt.expbits - 1: case 1<<flt.expbits - 1:
@@ -86,22 +127,22 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
} else { } else {
s = "+Inf"; s = "+Inf";
} }
append(buf, ..cast([]byte)s); append(buf, ..[]byte(s));
return buf; return buf;
case 0: // denormalized case 0: // denormalized
exp++; exp++;
default: default:
mant |= cast(u64)1 << flt.mantbits; mant |= u64(1) << flt.mantbits;
} }
exp += flt.bias; exp += flt.bias;
d_: Decimal; d_: Decimal;
d := ^d_; d := &d_;
assign(d, mant); assign(d, mant);
shift(d, exp - cast(int)flt.mantbits); shift(d, exp - int(flt.mantbits));
digs: Decimal_Slice; digs: Decimal_Slice;
shortest := prec < 0; shortest := prec < 0;
if shortest { if shortest {
@@ -133,50 +174,33 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []byte { format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []byte {
match fmt { match fmt {
case 'f', 'F': case 'f', 'F':
add_bytes :: proc(dst: ^[]byte, w: ^int, bytes: ..byte) { append(buf, neg ? '-' : '+');
for b in bytes {
if dst.capacity <= w^ {
break;
}
dst.count++;
dst[w^] = b;
w^++;
}
}
dst := buf[..];
w := 0;
if neg {
add_bytes(^dst, ^w, '-');
} else {
add_bytes(^dst, ^w, '+');
}
// integer, padded with zeros when needed // integer, padded with zeros when needed
if digs.decimal_point > 0 { if digs.decimal_point > 0 {
m := min(digs.count, digs.decimal_point); m := min(digs.count, digs.decimal_point);
add_bytes(^dst, ^w, ..digs.digits[..m]); append(buf, ..digs.digits[0..<m]);
for ; m < digs.decimal_point; m++ { for ; m < digs.decimal_point; m++ {
add_bytes(^dst, ^w, '0'); append(buf, '0');
} }
} else { } else {
add_bytes(^dst, ^w, '0'); append(buf, '0');
} }
// fractional part // fractional part
if prec > 0 { if prec > 0 {
add_bytes(^dst, ^w, '.'); append(buf, '.');
for i in 0..prec { for i in 0..prec {
c: byte = '0'; c: byte = '0';
if j := digs.decimal_point + i; 0 <= j && j < digs.count { if j := digs.decimal_point + i; 0 <= j && j < digs.count {
c = digs.digits[j]; c = digs.digits[j];
} }
add_bytes(^dst, ^w, c); append(buf, c);
} }
} }
return buf[..w]; return buf;
case 'e', 'E': case 'e', 'E':
panic("strconv: e/E float printing is not yet supported"); panic("strconv: e/E float printing is not yet supported");
@@ -207,14 +231,14 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
332*(dp-nd) >= 100*(exp-mantbits) 332*(dp-nd) >= 100*(exp-mantbits)
*/ */
minexp := flt.bias+1; minexp := flt.bias+1;
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - cast(int)flt.mantbits) { if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
// Number is already its shortest // Number is already its shortest
return; return;
} }
upper_: Decimal; upper: = ^upper_; upper_: Decimal; upper: = &upper_;
assign(upper, 2*mant - 1); assign(upper, 2*mant - 1);
shift(upper, exp - cast(int)flt.mantbits - 1); shift(upper, exp - int(flt.mantbits) - 1);
mantlo: u64; mantlo: u64;
explo: int; explo: int;
@@ -225,13 +249,13 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
mantlo = 2*mant - 1; mantlo = 2*mant - 1;
explo = exp-1; explo = exp-1;
} }
lower_: Decimal; lower: = ^lower_; lower_: Decimal; lower: = &lower_;
assign(lower, 2*mantlo + 1); assign(lower, 2*mantlo + 1);
shift(lower, explo - cast(int)flt.mantbits - 1); shift(lower, explo - int(flt.mantbits) - 1);
inclusive := mant%2 == 0; inclusive := mant%2 == 0;
for i in 0..d.count { for i in 0..<d.count {
l: byte = '0'; // lower digit l: byte = '0'; // lower digit
if i < lower.count { if i < lower.count {
l = lower.digits[i]; l = lower.digits[i];
@@ -265,7 +289,39 @@ MAX_BASE :: 32;
immutable digits := "0123456789abcdefghijklmnopqrstuvwxyz"; immutable digits := "0123456789abcdefghijklmnopqrstuvwxyz";
append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, flags: Int_Flag) -> string { is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned: u64, neg: bool) {
neg := false;
if is_signed {
match bit_size {
case 8:
i := i8(u);
neg = i < 0;
if neg { i = -i; }
u = u64(i);
case 16:
i := i16(u);
neg = i < 0;
if neg { i = -i; }
u = u64(i);
case 32:
i := i32(u);
neg = i < 0;
if neg { i = -i; }
u = u64(i);
case 64:
i := i64(u);
neg = i < 0;
if neg { i = -i; }
u = u64(i);
default:
panic("is_integer_negative: Unknown integer size");
}
}
return u, neg;
}
append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flag) -> string {
is_pow2 :: proc(x: i64) -> bool { is_pow2 :: proc(x: i64) -> bool {
if (x <= 0) { if (x <= 0) {
return false; return false;
@@ -278,33 +334,20 @@ append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, f
} }
a: [65]byte; a: [65]byte;
i := a.count; i := len(a);
if neg {
u = -u; neg: bool;
u, neg = is_integer_negative(u, is_signed, bit_size);
for b := u64(base); u >= b; {
i--;
q := u / b;
a[i] = digits[uint(u-q*b)];
u = q;
} }
if is_pow2(cast(i64)base) { i--;
b := cast(u64)base; a[i] = digits[uint(u)];
m := cast(uint)b - 1;
for u >= b {
i--;
a[i] = digits[cast(uint)u & m];
u >>= b;
}
i--;
a[i] = digits[cast(uint)u];
} else {
b := cast(u64)base;
for u >= b {
i--;
q := u / b;
a[i] = digits[cast(uint)(u-q*b)];
u = q;
}
i--;
a[i] = digits[cast(uint)u];
}
if flags&Int_Flag.PREFIX != 0 { if flags&Int_Flag.PREFIX != 0 {
ok := true; ok := true;
@@ -312,6 +355,7 @@ append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, f
case 2: i--; a[i] = 'b'; case 2: i--; a[i] = 'b';
case 8: i--; a[i] = 'o'; case 8: i--; a[i] = 'o';
case 10: i--; a[i] = 'd'; case 10: i--; a[i] = 'd';
case 12: i--; a[i] = 'z';
case 16: i--; a[i] = 'x'; case 16: i--; a[i] = 'x';
default: ok = false; default: ok = false;
} }
@@ -331,6 +375,6 @@ append_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, f
append(buf, ..a[i..]); append(buf, ..a[i..]);
return cast(string)buf; return string(buf);
} }
+8 -9
View File
@@ -1,15 +1,14 @@
new_c_string :: proc(s: string) -> ^byte { new_c_string :: proc(s: string) -> ^byte {
c := new_slice(byte, s.count+1); c := make([]byte, len(s)+1);
copy(c, cast([]byte)s); copy(c, []byte(s));
c[s.count] = 0; c[len(s)] = 0;
return c.data; return &c[0];
} }
to_odin_string :: proc(c: ^byte) -> string { to_odin_string :: proc(c: ^byte) -> string {
s: string; len := 0;
s.data = c; for (c+len)^ != 0 {
for (c+s.count)^ != 0 { len++;
s.count++;
} }
return s; return string(slice_ptr(c, len));
} }
+2 -91
View File
@@ -1,91 +1,2 @@
#import win32 "sys/windows.odin" when ODIN_OS == "windows"; #load "sync_windows.odin" when ODIN_OS == "windows";
#import "atomic.odin"; #load "sync_linux.odin" when ODIN_OS == "linux";
Semaphore :: struct {
_handle: win32.HANDLE,
}
Mutex :: struct {
_semaphore: Semaphore,
_counter: i32,
_owner: i32,
_recursion: i32,
}
current_thread_id :: proc() -> i32 {
return cast(i32)win32.GetCurrentThreadId();
}
semaphore_init :: proc(s: ^Semaphore) {
s._handle = win32.CreateSemaphoreA(nil, 0, 1<<31-1, nil);
}
semaphore_destroy :: proc(s: ^Semaphore) {
win32.CloseHandle(s._handle);
}
semaphore_post :: proc(s: ^Semaphore, count: int) {
win32.ReleaseSemaphore(s._handle, cast(i32)count, nil);
}
semaphore_release :: proc(s: ^Semaphore) #inline { semaphore_post(s, 1); }
semaphore_wait :: proc(s: ^Semaphore) {
win32.WaitForSingleObject(s._handle, win32.INFINITE);
}
mutex_init :: proc(m: ^Mutex) {
atomic.store(^m._counter, 0);
atomic.store(^m._owner, current_thread_id());
semaphore_init(^m._semaphore);
m._recursion = 0;
}
mutex_destroy :: proc(m: ^Mutex) {
semaphore_destroy(^m._semaphore);
}
mutex_lock :: proc(m: ^Mutex) {
thread_id := current_thread_id();
if atomic.fetch_add(^m._counter, 1) > 0 {
if thread_id != atomic.load(^m._owner) {
semaphore_wait(^m._semaphore);
}
}
atomic.store(^m._owner, thread_id);
m._recursion++;
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
thread_id := current_thread_id();
if atomic.load(^m._owner) == thread_id {
atomic.fetch_add(^m._counter, 1);
} else {
expected: i32 = 0;
if atomic.load(^m._counter) != 0 {
return false;
}
if atomic.compare_exchange(^m._counter, expected, 1) == 0 {
return false;
}
atomic.store(^m._owner, thread_id);
}
m._recursion++;
return true;
}
mutex_unlock :: proc(m: ^Mutex) {
recursion: i32;
thread_id := current_thread_id();
assert(thread_id == atomic.load(^m._owner));
m._recursion--;
recursion = m._recursion;
if recursion == 0 {
atomic.store(^m._owner, thread_id);
}
if atomic.fetch_add(^m._counter, -1) > 1 {
if recursion == 0 {
semaphore_release(^m._semaphore);
}
}
}
+35 -25
View File
@@ -7,11 +7,12 @@ CONTEXT_FLAGS_ARB :: 0x2094;
CONTEXT_PROFILE_MASK_ARB :: 0x9126; CONTEXT_PROFILE_MASK_ARB :: 0x9126;
CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002; CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002;
CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001; CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001;
CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x00000002;
HGLRC :: HANDLE; Hglrc :: Handle;
COLORREF :: u32; Color_Ref :: u32;
LAYERPLANEDESCRIPTOR :: struct #ordered { Layer_Plane_Descriptor :: struct #ordered {
size: u16, size: u16,
version: u16, version: u16,
flags: u32, flags: u32,
@@ -35,38 +36,47 @@ LAYERPLANEDESCRIPTOR :: struct #ordered {
aux_buffers: byte, aux_buffers: byte,
layer_type: byte, layer_type: byte,
reserved: byte, reserved: byte,
transparent: COLORREF, transparent: Color_Ref,
} }
POINTFLOAT :: struct #ordered { Point_Float :: struct #ordered {
x, y: f32, x, y: f32,
} }
GLYPHMETRICSFLOAT :: struct #ordered { Glyph_Metrics_Float :: struct #ordered {
black_box_x: f32, black_box_x: f32,
black_box_y: f32, black_box_y: f32,
glyph_origin: POINTFLOAT, glyph_origin: Point_Float,
cell_inc_x: f32, cell_inc_x: f32,
cell_inc_y: f32, cell_inc_y: f32,
} }
CreateContextAttribsARBType :: #type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC; CreateContextAttribsARB_Type :: #type proc(hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc;
ChoosePixelFormatARBType :: #type proc(hdc: HDC, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> BOOL #cc_c; ChoosePixelFormatARB_Type :: #type proc(hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool #cc_c;
SwapIntervalEXT_Type :: #type proc(interval : i32) -> bool #cc_c;
GetExtensionsStringARB_Type :: #type proc(Hdc) -> ^byte #cc_c;
CreateContext :: proc(hdc: HDC) -> HGLRC #foreign opengl32 "wglCreateContext"; CreateContextAttribsARB: CreateContextAttribsARB_Type;
MakeCurrent :: proc(hdc: HDC, hglrc: HGLRC) -> BOOL #foreign opengl32 "wglMakeCurrent"; ChoosePixelFormatARB: ChoosePixelFormatARB_Type;
GetProcAddress :: proc(c_str: ^u8) -> PROC #foreign opengl32 "wglGetProcAddress"; SwapIntervalEXT: SwapIntervalEXT_Type;
DeleteContext :: proc(hglrc: HGLRC) -> BOOL #foreign opengl32 "wglDeleteContext"; GetExtensionsStringARB: GetExtensionsStringARB_Type;
CopyContext :: proc(src, dst: HGLRC, mask: u32) -> BOOL #foreign opengl32 "wglCopyContext";
CreateLayerContext :: proc(hdc: HDC, layer_plane: i32) -> HGLRC #foreign opengl32 "wglCreateLayerContext";
DescribeLayerPlane :: proc(hdc: HDC, pixel_format, layer_plane: i32, bytes: u32, pd: ^LAYERPLANEDESCRIPTOR) -> BOOL #foreign opengl32 "wglDescribeLayerPlane";
GetCurrentContext :: proc() -> HGLRC #foreign opengl32 "wglGetCurrentContext"; CreateContext :: proc(hdc: Hdc) -> Hglrc #foreign opengl32 "wglCreateContext";
GetCurrentDC :: proc() -> HDC #foreign opengl32 "wglGetCurrentDC"; MakeCurrent :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool #foreign opengl32 "wglMakeCurrent";
GetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32 #foreign opengl32 "wglGetLayerPaletteEntries"; GetProcAddress :: proc(c_str: ^u8) -> Proc #foreign opengl32 "wglGetProcAddress";
RealizeLayerPalette :: proc(hdc: HDC, layer_plane: i32, realize: BOOL) -> BOOL #foreign opengl32 "wglRealizeLayerPalette"; DeleteContext :: proc(hglrc: Hglrc) -> Bool #foreign opengl32 "wglDeleteContext";
SetLayerPaletteEntries :: proc(hdc: HDC, layer_plane, start, entries: i32, cr: ^COLORREF) -> i32 #foreign opengl32 "wglSetLayerPaletteEntries"; CopyContext :: proc(src, dst: Hglrc, mask: u32) -> Bool #foreign opengl32 "wglCopyContext";
ShareLists :: proc(hglrc1, hglrc2: HGLRC) -> BOOL #foreign opengl32 "wglShareLists"; CreateLayerContext :: proc(hdc: Hdc, layer_plane: i32) -> Hglrc #foreign opengl32 "wglCreateLayerContext";
SwapLayerBuffers :: proc(hdc: HDC, planes: u32) -> BOOL #foreign opengl32 "wglSwapLayerBuffers"; DescribeLayerPlane :: proc(hdc: Hdc, pixel_format, layer_plane: i32, bytes: u32, pd: ^Layer_Plane_Descriptor) -> Bool #foreign opengl32 "wglDescribeLayerPlane";
UseFontBitmaps :: proc(hdc: HDC, first, count, list_base: u32) -> BOOL #foreign opengl32 "wglUseFontBitmaps"; GetCurrentContext :: proc() -> Hglrc #foreign opengl32 "wglGetCurrentContext";
UseFontOutlines :: proc(hdc: HDC, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^GLYPHMETRICSFLOAT) -> BOOL #foreign opengl32 "wglUseFontOutlines"; GetCurrentDC :: proc() -> Hdc #foreign opengl32 "wglGetCurrentDC";
GetLayerPaletteEntries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 #foreign opengl32 "wglGetLayerPaletteEntries";
RealizeLayerPalette :: proc(hdc: Hdc, layer_plane: i32, realize: Bool) -> Bool #foreign opengl32 "wglRealizeLayerPalette";
SetLayerPaletteEntries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^Color_Ref) -> i32 #foreign opengl32 "wglSetLayerPaletteEntries";
ShareLists :: proc(hglrc1, hglrc2: Hglrc) -> Bool #foreign opengl32 "wglShareLists";
SwapLayerBuffers :: proc(hdc: Hdc, planes: u32) -> Bool #foreign opengl32 "wglSwapLayerBuffers";
UseFontBitmaps :: proc(hdc: Hdc, first, count, list_base: u32) -> Bool #foreign opengl32 "wglUseFontBitmaps";
UseFontOutlines :: proc(hdc: Hdc, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^Glyph_Metrics_Float) -> Bool #foreign opengl32 "wglUseFontOutlines";
+135 -133
View File
@@ -2,29 +2,29 @@
#foreign_system_library "user32.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 "gdi32.lib" when ODIN_OS == "windows";
#foreign_system_library "winmm.lib" when ODIN_OS == "windows"; #foreign_system_library "winmm.lib" when ODIN_OS == "windows";
#foreign_system_library "shell32.lib" when ODIN_OS == "windows";
HANDLE :: rawptr; Handle :: rawptr;
HWND :: HANDLE; Hwnd :: Handle;
HDC :: HANDLE; Hdc :: Handle;
HINSTANCE :: HANDLE; Hinstance :: Handle;
HICON :: HANDLE; Hicon :: Handle;
HCURSOR :: HANDLE; Hcursor :: Handle;
HMENU :: HANDLE; Hmenu :: Handle;
HBRUSH :: HANDLE; Hbrush :: Handle;
HGDIOBJ :: HANDLE; Hgdiobj :: Handle;
HMODULE :: HANDLE; Hmodule :: Handle;
WPARAM :: uint; Wparam :: uint;
LPARAM :: int; Lparam :: int;
LRESULT :: int; Lresult :: int;
ATOM :: i16; Bool :: i32;
BOOL :: i32; Wnd_Proc :: #type proc(Hwnd, u32, Wparam, Lparam) -> Lresult #cc_c;
WNDPROC :: #type proc(HWND, u32, WPARAM, LPARAM) -> LRESULT #cc_c;
INVALID_HANDLE_VALUE :: cast(HANDLE)~cast(int)0; INVALID_HANDLE :: Handle(~int(0));
FALSE: BOOL : 0; FALSE: Bool : 0;
TRUE: BOOL : 1; TRUE: Bool : 1;
CS_VREDRAW :: 0x0001; CS_VREDRAW :: 0x0001;
CS_HREDRAW :: 0x0002; CS_HREDRAW :: 0x0002;
@@ -56,7 +56,7 @@ WM_CHAR :: 0x0102;
PM_REMOVE :: 1; PM_REMOVE :: 1;
COLOR_BACKGROUND :: cast(HBRUSH)(cast(int)1); COLOR_BACKGROUND :: Hbrush(int(1));
BLACK_BRUSH :: 4; BLACK_BRUSH :: 4;
SM_CXSCREEN :: 0; SM_CXSCREEN :: 0;
@@ -65,53 +65,53 @@ SM_CYSCREEN :: 1;
SW_SHOW :: 5; SW_SHOW :: 5;
POINT :: struct #ordered { Point :: struct #ordered {
x, y: i32, x, y: i32,
} }
WNDCLASSEXA :: struct #ordered { WndClassExA :: struct #ordered {
size, style: u32, size, style: u32,
wnd_proc: WNDPROC, wnd_proc: Wnd_Proc,
cls_extra, wnd_extra: i32, cls_extra, wnd_extra: i32,
instance: HINSTANCE, instance: Hinstance,
icon: HICON, icon: Hicon,
cursor: HCURSOR, cursor: Hcursor,
background: HBRUSH, background: Hbrush,
menu_name, class_name: ^u8, menu_name, class_name: ^u8,
sm: HICON, sm: Hicon,
} }
MSG :: struct #ordered { Msg :: struct #ordered {
hwnd: HWND, hwnd: Hwnd,
message: u32, message: u32,
wparam: WPARAM, wparam: Wparam,
lparam: LPARAM, lparam: Lparam,
time: u32, time: u32,
pt: POINT, pt: Point,
} }
RECT :: struct #ordered { Rect :: struct #ordered {
left: i32, left: i32,
top: i32, top: i32,
right: i32, right: i32,
bottom: i32, bottom: i32,
} }
FILETIME :: struct #ordered { Filetime :: struct #ordered {
lo, hi: u32, lo, hi: u32,
} }
SYSTEMTIME :: struct #ordered { Systemtime :: struct #ordered {
year, month: u16, year, month: u16,
day_of_week, day: u16, day_of_week, day: u16,
hour, minute, second, millisecond: u16, hour, minute, second, millisecond: u16,
} }
BY_HANDLE_FILE_INFORMATION :: struct #ordered { By_Handle_File_Information :: struct #ordered {
file_attributes: u32, file_attributes: u32,
creation_time, creation_time,
last_access_time, last_access_time,
last_write_time: FILETIME, last_write_time: Filetime,
volume_serial_number, volume_serial_number,
file_size_high, file_size_high,
file_size_low, file_size_low,
@@ -120,11 +120,11 @@ BY_HANDLE_FILE_INFORMATION :: struct #ordered {
file_index_low: u32, file_index_low: u32,
} }
FILE_ATTRIBUTE_DATA :: struct #ordered { File_Attribute_Data :: struct #ordered {
file_attributes: u32, file_attributes: u32,
creation_time, creation_time,
last_access_time, last_access_time,
last_write_time: FILETIME, last_write_time: Filetime,
file_size_high, file_size_high,
file_size_low: u32, file_size_low: u32,
} }
@@ -136,13 +136,13 @@ GetFileExMaxInfoLevel: GET_FILEEX_INFO_LEVELS : 1;
GetLastError :: proc() -> i32 #foreign kernel32; GetLastError :: proc() -> i32 #foreign kernel32;
ExitProcess :: proc(exit_code: u32) #foreign kernel32; ExitProcess :: proc(exit_code: u32) #foreign kernel32;
GetDesktopWindow :: proc() -> HWND #foreign user32; GetDesktopWindow :: proc() -> Hwnd #foreign user32;
GetCursorPos :: proc(p: ^POINT) -> i32 #foreign user32; GetCursorPos :: proc(p: ^Point) -> i32 #foreign user32;
ScreenToClient :: proc(h: HWND, p: ^POINT) -> i32 #foreign user32; ScreenToClient :: proc(h: Hwnd, p: ^Point) -> i32 #foreign user32;
GetModuleHandleA :: proc(module_name: ^u8) -> HINSTANCE #foreign kernel32; GetModuleHandleA :: proc(module_name: ^u8) -> Hinstance #foreign kernel32;
GetStockObject :: proc(fn_object: i32) -> HGDIOBJ #foreign gdi32; GetStockObject :: proc(fn_object: i32) -> Hgdiobj #foreign gdi32;
PostQuitMessage :: proc(exit_code: i32) #foreign user32; PostQuitMessage :: proc(exit_code: i32) #foreign user32;
SetWindowTextA :: proc(hwnd: HWND, c_string: ^u8) -> BOOL #foreign user32; SetWindowTextA :: proc(hwnd: Hwnd, c_string: ^u8) -> Bool #foreign user32;
QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign kernel32; QueryPerformanceFrequency :: proc(result: ^i64) -> i32 #foreign kernel32;
QueryPerformanceCounter :: proc(result: ^i64) -> i32 #foreign kernel32; QueryPerformanceCounter :: proc(result: ^i64) -> i32 #foreign kernel32;
@@ -152,64 +152,66 @@ Sleep :: proc(ms: i32) -> i32 #foreign kernel32;
OutputDebugStringA :: proc(c_str: ^u8) #foreign kernel32; OutputDebugStringA :: proc(c_str: ^u8) #foreign kernel32;
RegisterClassExA :: proc(wc: ^WNDCLASSEXA) -> ATOM #foreign user32; RegisterClassExA :: proc(wc: ^WndClassExA) -> i16 #foreign user32;
CreateWindowExA :: proc(ex_style: u32, CreateWindowExA :: proc(ex_style: u32,
class_name, title: ^u8, class_name, title: ^u8,
style: u32, style: u32,
x, y, w, h: i32, x, y, w, h: i32,
parent: HWND, menu: HMENU, instance: HINSTANCE, parent: Hwnd, menu: Hmenu, instance: Hinstance,
param: rawptr) -> HWND #foreign user32; param: rawptr) -> Hwnd #foreign user32;
ShowWindow :: proc(hwnd: HWND, cmd_show: i32) -> BOOL #foreign user32; ShowWindow :: proc(hwnd: Hwnd, cmd_show: i32) -> Bool #foreign user32;
TranslateMessage :: proc(msg: ^MSG) -> BOOL #foreign user32; TranslateMessage :: proc(msg: ^Msg) -> Bool #foreign user32;
DispatchMessageA :: proc(msg: ^MSG) -> LRESULT #foreign user32; DispatchMessageA :: proc(msg: ^Msg) -> Lresult #foreign user32;
UpdateWindow :: proc(hwnd: HWND) -> BOOL #foreign user32; UpdateWindow :: proc(hwnd: Hwnd) -> Bool #foreign user32;
PeekMessageA :: proc(msg: ^MSG, hwnd: HWND, PeekMessageA :: proc(msg: ^Msg, hwnd: Hwnd,
msg_filter_min, msg_filter_max, remove_msg: u32) -> BOOL #foreign user32; msg_filter_min, msg_filter_max, remove_msg: u32) -> Bool #foreign user32;
DefWindowProcA :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #foreign user32; DefWindowProcA :: proc(hwnd: Hwnd, msg: u32, wparam: Wparam, lparam: Lparam) -> Lresult #foreign user32;
AdjustWindowRect :: proc(rect: ^RECT, style: u32, menu: BOOL) -> BOOL #foreign user32; AdjustWindowRect :: proc(rect: ^Rect, style: u32, menu: Bool) -> Bool #foreign user32;
GetActiveWindow :: proc() -> HWND #foreign user32; GetActiveWindow :: proc() -> Hwnd #foreign user32;
DestroyWindow :: proc(wnd: HWND) -> BOOL #foreign user32; DestroyWindow :: proc(wnd: Hwnd) -> Bool #foreign user32;
DescribePixelFormat :: proc(dc: HDC, pixel_format: i32, bytes : u32, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign user32; DescribePixelFormat :: proc(dc: Hdc, pixel_format: i32, bytes : u32, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign user32;
GetQueryPerformanceFrequency :: proc() -> i64 { GetQueryPerformanceFrequency :: proc() -> i64 {
r: i64; r: i64;
QueryPerformanceFrequency(^r); QueryPerformanceFrequency(&r);
return r; return r;
} }
GetCommandLineA :: proc() -> ^u8 #foreign kernel32; GetCommandLineA :: proc() -> ^u8 #foreign kernel32;
GetSystemMetrics :: proc(index: i32) -> i32 #foreign kernel32; GetCommandLineW :: proc() -> ^u16 #foreign kernel32;
GetCurrentThreadId :: proc() -> u32 #foreign kernel32; GetSystemMetrics :: proc(index: i32) -> i32 #foreign kernel32;
GetCurrentThreadId :: proc() -> u32 #foreign kernel32;
CommandLineToArgvW :: proc(cmd_list: ^u16, num_args: ^i32) -> ^^u16 #foreign shell32;
timeGetTime :: proc() -> u32 #foreign winmm; timeGetTime :: proc() -> u32 #foreign winmm;
GetSystemTimeAsFileTime :: proc(system_time_as_file_time: ^FILETIME) #foreign kernel32; GetSystemTimeAsFileTime :: proc(system_time_as_file_time: ^Filetime) #foreign kernel32;
FileTimeToLocalFileTime :: proc(file_time: ^FILETIME, local_file_time: ^FILETIME) -> BOOL #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; FileTimeToSystemTime :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool #foreign kernel32;
SystemTimeToFileTime :: proc(system_time: ^SYSTEMTIME, file_time: ^FILETIME) -> BOOL #foreign kernel32; SystemTimeToFileTime :: proc(system_time: ^Systemtime, file_time: ^Filetime) -> Bool #foreign kernel32;
// File Stuff // File Stuff
CloseHandle :: proc(h: HANDLE) -> i32 #foreign kernel32; CloseHandle :: proc(h: Handle) -> i32 #foreign kernel32;
GetStdHandle :: proc(h: i32) -> HANDLE #foreign kernel32; GetStdHandle :: proc(h: i32) -> Handle #foreign kernel32;
CreateFileA :: proc(filename: ^u8, desired_access, share_mode: u32, CreateFileA :: proc(filename: ^u8, desired_access, share_mode: u32,
security: rawptr, security: rawptr,
creation, flags_and_attribs: u32, template_file: HANDLE) -> HANDLE #foreign kernel32; creation, flags_and_attribs: u32, template_file: Handle) -> Handle #foreign kernel32;
ReadFile :: proc(h: HANDLE, buf: rawptr, to_read: u32, bytes_read: ^i32, overlapped: rawptr) -> BOOL #foreign kernel32; 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; WriteFile :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool #foreign kernel32;
GetFileSizeEx :: proc(file_handle: HANDLE, file_size: ^i64) -> BOOL #foreign kernel32; 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; 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; GetFileInformationByHandle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool #foreign kernel32;
GetFileType :: proc(file_handle: HANDLE) -> u32 #foreign kernel32; GetFileType :: proc(file_handle: Handle) -> u32 #foreign kernel32;
SetFilePointer :: proc(file_handle: HANDLE, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign kernel32; SetFilePointer :: proc(file_handle: Handle, distance_to_move: i32, distance_to_move_high: ^i32, move_method: u32) -> u32 #foreign kernel32;
SetHandleInformation :: proc(obj: HANDLE, mask, flags: u32) -> BOOL #foreign kernel32; SetHandleInformation :: proc(obj: Handle, mask, flags: u32) -> Bool #foreign kernel32;
HANDLE_FLAG_INHERIT :: 1; HANDLE_FLAG_INHERIT :: 1;
HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2; HANDLE_FLAG_PROTECT_FROM_CLOSE :: 2;
@@ -242,13 +244,13 @@ TRUNCATE_EXISTING :: 5;
FILE_ATTRIBUTE_READONLY :: 0x00000001; FILE_ATTRIBUTE_READONLY :: 0x00000001;
FILE_ATTRIBUTE_HIDDEN :: 0x00000002; FILE_ATTRIBUTE_HIDDEN :: 0x00000002;
FILE_ATTRIBUTE_SYSTEM :: 0x00000004; FILE_ATTRIBUTE_SYSTEM :: 0x00000004;
FILE_ATTRIBUTE_DIRECTORY :: 0x00000010; FILE_ATTRIBUTE_DIRectORY :: 0x00000010;
FILE_ATTRIBUTE_ARCHIVE :: 0x00000020; FILE_ATTRIBUTE_ARCHIVE :: 0x00000020;
FILE_ATTRIBUTE_DEVICE :: 0x00000040; FILE_ATTRIBUTE_DEVICE :: 0x00000040;
FILE_ATTRIBUTE_NORMAL :: 0x00000080; FILE_ATTRIBUTE_NORMAL :: 0x00000080;
FILE_ATTRIBUTE_TEMPORARY :: 0x00000100; FILE_ATTRIBUTE_TEMPORARY :: 0x00000100;
FILE_ATTRIBUTE_SPARSE_FILE :: 0x00000200; FILE_ATTRIBUTE_SPARSE_FILE :: 0x00000200;
FILE_ATTRIBUTE_REPARSE_POINT :: 0x00000400; FILE_ATTRIBUTE_REPARSE_Point :: 0x00000400;
FILE_ATTRIBUTE_COMPRESSED :: 0x00000800; FILE_ATTRIBUTE_COMPRESSED :: 0x00000800;
FILE_ATTRIBUTE_OFFLINE :: 0x00001000; FILE_ATTRIBUTE_OFFLINE :: 0x00001000;
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED :: 0x00002000; FILE_ATTRIBUTE_NOT_CONTENT_INDEXED :: 0x00002000;
@@ -258,32 +260,32 @@ FILE_TYPE_DISK :: 0x0001;
FILE_TYPE_CHAR :: 0x0002; FILE_TYPE_CHAR :: 0x0002;
FILE_TYPE_PIPE :: 0x0003; FILE_TYPE_PIPE :: 0x0003;
INVALID_SET_FILE_POINTER :: ~cast(u32)0; INVALID_SET_FILE_POINTER :: ~u32(0);
HeapAlloc :: proc (h: HANDLE, flags: u32, bytes: int) -> rawptr #foreign kernel32; HeapAlloc :: proc (h: Handle, flags: u32, bytes: int) -> rawptr #foreign kernel32;
HeapReAlloc :: proc (h: HANDLE, flags: u32, memory: rawptr, 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; HeapFree :: proc (h: Handle, flags: u32, memory: rawptr) -> Bool #foreign kernel32;
GetProcessHeap :: proc () -> HANDLE #foreign kernel32; GetProcessHeap :: proc () -> Handle #foreign kernel32;
HEAP_ZERO_MEMORY :: 0x00000008; HEAP_ZERO_MEMORY :: 0x00000008;
// Synchronization // Synchronization
SECURITY_ATTRIBUTES :: struct #ordered { Security_Attributes :: struct #ordered {
length: u32, length: u32,
security_descriptor: rawptr, security_descriptor: rawptr,
inherit_handle: BOOL, inherit_handle: Bool,
} }
INFINITE :: 0xffffffff; INFINITE :: 0xffffffff;
CreateSemaphoreA :: proc(attributes: ^SECURITY_ATTRIBUTES, initial_count, maximum_count: i32, name: ^byte) -> HANDLE #foreign kernel32; CreateSemaphoreA :: proc(attributes: ^Security_Attributes, initial_count, maximum_count: i32, name: ^byte) -> Handle #foreign kernel32;
ReleaseSemaphore :: proc(semaphore: HANDLE, release_count: i32, previous_count: ^i32) -> BOOL #foreign kernel32; ReleaseSemaphore :: proc(semaphore: Handle, release_count: i32, previous_count: ^i32) -> Bool #foreign kernel32;
WaitForSingleObject :: proc(handle: HANDLE, milliseconds: u32) -> u32 #foreign kernel32; WaitForSingleObject :: proc(handle: Handle, milliseconds: u32) -> u32 #foreign kernel32;
InterlockedCompareExchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 #foreign kernel32; InterlockedCompareExchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 #foreign kernel32;
@@ -307,11 +309,11 @@ ReadBarrier :: proc() #foreign kernel32;
HMONITOR :: HANDLE; Hmonitor :: Handle;
GWL_STYLE :: -16; GWL_STYLE :: -16;
HWND_TOP :: cast(HWND)cast(uint)0; Hwnd_TOP :: Hwnd(uint(0));
MONITOR_DEFAULTTONULL :: 0x00000000; MONITOR_DEFAULTTONULL :: 0x00000000;
MONITOR_DEFAULTTOPRIMARY :: 0x00000001; MONITOR_DEFAULTTOPRIMARY :: 0x00000001;
@@ -324,39 +326,40 @@ SWP_NOSIZE :: 0x0001;
SWP_NOMOVE :: 0x0002; SWP_NOMOVE :: 0x0002;
MONITORINFO :: struct #ordered { Monitor_Info :: struct #ordered {
size: u32, size: u32,
monitor: RECT, monitor: Rect,
work: RECT, work: Rect,
flags: u32, flags: u32,
} }
WINDOWPLACEMENT :: struct #ordered { Window_Placement :: struct #ordered {
length: u32, length: u32,
flags: u32, flags: u32,
show_cmd: u32, show_cmd: u32,
min_pos: POINT, min_pos: Point,
max_pos: POINT, max_pos: Point,
normal_pos: RECT, normal_pos: Rect,
} }
GetMonitorInfoA :: proc(monitor: HMONITOR, mi: ^MONITORINFO) -> BOOL #foreign user32; GetMonitorInfoA :: proc(monitor: Hmonitor, mi: ^Monitor_Info) -> Bool #foreign user32;
MonitorFromWindow :: proc(wnd: HWND, flags : u32) -> HMONITOR #foreign user32; MonitorFromWindow :: proc(wnd: Hwnd, flags : u32) -> Hmonitor #foreign user32;
SetWindowPos :: proc(wnd: HWND, wndInsertAfter: HWND, x, y, width, height: i32, flags: u32) #foreign user32 "SetWindowPos"; SetWindowPos :: proc(wnd: Hwnd, wndInsertAfter: Hwnd, x, y, width, height: i32, flags: u32) #foreign user32 "SetWindowPos";
GetWindowPlacement :: proc(wnd: HWND, wndpl: ^WINDOWPLACEMENT) -> BOOL #foreign user32; GetWindowPlacement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #foreign user32;
SetWindowPlacement :: proc(wnd: HWND, wndpl: ^WINDOWPLACEMENT) -> BOOL #foreign user32; SetWindowPlacement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #foreign user32;
GetWindowRect :: proc(wnd: Hwnd, rect: ^Rect) -> Bool #foreign user32;
GetWindowLongPtrA :: proc(wnd: HWND, index: i32) -> i64 #foreign user32; GetWindowLongPtrA :: proc(wnd: Hwnd, index: i32) -> i64 #foreign user32;
SetWindowLongPtrA :: proc(wnd: HWND, index: i32, new: i64) -> i64 #foreign user32; SetWindowLongPtrA :: proc(wnd: Hwnd, index: i32, new: i64) -> i64 #foreign user32;
GetWindowText :: proc(wnd: HWND, str: ^byte, maxCount: i32) -> i32 #foreign user32; GetWindowText :: proc(wnd: Hwnd, str: ^byte, maxCount: i32) -> i32 #foreign user32;
HIWORD :: proc(wParam: WPARAM) -> u16 { return cast(u16)((cast(u32)wParam >> 16) & 0xffff); } HIWORD :: proc(wParam: Wparam) -> u16 { return u16((u32(wParam) >> 16) & 0xffff); }
HIWORD :: proc(lParam: LPARAM) -> u16 { return cast(u16)((cast(u32)lParam >> 16) & 0xffff); } HIWORD :: proc(lParam: Lparam) -> u16 { return u16((u32(lParam) >> 16) & 0xffff); }
LOWORD :: proc(wParam: WPARAM) -> u16 { return cast(u16)wParam; } LOWORD :: proc(wParam: Wparam) -> u16 { return u16(wParam); }
LOWORD :: proc(lParam: LPARAM) -> u16 { return cast(u16)lParam; } LOWORD :: proc(lParam: Lparam) -> u16 { return u16(lParam); }
@@ -367,7 +370,7 @@ LOWORD :: proc(lParam: LPARAM) -> u16 { return cast(u16)lParam; }
BITMAPINFOHEADER :: struct #ordered { Bitmap_Info_Header :: struct #ordered {
size: u32, size: u32,
width, height: i32, width, height: i32,
planes, bit_count: i16, planes, bit_count: i16,
@@ -378,33 +381,33 @@ BITMAPINFOHEADER :: struct #ordered {
clr_used: u32, clr_used: u32,
clr_important: u32, clr_important: u32,
} }
BITMAPINFO :: struct #ordered { Bitmap_Info :: struct #ordered {
using header: BITMAPINFOHEADER, using header: Bitmap_Info_Header,
colors: [1]RGBQUAD, colors: [1]Rgb_Quad,
} }
RGBQUAD :: struct #ordered { blue, green, red, reserved: byte } Rgb_Quad :: struct #ordered { blue, green, red, reserved: byte }
BI_RGB :: 0; BI_RGB :: 0;
DIB_RGB_COLORS :: 0x00; DIB_RGB_COLORS :: 0x00;
SRCCOPY: u32 : 0x00cc0020; SRCCOPY: u32 : 0x00cc0020;
StretchDIBits :: proc (hdc: HDC, StretchDIBits :: proc (hdc: Hdc,
x_dst, y_dst, width_dst, height_dst: i32, x_dst, y_dst, width_dst, height_dst: i32,
x_src, y_src, width_src, header_src: i32, x_src, y_src, width_src, header_src: i32,
bits: rawptr, bits_info: ^BITMAPINFO, bits: rawptr, bits_info: ^Bitmap_Info,
usage: u32, usage: u32,
rop: u32) -> i32 #foreign gdi32; rop: u32) -> i32 #foreign gdi32;
LoadLibraryA :: proc (c_str: ^u8) -> HMODULE #foreign kernel32; LoadLibraryA :: proc (c_str: ^u8) -> Hmodule #foreign kernel32;
FreeLibrary :: proc (h: HMODULE) #foreign kernel32; FreeLibrary :: proc (h: Hmodule) #foreign kernel32;
GetProcAddress :: proc (h: HMODULE, c_str: ^u8) -> PROC #foreign kernel32; GetProcAddress :: proc (h: Hmodule, c_str: ^u8) -> Proc #foreign kernel32;
GetClientRect :: proc(hwnd: HWND, rect: ^RECT) -> BOOL #foreign user32; GetClientRect :: proc(hwnd: Hwnd, rect: ^Rect) -> Bool #foreign user32;
// Windows OpenGL // Windows OpenGL
PFD_TYPE_RGBA :: 0; PFD_TYPE_RGBA :: 0;
@@ -461,20 +464,20 @@ PIXELFORMATDESCRIPTOR :: struct #ordered {
damage_mask: u32, damage_mask: u32,
} }
GetDC :: proc(h: HWND) -> HDC #foreign user32; GetDC :: proc(h: Hwnd) -> Hdc #foreign user32;
SetPixelFormat :: proc(hdc: HDC, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR) -> BOOL #foreign gdi32; SetPixelFormat :: proc(hdc: Hdc, pixel_format: i32, pfd: ^PIXELFORMATDESCRIPTOR) -> Bool #foreign gdi32;
ChoosePixelFormat :: proc(hdc: HDC, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign gdi32; ChoosePixelFormat :: proc(hdc: Hdc, pfd: ^PIXELFORMATDESCRIPTOR) -> i32 #foreign gdi32;
SwapBuffers :: proc(hdc: HDC) -> BOOL #foreign gdi32; SwapBuffers :: proc(hdc: Hdc) -> Bool #foreign gdi32;
ReleaseDC :: proc(wnd: HWND, hdc: HDC) -> i32 #foreign user32; ReleaseDC :: proc(wnd: Hwnd, hdc: Hdc) -> i32 #foreign user32;
PROC :: #type proc() #cc_c; Proc :: #type proc() #cc_c;
GetKeyState :: proc(v_key: i32) -> i16 #foreign user32; GetKeyState :: proc(v_key: i32) -> i16 #foreign user32;
GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign user32; GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign user32;
is_key_down :: proc(key: Key_Code) -> bool #inline { return GetAsyncKeyState(cast(i32)key) < 0; } is_key_down :: proc(key: Key_Code) -> bool #inline { return GetAsyncKeyState(i32(key)) < 0; }
Key_Code :: enum i32 { Key_Code :: enum i32 {
LBUTTON = 0x01, LBUTTON = 0x01,
@@ -611,7 +614,7 @@ Key_Code :: enum i32 {
RCONTROL = 0xA3, RCONTROL = 0xA3,
LMENU = 0xA4, LMENU = 0xA4,
RMENU = 0xA5, RMENU = 0xA5,
PROCESSKEY = 0xE5, ProcESSKEY = 0xE5,
ATTN = 0xF6, ATTN = 0xF6,
CRSEL = 0xF7, CRSEL = 0xF7,
EXSEL = 0xF8, EXSEL = 0xF8,
@@ -622,4 +625,3 @@ Key_Code :: enum i32 {
PA1 = 0xFD, PA1 = 0xFD,
OEM_CLEAR = 0xFE, OEM_CLEAR = 0xFE,
} }
+43 -91
View File
@@ -1,146 +1,98 @@
is_signed :: proc(info: ^Type_Info) -> bool { is_signed :: proc(info: ^Type_Info) -> bool {
if is_integer(info) { if info == nil { return false; }
i := union_cast(^Type_Info.Integer)info; match i in type_info_base(info) {
return i.signed; case Type_Info.Integer: return i.signed;
} case Type_Info.Float: return true;
if is_float(info) {
return true;
} }
return false; return false;
} }
is_integer :: proc(info: ^Type_Info) -> bool { is_integer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Integer);
match i in type_info_base(info) { return ok;
case Type_Info.Integer: return true;
}
return false;
} }
is_float :: proc(info: ^Type_Info) -> bool { is_float :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Float);
match i in type_info_base(info) { return ok;
case Type_Info.Float: return true; }
} is_complex :: proc(info: ^Type_Info) -> bool {
return false; if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Complex);
return ok;
} }
is_any :: proc(info: ^Type_Info) -> bool { is_any :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Any);
match i in type_info_base(info) { return ok;
case Type_Info.Any: return true;
}
return false;
} }
is_string :: proc(info: ^Type_Info) -> bool { is_string :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.String);
match i in type_info_base(info) { return ok;
case Type_Info.String: return true;
}
return false;
} }
is_boolean :: proc(info: ^Type_Info) -> bool { is_boolean :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Boolean);
match i in type_info_base(info) { return ok;
case Type_Info.Boolean: return true;
}
return false;
} }
is_pointer :: proc(info: ^Type_Info) -> bool { is_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Pointer);
match i in type_info_base(info) { return ok;
case Type_Info.Pointer: return true;
}
return false;
} }
is_procedure :: proc(info: ^Type_Info) -> bool { is_procedure :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Procedure);
match i in type_info_base(info) { return ok;
case Type_Info.Procedure: return true;
}
return false;
} }
is_array :: proc(info: ^Type_Info) -> bool { is_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Array);
match i in type_info_base(info) { return ok;
case Type_Info.Array: return true;
}
return false;
} }
is_dynamic_array :: proc(info: ^Type_Info) -> bool { is_dynamic_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Dynamic_Array);
match i in type_info_base(info) { return ok;
case Type_Info.Dynamic_Array: return true;
}
return false;
} }
is_dynamic_map :: proc(info: ^Type_Info) -> bool { is_dynamic_map :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Map);
match i in type_info_base(info) { return ok;
case Type_Info.Map: return i.count == 0;
}
return false;
} }
is_slice :: proc(info: ^Type_Info) -> bool { is_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Slice);
match i in type_info_base(info) { return ok;
case Type_Info.Slice: return true;
}
return false;
} }
is_vector :: proc(info: ^Type_Info) -> bool { is_vector :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Vector);
match i in type_info_base(info) { return ok;
case Type_Info.Vector: return true;
}
return false;
} }
is_tuple :: proc(info: ^Type_Info) -> bool { is_tuple :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Tuple);
match i in type_info_base(info) { return ok;
case Type_Info.Tuple: return true;
}
return false;
} }
is_struct :: proc(info: ^Type_Info) -> bool { is_struct :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Struct);
match i in type_info_base(info) { return ok;
case Type_Info.Struct: return true;
}
return false;
} }
is_union :: proc(info: ^Type_Info) -> bool { is_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Union);
match i in type_info_base(info) { return ok;
case Type_Info.Union: return true;
}
return false;
} }
is_raw_union :: proc(info: ^Type_Info) -> bool { is_raw_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Raw_Union);
match i in type_info_base(info) { return ok;
case Type_Info.Raw_Union: return true;
}
return false;
} }
is_enum :: proc(info: ^Type_Info) -> bool { is_enum :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; } if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Enum);
match i in type_info_base(info) { return ok;
case Type_Info.Enum: return true;
}
return false;
} }
+58
View File
@@ -0,0 +1,58 @@
REPLACEMENT_CHAR :: '\uFFFD';
MAX_RUNE :: '\U0010FFFF';
_surr1 :: 0xd800;
_surr2 :: 0xdc00;
_surr3 :: 0xe000;
_surr_self :: 0x10000;
is_surrogate :: proc(r: rune) -> bool {
return _surr1 <= r && r < _surr3;
}
decode_surrogate_pair :: proc(r1, r2: rune) -> rune {
if _surr1 <= r1 && r1 < _surr2 && _surr2 <= r2 && r2 < _surr3 {
return (r1-_surr1)<<10 | (r2 - _surr2) + _surr_self;
}
return REPLACEMENT_CHAR;
}
encode_surrogate_pair :: proc(r: rune) -> (r1, r2: rune) {
if r < _surr_self || r > MAX_RUNE {
return REPLACEMENT_CHAR, REPLACEMENT_CHAR;
}
r -= _surr_self;
return _surr1 + (r>>10)&0x3ff, _surr2 + r&0x3ff;
}
encode :: proc(d: []u16, s: []rune) {
n := len(s);
for r in s {
if r >= _surr_self {
n++;
}
}
max_n := min(len(d), n);
n = 0;
for r in s {
match r {
case 0..<_surr1, _surr3..<_surr_self:
d[n] = u16(r);
n++;
case _surr_self..MAX_RUNE:
r1, r2 := encode_surrogate_pair(r);
d[n] = u16(r1);
d[n+1] = u16(r2);
n += 2;
default:
d[n] = u16(REPLACEMENT_CHAR);
n++;
}
}
}
+29 -29
View File
@@ -1,7 +1,7 @@
RUNE_ERROR :: '\ufffd'; RUNE_ERROR :: '\ufffd';
RUNE_SELF :: 0x80; RUNE_SELF :: 0x80;
RUNE_BOM :: 0xfeff; RUNE_BOM :: 0xfeff;
RUNE_EOF :: ~cast(rune)0; RUNE_EOF :: ~rune(0);
MAX_RUNE :: '\U0010ffff'; MAX_RUNE :: '\U0010ffff';
UTF_MAX :: 4; UTF_MAX :: 4;
@@ -60,15 +60,15 @@ immutable accept_sizes := [256]byte{
encode_rune :: proc(r: rune) -> ([4]byte, int) { encode_rune :: proc(r: rune) -> ([4]byte, int) {
buf: [4]byte; buf: [4]byte;
i := cast(u32)r; i := u32(r);
mask: byte : 0x3f; mask: byte : 0x3f;
if i <= 1<<7-1 { if i <= 1<<7-1 {
buf[0] = cast(byte)r; buf[0] = byte(r);
return buf, 1; return buf, 1;
} }
if i <= 1<<11-1 { if i <= 1<<11-1 {
buf[0] = 0xc0 | cast(byte)(r>>6); buf[0] = 0xc0 | byte(r>>6);
buf[1] = 0x80 | cast(byte)r & mask; buf[1] = 0x80 | byte(r) & mask;
return buf, 2; return buf, 2;
} }
@@ -79,34 +79,34 @@ encode_rune :: proc(r: rune) -> ([4]byte, int) {
} }
if i <= 1<<16-1 { if i <= 1<<16-1 {
buf[0] = 0xe0 | cast(byte)(r>>12); buf[0] = 0xe0 | byte(r>>12);
buf[1] = 0x80 | cast(byte)(r>>6) & mask; buf[1] = 0x80 | byte(r>>6) & mask;
buf[2] = 0x80 | cast(byte)r & mask; buf[2] = 0x80 | byte(r) & mask;
return buf, 3; return buf, 3;
} }
buf[0] = 0xf0 | cast(byte)(r>>18); buf[0] = 0xf0 | byte(r>>18);
buf[1] = 0x80 | cast(byte)(r>>12) & mask; buf[1] = 0x80 | byte(r>>12) & mask;
buf[2] = 0x80 | cast(byte)(r>>6) & mask; buf[2] = 0x80 | byte(r>>6) & mask;
buf[3] = 0x80 | cast(byte)r & mask; buf[3] = 0x80 | byte(r) & mask;
return buf, 4; return buf, 4;
} }
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune(cast([]byte)s); } decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune([]byte(s)); }
decode_rune :: proc(s: []byte) -> (rune, int) { decode_rune :: proc(s: []byte) -> (rune, int) {
n := s.count; n := len(s);
if n < 1 { if n < 1 {
return RUNE_ERROR, 0; return RUNE_ERROR, 0;
} }
s0 := s[0]; s0 := s[0];
x := accept_sizes[s0]; x := accept_sizes[s0];
if x >= 0xF0 { if x >= 0xF0 {
mask := cast(rune)(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff. mask := rune(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
return cast(rune)(s[0])&~mask | RUNE_ERROR&mask, 1; return rune(s[0])&~mask | RUNE_ERROR&mask, 1;
} }
sz := x & 7; sz := x & 7;
accept := accept_ranges[x>>4]; accept := accept_ranges[x>>4];
if n < cast(int)sz { if n < int(sz) {
return RUNE_ERROR, 1; return RUNE_ERROR, 1;
} }
b1 := s[1]; b1 := s[1];
@@ -114,36 +114,36 @@ decode_rune :: proc(s: []byte) -> (rune, int) {
return RUNE_ERROR, 1; return RUNE_ERROR, 1;
} }
if sz == 2 { if sz == 2 {
return cast(rune)(s0&MASK2)<<6 | cast(rune)(b1&MASKX), 2; return rune(s0&MASK2)<<6 | rune(b1&MASKX), 2;
} }
b2 := s[2]; b2 := s[2];
if b2 < LOCB || HICB < b2 { if b2 < LOCB || HICB < b2 {
return RUNE_ERROR, 1; return RUNE_ERROR, 1;
} }
if sz == 3 { if sz == 3 {
return cast(rune)(s0&MASK3)<<12 | cast(rune)(b1&MASKX)<<6 | cast(rune)(b2&MASKX), 3; return rune(s0&MASK3)<<12 | rune(b1&MASKX)<<6 | rune(b2&MASKX), 3;
} }
b3 := s[3]; b3 := s[3];
if b3 < LOCB || HICB < b3 { if b3 < LOCB || HICB < b3 {
return RUNE_ERROR, 1; return RUNE_ERROR, 1;
} }
return cast(rune)(s0&MASK4)<<18 | cast(rune)(b1&MASKX)<<12 | cast(rune)(b2&MASKX)<<6 | cast(rune)(b3&MASKX), 4; return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
} }
decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune(cast([]byte)s); } decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune([]byte(s)); }
decode_last_rune :: proc(s: []byte) -> (rune, int) { decode_last_rune :: proc(s: []byte) -> (rune, int) {
r: rune; r: rune;
size: int; size: int;
start, end, limit: int; start, end, limit: int;
end = s.count; end = len(s);
if end == 0 { if end == 0 {
return RUNE_ERROR, 0; return RUNE_ERROR, 0;
} }
start = end-1; start = end-1;
r = cast(rune)s[start]; r = rune(s[start]);
if r < RUNE_SELF { if r < RUNE_SELF {
return r, 1; return r, 1;
} }
@@ -160,7 +160,7 @@ decode_last_rune :: proc(s: []byte) -> (rune, int) {
} }
start = max(start, 0); start = max(start, 0);
r, size = decode_rune(s[start..end]); r, size = decode_rune(s[start..<end]);
if start+size != end { if start+size != end {
return RUNE_ERROR, 1; return RUNE_ERROR, 1;
} }
@@ -183,7 +183,7 @@ valid_rune :: proc(r: rune) -> bool {
} }
valid_string :: proc(s: string) -> bool { valid_string :: proc(s: string) -> bool {
n := s.count; n := len(s);
for i := 0; i < n; { for i := 0; i < n; {
si := s[i]; si := s[i];
if si < RUNE_SELF { // ascii if si < RUNE_SELF { // ascii
@@ -194,7 +194,7 @@ valid_string :: proc(s: string) -> bool {
if x == 0xf1 { if x == 0xf1 {
return false; return false;
} }
size := cast(int)(x & 7); size := int(x & 7);
if i+size > n { if i+size > n {
return false; return false;
} }
@@ -217,10 +217,10 @@ valid_string :: proc(s: string) -> bool {
rune_start :: proc(b: byte) -> bool #inline { return b&0xc0 != 0x80; } rune_start :: proc(b: byte) -> bool #inline { return b&0xc0 != 0x80; }
rune_count :: proc(s: string) -> int #inline { return rune_count(cast([]byte)s); } rune_count :: proc(s: string) -> int #inline { return rune_count([]byte(s)); }
rune_count :: proc(s: []byte) -> int { rune_count :: proc(s: []byte) -> int {
count := 0; count := 0;
n := s.count; n := len(s);
for i := 0; i < n; { for i := 0; i < n; {
defer count++; defer count++;
@@ -234,7 +234,7 @@ rune_count :: proc(s: []byte) -> int {
i++; i++;
continue; continue;
} }
size := cast(int)(x & 7); size := int(x & 7);
if i+size > n { if i+size > n {
i++; i++;
continue; continue;
+11
View File
@@ -0,0 +1,11 @@
@echo off
setlocal EnableDelayedExpansion
set file_input=%1
set name=%1
FOR %%f IN (name) do (
FOR %%g in (!%%f!) do set "%%f=%%~ng"
)
call clang -O2 -c %file_input% -o %name%.o ^
&& call ar %name%.o -rcs %name%.lib
View File

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 246 KiB

View File
+1 -2
View File
@@ -1,8 +1,7 @@
@echo off @echo off
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL
set _NO_DEBUG_HEAP=1 set _NO_DEBUG_HEAP=1
set path=w:\Odin\misc;%path% set path=w:\Odin\misc;%path%
View File
-4
View File
@@ -1,4 +0,0 @@
@echo off
rem call clang -c -emit-llvm -DGB_IMPLEMENTATION -DGB_DEF=GB_DLL_EXPORT ..\src\gb\gb.h
+2 -8
View File
@@ -87,14 +87,8 @@ void array__set_capacity(void *ptr, isize capacity, isize element_size) {
x->count = capacity; x->count = capacity;
} }
{ x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
// TODO(bill): Resize rather than copy and delete x->capacity = capacity;
void *new_data = gb_alloc(x->allocator, element_size*capacity);
gb_memmove(new_data, x->e, element_size*x->count);
gb_free(x->allocator, x->e);
x->capacity = capacity;
x->e = new_data;
}
} }
+99 -9
View File
@@ -135,7 +135,62 @@ String odin_root_dir(void) {
return path; return path;
} }
#else #else
#error Implement system
// NOTE: Linux / Unix is unfinished and not tested very well.
#include <sys/stat.h>
String odin_root_dir(void) {
String path = global_module_path;
Array(char) path_buf;
isize len, i;
gbTempArenaMemory tmp;
u8 *text;
if (global_module_path_set) {
return global_module_path;
}
array_init_count(&path_buf, heap_allocator(), 300);
len = 0;
for (;;) {
// This is not a 100% reliable system, but for the purposes
// of this compiler, it should be _good enough_.
// That said, there's no solid 100% method on Linux to get the program's
// path without checking this link. Sorry.
len = readlink("/proc/self/exe", &path_buf.e[0], path_buf.count);
if(len == 0) {
return make_string(NULL, 0);
}
if (len < path_buf.count) {
break;
}
array_resize(&path_buf, 2*path_buf.count + 300);
}
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
gb_memmove(text, &path_buf.e[0], len);
path = make_string(text, len);
for (i = path.len-1; i >= 0; i--) {
u8 c = path.text[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
gb_temp_arena_memory_end(tmp);
array_free(&path_buf);
return path;
}
#endif #endif
@@ -157,11 +212,10 @@ String path_to_fullpath(gbAllocator a, String s) {
} }
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX) #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
String path_to_fullpath(gbAllocator a, String s) { String path_to_fullpath(gbAllocator a, String s) {
char* p = realpath(s.text, 0); char *p = realpath(cast(char *)s.text, 0);
// GB_ASSERT(p && "file does not exist"); if(p == NULL) return make_string_c("");
if(!p) return make_string_c("");
return make_string(p, strlen(p)); return make_string_c(p);
} }
#else #else
#error Implement system #error Implement system
@@ -209,7 +263,7 @@ String get_fullpath_core(gbAllocator a, String path) {
void init_build_context(void) { void init_build_context(void) {
BuildContext *bc = &build_context; BuildContext *bc = &build_context;
bc->ODIN_VENDOR = str_lit("odin"); bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = str_lit("0.1.1"); bc->ODIN_VERSION = str_lit("0.2.0");
bc->ODIN_ROOT = odin_root_dir(); bc->ODIN_ROOT = odin_root_dir();
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
@@ -221,18 +275,54 @@ void init_build_context(void) {
bc->ODIN_ARCH = str_lit("amd64"); bc->ODIN_ARCH = str_lit("amd64");
bc->ODIN_ENDIAN = str_lit("little"); bc->ODIN_ENDIAN = str_lit("little");
#else #else
#error Implement system bc->ODIN_OS = str_lit("linux");
bc->ODIN_ARCH = str_lit("amd64");
bc->ODIN_ENDIAN = str_lit("little");
#endif #endif
// NOTE(zangent): The linker flags to set the build architecture are different
// across OSs. It doesn't make sense to allocate extra data on the heap
// here, so I just #defined the linker flags to keep things concise.
#if defined(GB_SYSTEM_WINDOWS)
#define LINK_FLAG_X64 "/machine:x64"
#define LINK_FLAG_X86 "/machine:x86"
#elif defined(GB_SYSTEM_OSX)
// NOTE(zangent): MacOS systems are x64 only, so ld doesn't have
// an architecture option. All compilation done on MacOS must be x64.
GB_ASSERT(str_eq(bc->ODIN_ARCH, str_lit("amd64")));
#define LINK_FLAG_X64 ""
#define LINK_FLAG_X86 ""
#else
// Linux, but also BSDs and the like.
// NOTE(zangent): When clang is swapped out with ld as the linker,
// the commented flags here should be used. Until then, we'll have
// to use alternative build flags made for clang.
/*
#define LINK_FLAG_X64 "-m elf_x86_64"
#define LINK_FLAG_X86 "-m elf_i386"
*/
#define LINK_FLAG_X64 "-arch x86-64"
#define LINK_FLAG_X86 "-arch x86"
#endif
if (str_eq(bc->ODIN_ARCH, str_lit("amd64"))) { if (str_eq(bc->ODIN_ARCH, str_lit("amd64"))) {
bc->word_size = 8; bc->word_size = 8;
bc->max_align = 16; bc->max_align = 16;
bc->llc_flags = str_lit("-march=x86-64 "); bc->llc_flags = str_lit("-march=x86-64 ");
bc->link_flags = str_lit("/machine:x64 "); bc->link_flags = str_lit(LINK_FLAG_X64 " ");
} else if (str_eq(bc->ODIN_ARCH, str_lit("x86"))) { } else if (str_eq(bc->ODIN_ARCH, str_lit("x86"))) {
bc->word_size = 4; bc->word_size = 4;
bc->max_align = 8; bc->max_align = 8;
bc->llc_flags = str_lit("-march=x86 "); bc->llc_flags = str_lit("-march=x86 ");
bc->link_flags = str_lit("/machine:x86 "); bc->link_flags = str_lit(LINK_FLAG_X86 " ");
} }
#undef LINK_FLAG_X64
#undef LINK_FLAG_X86
} }
+108 -32
View File
@@ -1,21 +1,21 @@
bool check_is_terminating(AstNode *node); bool check_is_terminating(AstNode *node);
void check_stmt (Checker *c, AstNode *node, u32 flags); void check_stmt (Checker *c, AstNode *node, u32 flags);
void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
// NOTE(bill): `content_name` is for debugging and error messages // NOTE(bill): `content_name` is for debugging and error messages
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) { Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
if (operand->mode == Addressing_Invalid || if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid || operand->type == t_invalid ||
e->type == t_invalid) { e->type == t_invalid) {
if (operand->mode == Addressing_Builtin) { if (operand->mode == Addressing_Builtin) {
gbString expr_str = expr_to_string(operand->expr); gbString expr_str = expr_to_string(operand->expr);
// TODO(bill): is this a good enough error message? // TODO(bill): is this a good enough error message?
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
error_node(operand->expr, error_node(operand->expr,
"Cannot assign builtin procedure `%s` in %.*s", "Cannot assign builtin procedure `%s` in %.*s",
expr_str, expr_str,
LIT(context_name)); LIT(context_name));
operand->mode = Addressing_Invalid; operand->mode = Addressing_Invalid;
@@ -85,8 +85,8 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
void check_init_constant(Checker *c, Entity *e, Operand *operand) { void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid || if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid || operand->type == t_invalid ||
e->type == t_invalid) { e->type == t_invalid) {
if (e->type == NULL) { if (e->type == NULL) {
e->type = t_invalid; e->type = t_invalid;
} }
@@ -181,7 +181,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
check_init_constant(c, e, &operand); check_init_constant(c, e, &operand);
if (operand.mode == Addressing_Invalid || if (operand.mode == Addressing_Invalid ||
base_type(operand.type) == t_invalid) { base_type(operand.type) == t_invalid) {
error(e->token, "Invalid declaration type"); error(e->token, "Invalid declaration type");
} }
} }
@@ -241,14 +241,15 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
check_open_scope(c, pd->type); check_open_scope(c, pd->type);
check_procedure_type(c, proc_type, pd->type); check_procedure_type(c, proc_type, pd->type);
bool is_foreign = (pd->tags & ProcTag_foreign) != 0; bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
bool is_link_name = (pd->tags & ProcTag_link_name) != 0; bool is_link_name = (pd->tags & ProcTag_link_name) != 0;
bool is_export = (pd->tags & ProcTag_export) != 0; bool is_export = (pd->tags & ProcTag_export) != 0;
bool is_inline = (pd->tags & ProcTag_inline) != 0; bool is_inline = (pd->tags & ProcTag_inline) != 0;
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0; bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
bool is_require_results = (pd->tags & ProcTag_require_results) != 0;
if ((d->scope->is_file || d->scope->is_global) &&
str_eq(e->token.string, str_lit("main"))) { if (d->scope->is_file && str_eq(e->token.string, str_lit("main"))) {
if (proc_type != NULL) { if (proc_type != NULL) {
TypeProc *pt = &proc_type->Proc; TypeProc *pt = &proc_type->Proc;
if (pt->param_count != 0 || if (pt->param_count != 0 ||
@@ -276,18 +277,22 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body"); error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
} }
// TODO(bill): Is this the best option? What about passing to external shit?!
// if (proc_type->Proc.calling_convention != ProcCC_Odin) {
// error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention");
// proc_type->Proc.calling_convention = ProcCC_Odin;
// }
d->scope = c->context.scope; d->scope = c->context.scope;
GB_ASSERT(pd->body->kind == AstNode_BlockStmt); GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags); check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
} }
if (proc_type != NULL && is_type_proc(proc_type)) {
TypeProc *tp = &proc_type->Proc;
if (tp->result_count == 0 && is_require_results) {
error_node(pd->type, "`#require_results` is not needed on a procedure with no results");
} else {
tp->require_results = is_require_results;
}
}
if (is_foreign) { if (is_foreign) {
MapEntity *fp = &c->info.foreigns; MapEntity *fp = &c->info.foreigns;
String name = e->token.string; String name = e->token.string;
@@ -330,9 +335,9 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
Type *other_type = base_type(f->type); Type *other_type = base_type(f->type);
if (!are_signatures_similar_enough(this_type, other_type)) { if (!are_signatures_similar_enough(this_type, other_type)) {
error_node(d->proc_lit, error_node(d->proc_lit,
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n" "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
"\tat %.*s(%td:%td)", "\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column); LIT(name), LIT(pos.file), pos.line, pos.column);
} }
} else { } else {
map_entity_set(fp, key, e); map_entity_set(fp, key, e);
@@ -355,9 +360,9 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
TokenPos pos = f->token.pos; TokenPos pos = f->token.pos;
// TODO(bill): Better error message? // TODO(bill): Better error message?
error_node(d->proc_lit, error_node(d->proc_lit,
"Non unique linking name for procedure `%.*s`\n" "Non unique linking name for procedure `%.*s`\n"
"\tother at %.*s(%td:%td)", "\tother at %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column); LIT(name), LIT(pos.file), pos.line, pos.column);
} else { } else {
map_entity_set(fp, key, e); map_entity_set(fp, key, e);
} }
@@ -408,6 +413,67 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
} }
void check_alias_decl(Checker *c, Entity *e, AstNode *expr, Type *named_type) {
GB_ASSERT(e->type == NULL);
GB_ASSERT(e->kind == Entity_Alias);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
return;
}
e->flags |= EntityFlag_Visited;
e->type = t_invalid;
expr = unparen_expr(expr);
if (expr->kind == AstNode_Alias) {
error_node(expr, "#alias of an #alias is not allowed");
return;
}
Operand operand = {0};
check_expr_or_type(c, &operand, expr);
if (operand.mode != Addressing_Type) {
error_node(expr, "#alias declarations only allow types");
return;
}
e->kind = Entity_TypeName;
e->TypeName.is_type_alias = true;
e->type = NULL;
DeclInfo *d = c->context.decl;
d->type_expr = expr;
check_type_decl(c, e, d->type_expr, named_type);
// Operand o = {0};
// Entity *f = NULL;
// if (expr->kind == AstNode_Ident) {
// f = check_ident(c, &o, expr, NULL, NULL, true);
// } else if (expr->kind == AstNode_SelectorExpr) {
// f = check_selector(c, &o, expr, NULL);
// } else {
// check_expr_or_type(c, &o, expr);
// }
// if (o.mode == Addressing_Invalid) {
// return;
// }
// switch (o.mode) {
// case Addressing_Type:
// e->type = o.type;
// // e->kind = Entity_TypeName;
// // e->TypeName.is_type_alias = true;
// e->Alias.kind = EntityAlias_Type;
// e->Alias.original = f;
// break;
// default:
// error_node(expr, "#alias declarations only allow types");
// e->kind = Entity_Invalid;
// e->type = t_invalid;
// break;
// }
}
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) { void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
if (e->type != NULL) { if (e->type != NULL) {
return; return;
@@ -440,6 +506,9 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
case Entity_TypeName: case Entity_TypeName:
check_type_decl(c, e, d->type_expr, named_type); check_type_decl(c, e, d->type_expr, named_type);
break; break;
case Entity_Alias:
check_alias_decl(c, e, d->init_expr, named_type);
break;
case Entity_Procedure: case Entity_Procedure:
check_proc_lit(c, e, d); check_proc_lit(c, e, d);
break; break;
@@ -466,14 +535,13 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
c->context.decl = decl; c->context.decl = decl;
c->context.proc_name = proc_name; c->context.proc_name = proc_name;
GB_ASSERT(type->kind == Type_Proc); GB_ASSERT(type->kind == Type_Proc);
if (type->Proc.param_count > 0) { if (type->Proc.param_count > 0) {
TypeTuple *params = &type->Proc.params->Tuple; TypeTuple *params = &type->Proc.params->Tuple;
for (isize i = 0; i < params->variable_count; i++) { for (isize i = 0; i < params->variable_count; i++) {
Entity *e = params->variables[i]; Entity *e = params->variables[i];
GB_ASSERT(e->kind == Entity_Variable); GB_ASSERT(e->kind == Entity_Variable);
if (!(e->flags & EntityFlag_Anonymous)) { if (!(e->flags & EntityFlag_Using)) {
continue; continue;
} }
bool is_immutable = e->Variable.is_immutable; bool is_immutable = e->Variable.is_immutable;
@@ -504,7 +572,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
push_procedure(c, type); push_procedure(c, type);
{ {
ast_node(bs, BlockStmt, body); ast_node(bs, BlockStmt, body);
check_stmt_list(c, bs->stmts, 0); check_stmt_list(c, bs->stmts, Stmt_CheckScopeDecls);
if (type->Proc.result_count > 0) { if (type->Proc.result_count > 0) {
if (!check_is_terminating(body)) { if (!check_is_terminating(body)) {
if (token.kind == Token_Ident) { if (token.kind == Token_Ident) {
@@ -519,8 +587,16 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
check_scope_usage(c, c->context.scope); check_scope_usage(c, c->context.scope);
c->context = old_context; c->context = old_context;
if (decl->parent != NULL) {
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
for_array(i, decl->deps.entries) {
HashKey key = decl->deps.entries.e[i].key;
Entity *e = cast(Entity *)key.ptr;
map_bool_set(&decl->parent->deps, key, true);
}
}
} }
+1201 -576
View File
File diff suppressed because it is too large Load Diff
+422 -257
View File
@@ -3,7 +3,9 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
return; return;
} }
check_scope_decls(c, stmts, 1.2*stmts.count); if (flags&Stmt_CheckScopeDecls) {
check_scope_decls(c, stmts, 1.2*stmts.count);
}
bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0; bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
flags &= ~Stmt_FallthroughAllowed; flags &= ~Stmt_FallthroughAllowed;
@@ -123,15 +125,13 @@ bool check_is_terminating(AstNode *node) {
case_end; case_end;
case_ast_node(fs, ForStmt, node); case_ast_node(fs, ForStmt, node);
if (!check_has_break(fs->body, true)) { if (fs->cond == NULL && !check_has_break(fs->body, true)) {
return check_is_terminating(fs->body); return check_is_terminating(fs->body);
} }
case_end; case_end;
case_ast_node(rs, RangeStmt, node); case_ast_node(rs, RangeStmt, node);
if (!check_has_break(rs->body, true)) { return false;
return check_is_terminating(rs->body);
}
case_end; case_end;
case_ast_node(ms, MatchStmt, node); case_ast_node(ms, MatchStmt, node);
@@ -307,16 +307,21 @@ Type *check_assignment_variable(Checker *c, Operand *rhs, AstNode *lhs_node) {
return rhs->type; return rhs->type;
} }
bool check_valid_type_match_type(Type *type, bool *is_union_ptr, bool *is_any) { typedef enum MatchTypeKind {
if (is_type_pointer(type)) { MatchType_Invalid,
*is_union_ptr = is_type_union(type_deref(type)); MatchType_Union,
return *is_union_ptr; MatchType_Any,
} MatchTypeKind;
MatchTypeKind check_valid_type_match_type(Type *type) {
type = type_deref(type);
if (is_type_union(type)) {
return MatchType_Union;
} }
if (is_type_any(type)) { if (is_type_any(type)) {
*is_any = true; return MatchType_Any;
return *is_any;
} }
return false; return MatchType_Invalid;
} }
void check_stmt_internal(Checker *c, AstNode *node, u32 flags); void check_stmt_internal(Checker *c, AstNode *node, u32 flags);
@@ -357,6 +362,7 @@ typedef struct TypeAndToken {
#include "map.c" #include "map.c"
void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) { void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
flags &= ~Stmt_CheckScopeDecls;
Operand operand = {Addressing_Invalid}; Operand operand = {Addressing_Invalid};
check_expr(c, &operand, ws->cond); check_expr(c, &operand, ws->cond);
if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) { if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
@@ -385,6 +391,175 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
} }
} }
void check_label(Checker *c, AstNode *label) {
if (label == NULL) {
return;
}
ast_node(l, Label, label);
if (l->name->kind != AstNode_Ident) {
error_node(l->name, "A label's name must be an identifier");
return;
}
String name = l->name->Ident.string;
if (str_eq(name, str_lit("_"))) {
error_node(l->name, "A label's name cannot be a blank identifier");
return;
}
if (c->proc_stack.count == 0) {
error_node(l->name, "A label is only allowed within a procedure");
return;
}
GB_ASSERT(c->context.decl != NULL);
bool ok = true;
for_array(i, c->context.decl->labels) {
BlockLabel bl = c->context.decl->labels.e[i];
if (str_eq(bl.name, name)) {
error_node(label, "Duplicate label with the name `%.*s`", LIT(name));
ok = false;
break;
}
}
Entity *e = make_entity_label(c->allocator, c->context.scope, l->name->Ident, t_invalid, label);
add_entity(c, c->context.scope, l->name, e);
if (ok) {
BlockLabel bl = {name, label};
array_add(&c->context.decl->labels, bl);
}
}
// Returns `true` for `continue`, `false` for `return`
bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bool is_selector, Entity *e) {
if (e == NULL) {
error(us->token, "`using` applied to an unknown entity");
return true;
}
switch (e->kind) {
case Entity_Alias: {
if (e->Alias.original != NULL) {
check_using_stmt_entity(c, us, expr, is_selector, e->Alias.original);
} else {
error(us->token, "`using` cannot be applied to the alias `%.*s`", LIT(e->token.string));
return false;
}
} break;
case Entity_TypeName: {
Type *t = base_type(e->type);
if (is_type_union(t)) {
TokenPos pos = ast_node_token(expr).pos;
for (isize i = 1; i < t->Record.variant_count; i++) {
Entity *f = t->Record.variants[i];
// gb_printf_err("%s\n", type_to_string(f->type));
Entity *found = scope_insert_entity(c->context.scope, f);
if (found != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
gb_string_free(expr_str);
return false;
}
f->using_parent = e;
}
} else if (is_type_enum(t)) {
for (isize i = 0; i < t->Record.field_count; i++) {
Entity *f = t->Record.fields[i];
Entity *found = scope_insert_entity(c->context.scope, f);
if (found != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
gb_string_free(expr_str);
return false;
}
f->using_parent = e;
}
} else {
error(us->token, "`using` can be only applied to `union` or `enum` type entities");
}
} break;
case Entity_ImportName: {
Scope *scope = e->ImportName.scope;
for_array(i, scope->elements.entries) {
Entity *decl = scope->elements.entries.e[i].value;
Entity *found = scope_insert_entity(c->context.scope, decl);
if (found != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token,
"Namespace collision while `using` `%s` of: %.*s\n"
"\tat %.*s(%td:%td)\n"
"\tat %.*s(%td:%td)",
expr_str, LIT(found->token.string),
LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column,
LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column
);
gb_string_free(expr_str);
return false;
}
}
} break;
case Entity_Variable: {
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
// TODO(bill): Make it work for unions too
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
if (is_selector) {
uvar->using_expr = expr;
}
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
gb_string_free(expr_str);
return false;
}
}
}
} else {
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
return false;
}
} break;
case Entity_Constant:
error(us->token, "`using` cannot be applied to a constant");
break;
case Entity_Procedure:
case Entity_Builtin:
error(us->token, "`using` cannot be applied to a procedure");
break;
case Entity_Nil:
error(us->token, "`using` cannot be applied to `nil`");
break;
case Entity_Label:
error(us->token, "`using` cannot be applied to a label");
break;
case Entity_Invalid:
error(us->token, "`using` cannot be applied to an invalid entity");
break;
default:
GB_PANIC("TODO(bill): `using` other expressions?");
}
return true;
}
void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed); u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) { switch (node->kind) {
@@ -396,9 +571,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
Operand operand = {Addressing_Invalid}; Operand operand = {Addressing_Invalid};
ExprKind kind = check_expr_base(c, &operand, es->expr, NULL); ExprKind kind = check_expr_base(c, &operand, es->expr, NULL);
switch (operand.mode) { switch (operand.mode) {
case Addressing_Type: case Addressing_Type: {
error_node(node, "Is not an expression"); gbString str = type_to_string(operand.type);
break; error_node(node, "`%s` is not an expression", str);
gb_string_free(str);
} break;
case Addressing_NoValue: case Addressing_NoValue:
return; return;
default: { default: {
@@ -406,6 +583,15 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
return; return;
} }
if (operand.expr->kind == AstNode_CallExpr) { if (operand.expr->kind == AstNode_CallExpr) {
AstNodeCallExpr *ce = &operand.expr->CallExpr;
Type *t = type_of_expr(&c->info, ce->proc);
if (is_type_proc(t)) {
if (t->Proc.require_results) {
gbString expr_str = expr_to_string(ce->proc);
error_node(node, "`%s` requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
}
return; return;
} }
gbString expr_str = expr_to_string(operand.expr); gbString expr_str = expr_to_string(operand.expr);
@@ -475,9 +661,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
return; return;
} }
// TODO(bill): This is a very similar to check_init_variables, should I merge the two some how or just
// leave it?
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be // NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
@@ -515,7 +698,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
error(op, "Unknown Assignment operation `%.*s`", LIT(op.string)); error(op, "Unknown Assignment operation `%.*s`", LIT(op.string));
return; return;
} }
// TODO(bill): Check if valid assignment operator
Operand operand = {Addressing_Invalid}; Operand operand = {Addressing_Invalid};
AstNode binary_expr = {AstNode_BinaryExpr}; AstNode binary_expr = {AstNode_BinaryExpr};
ast_node(be, BinaryExpr, &binary_expr); ast_node(be, BinaryExpr, &binary_expr);
@@ -580,7 +762,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
if (c->context.in_defer) { if (c->context.in_defer) {
error(rs->token, "You cannot `return` within a defer statement"); error(rs->token, "You cannot `return` within a defer statement");
// TODO(bill): Should I break here?
break; break;
} }
@@ -619,7 +800,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(fs, ForStmt, node); case_ast_node(fs, ForStmt, node);
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(c, node); check_open_scope(c, node);
check_label(c, fs->label); // TODO(bill): What should the label's "scope" be?
if (fs->init != NULL) { if (fs->init != NULL) {
check_stmt(c, fs->init, 0); check_stmt(c, fs->init, 0);
@@ -646,16 +829,20 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(rs, RangeStmt, node); case_ast_node(rs, RangeStmt, node);
u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed; u32 new_flags = mod_flags | Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(c, node); check_open_scope(c, node);
check_label(c, rs->label);
Type *val = NULL; Type *val = NULL;
Type *idx = NULL; Type *idx = NULL;
Entity *entities[2] = {0}; Entity *entities[2] = {0};
isize entity_count = 0; isize entity_count = 0;
AstNode *expr = unparen_expr(rs->expr);
if (rs->expr != NULL && rs->expr->kind == AstNode_IntervalExpr) {
ast_node(ie, IntervalExpr, rs->expr); if (is_ast_node_a_range(expr)) {
ast_node(ie, BinaryExpr, expr);
Operand x = {Addressing_Invalid}; Operand x = {Addressing_Invalid};
Operand y = {Addressing_Invalid}; Operand y = {Addressing_Invalid};
@@ -715,10 +902,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
TokenKind op = Token_Lt; TokenKind op = Token_Lt;
switch (ie->op.kind) { switch (ie->op.kind) {
case Token_Ellipsis: op = Token_Lt; break; case Token_Ellipsis: op = Token_LtEq; break;
case Token_HalfClosed: op = Token_Lt; break;
default: error(ie->op, "Invalid range operator"); break; default: error(ie->op, "Invalid range operator"); break;
} }
bool ok = compare_exact_values(Token_Lt, a, b); bool ok = compare_exact_values(op, a, b);
if (!ok) { if (!ok) {
// TODO(bill): Better error message // TODO(bill): Better error message
error(ie->op, "Invalid interval range"); error(ie->op, "Invalid interval range");
@@ -840,6 +1028,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
mod_flags |= Stmt_BreakAllowed; mod_flags |= Stmt_BreakAllowed;
check_open_scope(c, node); check_open_scope(c, node);
check_label(c, ms->label); // TODO(bill): What should the label's "scope" be?
if (ms->init != NULL) { if (ms->init != NULL) {
check_stmt(c, ms->init, 0); check_stmt(c, ms->init, 0);
@@ -850,13 +1039,20 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
} else { } else {
x.mode = Addressing_Constant; x.mode = Addressing_Constant;
x.type = t_bool; x.type = t_bool;
x.value = make_exact_value_bool(true); x.value = exact_value_bool(true);
Token token = {0}; Token token = {0};
token.pos = ast_node_token(ms->body).pos; token.pos = ast_node_token(ms->body).pos;
token.string = str_lit("true"); token.string = str_lit("true");
x.expr = ast_ident(c->curr_ast_file, token); x.expr = ast_ident(c->curr_ast_file, token);
} }
if (is_type_vector(x.type)) {
gbString str = type_to_string(x.type);
error_node(x.expr, "Invalid match expression type: %s", str);
gb_string_free(str);
break;
}
// NOTE(bill): Check for multiple defaults // NOTE(bill): Check for multiple defaults
AstNode *first_default = NULL; AstNode *first_default = NULL;
@@ -897,68 +1093,121 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
} }
ast_node(cc, CaseClause, stmt); ast_node(cc, CaseClause, stmt);
for_array(j, cc->list) { for_array(j, cc->list) {
AstNode *expr = cc->list.e[j]; AstNode *expr = unparen_expr(cc->list.e[j]);
Operand y = {0};
check_expr(c, &y, expr); if (is_ast_node_a_range(expr)) {
if (x.mode == Addressing_Invalid || ast_node(ie, BinaryExpr, expr);
y.mode == Addressing_Invalid) { Operand lhs = {0};
continue; Operand rhs = {0};
} check_expr(c, &lhs, ie->left);
if (x.mode == Addressing_Invalid) {
continue;
}
if (lhs.mode == Addressing_Invalid) {
continue;
}
check_expr(c, &rhs, ie->right);
if (rhs.mode == Addressing_Invalid) {
continue;
}
convert_to_typed(c, &y, x.type, 0); if (!is_type_ordered(x.type)) {
if (y.mode == Addressing_Invalid) { gbString str = type_to_string(x.type);
continue; error_node(x.expr, "Unordered type `%s`, is invalid for an interval expression", str);
} gb_string_free(str);
continue;
// NOTE(bill): the ordering here matters }
Operand z = y;
check_comparison(c, &z, &x, Token_CmpEq);
if (z.mode == Addressing_Invalid) {
continue;
}
if (y.mode != Addressing_Constant) {
continue;
}
if (y.value.kind != ExactValue_Invalid) { TokenKind op = {0};
HashKey key = hash_exact_value(y.value);
TypeAndToken *found = map_type_and_token_get(&seen, key);
if (found != NULL) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
isize count = map_type_and_token_multi_count(&seen, key);
TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
map_type_and_token_multi_get_all(&seen, key, taps); Operand a = lhs;
bool continue_outer = false; Operand b = rhs;
check_comparison(c, &a, &x, Token_LtEq);
if (a.mode == Addressing_Invalid) {
continue;
}
switch (ie->op.kind) {
case Token_Ellipsis: op = Token_GtEq; break;
case Token_HalfClosed: op = Token_Gt; break;
default: error(ie->op, "Invalid interval operator"); continue;
}
for (isize i = 0; i < count; i++) { check_comparison(c, &b, &x, op);
TypeAndToken tap = taps[i]; if (b.mode == Addressing_Invalid) {
if (are_types_identical(y.type, tap.type)) { continue;
TokenPos pos = tap.token.pos; }
gbString expr_str = expr_to_string(y.expr);
error_node(y.expr, switch (ie->op.kind) {
"Duplicate case `%s`\n" case Token_Ellipsis: op = Token_LtEq; break;
"\tprevious case at %.*s(%td:%td)", case Token_HalfClosed: op = Token_Lt; break;
expr_str, default: error(ie->op, "Invalid interval operator"); continue;
LIT(pos.file), pos.line, pos.column); }
gb_string_free(expr_str);
continue_outer = true; Operand a1 = lhs;
break; Operand b1 = rhs;
check_comparison(c, &a1, &b1, op);
} else {
Operand y = {0};
check_expr(c, &y, expr);
if (x.mode == Addressing_Invalid ||
y.mode == Addressing_Invalid) {
continue;
}
convert_to_typed(c, &y, x.type, 0);
if (y.mode == Addressing_Invalid) {
continue;
}
// NOTE(bill): the ordering here matters
Operand z = y;
check_comparison(c, &z, &x, Token_CmpEq);
if (z.mode == Addressing_Invalid) {
continue;
}
if (y.mode != Addressing_Constant) {
continue;
}
if (y.value.kind != ExactValue_Invalid) {
HashKey key = hash_exact_value(y.value);
TypeAndToken *found = map_type_and_token_get(&seen, key);
if (found != NULL) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
isize count = map_type_and_token_multi_count(&seen, key);
TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
map_type_and_token_multi_get_all(&seen, key, taps);
bool continue_outer = false;
for (isize i = 0; i < count; i++) {
TypeAndToken tap = taps[i];
if (are_types_identical(y.type, tap.type)) {
TokenPos pos = tap.token.pos;
gbString expr_str = expr_to_string(y.expr);
error_node(y.expr,
"Duplicate case `%s`\n"
"\tprevious case at %.*s(%td:%td)",
expr_str,
LIT(pos.file), pos.line, pos.column);
gb_string_free(expr_str);
continue_outer = true;
break;
}
}
gb_temp_arena_memory_end(tmp);
if (continue_outer) {
continue;
} }
} }
TypeAndToken tap = {y.type, ast_node_token(y.expr)};
gb_temp_arena_memory_end(tmp); map_type_and_token_multi_insert(&seen, key, tap);
if (continue_outer) {
continue;
}
} }
TypeAndToken tap = {y.type, ast_node_token(y.expr)};
map_type_and_token_multi_insert(&seen, key, tap);
} }
} }
@@ -981,9 +1230,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
mod_flags |= Stmt_BreakAllowed; mod_flags |= Stmt_BreakAllowed;
check_open_scope(c, node); check_open_scope(c, node);
check_label(c, ms->label); // TODO(bill): What should the label's "scope" be?
bool is_union_ptr = false; MatchTypeKind match_type_kind = MatchType_Invalid;
bool is_any = false;
if (ms->tag->kind != AstNode_AssignStmt) { if (ms->tag->kind != AstNode_AssignStmt) {
error_node(ms->tag, "Expected an `in` assignment for this type match statement"); error_node(ms->tag, "Expected an `in` assignment for this type match statement");
@@ -1005,14 +1254,15 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
check_expr(c, &x, rhs); check_expr(c, &x, rhs);
check_assignment(c, &x, NULL, str_lit("type match expression")); check_assignment(c, &x, NULL, str_lit("type match expression"));
if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) { match_type_kind = check_valid_type_match_type(x.type);
if (check_valid_type_match_type(x.type) == MatchType_Invalid) {
gbString str = type_to_string(x.type); gbString str = type_to_string(x.type);
error_node(x.expr, error_node(x.expr, "Invalid type for this type match expression, got `%s`", str);
"Invalid type for this type match expression, got `%s`", str);
gb_string_free(str); gb_string_free(str);
break; break;
} }
bool is_ptr = is_type_pointer(x.type);
// NOTE(bill): Check for multiple defaults // NOTE(bill): Check for multiple defaults
AstNode *first_default = NULL; AstNode *first_default = NULL;
@@ -1048,7 +1298,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
} }
MapBool seen = {0}; MapBool seen = {0}; // Multimap
map_bool_init(&seen, heap_allocator()); map_bool_init(&seen, heap_allocator());
for_array(i, bs->stmts) { for_array(i, bs->stmts) {
@@ -1062,74 +1312,76 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
// TODO(bill): Make robust // TODO(bill): Make robust
Type *bt = base_type(type_deref(x.type)); Type *bt = base_type(type_deref(x.type));
AstNode *type_expr = cc->list.count > 0 ? cc->list.e[0] : NULL;
Type *case_type = NULL; Type *case_type = NULL;
if (type_expr != NULL) { // Otherwise it's a default expression for_array(type_index, cc->list) {
Operand y = {0}; AstNode *type_expr = cc->list.e[type_index];
check_expr_or_type(c, &y, type_expr); if (type_expr != NULL) { // Otherwise it's a default expression
Operand y = {0};
check_expr_or_type(c, &y, type_expr);
if (is_union_ptr) { if (match_type_kind == MatchType_Union) {
GB_ASSERT(is_type_union(bt)); GB_ASSERT(is_type_union(bt));
bool tag_type_found = false; bool tag_type_found = false;
for (isize i = 0; i < bt->Record.variant_count; i++) { for (isize i = 0; i < bt->Record.variant_count; i++) {
Entity *f = bt->Record.variants[i]; Entity *f = bt->Record.variants[i];
if (are_types_identical(f->type, y.type)) { if (are_types_identical(f->type, y.type)) {
tag_type_found = true; tag_type_found = true;
break; break;
}
} }
if (!tag_type_found) {
gbString type_str = type_to_string(y.type);
error_node(y.expr, "Unknown tag type, got `%s`", type_str);
gb_string_free(type_str);
continue;
}
case_type = y.type;
} else if (match_type_kind == MatchType_Any) {
case_type = y.type;
} else {
GB_PANIC("Unknown type to type match statement");
} }
if (!tag_type_found) {
gbString type_str = type_to_string(y.type);
error_node(y.expr, "Unknown tag type, got `%s`", type_str);
gb_string_free(type_str);
continue;
}
case_type = y.type;
} else if (is_any) {
case_type = y.type;
} else {
GB_PANIC("Unknown type to type match statement");
}
HashKey key = hash_pointer(y.type); HashKey key = hash_pointer(y.type);
bool *found = map_bool_get(&seen, key); bool *found = map_bool_get(&seen, key);
if (found) { if (found) {
TokenPos pos = cc->token.pos; TokenPos pos = cc->token.pos;
gbString expr_str = expr_to_string(y.expr); gbString expr_str = expr_to_string(y.expr);
error_node(y.expr, error_node(y.expr,
"Duplicate type case `%s`\n" "Duplicate type case `%s`\n"
"\tprevious type case at %.*s(%td:%td)", "\tprevious type case at %.*s(%td:%td)",
expr_str, expr_str,
LIT(pos.file), pos.line, pos.column); LIT(pos.file), pos.line, pos.column);
gb_string_free(expr_str); gb_string_free(expr_str);
break; break;
}
map_bool_set(&seen, key, cast(bool)true);
} }
map_bool_set(&seen, key, cast(bool)true);
} }
check_open_scope(c, stmt); if (is_ptr &&
!is_type_any(type_deref(x.type)) &&
cc->list.count == 1 &&
case_type != NULL) {
case_type = make_type_pointer(c->allocator, case_type);
}
if (cc->list.count > 1) {
case_type = NULL;
}
if (case_type == NULL) { if (case_type == NULL) {
if (is_union_ptr) { case_type = x.type;
case_type = type_deref(x.type);
} else {
case_type = x.type;
}
} }
add_type_info_type(c, case_type); add_type_info_type(c, case_type);
check_open_scope(c, stmt);
{ {
// NOTE(bill): Dummy type Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, case_type, false);
Type *tt = case_type;
if (is_union_ptr) {
tt = make_type_pointer(c->allocator, case_type);
add_type_info_type(c, tt);
}
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, tt, true);
tag_var->flags |= EntityFlag_Used; tag_var->flags |= EntityFlag_Used;
tag_var->flags |= EntityFlag_Value;
add_entity(c, c->context.scope, lhs, tag_var); add_entity(c, c->context.scope, lhs, tag_var);
add_entity_use(c, lhs, tag_var); add_entity_use(c, lhs, tag_var);
add_implicit_entity(c, stmt, tag_var);
} }
check_stmt_list(c, cc->stmts, mod_flags); check_stmt_list(c, cc->stmts, mod_flags);
check_close_scope(c); check_close_scope(c);
@@ -1173,20 +1425,38 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string)); error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
break; break;
} }
if (bs->label != NULL) {
if (bs->label->kind != AstNode_Ident) {
error_node(bs->label, "A branch statement's label name must be an identifier");
return;
}
AstNode *ident = bs->label;
String name = ident->Ident.string;
Entity *e = scope_lookup_entity(c->context.scope, name);
if (e == NULL) {
error_node(ident, "Undeclared label name: %.*s", LIT(name));
return;
}
add_entity_use(c, ident, e);
if (e->kind != Entity_Label) {
error_node(ident, "`%.*s` is not a label", LIT(name));
return;
}
}
case_end; case_end;
case_ast_node(us, UsingStmt, node); case_ast_node(us, UsingStmt, node);
switch (us->node->kind) { if (us->list.count == 0) {
default: error(us->token, "Empty `using` list");
// TODO(bill): Better error message for invalid using statement return;
error(us->token, "Invalid `using` statement"); }
break; for_array(i, us->list) {
case_ast_node(es, ExprStmt, us->node); AstNode *expr = unparen_expr(us->list.e[0]);
// TODO(bill): Allow for just a LHS expression list rather than this silly code
Entity *e = NULL; Entity *e = NULL;
bool is_selector = false; bool is_selector = false;
AstNode *expr = unparen_expr(es->expr);
if (expr->kind == AstNode_Ident) { if (expr->kind == AstNode_Ident) {
String name = expr->Ident.string; String name = expr->Ident.string;
e = scope_lookup_entity(c->context.scope, name); e = scope_lookup_entity(c->context.scope, name);
@@ -1194,122 +1464,18 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
Operand o = {0}; Operand o = {0};
e = check_selector(c, &o, expr, NULL); e = check_selector(c, &o, expr, NULL);
is_selector = true; is_selector = true;
} else if (expr->kind == AstNode_Implicit) {
error(us->token, "`using` applied to an implicit value");
continue;
} }
if (e == NULL) { if (!check_using_stmt_entity(c, us, expr, is_selector, e)) {
error(us->token, "`using` applied to an unknown entity");
return; return;
} }
switch (e->kind) {
case Entity_TypeName: {
Type *t = base_type(e->type);
if (is_type_union(t)) {
for (isize i = 0; i < t->Record.variant_count; i++) {
Entity *f = t->Record.variants[i];
Entity *found = scope_insert_entity(c->context.scope, f);
if (found != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
gb_string_free(expr_str);
return;
}
f->using_parent = e;
}
} else if (is_type_enum(t)) {
for (isize i = 0; i < t->Record.field_count; i++) {
Entity *f = t->Record.fields[i];
Entity *found = scope_insert_entity(c->context.scope, f);
if (found != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
gb_string_free(expr_str);
return;
}
f->using_parent = e;
}
} else {
error(us->token, "`using` can be only applied to `union` or `enum` type entities");
}
} break;
case Entity_ImportName: {
Scope *scope = e->ImportName.scope;
for_array(i, scope->elements.entries) {
Entity *decl = scope->elements.entries.e[i].value;
Entity *found = scope_insert_entity(c->context.scope, decl);
if (found != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token,
"Namespace collision while `using` `%s` of: %.*s\n"
"\tat %.*s(%td:%td)\n"
"\tat %.*s(%td:%td)",
expr_str, LIT(found->token.string),
LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column,
LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column
);
gb_string_free(expr_str);
return;
}
}
} break;
case Entity_Variable: {
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
// TODO(bill): Make it work for unions too
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
if (is_selector) {
uvar->using_expr = expr;
}
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
gb_string_free(expr_str);
return;
}
}
}
} else {
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
return;
}
} break;
case Entity_Constant:
error(us->token, "`using` cannot be applied to a constant");
break;
case Entity_Procedure:
case Entity_Builtin:
error(us->token, "`using` cannot be applied to a procedure");
break;
case Entity_Nil:
error(us->token, "`using` cannot be applied to `nil`");
break;
case Entity_Invalid:
error(us->token, "`using` cannot be applied to an invalid entity");
break;
default:
GB_PANIC("TODO(bill): `using` other expressions?");
}
case_end;
} }
case_end; case_end;
case_ast_node(pa, PushAllocator, node); case_ast_node(pa, PushAllocator, node);
Operand op = {0}; Operand op = {0};
check_expr(c, &op, pa->expr); check_expr(c, &op, pa->expr);
@@ -1328,7 +1494,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(vd, ValueDecl, node); case_ast_node(vd, ValueDecl, node);
GB_ASSERT(!c->context.scope->is_file); GB_ASSERT(!c->context.scope->is_file);
if (vd->is_var) { if (!vd->is_var) {
// NOTE(bill): Handled elsewhere
} else {
Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count); Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
isize entity_count = 0; isize entity_count = 0;
@@ -1438,9 +1606,6 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
} }
} }
} }
} else {
// NOTE(bill): Handled elsewhere
} }
case_end; case_end;
} }
+188 -86
View File
@@ -11,7 +11,8 @@ typedef enum StmtFlag {
Stmt_BreakAllowed = 1<<0, Stmt_BreakAllowed = 1<<0,
Stmt_ContinueAllowed = 1<<1, Stmt_ContinueAllowed = 1<<1,
Stmt_FallthroughAllowed = 1<<2, Stmt_FallthroughAllowed = 1<<2,
Stmt_GiveAllowed = 1<<3,
Stmt_CheckScopeDecls = 1<<5,
} StmtFlag; } StmtFlag;
typedef struct BuiltinProc { typedef struct BuiltinProc {
@@ -23,9 +24,12 @@ typedef struct BuiltinProc {
typedef enum BuiltinProcId { typedef enum BuiltinProcId {
BuiltinProc_Invalid, BuiltinProc_Invalid,
BuiltinProc_len,
BuiltinProc_cap,
BuiltinProc_new, BuiltinProc_new,
BuiltinProc_new_slice,
BuiltinProc_free, BuiltinProc_free,
BuiltinProc_make,
BuiltinProc_reserve, BuiltinProc_reserve,
BuiltinProc_clear, BuiltinProc_clear,
@@ -48,12 +52,17 @@ typedef enum BuiltinProcId {
BuiltinProc_panic, BuiltinProc_panic,
BuiltinProc_copy, BuiltinProc_copy,
// BuiltinProc_append,
BuiltinProc_swizzle, BuiltinProc_swizzle,
// BuiltinProc_ptr_offset, BuiltinProc_complex,
// BuiltinProc_ptr_sub, BuiltinProc_quaternion,
BuiltinProc_real,
BuiltinProc_imag,
BuiltinProc_jmag,
BuiltinProc_kmag,
BuiltinProc_conj,
BuiltinProc_slice_ptr, BuiltinProc_slice_ptr,
BuiltinProc_slice_to_bytes, BuiltinProc_slice_to_bytes,
@@ -62,14 +71,19 @@ typedef enum BuiltinProcId {
BuiltinProc_abs, BuiltinProc_abs,
BuiltinProc_clamp, BuiltinProc_clamp,
BuiltinProc_transmute,
BuiltinProc_Count, BuiltinProc_Count,
} BuiltinProcId; } BuiltinProcId;
gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
{STR_LIT(""), 0, false, Expr_Stmt}, {STR_LIT(""), 0, false, Expr_Stmt},
{STR_LIT("len"), 1, false, Expr_Expr},
{STR_LIT("cap"), 1, false, Expr_Expr},
{STR_LIT("new"), 1, false, Expr_Expr}, {STR_LIT("new"), 1, false, Expr_Expr},
{STR_LIT("new_slice"), 2, true, Expr_Expr},
{STR_LIT("free"), 1, false, Expr_Stmt}, {STR_LIT("free"), 1, false, Expr_Stmt},
{STR_LIT("make"), 1, true, Expr_Expr},
{STR_LIT("reserve"), 2, false, Expr_Stmt}, {STR_LIT("reserve"), 2, false, Expr_Stmt},
{STR_LIT("clear"), 1, false, Expr_Stmt}, {STR_LIT("clear"), 1, false, Expr_Stmt},
@@ -87,24 +101,31 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
{STR_LIT("type_info"), 1, false, Expr_Expr}, {STR_LIT("type_info"), 1, false, Expr_Expr},
{STR_LIT("type_info_of_val"), 1, false, Expr_Expr}, {STR_LIT("type_info_of_val"), 1, false, Expr_Expr},
{STR_LIT("compile_assert"), 1, false, Expr_Stmt}, {STR_LIT("compile_assert"), 1, false, Expr_Expr},
{STR_LIT("assert"), 1, false, Expr_Stmt}, {STR_LIT("assert"), 1, false, Expr_Expr},
{STR_LIT("panic"), 1, false, Expr_Stmt}, {STR_LIT("panic"), 1, false, Expr_Stmt},
{STR_LIT("copy"), 2, false, Expr_Expr}, {STR_LIT("copy"), 2, false, Expr_Expr},
// {STR_LIT("append"), 2, false, Expr_Expr},
{STR_LIT("swizzle"), 1, true, Expr_Expr}, {STR_LIT("swizzle"), 1, true, Expr_Expr},
// {STR_LIT("ptr_offset"), 2, false, Expr_Expr}, {STR_LIT("complex"), 2, false, Expr_Expr},
// {STR_LIT("ptr_sub"), 2, false, Expr_Expr}, {STR_LIT("quaternion"), 4, false, Expr_Expr},
{STR_LIT("slice_ptr"), 2, false, Expr_Expr}, {STR_LIT("real"), 1, false, Expr_Expr},
{STR_LIT("imag"), 1, false, Expr_Expr},
{STR_LIT("jmag"), 1, false, Expr_Expr},
{STR_LIT("kmag"), 1, false, Expr_Expr},
{STR_LIT("conj"), 1, false, Expr_Expr},
{STR_LIT("slice_ptr"), 2, true, Expr_Expr},
{STR_LIT("slice_to_bytes"), 1, false, Expr_Stmt}, {STR_LIT("slice_to_bytes"), 1, false, Expr_Stmt},
{STR_LIT("min"), 2, false, Expr_Expr}, {STR_LIT("min"), 2, false, Expr_Expr},
{STR_LIT("max"), 2, false, Expr_Expr}, {STR_LIT("max"), 2, false, Expr_Expr},
{STR_LIT("abs"), 1, false, Expr_Expr}, {STR_LIT("abs"), 1, false, Expr_Expr},
{STR_LIT("clamp"), 3, false, Expr_Expr}, {STR_LIT("clamp"), 3, false, Expr_Expr},
{STR_LIT("transmute"), 2, false, Expr_Expr},
}; };
@@ -163,28 +184,38 @@ bool is_operand_nil(Operand o) {
} }
typedef struct BlockLabel {
String name;
AstNode *label; // AstNode_Label;
} BlockLabel;
// DeclInfo is used to store information of certain declarations to allow for "any order" usage // DeclInfo is used to store information of certain declarations to allow for "any order" usage
typedef struct DeclInfo { typedef struct DeclInfo DeclInfo;
Scope *scope; struct DeclInfo {
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
Scope * scope;
Entity **entities; Entity ** entities;
isize entity_count; isize entity_count;
AstNode *type_expr; AstNode * type_expr;
AstNode *init_expr; AstNode * init_expr;
AstNode *proc_lit; // AstNode_ProcLit AstNode * proc_lit; // AstNode_ProcLit
MapBool deps; // Key: Entity * MapBool deps; // Key: Entity *
} DeclInfo; Array(BlockLabel) labels;
};
// ProcedureInfo stores the information needed for checking a procedure // ProcedureInfo stores the information needed for checking a procedure
typedef struct ProcedureInfo { typedef struct ProcedureInfo {
AstFile * file; AstFile * file;
Token token; Token token;
DeclInfo *decl; DeclInfo * decl;
Type * type; // Type_Procedure Type * type; // Type_Procedure
AstNode * body; // AstNode_BlockStmt AstNode * body; // AstNode_BlockStmt
u32 tags; u32 tags;
} ProcedureInfo; } ProcedureInfo;
// ExprInfo stores information used for "untyped" expressions // ExprInfo stores information used for "untyped" expressions
@@ -208,21 +239,21 @@ ExprInfo make_expr_info(bool is_lhs, AddressingMode mode, Type *type, ExactValue
#include "map.c" #include "map.c"
typedef struct Scope { typedef struct Scope {
Scope * parent; Scope * parent;
Scope * prev, *next; Scope * prev, *next;
Scope * first_child; Scope * first_child;
Scope * last_child; Scope * last_child;
MapEntity elements; // Key: String MapEntity elements; // Key: String
MapBool implicit; // Key: Entity * MapBool implicit; // Key: Entity *
Array(Scope *) shared; Array(Scope *) shared;
Array(Scope *) imported; Array(Scope *) imported;
bool is_proc; bool is_proc;
bool is_global; bool is_global;
bool is_file; bool is_file;
bool is_init; bool is_init;
bool has_been_imported; // This is only applicable to file scopes bool has_been_imported; // This is only applicable to file scopes
AstFile * file; AstFile * file;
} Scope; } Scope;
gb_global Scope *universal_scope = NULL; gb_global Scope *universal_scope = NULL;
@@ -278,6 +309,7 @@ typedef struct CheckerInfo {
MapScope scopes; // Key: AstNode * | Node -> Scope MapScope scopes; // Key: AstNode * | Node -> Scope
MapExprInfo untyped; // Key: AstNode * | Expression -> ExprInfo MapExprInfo untyped; // Key: AstNode * | Expression -> ExprInfo
MapDeclInfo entities; // Key: Entity * MapDeclInfo entities; // Key: Entity *
MapEntity implicits; // Key: AstNode *
MapEntity foreigns; // Key: String MapEntity foreigns; // Key: String
MapAstFile files; // Key: String (full path) MapAstFile files; // Key: String (full path)
MapIsize type_info_map; // Key: Type * MapIsize type_info_map; // Key: Type *
@@ -317,14 +349,16 @@ typedef Array(DelayedEntity) DelayedEntities;
void init_declaration_info(DeclInfo *d, Scope *scope) { void init_declaration_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
d->scope = scope; d->parent = parent;
d->scope = scope;
map_bool_init(&d->deps, heap_allocator()); map_bool_init(&d->deps, heap_allocator());
array_init(&d->labels, heap_allocator());
} }
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) { DeclInfo *make_declaration_info(gbAllocator a, Scope *scope, DeclInfo *parent) {
DeclInfo *d = gb_alloc_item(a, DeclInfo); DeclInfo *d = gb_alloc_item(a, DeclInfo);
init_declaration_info(d, scope); init_declaration_info(d, scope, parent);
return d; return d;
} }
@@ -357,9 +391,9 @@ Scope *make_scope(Scope *parent, gbAllocator allocator) {
Scope *s = gb_alloc_item(allocator, Scope); Scope *s = gb_alloc_item(allocator, Scope);
s->parent = parent; s->parent = parent;
map_entity_init(&s->elements, heap_allocator()); map_entity_init(&s->elements, heap_allocator());
map_bool_init(&s->implicit, heap_allocator()); map_bool_init(&s->implicit, heap_allocator());
array_init(&s->shared, heap_allocator()); array_init(&s->shared, heap_allocator());
array_init(&s->imported, heap_allocator()); array_init(&s->imported, heap_allocator());
if (parent != NULL && parent != universal_scope) { if (parent != NULL && parent != universal_scope) {
DLIST_APPEND(parent->first_child, parent->last_child, s); DLIST_APPEND(parent->first_child, parent->last_child, s);
@@ -455,6 +489,9 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit
if (found) { if (found) {
Entity *e = *found; Entity *e = *found;
if (gone_thru_proc) { if (gone_thru_proc) {
if (e->kind == Entity_Label) {
continue;
}
if (e->kind == Entity_Variable && if (e->kind == Entity_Variable &&
!e->scope->is_file && !e->scope->is_file &&
!e->scope->is_global) { !e->scope->is_global) {
@@ -520,6 +557,10 @@ Entity *scope_lookup_entity(Scope *s, String name) {
Entity *scope_insert_entity(Scope *s, Entity *entity) { Entity *scope_insert_entity(Scope *s, Entity *entity) {
String name = entity->token.string; String name = entity->token.string;
if (str_eq(name, str_lit("output"))) {
gb_printf_err("Here! %.*s\n", LIT(name));
}
HashKey key = hash_string(name); HashKey key = hash_string(name);
Entity **found = map_entity_get(&s->elements, key); Entity **found = map_entity_get(&s->elements, key);
@@ -596,7 +637,7 @@ void add_global_constant(gbAllocator a, String name, Type *type, ExactValue valu
void add_global_string_constant(gbAllocator a, String name, String value) { void add_global_string_constant(gbAllocator a, String name, String value) {
add_global_constant(a, name, t_untyped_string, make_exact_value_string(value)); add_global_constant(a, name, t_untyped_string, exact_value_string(value));
} }
@@ -616,8 +657,8 @@ void init_universal_scope(void) {
} }
// Constants // Constants
add_global_constant(a, str_lit("true"), t_untyped_bool, make_exact_value_bool(true)); add_global_constant(a, str_lit("true"), t_untyped_bool, exact_value_bool(true));
add_global_constant(a, str_lit("false"), t_untyped_bool, make_exact_value_bool(false)); add_global_constant(a, str_lit("false"), t_untyped_bool, exact_value_bool(false));
add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil)); add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil));
add_global_entity(make_entity_library_name(a, universal_scope, add_global_entity(make_entity_library_name(a, universal_scope,
@@ -662,6 +703,7 @@ void init_checker_info(CheckerInfo *i) {
map_decl_info_init(&i->entities, a); map_decl_info_init(&i->entities, a);
map_expr_info_init(&i->untyped, a); map_expr_info_init(&i->untyped, a);
map_entity_init(&i->foreigns, a); map_entity_init(&i->foreigns, a);
map_entity_init(&i->implicits, a);
map_isize_init(&i->type_info_map, a); map_isize_init(&i->type_info_map, a);
map_ast_file_init(&i->files, a); map_ast_file_init(&i->files, a);
i->type_info_count = 0; i->type_info_count = 0;
@@ -676,12 +718,17 @@ void destroy_checker_info(CheckerInfo *i) {
map_decl_info_destroy(&i->entities); map_decl_info_destroy(&i->entities);
map_expr_info_destroy(&i->untyped); map_expr_info_destroy(&i->untyped);
map_entity_destroy(&i->foreigns); map_entity_destroy(&i->foreigns);
map_entity_destroy(&i->implicits);
map_isize_destroy(&i->type_info_map); map_isize_destroy(&i->type_info_map);
map_ast_file_destroy(&i->files); map_ast_file_destroy(&i->files);
} }
void init_checker(Checker *c, Parser *parser, BuildContext *bc) { void init_checker(Checker *c, Parser *parser, BuildContext *bc) {
if (global_error_collector.count > 0) {
gb_exit(1);
}
gbAllocator a = heap_allocator(); gbAllocator a = heap_allocator();
c->parser = parser; c->parser = parser;
@@ -816,7 +863,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
return false; return false;
} }
error(entity->token, error(entity->token,
"Redeclararation of `%.*s` in this scope through `using`\n" "Redeclaration of `%.*s` in this scope through `using`\n"
"\tat %.*s(%td:%td)", "\tat %.*s(%td:%td)",
LIT(name), LIT(name),
LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column); LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
@@ -827,7 +874,7 @@ bool add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
return false; return false;
} }
error(entity->token, error(entity->token,
"Redeclararation of `%.*s` in this scope\n" "Redeclaration of `%.*s` in this scope\n"
"\tat %.*s(%td:%td)", "\tat %.*s(%td:%td)",
LIT(name), LIT(name),
LIT(pos.file), pos.line, pos.column); LIT(pos.file), pos.line, pos.column);
@@ -861,6 +908,12 @@ void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclIn
} }
void add_implicit_entity(Checker *c, AstNode *node, Entity *e) {
GB_ASSERT(node != NULL);
GB_ASSERT(e != NULL);
map_entity_set(&c->info.implicits, hash_pointer(node), e);
}
void add_type_info_type(Checker *c, Type *t) { void add_type_info_type(Checker *c, Type *t) {
if (t == NULL) { if (t == NULL) {
@@ -919,6 +972,24 @@ void add_type_info_type(Checker *c, Type *t) {
add_type_info_type(c, t_type_info_ptr); add_type_info_type(c, t_type_info_ptr);
add_type_info_type(c, t_rawptr); add_type_info_type(c, t_rawptr);
break; break;
case Basic_complex64:
add_type_info_type(c, t_type_info_float);
add_type_info_type(c, t_f32);
break;
case Basic_complex128:
add_type_info_type(c, t_type_info_float);
add_type_info_type(c, t_f64);
break;
case Basic_quaternion128:
add_type_info_type(c, t_type_info_float);
add_type_info_type(c, t_f32);
break;
case Basic_quaternion256:
add_type_info_type(c, t_type_info_float);
add_type_info_type(c, t_f64);
break;
} }
} break; } break;
@@ -926,6 +997,10 @@ void add_type_info_type(Checker *c, Type *t) {
add_type_info_type(c, bt->Pointer.elem); add_type_info_type(c, bt->Pointer.elem);
break; break;
case Type_Atomic:
add_type_info_type(c, bt->Atomic.elem);
break;
case Type_Array: case Type_Array:
add_type_info_type(c, bt->Array.elem); add_type_info_type(c, bt->Array.elem);
add_type_info_type(c, make_type_pointer(c->allocator, bt->Array.elem)); add_type_info_type(c, make_type_pointer(c->allocator, bt->Array.elem));
@@ -1107,35 +1182,41 @@ void init_preload(Checker *c) {
if (record->variant_count != 19) { if (record->variant_count != 22) {
compiler_error("Invalid `Type_Info` layout"); compiler_error("Invalid `Type_Info` layout");
} }
t_type_info_named = record->variants[ 1]->type; t_type_info_named = record->variants[ 1]->type;
t_type_info_integer = record->variants[ 2]->type; t_type_info_integer = record->variants[ 2]->type;
t_type_info_float = record->variants[ 3]->type; t_type_info_float = record->variants[ 3]->type;
t_type_info_string = record->variants[ 4]->type; t_type_info_complex = record->variants[ 4]->type;
t_type_info_boolean = record->variants[ 5]->type; t_type_info_quaternion = record->variants[ 5]->type;
t_type_info_any = record->variants[ 6]->type; t_type_info_string = record->variants[ 6]->type;
t_type_info_pointer = record->variants[ 7]->type; t_type_info_boolean = record->variants[ 7]->type;
t_type_info_procedure = record->variants[ 8]->type; t_type_info_any = record->variants[ 8]->type;
t_type_info_array = record->variants[ 9]->type; t_type_info_pointer = record->variants[ 9]->type;
t_type_info_dynamic_array = record->variants[10]->type; t_type_info_atomic = record->variants[10]->type;
t_type_info_slice = record->variants[11]->type; t_type_info_procedure = record->variants[11]->type;
t_type_info_vector = record->variants[12]->type; t_type_info_array = record->variants[12]->type;
t_type_info_tuple = record->variants[13]->type; t_type_info_dynamic_array = record->variants[13]->type;
t_type_info_struct = record->variants[14]->type; t_type_info_slice = record->variants[14]->type;
t_type_info_raw_union = record->variants[15]->type; t_type_info_vector = record->variants[15]->type;
t_type_info_union = record->variants[16]->type; t_type_info_tuple = record->variants[16]->type;
t_type_info_enum = record->variants[17]->type; t_type_info_struct = record->variants[17]->type;
t_type_info_map = record->variants[18]->type; t_type_info_raw_union = record->variants[18]->type;
t_type_info_union = record->variants[19]->type;
t_type_info_enum = record->variants[20]->type;
t_type_info_map = record->variants[21]->type;
t_type_info_named_ptr = make_type_pointer(c->allocator, t_type_info_named); t_type_info_named_ptr = make_type_pointer(c->allocator, t_type_info_named);
t_type_info_integer_ptr = make_type_pointer(c->allocator, t_type_info_integer); t_type_info_integer_ptr = make_type_pointer(c->allocator, t_type_info_integer);
t_type_info_float_ptr = make_type_pointer(c->allocator, t_type_info_float); t_type_info_float_ptr = make_type_pointer(c->allocator, t_type_info_float);
t_type_info_complex_ptr = make_type_pointer(c->allocator, t_type_info_complex);
t_type_info_quaternion_ptr = make_type_pointer(c->allocator, t_type_info_quaternion);
t_type_info_string_ptr = make_type_pointer(c->allocator, t_type_info_string); t_type_info_string_ptr = make_type_pointer(c->allocator, t_type_info_string);
t_type_info_boolean_ptr = make_type_pointer(c->allocator, t_type_info_boolean); t_type_info_boolean_ptr = make_type_pointer(c->allocator, t_type_info_boolean);
t_type_info_any_ptr = make_type_pointer(c->allocator, t_type_info_any); t_type_info_any_ptr = make_type_pointer(c->allocator, t_type_info_any);
t_type_info_pointer_ptr = make_type_pointer(c->allocator, t_type_info_pointer); t_type_info_pointer_ptr = make_type_pointer(c->allocator, t_type_info_pointer);
t_type_info_atomic_ptr = make_type_pointer(c->allocator, t_type_info_atomic);
t_type_info_procedure_ptr = make_type_pointer(c->allocator, t_type_info_procedure); t_type_info_procedure_ptr = make_type_pointer(c->allocator, t_type_info_procedure);
t_type_info_array_ptr = make_type_pointer(c->allocator, t_type_info_array); t_type_info_array_ptr = make_type_pointer(c->allocator, t_type_info_array);
t_type_info_dynamic_array_ptr = make_type_pointer(c->allocator, t_type_info_dynamic_array); t_type_info_dynamic_array_ptr = make_type_pointer(c->allocator, t_type_info_dynamic_array);
@@ -1162,12 +1243,6 @@ void init_preload(Checker *c) {
t_context_ptr = make_type_pointer(c->allocator, t_context); t_context_ptr = make_type_pointer(c->allocator, t_context);
} }
if (t_raw_dynamic_array == NULL) {
Entity *e = find_core_entity(c, str_lit("Raw_Dynamic_Array"));
t_raw_dynamic_array = e->type;
t_raw_dynamic_array = make_type_pointer(c->allocator, t_raw_dynamic_array);
}
if (t_map_key == NULL) { if (t_map_key == NULL) {
Entity *e = find_core_entity(c, str_lit("__Map_Key")); Entity *e = find_core_entity(c, str_lit("__Map_Key"));
t_map_key = e->type; t_map_key = e->type;
@@ -1234,7 +1309,6 @@ void check_procedure_overloading(Checker *c, Entity *e) {
continue; continue;
} }
String name = p->token.string; String name = p->token.string;
GB_ASSERT(p->kind == Entity_Procedure); GB_ASSERT(p->kind == Entity_Procedure);
@@ -1397,7 +1471,7 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_cap); Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_cap);
DeclInfo *di = NULL; DeclInfo *di = NULL;
if (vd->values.count > 0) { if (vd->values.count > 0) {
di = make_declaration_info(heap_allocator(), c->context.scope); di = make_declaration_info(heap_allocator(), c->context.scope, c->context.decl);
di->entities = entities; di->entities = entities;
di->type_expr = vd->type; di->type_expr = vd->type;
di->init_expr = vd->values.e[0]; di->init_expr = vd->values.e[0];
@@ -1432,7 +1506,7 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
DeclInfo *d = di; DeclInfo *d = di;
if (d == NULL) { if (d == NULL) {
AstNode *init_expr = value; AstNode *init_expr = value;
d = make_declaration_info(heap_allocator(), e->scope); d = make_declaration_info(heap_allocator(), e->scope, c->context.decl);
d->type_expr = vd->type; d->type_expr = vd->type;
d->init_expr = init_expr; d->init_expr = init_expr;
} }
@@ -1458,15 +1532,25 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
init = vd->values.e[i]; init = vd->values.e[i];
} }
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope); DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);
Entity *e = NULL; Entity *e = NULL;
AstNode *up_init = unparen_expr(init); AstNode *up_init = unparen_expr(init);
if (up_init != NULL && is_ast_node_type(up_init)) { if (up_init != NULL && is_ast_node_type(up_init)) {
AstNode *type = up_init;
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL); e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
// TODO(bill): What if vd->type != NULL??? How to handle this case? // TODO(bill): What if vd->type != NULL??? How to handle this case?
d->type_expr = init; d->type_expr = type;
d->init_expr = init; d->init_expr = type;
} else if (up_init != NULL && up_init->kind == AstNode_Alias) {
#if 1
error_node(up_init, "#alias declarations are not yet supported");
continue;
#else
e = make_entity_alias(c->allocator, d->scope, name->Ident, NULL, EntityAlias_Invalid, NULL);
d->type_expr = vd->type;
d->init_expr = up_init->Alias.expr;
#endif
} else if (init != NULL && up_init->kind == AstNode_ProcLit) { } else if (init != NULL && up_init->kind == AstNode_ProcLit) {
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags); e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
d->proc_lit = up_init; d->proc_lit = up_init;
@@ -1512,6 +1596,19 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
continue; continue;
} }
if (fl->cond != NULL) {
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, fl->cond);
if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
error_node(fl->cond, "Non-constant boolean `when` condition");
continue;
}
if (operand.value.kind == ExactValue_Bool &&
!operand.value.value_bool) {
continue;
}
}
DelayedDecl di = {c->context.scope, decl}; DelayedDecl di = {c->context.scope, decl};
array_add(&c->delayed_foreign_libraries, di); array_add(&c->delayed_foreign_libraries, di);
case_end; case_end;
@@ -1826,7 +1923,7 @@ void check_parsed_files(Checker *c) {
} }
f->scope = scope; f->scope = scope;
f->decl_info = make_declaration_info(c->allocator, f->scope); f->decl_info = make_declaration_info(c->allocator, f->scope, c->context.decl);
HashKey key = hash_string(f->tokenizer.fullpath); HashKey key = hash_string(f->tokenizer.fullpath);
map_scope_set(&file_scopes, key, scope); map_scope_set(&file_scopes, key, scope);
map_ast_file_set(&c->info.files, key, f); map_ast_file_set(&c->info.files, key, f);
@@ -1903,17 +2000,25 @@ void check_parsed_files(Checker *c) {
} }
} }
// NOTE(bill): Check for illegal cyclic type declarations // NOTE(bill): Check for illegal cyclic type declarations
for_array(i, c->info.definitions.entries) { for_array(i, c->info.definitions.entries) {
Entity *e = c->info.definitions.entries.e[i].value; Entity *e = c->info.definitions.entries.e[i].value;
if (e->kind == Entity_TypeName) { if (e->kind == Entity_TypeName ||
(e->kind == Entity_Alias && e->Alias.kind == EntityAlias_Type)) {
if (e->type != NULL) { if (e->type != NULL) {
// i64 size = type_size_of(c->sizes, c->allocator, e->type); // i64 size = type_size_of(c->sizes, c->allocator, e->type);
i64 align = type_align_of(c->allocator, e->type); i64 align = type_align_of(c->allocator, e->type);
if (align > 0) {
// add_type_info_type(c, e->type);
}
} }
} }
} }
// gb_printf_err("Count: %td\n", c->info.type_info_count++);
for_array(i, file_scopes.entries) { for_array(i, file_scopes.entries) {
Scope *s = file_scopes.entries.e[i].value; Scope *s = file_scopes.entries.e[i].value;
if (s->is_init) { if (s->is_init) {
@@ -1938,6 +2043,3 @@ void check_parsed_files(Checker *c) {
map_scope_destroy(&file_scopes); map_scope_destroy(&file_scopes);
} }
+99
View File
@@ -1,7 +1,14 @@
#if defined(GB_SYSTEM_UNIX)
// Required for intrinsics on GCC
#include <xmmintrin.h>
#endif
#define GB_NO_DEFER #define GB_NO_DEFER
#define GB_IMPLEMENTATION #define GB_IMPLEMENTATION
#include "gb/gb.h" #include "gb/gb.h"
#include <math.h>
gbAllocator heap_allocator(void) { gbAllocator heap_allocator(void) {
return gb_heap_allocator(); return gb_heap_allocator();
} }
@@ -24,6 +31,94 @@ gbAllocator scratch_allocator(void) {
return gb_scratch_allocator(&scratch_memory); return gb_scratch_allocator(&scratch_memory);
} }
typedef struct DynamicArenaBlock DynamicArenaBlock;
typedef struct DynamicArena DynamicArena;
struct DynamicArenaBlock {
DynamicArenaBlock *prev;
DynamicArenaBlock *next;
u8 * start;
isize count;
isize capacity;
gbVirtualMemory vm;
};
struct DynamicArena {
DynamicArenaBlock *start_block;
DynamicArenaBlock *current_block;
isize block_size;
};
DynamicArenaBlock *add_dynamic_arena_block(DynamicArena *a) {
GB_ASSERT(a != NULL);
GB_ASSERT(a->block_size > 0);
gbVirtualMemory vm = gb_vm_alloc(NULL, a->block_size);
DynamicArenaBlock *block = cast(DynamicArenaBlock *)vm.data;
u8 *start = cast(u8 *)gb_align_forward(cast(u8 *)(block + 1), GB_DEFAULT_MEMORY_ALIGNMENT);
u8 *end = cast(u8 *)vm.data + vm.size;
block->vm = vm;
block->start = start;
block->count = 0;
block->capacity = end-start;
if (a->current_block != NULL) {
a->current_block->next = block;
block->prev = a->current_block;
}
a->current_block = block;
return block;
}
void init_dynamic_arena(DynamicArena *a, isize block_size) {
isize size = gb_size_of(DynamicArenaBlock) + block_size;
size = cast(isize)gb_align_forward(cast(void *)cast(uintptr)size, GB_DEFAULT_MEMORY_ALIGNMENT);
a->block_size = size;
a->start_block = add_dynamic_arena_block(a);
}
void destroy_dynamic_arena(DynamicArena *a) {
DynamicArenaBlock *b = a->current_block;
while (b != NULL) {
gbVirtualMemory vm = b->vm;
b = b->prev;
gb_vm_free(b->vm);
}
}
GB_ALLOCATOR_PROC(dynamic_arena_allocator_proc) {
DynamicArena *a = cast(DynamicArena *)allocator_data;
void *ptr = NULL;
switch (type) {
case gbAllocation_Alloc: {
} break;
case gbAllocation_Free: {
} break;
case gbAllocation_Resize: {
} break;
case gbAllocation_FreeAll:
GB_PANIC("free_all is not supported by this allocator");
break;
}
return ptr;
}
gbAllocator dynamic_arena_allocator(DynamicArena *a) {
gbAllocator allocator = {dynamic_arena_allocator_proc, a};
return allocator;
}
i64 next_pow2(i64 n) { i64 next_pow2(i64 n) {
if (n <= 0) { if (n <= 0) {
@@ -104,6 +199,10 @@ i16 f32_to_f16(f32 value) {
} }
} }
f64 gb_sqrt(f64 x) {
return sqrt(x);
}
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++) #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
+47 -8
View File
@@ -13,13 +13,15 @@ typedef struct Type Type;
ENTITY_KIND(Builtin) \ ENTITY_KIND(Builtin) \
ENTITY_KIND(ImportName) \ ENTITY_KIND(ImportName) \
ENTITY_KIND(LibraryName) \ ENTITY_KIND(LibraryName) \
ENTITY_KIND(Alias) \
ENTITY_KIND(Nil) \ ENTITY_KIND(Nil) \
ENTITY_KIND(Count) ENTITY_KIND(Label)
typedef enum EntityKind { typedef enum EntityKind {
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k), #define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
ENTITY_KINDS ENTITY_KINDS
#undef ENTITY_KIND #undef ENTITY_KIND
Entity_Count,
} EntityKind; } EntityKind;
String const entity_strings[] = { String const entity_strings[] = {
@@ -31,13 +33,14 @@ String const entity_strings[] = {
typedef enum EntityFlag { typedef enum EntityFlag {
EntityFlag_Visited = 1<<0, EntityFlag_Visited = 1<<0,
EntityFlag_Used = 1<<1, EntityFlag_Used = 1<<1,
EntityFlag_Anonymous = 1<<2, EntityFlag_Using = 1<<2,
EntityFlag_Field = 1<<3, EntityFlag_Field = 1<<3,
EntityFlag_Param = 1<<4, EntityFlag_Param = 1<<4,
EntityFlag_VectorElem = 1<<5, EntityFlag_VectorElem = 1<<5,
EntityFlag_Ellipsis = 1<<6, EntityFlag_Ellipsis = 1<<6,
EntityFlag_NoAlias = 1<<7, EntityFlag_NoAlias = 1<<7,
EntityFlag_TypeField = 1<<8, EntityFlag_TypeField = 1<<8,
EntityFlag_Value = 1<<9,
} EntityFlag; } EntityFlag;
// Zero value means the overloading process is not yet done // Zero value means the overloading process is not yet done
@@ -47,11 +50,18 @@ typedef enum OverloadKind {
Overload_Yes, Overload_Yes,
} OverloadKind; } OverloadKind;
typedef enum EntityAliasKind {
EntityAlias_Invalid,
EntityAlias_Type,
EntityAlias_Entity,
} EntityAliasKind;
// An Entity is a named "thing" in the language // An Entity is a named "thing" in the language
typedef struct Entity Entity; typedef struct Entity Entity;
struct Entity { struct Entity {
EntityKind kind; EntityKind kind;
u64 id;
u32 flags; u32 flags;
Token token; Token token;
Scope * scope; Scope * scope;
@@ -72,7 +82,9 @@ struct Entity {
bool is_immutable; bool is_immutable;
bool is_thread_local; bool is_thread_local;
} Variable; } Variable;
i32 TypeName; struct {
bool is_type_alias;
} TypeName;
struct { struct {
bool is_foreign; bool is_foreign;
String foreign_name; String foreign_name;
@@ -95,7 +107,15 @@ struct Entity {
String name; String name;
bool used; bool used;
} LibraryName; } LibraryName;
struct {
EntityAliasKind kind;
Entity * original;
} Alias;
i32 Nil; i32 Nil;
struct {
String name;
AstNode *node;
} Label;
}; };
}; };
@@ -126,6 +146,7 @@ bool is_entity_exported(Entity *e) {
return name.text[0] != '_'; return name.text[0] != '_';
} }
gb_global u64 global_entity_id = 0;
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) { Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
Entity *entity = gb_alloc_item(a, Entity); Entity *entity = gb_alloc_item(a, Entity);
@@ -133,6 +154,7 @@ Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token,
entity->scope = scope; entity->scope = scope;
entity->token = token; entity->token = token;
entity->type = type; entity->type = type;
entity->id = ++global_entity_id;
return entity; return entity;
} }
@@ -147,7 +169,7 @@ Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, T
token.pos = parent->token.pos; token.pos = parent->token.pos;
Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type); Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
entity->using_parent = parent; entity->using_parent = parent;
entity->flags |= EntityFlag_Anonymous; entity->flags |= EntityFlag_Using;
return entity; return entity;
} }
@@ -163,20 +185,20 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty
return entity; return entity;
} }
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, bool is_immutable) { Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, bool is_immutable) {
Entity *entity = make_entity_variable(a, scope, token, type, is_immutable); Entity *entity = make_entity_variable(a, scope, token, type, is_immutable);
entity->flags |= EntityFlag_Used; entity->flags |= EntityFlag_Used;
if (anonymous) entity->flags |= EntityFlag_Anonymous; if (is_using) entity->flags |= EntityFlag_Using;
entity->flags |= EntityFlag_Param; entity->flags |= EntityFlag_Param;
return entity; return entity;
} }
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) { Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type, false); Entity *entity = make_entity_variable(a, scope, token, type, false);
entity->Variable.field_src_index = field_src_index; entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index; entity->Variable.field_index = field_src_index;
if (is_using) entity->flags |= EntityFlag_Using;
entity->flags |= EntityFlag_Field; entity->flags |= EntityFlag_Field;
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
return entity; return entity;
} }
@@ -218,12 +240,29 @@ Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type
return entity; return entity;
} }
Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type,
EntityAliasKind kind, Entity *original) {
Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
entity->Alias.kind = kind;
entity->Alias.original = original;
return entity;
}
Entity *make_entity_nil(gbAllocator a, String name, Type *type) { Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
Token token = make_token_ident(name); Token token = make_token_ident(name);
Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type); Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
return entity; return entity;
} }
Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type,
AstNode *node) {
Entity *entity = alloc_entity(a, Entity_Label, scope, token, type);
entity->Label.node = node;
return entity;
}
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) { Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
token.string = str_lit("_"); token.string = str_lit("_");
return make_entity_variable(a, scope, token, NULL, false); return make_entity_variable(a, scope, token, NULL, false);
+368 -58
View File
@@ -5,6 +5,14 @@
typedef struct AstNode AstNode; typedef struct AstNode AstNode;
typedef struct Complex128 {
f64 real, imag;
} Complex128;
typedef struct Quaternion256 {
f64 real, imag, jmag, kmag;
} Quaternion256;
typedef enum ExactValueKind { typedef enum ExactValueKind {
ExactValue_Invalid, ExactValue_Invalid,
@@ -12,6 +20,8 @@ typedef enum ExactValueKind {
ExactValue_String, ExactValue_String,
ExactValue_Integer, ExactValue_Integer,
ExactValue_Float, ExactValue_Float,
ExactValue_Complex,
ExactValue_Quaternion,
ExactValue_Pointer, ExactValue_Pointer,
ExactValue_Compound, // TODO(bill): Is this good enough? ExactValue_Compound, // TODO(bill): Is this good enough?
@@ -21,12 +31,14 @@ typedef enum ExactValueKind {
typedef struct ExactValue { typedef struct ExactValue {
ExactValueKind kind; ExactValueKind kind;
union { union {
bool value_bool; bool value_bool;
String value_string; String value_string;
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
f64 value_float; f64 value_float;
i64 value_pointer; i64 value_pointer;
AstNode *value_compound; Complex128 value_complex;
Quaternion256 value_quaternion;
AstNode * value_compound;
}; };
} ExactValue; } ExactValue;
@@ -35,45 +47,62 @@ HashKey hash_exact_value(ExactValue v) {
} }
ExactValue make_exact_value_compound(AstNode *node) { ExactValue exact_value_compound(AstNode *node) {
ExactValue result = {ExactValue_Compound}; ExactValue result = {ExactValue_Compound};
result.value_compound = node; result.value_compound = node;
return result; return result;
} }
ExactValue make_exact_value_bool(bool b) { ExactValue exact_value_bool(bool b) {
ExactValue result = {ExactValue_Bool}; ExactValue result = {ExactValue_Bool};
result.value_bool = (b != 0); result.value_bool = (b != 0);
return result; return result;
} }
ExactValue make_exact_value_string(String string) { ExactValue exact_value_string(String string) {
// TODO(bill): Allow for numbers with underscores in them // TODO(bill): Allow for numbers with underscores in them
ExactValue result = {ExactValue_String}; ExactValue result = {ExactValue_String};
result.value_string = string; result.value_string = string;
return result; return result;
} }
ExactValue make_exact_value_integer(i64 i) { ExactValue exact_value_integer(i64 i) {
ExactValue result = {ExactValue_Integer}; ExactValue result = {ExactValue_Integer};
result.value_integer = i; result.value_integer = i;
return result; return result;
} }
ExactValue make_exact_value_float(f64 f) { ExactValue exact_value_float(f64 f) {
ExactValue result = {ExactValue_Float}; ExactValue result = {ExactValue_Float};
result.value_float = f; result.value_float = f;
return result; return result;
} }
ExactValue make_exact_value_pointer(i64 ptr) { ExactValue exact_value_complex(f64 real, f64 imag) {
ExactValue result = {ExactValue_Complex};
result.value_complex.real = real;
result.value_complex.imag = imag;
return result;
}
ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
ExactValue result = {ExactValue_Quaternion};
result.value_quaternion.real = real;
result.value_quaternion.imag = imag;
result.value_quaternion.jmag = jmag;
result.value_quaternion.kmag = kmag;
return result;
}
ExactValue exact_value_pointer(i64 ptr) {
ExactValue result = {ExactValue_Pointer}; ExactValue result = {ExactValue_Pointer};
result.value_pointer = ptr; result.value_pointer = ptr;
return result; return result;
} }
ExactValue make_exact_value_integer_from_string(String string) { ExactValue exact_value_integer_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them // TODO(bill): Allow for numbers with underscores in them
i32 base = 10; i32 base = 10;
bool has_prefix = false; bool has_prefix = false;
@@ -82,6 +111,7 @@ ExactValue make_exact_value_integer_from_string(String string) {
case 'b': base = 2; has_prefix = true; break; case 'b': base = 2; has_prefix = true; break;
case 'o': base = 8; has_prefix = true; break; case 'o': base = 8; has_prefix = true; break;
case 'd': base = 10; has_prefix = true; break; case 'd': base = 10; has_prefix = true; break;
case 'z': base = 12; has_prefix = true; break;
case 'x': base = 16; has_prefix = true; break; case 'x': base = 16; has_prefix = true; break;
} }
} }
@@ -100,25 +130,19 @@ ExactValue make_exact_value_integer_from_string(String string) {
continue; continue;
} }
i64 v = 0; i64 v = 0;
if (gb_char_is_digit(r)) { v = digit_value(r);
v = r - '0'; if (v >= base) {
} else if (gb_char_is_hex_digit(r)) {
v = gb_hex_digit_to_int(r);
} else {
break; break;
} }
result *= base; result *= base;
result += v; result += v;
} }
return make_exact_value_integer(result); return exact_value_integer(result);
} }
f64 float_from_string(String string) {
ExactValue make_exact_value_float_from_string(String string) {
isize i = 0; isize i = 0;
u8 *str = string.text; u8 *str = string.text;
isize len = string.len; isize len = string.len;
@@ -137,10 +161,10 @@ ExactValue make_exact_value_float_from_string(String string) {
if (r == '_') { if (r == '_') {
continue; continue;
} }
if (!gb_char_is_digit(r)) { i64 v = digit_value(r);
if (v >= 10) {
break; break;
} }
i64 v = r - '0';
value *= 10.0; value *= 10.0;
value += v; value += v;
} }
@@ -153,29 +177,38 @@ ExactValue make_exact_value_float_from_string(String string) {
if (r == '_') { if (r == '_') {
continue; continue;
} }
if (!gb_char_is_digit(r)) { i64 v = digit_value(r);
if (v >= 10) {
break; break;
} }
value += (r-'0')/pow10; value += v/pow10;
pow10 *= 10.0; pow10 *= 10.0;
} }
} }
f64 frac = 0; bool frac = false;
f64 scale = 1.0; f64 scale = 1.0;
if ((str[i] == 'e') || (str[i] == 'E')) { if ((str[i] == 'e') || (str[i] == 'E')) {
i++; i++;
if (str[i] == '-') { if (str[i] == '-') {
frac = 1; frac = true;
i++; i++;
} else if (str[i] == '+') { } else if (str[i] == '+') {
i++; i++;
} }
u32 exp; u32 exp = 0;
for (exp = 0; gb_char_is_digit(str[i]); i++) { for (; i < len; i++) {
exp = exp * 10 + (str[i]-'0'); Rune r = cast(Rune)str[i];
if (r == '_') {
continue;
}
u32 d = cast(u32)digit_value(r);
if (d >= 10) {
break;
}
exp = exp * 10 + d;
} }
if (exp > 308) exp = 308; if (exp > 308) exp = 308;
@@ -184,21 +217,36 @@ ExactValue make_exact_value_float_from_string(String string) {
while (exp > 0) { scale *= 10.0; exp -= 1; } while (exp > 0) { scale *= 10.0; exp -= 1; }
} }
f64 result = sign * (frac ? (value / scale) : (value * scale)); return sign * (frac ? (value / scale) : (value * scale));
return make_exact_value_float(result); }
ExactValue exact_value_float_from_string(String string) {
return exact_value_float(float_from_string(string));
} }
ExactValue make_exact_value_from_basic_literal(Token token) { ExactValue exact_value_from_basic_literal(Token token) {
switch (token.kind) { switch (token.kind) {
case Token_String: return make_exact_value_string(token.string); case Token_String: return exact_value_string(token.string);
case Token_Integer: return make_exact_value_integer_from_string(token.string); case Token_Integer: return exact_value_integer_from_string(token.string);
case Token_Float: return make_exact_value_float_from_string(token.string); case Token_Float: return exact_value_float_from_string(token.string);
case Token_Imag: {
String str = token.string;
Rune last_rune = cast(Rune)str.text[str.len-1];
str.len--; // Ignore the `i|j|k`
f64 imag = float_from_string(str);
switch (last_rune) {
case 'i': return exact_value_complex(0, imag);
case 'j': return exact_value_quaternion(0, 0, imag, 0);
case 'k': return exact_value_quaternion(0, 0, 0, imag);
}
}
case Token_Rune: { case Token_Rune: {
Rune r = GB_RUNE_INVALID; Rune r = GB_RUNE_INVALID;
gb_utf8_decode(token.string.text, token.string.len, &r); gb_utf8_decode(token.string.text, token.string.len, &r);
// gb_printf("%.*s rune: %d\n", LIT(token.string), r); // gb_printf("%.*s rune: %d\n", LIT(token.string), r);
return make_exact_value_integer(r); return exact_value_integer(r);
} }
default: default:
GB_PANIC("Invalid token for basic literal"); GB_PANIC("Invalid token for basic literal");
@@ -217,12 +265,12 @@ ExactValue exact_value_to_integer(ExactValue v) {
i64 i = cast(i64)v.value_float; i64 i = cast(i64)v.value_float;
f64 f = cast(f64)i; f64 f = cast(f64)i;
if (f == v.value_float) { if (f == v.value_float) {
return make_exact_value_integer(i); return exact_value_integer(i);
} }
} break; } break;
case ExactValue_Pointer: case ExactValue_Pointer:
return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer); return exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
} }
ExactValue r = {ExactValue_Invalid}; ExactValue r = {ExactValue_Invalid};
return r; return r;
@@ -231,7 +279,7 @@ ExactValue exact_value_to_integer(ExactValue v) {
ExactValue exact_value_to_float(ExactValue v) { ExactValue exact_value_to_float(ExactValue v) {
switch (v.kind) { switch (v.kind) {
case ExactValue_Integer: case ExactValue_Integer:
return make_exact_value_float(cast(i64)v.value_integer); return exact_value_float(cast(i64)v.value_integer);
case ExactValue_Float: case ExactValue_Float:
return v; return v;
} }
@@ -239,6 +287,128 @@ ExactValue exact_value_to_float(ExactValue v) {
return r; return r;
} }
ExactValue exact_value_to_complex(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_complex(cast(i64)v.value_integer, 0);
case ExactValue_Float:
return exact_value_complex(v.value_float, 0);
case ExactValue_Complex:
return v;
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_to_quaternion(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_quaternion(cast(i64)v.value_integer, 0, 0, 0);
case ExactValue_Float:
return exact_value_quaternion(v.value_float, 0, 0, 0);
case ExactValue_Complex:
return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0);
case ExactValue_Quaternion:
return v;
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_real(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
return v;
case ExactValue_Complex:
return exact_value_float(v.value_complex.real);
case ExactValue_Quaternion:
return exact_value_float(v.value_quaternion.real);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_imag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
return exact_value_integer(0);
case ExactValue_Complex:
return exact_value_float(v.value_complex.imag);
case ExactValue_Quaternion:
return exact_value_float(v.value_quaternion.imag);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_jmag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
case ExactValue_Complex:
return exact_value_integer(0);
case ExactValue_Quaternion:
return exact_value_float(v.value_quaternion.jmag);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_kmag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
case ExactValue_Complex:
return exact_value_integer(0);
case ExactValue_Quaternion:
return exact_value_float(v.value_quaternion.kmag);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_make_imag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_complex(0, exact_value_to_float(v).value_float);
case ExactValue_Float:
return exact_value_complex(0, v.value_float);
default:
GB_PANIC("Expected an integer or float type for `exact_value_make_imag`");
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_make_jmag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_quaternion(0, 0, exact_value_to_float(v).value_float, 0);
case ExactValue_Float:
return exact_value_quaternion(0, 0, v.value_float, 0);
default:
GB_PANIC("Expected an integer or float type for `exact_value_make_jmag`");
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_make_kmag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_quaternion(0, 0, 0, exact_value_to_float(v).value_float);
case ExactValue_Float:
return exact_value_quaternion(0, 0, 0, v.value_float);
default:
GB_PANIC("Expected an integer or float type for `exact_value_make_kmag`");
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) { ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
switch (op) { switch (op) {
@@ -247,6 +417,8 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
case ExactValue_Invalid: case ExactValue_Invalid:
case ExactValue_Integer: case ExactValue_Integer:
case ExactValue_Float: case ExactValue_Float:
case ExactValue_Complex:
case ExactValue_Quaternion:
return v; return v;
} }
} break; } break;
@@ -265,6 +437,18 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
i.value_float = -i.value_float; i.value_float = -i.value_float;
return i; return i;
} }
case ExactValue_Complex: {
f64 real = v.value_complex.real;
f64 imag = v.value_complex.imag;
return exact_value_complex(-real, -imag);
}
case ExactValue_Quaternion: {
f64 real = v.value_quaternion.real;
f64 imag = v.value_quaternion.imag;
f64 jmag = v.value_quaternion.jmag;
f64 kmag = v.value_quaternion.kmag;
return exact_value_quaternion(-real, -imag, -jmag, -kmag);
}
} }
} break; } break;
@@ -287,14 +471,14 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
i &= ~((~0ll)<<precision); i &= ~((~0ll)<<precision);
} }
return make_exact_value_integer(i); return exact_value_integer(i);
} break; } break;
case Token_Not: { case Token_Not: {
switch (v.kind) { switch (v.kind) {
case ExactValue_Invalid: return v; case ExactValue_Invalid: return v;
case ExactValue_Bool: case ExactValue_Bool:
return make_exact_value_bool(!v.value_bool); return exact_value_bool(!v.value_bool);
} }
} break; } break;
} }
@@ -318,8 +502,12 @@ i32 exact_value_order(ExactValue v) {
return 2; return 2;
case ExactValue_Float: case ExactValue_Float:
return 3; return 3;
case ExactValue_Pointer: case ExactValue_Complex:
return 4; return 4;
case ExactValue_Quaternion:
return 5;
case ExactValue_Pointer:
return 6;
default: default:
GB_PANIC("How'd you get here? Invalid Value.kind"); GB_PANIC("How'd you get here? Invalid Value.kind");
@@ -340,6 +528,8 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
case ExactValue_Bool: case ExactValue_Bool:
case ExactValue_String: case ExactValue_String:
case ExactValue_Complex:
case ExactValue_Quaternion:
return; return;
case ExactValue_Integer: case ExactValue_Integer:
@@ -348,18 +538,32 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
return; return;
case ExactValue_Float: case ExactValue_Float:
// TODO(bill): Is this good enough? // TODO(bill): Is this good enough?
*x = make_exact_value_float(cast(f64)x->value_integer); *x = exact_value_float(cast(f64)x->value_integer);
return;
case ExactValue_Complex:
*x = exact_value_complex(cast(f64)x->value_integer, 0);
return;
case ExactValue_Quaternion:
*x = exact_value_quaternion(cast(f64)x->value_integer, 0, 0, 0);
return; return;
} }
break; break;
case ExactValue_Float: case ExactValue_Float:
if (y->kind == ExactValue_Float) switch (y->kind) {
case ExactValue_Float:
return; return;
case ExactValue_Complex:
*x = exact_value_to_complex(*x);
return;
case ExactValue_Quaternion:
*x = exact_value_to_quaternion(*x);
return;
}
break; break;
} }
compiler_error("How'd you get here? Invalid ExactValueKind"); compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
} }
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough? // TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
@@ -372,10 +576,10 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
case ExactValue_Bool: case ExactValue_Bool:
switch (op) { switch (op) {
case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool); case Token_CmpAnd: return exact_value_bool(x.value_bool && y.value_bool);
case Token_CmpOr: return make_exact_value_bool(x.value_bool || y.value_bool); case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool);
case Token_And: return make_exact_value_bool(x.value_bool & y.value_bool); case Token_And: return exact_value_bool(x.value_bool & y.value_bool);
case Token_Or: return make_exact_value_bool(x.value_bool | y.value_bool); case Token_Or: return exact_value_bool(x.value_bool | y.value_bool);
default: goto error; default: goto error;
} }
break; break;
@@ -388,7 +592,7 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
case Token_Add: c = a + b; break; case Token_Add: c = a + b; break;
case Token_Sub: c = a - b; break; case Token_Sub: c = a - b; break;
case Token_Mul: c = a * b; break; case Token_Mul: c = a * b; break;
case Token_Quo: return make_exact_value_float(fmod(cast(f64)a, cast(f64)b)); case Token_Quo: return exact_value_float(fmod(cast(f64)a, cast(f64)b));
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
case Token_Mod: c = a % b; break; case Token_Mod: c = a % b; break;
case Token_And: c = a & b; break; case Token_And: c = a & b; break;
@@ -400,20 +604,98 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
default: goto error; default: goto error;
} }
return make_exact_value_integer(c); return exact_value_integer(c);
} break; } break;
case ExactValue_Float: { case ExactValue_Float: {
f64 a = x.value_float; f64 a = x.value_float;
f64 b = y.value_float; f64 b = y.value_float;
switch (op) { switch (op) {
case Token_Add: return make_exact_value_float(a + b); case Token_Add: return exact_value_float(a + b);
case Token_Sub: return make_exact_value_float(a - b); case Token_Sub: return exact_value_float(a - b);
case Token_Mul: return make_exact_value_float(a * b); case Token_Mul: return exact_value_float(a * b);
case Token_Quo: return make_exact_value_float(a / b); case Token_Quo: return exact_value_float(a / b);
default: goto error; default: goto error;
} }
} break; } break;
case ExactValue_Complex: {
y = exact_value_to_complex(y);
f64 a = x.value_complex.real;
f64 b = x.value_complex.imag;
f64 c = y.value_complex.real;
f64 d = y.value_complex.imag;
f64 real = 0;
f64 imag = 0;
switch (op) {
case Token_Add:
real = a + c;
imag = b + d;
break;
case Token_Sub:
real = a - c;
imag = b - d;
break;
case Token_Mul:
real = (a*c - b*d);
imag = (b*c + a*d);
break;
case Token_Quo: {
f64 s = c*c + d*d;
real = (a*c + b*d)/s;
imag = (b*c - a*d)/s;
} break;
default: goto error;
}
return exact_value_complex(real, imag);
} break;
case ExactValue_Quaternion: {
y = exact_value_to_quaternion(y);
f64 a = x.value_quaternion.real;
f64 b = x.value_quaternion.imag;
f64 c = x.value_quaternion.jmag;
f64 d = x.value_quaternion.kmag;
f64 e = x.value_quaternion.real;
f64 f = x.value_quaternion.imag;
f64 g = x.value_quaternion.jmag;
f64 h = x.value_quaternion.kmag;
f64 real = 0;
f64 imag = 0;
f64 jmag = 0;
f64 kmag = 0;
switch (op) {
case Token_Add:
real = a + e;
imag = b + f;
jmag = c + g;
kmag = d + h;
break;
case Token_Sub:
real = a - e;
imag = b - f;
jmag = c - g;
kmag = d - h;
break;
case Token_Mul:
real = a*f + b*e + c*h - d*g;
imag = a*g - b*h + c*e + d*f;
jmag = a*h + b*g - c*f + d*e;
kmag = a*e - b*f - c*g - d*h;
break;
case Token_Quo: {
f64 s = e*e + f*f + g*g + h*h;
real = (+a*e + b*f + c*g + d*h)/s;
imag = (-a*f + b*e - c*h + d*h)/s;
jmag = (-a*g + b*h + c*e - d*f)/s;
kmag = (-a*h - b*g + c*f + d*e)/s;
} break;
default: goto error;
}
return exact_value_quaternion(real, imag, jmag, kmag);
} break;
} }
error: error:
@@ -474,6 +756,34 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
} }
} break; } break;
case ExactValue_Complex: {
f64 a = x.value_complex.real;
f64 b = x.value_complex.imag;
f64 c = y.value_complex.real;
f64 d = y.value_complex.imag;
switch (op) {
case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
}
} break;
case ExactValue_Quaternion: {
f64 a = x.value_quaternion.real;
f64 b = x.value_quaternion.imag;
f64 c = x.value_quaternion.jmag;
f64 d = x.value_quaternion.kmag;
f64 e = y.value_quaternion.real;
f64 f = y.value_quaternion.imag;
f64 g = y.value_quaternion.jmag;
f64 h = y.value_quaternion.kmag;
switch (op) {
case Token_CmpEq: return cmp_f64(a, e) == 0 && cmp_f64(b, f) == 0 && cmp_f64(c, g) == 0 && cmp_f64(d, h) == 0;
case Token_NotEq: return cmp_f64(a, e) != 0 || cmp_f64(b, f) != 0 || cmp_f64(c, g) != 0 || cmp_f64(d, h) != 0;
}
} break;
case ExactValue_String: { case ExactValue_String: {
String a = x.value_string; String a = x.value_string;
String b = y.value_string; String b = y.value_string;
+429 -155
View File
File diff suppressed because it is too large Load Diff
+1753 -826
View File
File diff suppressed because it is too large Load Diff
+281 -85
View File
@@ -135,6 +135,28 @@ void ir_print_encoded_global(irFileBuffer *f, String name, bool remove_prefix) {
ir_print_escape_string(f, name, true); ir_print_escape_string(f, name, true);
} }
void ir_print_type(irFileBuffer *f, irModule *m, Type *t);
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
GB_ASSERT(is_type_proc(t));
t = base_type(t);
isize result_count = t->Proc.result_count;
if (result_count == 0) {
ir_fprintf(f, "void");
} else if (result_count == 1) {
ir_print_type(f, m, t->Proc.abi_compat_results[0]);
} else {
ir_fprintf(f, "{");
for (isize i = 0; i < result_count; i++) {
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, t->Proc.abi_compat_results[i]);
}
ir_fprintf(f, "}");
}
}
void ir_print_type(irFileBuffer *f, irModule *m, Type *t) { void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
i64 word_bits = 8*build_context.word_size; i64 word_bits = 8*build_context.word_size;
@@ -145,32 +167,39 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
switch (t->kind) { switch (t->kind) {
case Type_Basic: case Type_Basic:
switch (t->Basic.kind) { switch (t->Basic.kind) {
case Basic_bool: ir_fprintf(f, "i1"); return; case Basic_bool: ir_fprintf(f, "i1"); return;
case Basic_i8: ir_fprintf(f, "i8"); return; case Basic_i8: ir_fprintf(f, "i8"); return;
case Basic_u8: ir_fprintf(f, "i8"); return; case Basic_u8: ir_fprintf(f, "i8"); return;
case Basic_i16: ir_fprintf(f, "i16"); return; case Basic_i16: ir_fprintf(f, "i16"); return;
case Basic_u16: ir_fprintf(f, "i16"); return; case Basic_u16: ir_fprintf(f, "i16"); return;
case Basic_i32: ir_fprintf(f, "i32"); return; case Basic_i32: ir_fprintf(f, "i32"); return;
case Basic_u32: ir_fprintf(f, "i32"); return; case Basic_u32: ir_fprintf(f, "i32"); return;
case Basic_i64: ir_fprintf(f, "i64"); return; case Basic_i64: ir_fprintf(f, "i64"); return;
case Basic_u64: 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_f32: ir_fprintf(f, "float"); return;
// case Basic_f16: ir_fprintf(f, "half"); return; case Basic_f64: ir_fprintf(f, "double"); return;
case Basic_f32: ir_fprintf(f, "float"); return;
case Basic_f64: ir_fprintf(f, "double"); return; case Basic_complex64: ir_fprintf(f, "%%..complex64"); return;
// case Basic_f128: ir_fprintf(f, "fp128"); return; case Basic_complex128: ir_fprintf(f, "%%..complex128"); return;
case Basic_rawptr: ir_fprintf(f, "%%..rawptr"); return;
case Basic_string: ir_fprintf(f, "%%..string"); return; case Basic_quaternion128: ir_fprintf(f, "%%..quaternion128"); return;
case Basic_uint: ir_fprintf(f, "i%lld", word_bits); return; case Basic_quaternion256: ir_fprintf(f, "%%..quaternion256"); return;
case Basic_int: ir_fprintf(f, "i%lld", word_bits); return;
case Basic_any: ir_fprintf(f, "%%..any"); 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; break;
case Type_Pointer: case Type_Pointer:
ir_print_type(f, m, t->Pointer.elem); ir_print_type(f, m, t->Pointer.elem);
ir_fprintf(f, "*"); ir_fprintf(f, "*");
return; return;
case Type_Atomic:
ir_print_type(f, m, t->Atomic.elem);
return;
case Type_Array: case Type_Array:
ir_fprintf(f, "[%lld x ", t->Array.count); ir_fprintf(f, "[%lld x ", t->Array.count);
ir_print_type(f, m, t->Array.elem); ir_print_type(f, m, t->Array.elem);
@@ -227,9 +256,30 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
case TypeRecord_Union: { case TypeRecord_Union: {
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as // 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) // LLVM takes the first element's alignment as the entire alignment (like C)
i64 size_of_union = type_size_of(heap_allocator(), t) - build_context.word_size; i64 align = type_align_of(heap_allocator(), t);
i64 align_of_union = type_align_of(heap_allocator(), t); i64 total_size = type_size_of(heap_allocator(), t);
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align_of_union, size_of_union, word_bits); #if 1
i64 fields_size = 0;
if (t->Record.field_count > 0) {
type_set_offsets(m->allocator, t);
isize end_index = t->Record.field_count-1;
i64 end_offset = t->Record.offsets[end_index];
isize end_size = type_size_of(m->allocator, t->Record.fields[end_index]->type);
fields_size = align_formula(end_offset + end_size, build_context.word_size);
}
i64 block_size = total_size - fields_size - build_context.word_size;
ir_fprintf(f, "{[0 x <%lld x i8>], ", align);
for (isize i = 0; i < t->Record.field_count; i++) {
ir_print_type(f, m, t->Record.fields[i]->type);
ir_fprintf(f, ", ");
}
ir_fprintf(f, "[%lld x i8], ", block_size);
ir_fprintf(f, "i%lld}", word_bits);
#else
i64 block_size = total_size - build_context.word_size;
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align, block_size, word_bits);
#endif
} return; } return;
case TypeRecord_RawUnion: { case TypeRecord_RawUnion: {
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as // NOTE(bill): The zero size array is used to fix the alignment used in a structure as
@@ -247,7 +297,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
case Type_Named: case Type_Named:
if (is_type_struct(t) || is_type_union(t)) { if (is_type_struct(t) || is_type_union(t)) {
String *name = map_string_get(&m->type_names, hash_pointer(t)); String *name = map_string_get(&m->entity_names, hash_pointer(t->Named.type_name));
GB_ASSERT_MSG(name != NULL, "%.*s", LIT(t->Named.name)); GB_ASSERT_MSG(name != NULL, "%.*s", LIT(t->Named.name));
ir_print_encoded_local(f, *name); ir_print_encoded_local(f, *name);
} else { } else {
@@ -269,18 +319,15 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
} }
return; return;
case Type_Proc: { case Type_Proc: {
if (t->Proc.result_count == 0) { isize param_count = t->Proc.param_count;
ir_fprintf(f, "void"); isize result_count = t->Proc.result_count;
} else { ir_print_proc_results(f, m, t);
ir_print_type(f, m, t->Proc.results);
}
ir_fprintf(f, " ("); ir_fprintf(f, " (");
TypeTuple *params = &t->Proc.params->Tuple; for (isize i = 0; i < param_count; i++) {
for (isize i = 0; i < t->Proc.param_count; i++) {
if (i > 0) { if (i > 0) {
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
} }
ir_print_type(f, m, params->variables[i]->type); ir_print_type(f, m, t->Proc.abi_compat_params[i]);
} }
ir_fprintf(f, ")*"); ir_fprintf(f, ")*");
} return; } return;
@@ -306,7 +353,7 @@ void ir_print_compound_element(irFileBuffer *f, irModule *m, ExactValue v, Type
} }
void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type) { void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type) {
type = base_type(base_enum_type(type)); type = core_type(type);
if (is_type_float(type)) { if (is_type_float(type)) {
value = exact_value_to_float(value); value = exact_value_to_float(value);
} else if (is_type_integer(type)) { } else if (is_type_integer(type)) {
@@ -334,7 +381,6 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end // HACK NOTE(bill): This is a hack but it works because strings are created at the very end
// of the .ll file // of the .ll file
irValue *str_array = ir_add_global_string_array(m, str); irValue *str_array = ir_add_global_string_array(m, str);
ir_fprintf(f, "{i8* getelementptr inbounds ("); ir_fprintf(f, "{i8* getelementptr inbounds (");
ir_print_type(f, m, str_array->Global.entity->type); ir_print_type(f, m, str_array->Global.entity->type);
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
@@ -364,8 +410,8 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
} }
} break; } break;
case ExactValue_Float: { case ExactValue_Float: {
GB_ASSERT(is_type_float(type)); GB_ASSERT_MSG(is_type_float(type), "%s", type_to_string(type));
type = base_type(type); type = core_type(type);
u64 u = *cast(u64*)&value.value_float; u64 u = *cast(u64*)&value.value_float;
switch (type->Basic.kind) { switch (type->Basic.kind) {
case Basic_f32: case Basic_f32:
@@ -382,28 +428,52 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
switch (type->Basic.kind) { switch (type->Basic.kind) {
case 0: break; case 0: break;
#if 0
case Basic_f16:
ir_fprintf(f, "bitcast (");
ir_print_type(f, m, t_u16);
ir_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float));
ir_print_type(f, m, t_f16);
ir_fprintf(f, ")");
break;
case Basic_f128:
ir_fprintf(f, "bitcast (");
ir_fprintf(f, "i128");
// TODO(bill): Actually support f128
ir_fprintf(f, " %llu to ", u);
ir_print_type(f, m, t_f128);
ir_fprintf(f, ")");
break;
#endif
default: default:
ir_fprintf(f, "0x%016llx", u); ir_fprintf(f, "0x%016llx", u);
break; break;
} }
} break; } break;
case ExactValue_Complex: {
type = core_type(type);
if (is_type_quaternion(type)) {
Type *ft = base_quaternion_elem_type(type);
ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(0), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(0), ft);
ir_fprintf(f, "}");
} else {
GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type));
Type *ft = base_complex_elem_type(type);
ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
ir_fprintf(f, "}");
}
} break;
case ExactValue_Quaternion: {
GB_ASSERT_MSG(is_type_quaternion(type), "%s", type_to_string(type));
type = core_type(type);
Type *ft = base_quaternion_elem_type(type);
ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft);
ir_fprintf(f, "}");
} break;
case ExactValue_Pointer: case ExactValue_Pointer:
if (value.value_pointer == 0) { if (value.value_pointer == 0) {
ir_fprintf(f, "null"); ir_fprintf(f, "null");
@@ -515,12 +585,15 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
} }
} else { } else {
for (isize i = 0; i < value_count; i++) { for (isize i = 0; i < value_count; i++) {
TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]);
GB_ASSERT(tav != NULL);
Entity *f = type->Record.fields_in_src_order[i]; Entity *f = type->Record.fields_in_src_order[i];
values[f->Variable.field_index] = tav->value; if (str_eq(f->token.string, str_lit("_"))) {
values[f->Variable.field_index] = (ExactValue){0};
} else {
TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]);
GB_ASSERT(tav != NULL);
values[f->Variable.field_index] = tav->value;
}
} }
} }
@@ -616,6 +689,8 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
ir_fprintf(f, " 0, i32 0), "); ir_fprintf(f, " 0, i32 0), ");
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
ir_fprintf(f, " %lld, ", cs->count);
ir_print_type(f, m, t_int);
ir_fprintf(f, " %lld}", cs->count); ir_fprintf(f, " %lld}", cs->count);
} }
} break; } break;
@@ -631,9 +706,10 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
Scope *scope = value->Global.entity->scope; Scope *scope = value->Global.entity->scope;
bool in_global_scope = false; bool in_global_scope = false;
if (scope != NULL) { if (scope != NULL) {
// TODO(bill): Fix this rule. What should it be?
in_global_scope = scope->is_global || scope->is_init; in_global_scope = scope->is_global || scope->is_init;
} }
ir_print_encoded_global(f, value->Global.entity->token.string, in_global_scope); ir_print_encoded_global(f, ir_get_global_name(m, value), in_global_scope);
} break; } break;
case irValue_Param: case irValue_Param:
ir_print_encoded_local(f, value->Param.entity->token.string); ir_print_encoded_local(f, value->Param.entity->token.string);
@@ -681,9 +757,13 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_Local: { case irInstr_Local: {
Type *type = instr->Local.entity->type; Type *type = instr->Local.entity->type;
i64 align = instr->Local.alignment;
if (align <= 0) {
align = type_align_of(m->allocator, type);
}
ir_fprintf(f, "%%%d = alloca ", value->index); ir_fprintf(f, "%%%d = alloca ", value->index);
ir_print_type(f, m, type); ir_print_type(f, m, type);
ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type)); ir_fprintf(f, ", align %lld\n", align);
} break; } break;
case irInstr_ZeroInit: { case irInstr_ZeroInit: {
@@ -698,6 +778,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_Store: { case irInstr_Store: {
Type *type = type_deref(ir_type(instr->Store.address)); Type *type = type_deref(ir_type(instr->Store.address));
ir_fprintf(f, "store "); ir_fprintf(f, "store ");
if (instr->Store.atomic) {
ir_fprintf(f, "atomic ");
}
ir_print_type(f, m, type); ir_print_type(f, m, type);
ir_fprintf(f, " "); ir_fprintf(f, " ");
ir_print_value(f, m, instr->Store.value, type); ir_print_value(f, m, instr->Store.value, type);
@@ -705,17 +788,29 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_print_type(f, m, type); ir_print_type(f, m, type);
ir_fprintf(f, "* "); ir_fprintf(f, "* ");
ir_print_value(f, m, instr->Store.address, type); ir_print_value(f, m, instr->Store.address, type);
if (is_type_atomic(type)) {
// TODO(bill): Do ordering
ir_fprintf(f, " unordered");
ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
}
ir_fprintf(f, "\n"); ir_fprintf(f, "\n");
} break; } break;
case irInstr_Load: { case irInstr_Load: {
Type *type = instr->Load.type; Type *type = instr->Load.type;
ir_fprintf(f, "%%%d = load ", value->index); ir_fprintf(f, "%%%d = load ", value->index);
if (is_type_atomic(type)) {
ir_fprintf(f, "atomic ");
}
ir_print_type(f, m, type); ir_print_type(f, m, type);
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
ir_print_type(f, m, type); ir_print_type(f, m, type);
ir_fprintf(f, "* "); ir_fprintf(f, "* ");
ir_print_value(f, m, instr->Load.address, type); ir_print_value(f, m, instr->Load.address, type);
if (is_type_atomic(type)) {
// TODO(bill): Do ordering
ir_fprintf(f, " unordered");
}
ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type)); ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
} break; } break;
@@ -753,6 +848,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
if (st->Record.custom_align > 0) { if (st->Record.custom_align > 0) {
index += 1; index += 1;
} }
} else if (is_type_union(st)) {
index += 1;
} }
ir_print_type(f, m, type_deref(et)); ir_print_type(f, m, type_deref(et));
@@ -821,6 +918,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
if (st->Record.custom_align > 0) { if (st->Record.custom_align > 0) {
index += 1; index += 1;
} }
} else if (is_type_union(st)) {
index += 1;
} }
@@ -833,6 +932,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_UnionTagPtr: { case irInstr_UnionTagPtr: {
Type *et = ir_type(instr->UnionTagPtr.address); Type *et = ir_type(instr->UnionTagPtr.address);
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index); ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
Type *t = base_type(type_deref(et));
GB_ASSERT(is_type_union(t));
ir_print_type(f, m, type_deref(et)); ir_print_type(f, m, type_deref(et));
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
@@ -843,7 +944,11 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
ir_fprintf(f, " 0, "); ir_fprintf(f, " 0, ");
ir_print_type(f, m, t_i32); ir_print_type(f, m, t_i32);
#if 1
ir_fprintf(f, " %d", 2 + t->Record.field_count);
#else
ir_fprintf(f, " %d", 2); ir_fprintf(f, " %d", 2);
#endif
ir_fprintf(f, " ; UnionTagPtr"); ir_fprintf(f, " ; UnionTagPtr");
ir_fprintf(f, "\n"); ir_fprintf(f, "\n");
} break; } break;
@@ -851,11 +956,19 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_UnionTagValue: { case irInstr_UnionTagValue: {
Type *et = ir_type(instr->UnionTagValue.address); Type *et = ir_type(instr->UnionTagValue.address);
ir_fprintf(f, "%%%d = extractvalue ", value->index); ir_fprintf(f, "%%%d = extractvalue ", value->index);
Type *t = base_type(et);
GB_ASSERT(is_type_union(t));
ir_print_type(f, m, et); ir_print_type(f, m, et);
ir_fprintf(f, " "); ir_fprintf(f, " ");
ir_print_value(f, m, instr->UnionTagValue.address, et); ir_print_value(f, m, instr->UnionTagValue.address, et);
ir_fprintf(f, ", %d", 2); ir_fprintf(f, ",");
#if 1
ir_fprintf(f, " %d", 2 + t->Record.field_count);
#else
ir_fprintf(f, " %d", 2);
#endif
ir_fprintf(f, ", %d", 2 + t->Record.field_count);
ir_fprintf(f, " ; UnionTagValue"); ir_fprintf(f, " ; UnionTagValue");
ir_fprintf(f, "\n"); ir_fprintf(f, "\n");
} break; } break;
@@ -942,7 +1055,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
switch (uo->op) { switch (uo->op) {
case Token_Sub: case Token_Sub:
if (is_type_float(elem_type)) { if (is_type_float(elem_type)) {
ir_print_exact_value(f, m, make_exact_value_float(0), type); ir_print_exact_value(f, m, exact_value_float(0), elem_type);
} else { } else {
ir_fprintf(f, "0"); ir_fprintf(f, "0");
} }
@@ -1007,6 +1120,72 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case Token_LtEq: ir_fprintf(f, "ole"); break; case Token_LtEq: ir_fprintf(f, "ole"); break;
case Token_GtEq: ir_fprintf(f, "oge"); break; case Token_GtEq: ir_fprintf(f, "oge"); break;
} }
} else if (is_type_complex(elem_type)) {
ir_fprintf(f, "call ");
ir_print_calling_convention(f, m, ProcCC_Odin);
ir_print_type(f, m, t_bool);
char *runtime_proc = "";
i64 sz = 8*type_size_of(m->allocator, elem_type);
switch (sz) {
case 64:
switch (bo->op) {
case Token_CmpEq: runtime_proc = "__complex64_eq"; break;
case Token_NotEq: runtime_proc = "__complex64_ne"; break;
}
break;
case 128:
switch (bo->op) {
case Token_CmpEq: runtime_proc = "__complex128_eq"; break;
case Token_NotEq: runtime_proc = "__complex128_ne"; break;
}
break;
}
ir_fprintf(f, " ");
ir_print_encoded_global(f, make_string_c(runtime_proc), false);
ir_fprintf(f, "(");
ir_print_type(f, m, type);
ir_fprintf(f, " ");
ir_print_value(f, m, bo->left, type);
ir_fprintf(f, ", ");
ir_print_type(f, m, type);
ir_fprintf(f, " ");
ir_print_value(f, m, bo->right, type);
ir_fprintf(f, ")\n");
return;
} else if (is_type_quaternion(elem_type)) {
ir_fprintf(f, "call ");
ir_print_calling_convention(f, m, ProcCC_Odin);
ir_print_type(f, m, t_bool);
char *runtime_proc = "";
i64 sz = 8*type_size_of(m->allocator, elem_type);
switch (sz) {
case 128:
switch (bo->op) {
case Token_CmpEq: runtime_proc = "__quaternion128_eq"; break;
case Token_NotEq: runtime_proc = "__quaternion128_ne"; break;
}
break;
case 256:
switch (bo->op) {
case Token_CmpEq: runtime_proc = "__quaternion256_eq"; break;
case Token_NotEq: runtime_proc = "__quaternion256_ne"; break;
}
break;
}
ir_fprintf(f, " ");
ir_print_encoded_global(f, make_string_c(runtime_proc), false);
ir_fprintf(f, "(");
ir_print_type(f, m, type);
ir_fprintf(f, " ");
ir_print_value(f, m, bo->left, type);
ir_fprintf(f, ", ");
ir_print_type(f, m, type);
ir_fprintf(f, " ");
ir_print_value(f, m, bo->right, type);
ir_fprintf(f, ")\n");
return;
} else { } else {
ir_fprintf(f, "icmp "); ir_fprintf(f, "icmp ");
if (bo->op != Token_CmpEq && if (bo->op != Token_CmpEq &&
@@ -1082,7 +1261,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_fprintf(f, "call "); ir_fprintf(f, "call ");
ir_print_calling_convention(f, m, proc_type->Proc.calling_convention); ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
if (result_type) { if (result_type) {
ir_print_type(f, m, result_type); ir_print_proc_results(f, m, proc_type);
} else { } else {
ir_fprintf(f, "void"); ir_fprintf(f, "void");
} }
@@ -1098,7 +1277,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
for (isize i = 0; i < call->arg_count; i++) { for (isize i = 0; i < call->arg_count; i++) {
Entity *e = params->variables[i]; Entity *e = params->variables[i];
GB_ASSERT(e != NULL); GB_ASSERT(e != NULL);
Type *t = e->type; Type *t = proc_type->Proc.abi_compat_params[i];
if (i > 0) { if (i > 0) {
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
} }
@@ -1197,17 +1376,17 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_fprintf(f, "call void "); ir_fprintf(f, "call void ");
ir_print_encoded_global(f, str_lit("__bounds_check_error"), false); ir_print_encoded_global(f, str_lit("__bounds_check_error"), false);
ir_fprintf(f, "("); ir_fprintf(f, "(");
ir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); ir_print_compound_element(f, m, exact_value_string(bc->pos.file), t_string);
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
ir_fprintf(f, " "); ir_fprintf(f, " ");
ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); ir_print_exact_value(f, m, exact_value_integer(bc->pos.line), t_int);
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
ir_fprintf(f, " "); ir_fprintf(f, " ");
ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); ir_print_exact_value(f, m, exact_value_integer(bc->pos.column), t_int);
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
@@ -1232,17 +1411,17 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
} }
ir_fprintf(f, "("); ir_fprintf(f, "(");
ir_print_compound_element(f, m, make_exact_value_string(bc->pos.file), t_string); ir_print_compound_element(f, m, exact_value_string(bc->pos.file), t_string);
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
ir_fprintf(f, " "); ir_fprintf(f, " ");
ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.line), t_int); ir_print_exact_value(f, m, exact_value_integer(bc->pos.line), t_int);
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
ir_fprintf(f, " "); ir_fprintf(f, " ");
ir_print_exact_value(f, m, make_exact_value_integer(bc->pos.column), t_int); ir_print_exact_value(f, m, exact_value_integer(bc->pos.column), t_int);
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
@@ -1311,31 +1490,39 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
ir_print_calling_convention(f, m, proc_type->calling_convention); ir_print_calling_convention(f, m, proc_type->calling_convention);
if (proc_type->result_count == 0) { isize param_count = proc_type->param_count;
ir_fprintf(f, "void"); isize result_count = proc_type->result_count;
} else { ir_print_proc_results(f, m, proc->type);
ir_print_type(f, m, proc_type->results);
}
ir_fprintf(f, " "); ir_fprintf(f, " ");
// #ifndef GB_SYSTEM_WINDOWS
#if 0
if(uses_args)
ir_fprintf(f, "@.nix_argpatch_main");
else
#endif
ir_print_encoded_global(f, proc->name, ir_print_is_proc_global(m, proc)); ir_print_encoded_global(f, proc->name, ir_print_is_proc_global(m, proc));
ir_fprintf(f, "("); ir_fprintf(f, "(");
if (proc_type->param_count > 0) { if (param_count > 0) {
TypeTuple *params = &proc_type->params->Tuple; TypeTuple *params = &proc_type->params->Tuple;
for (isize i = 0; i < params->variable_count; i++) { for (isize i = 0; i < params->variable_count; i++) {
Entity *e = params->variables[i]; Entity *e = params->variables[i];
Type *original_type = e->type;
Type *abi_type = proc_type->abi_compat_params[i];
if (i > 0) { if (i > 0) {
ir_fprintf(f, ", "); ir_fprintf(f, ", ");
} }
ir_print_type(f, m, e->type); ir_print_type(f, m, abi_type);
if (e->flags&EntityFlag_NoAlias) { if (e->flags&EntityFlag_NoAlias) {
ir_fprintf(f, " noalias"); ir_fprintf(f, " noalias");
} }
if (proc->body != NULL) { if (proc->body != NULL) {
if (!str_eq(e->token.string, str_lit("")) && if (!str_eq(e->token.string, str_lit("")) &&
!str_eq(e->token.string, str_lit("_"))) { !str_eq(e->token.string, str_lit("_"))) {
ir_fprintf(f, " %%%.*s", LIT(e->token.string)); ir_fprintf(f, " ");
ir_print_encoded_local(f, e->token.string);
} else { } else {
ir_fprintf(f, " %%_.param_%td", i); ir_fprintf(f, " %%_.param_%td", i);
} }
@@ -1408,10 +1595,6 @@ void print_llvm_ir(irGen *ir) {
irFileBuffer buf = {0}, *f = &buf; irFileBuffer buf = {0}, *f = &buf;
ir_file_buffer_init(f, &ir->output_file); ir_file_buffer_init(f, &ir->output_file);
if (m->layout.len > 0) {
ir_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout));
}
ir_print_encoded_local(f, str_lit("..string")); ir_print_encoded_local(f, str_lit("..string"));
ir_fprintf(f, " = type {i8*, "); ir_fprintf(f, " = type {i8*, ");
ir_print_type(f, m, t_int); ir_print_type(f, m, t_int);
@@ -1419,11 +1602,21 @@ void print_llvm_ir(irGen *ir) {
ir_print_encoded_local(f, str_lit("..rawptr")); ir_print_encoded_local(f, str_lit("..rawptr"));
ir_fprintf(f, " = type i8* ; Basic_rawptr\n"); ir_fprintf(f, " = type i8* ; Basic_rawptr\n");
ir_print_encoded_local(f, str_lit("..complex64"));
ir_fprintf(f, " = type {float, float} ; Basic_complex64\n");
ir_print_encoded_local(f, str_lit("..complex128"));
ir_fprintf(f, " = type {double, double} ; Basic_complex128\n");
ir_print_encoded_local(f, str_lit("..quaternion128"));
ir_fprintf(f, " = type {float, float, float, float} ; Basic_quaternion128\n");
ir_print_encoded_local(f, str_lit("..quaternion256"));
ir_fprintf(f, " = type {double, double, double, double} ; Basic_quaternion256\n");
ir_print_encoded_local(f, str_lit("..any")); ir_print_encoded_local(f, str_lit("..any"));
ir_fprintf(f, " = type {"); ir_fprintf(f, " = type {");
ir_print_type(f, m, t_type_info_ptr);
ir_fprintf(f, ", ");
ir_print_type(f, m, t_rawptr); ir_print_type(f, m, t_rawptr);
ir_fprintf(f, ", ");
ir_print_type(f, m, t_type_info_ptr);
ir_fprintf(f, "} ; Basic_any\n"); ir_fprintf(f, "} ; Basic_any\n");
ir_fprintf(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone \n"); ir_fprintf(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone \n");
@@ -1476,9 +1669,12 @@ void print_llvm_ir(irGen *ir) {
Scope *scope = g->entity->scope; Scope *scope = g->entity->scope;
bool in_global_scope = false; bool in_global_scope = false;
if (scope != NULL) { if (scope != NULL) {
// TODO(bill): Fix this rule. What should it be?
in_global_scope = scope->is_global || scope->is_init; in_global_scope = scope->is_global || scope->is_init;
// in_global_scope = value->Global.name_is_not_mangled;
} }
ir_print_encoded_global(f, g->entity->token.string, in_global_scope);
ir_print_encoded_global(f, ir_get_global_name(m, v), in_global_scope);
ir_fprintf(f, " = "); ir_fprintf(f, " = ");
if (g->is_foreign) { if (g->is_foreign) {
ir_fprintf(f, "external "); ir_fprintf(f, "external ");
+142 -13
View File
@@ -2,15 +2,15 @@
extern "C" { extern "C" {
#endif #endif
#define USE_CUSTOM_BACKEND false
#include "common.c" #include "common.c"
#include "timings.c" #include "timings.c"
#include "build_settings.c" #include "build_settings.c"
#include "tokenizer.c" #include "tokenizer.c"
#include "parser.c" #include "parser.c"
// #include "printer.c"
#include "checker.c" #include "checker.c"
// #include "ssa.c" #include "ssa.c"
#include "ir.c" #include "ir.c"
#include "ir_opt.c" #include "ir_opt.c"
#include "ir_print.c" #include "ir_print.c"
@@ -102,6 +102,8 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
// } // }
// exit_code = status; // exit_code = status;
return exit_code;
} }
#endif #endif
@@ -144,6 +146,7 @@ int main(int argc, char **argv) {
init_scratch_memory(gb_megabytes(10)); init_scratch_memory(gb_megabytes(10));
init_global_error_collector(); init_global_error_collector();
#if 1 #if 1
init_build_context(); init_build_context();
@@ -208,7 +211,7 @@ int main(int argc, char **argv) {
#endif #endif
#if 0 #if USE_CUSTOM_BACKEND
if (global_error_collector.count != 0) { if (global_error_collector.count != 0) {
return 1; return 1;
} }
@@ -217,7 +220,7 @@ int main(int argc, char **argv) {
return 1; return 1;
} }
if (!ssa_generate(&checker.info)) { if (!ssa_generate(&parser, &checker.info)) {
return 1; return 1;
} }
#else #else
@@ -241,17 +244,19 @@ int main(int argc, char **argv) {
#if 1 #if 1
timings_start_section(&timings, str_lit("llvm-opt")); timings_start_section(&timings, str_lit("llvm-opt"));
char const *output_name = ir_gen.output_file.filename; String output_name = ir_gen.output_name;
isize base_name_len = gb_path_extension(output_name)-1 - output_name; String output_base = ir_gen.output_base;
String output = make_string(cast(u8 *)output_name, base_name_len); int base_name_len = output_base.len;
i32 optimization_level = 0; i32 optimization_level = 0;
optimization_level = gb_clamp(optimization_level, 0, 3); optimization_level = gb_clamp(optimization_level, 0, 3);
i32 exit_code = 0; i32 exit_code = 0;
#if defined(GB_SYSTEM_WINDOWS)
// For more passes arguments: http://llvm.org/docs/Passes.html // For more passes arguments: http://llvm.org/docs/Passes.html
exit_code = system_exec_command_line_app("llvm-opt", false, exit_code = system_exec_command_line_app("llvm-opt", false,
"\"%.*sbin/opt\" \"%s\" -o \"%.*s\".bc " "\"%.*sbin/opt\" \"%.*s\".ll -o \"%.*s\".bc "
"-mem2reg " "-mem2reg "
"-memcpyopt " "-memcpyopt "
"-die " "-die "
@@ -260,10 +265,33 @@ int main(int argc, char **argv) {
// "-S " // "-S "
"", "",
LIT(build_context.ODIN_ROOT), LIT(build_context.ODIN_ROOT),
output_name, LIT(output)); LIT(output_base), LIT(output_base));
if (exit_code != 0) { if (exit_code != 0) {
return exit_code; return exit_code;
} }
#else
// NOTE(zangent): This is separate because it seems that LLVM tools are packaged
// with the Windows version, while they will be system-provided on MacOS and GNU/Linux
exit_code = system_exec_command_line_app("llvm-opt", false,
"opt \"%.*s\".ll -o \"%.*s\".bc "
"-mem2reg "
"-memcpyopt "
"-die "
#if defined(GB_SYSTEM_OSX)
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
// make sure to also change the `macosx_version_min` param passed to `llc`
"-mtriple=x86_64-apple-macosx10.8 "
#endif
// "-dse "
// "-dce "
// "-S "
"",
LIT(output_base), LIT(output_base));
if (exit_code != 0) {
return exit_code;
}
#endif
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
timings_start_section(&timings, str_lit("llvm-llc")); timings_start_section(&timings, str_lit("llvm-llc"));
@@ -274,7 +302,7 @@ int main(int argc, char **argv) {
// "-debug-pass=Arguments " // "-debug-pass=Arguments "
"", "",
LIT(build_context.ODIN_ROOT), LIT(build_context.ODIN_ROOT),
LIT(output), LIT(output_base),
optimization_level, optimization_level,
LIT(build_context.llc_flags)); LIT(build_context.llc_flags));
if (exit_code != 0) { if (exit_code != 0) {
@@ -310,7 +338,7 @@ int main(int argc, char **argv) {
" %.*s " " %.*s "
" %s " " %s "
"", "",
LIT(output), LIT(output), output_ext, LIT(output_base), LIT(output_base), output_ext,
lib_str, LIT(build_context.link_flags), lib_str, LIT(build_context.link_flags),
link_settings link_settings
); );
@@ -321,11 +349,112 @@ int main(int argc, char **argv) {
// timings_print_all(&timings); // timings_print_all(&timings);
if (run_output) { if (run_output) {
system_exec_command_line_app("odin run", false, "%.*s.exe", cast(int)base_name_len, output_name); system_exec_command_line_app("odin run", false, "%.*s.exe", LIT(output_base));
} }
#else #else
#error Implement build stuff for this platform
// NOTE(zangent): Linux / Unix is unfinished and not tested very well.
timings_start_section(&timings, str_lit("llvm-llc"));
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
exit_code = system_exec_command_line_app("llc", false,
"llc \"%.*s.bc\" -filetype=obj -O%d "
"%.*s "
// "-debug-pass=Arguments "
"",
LIT(output_base),
optimization_level,
LIT(build_context.llc_flags));
if (exit_code != 0) {
return exit_code;
}
timings_start_section(&timings, str_lit("ld-link"));
gbString lib_str = gb_string_make(heap_allocator(), "");
// defer (gb_string_free(lib_str));
char lib_str_buf[1024] = {0};
for_array(i, ir_gen.module.foreign_library_paths) {
String lib = ir_gen.module.foreign_library_paths.e[i];
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
// This allows you to specify '-f' in a #foreign_system_library,
// without having to implement any new syntax specifically for MacOS.
#if defined(GB_SYSTEM_OSX)
isize len;
if(lib.len > 2 && lib.text[0] == '-' && lib.text[1] == 'f') {
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
} else {
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" -l%.*s ", LIT(lib));
}
#else
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" -l%.*s ", LIT(lib));
#endif
lib_str = gb_string_appendc(lib_str, lib_str_buf);
}
// Unlike the Win32 linker code, the output_ext includes the dot, because
// typically executable files on *NIX systems don't have extensions.
char *output_ext = "";
char *link_settings = "";
char *linker;
if (build_context.is_dll) {
// Shared libraries are .dylib on MacOS and .so on Linux.
// TODO(zangent): Is that statement entirely truthful?
#if defined(GB_SYSTEM_OSX)
output_ext = ".dylib";
#else
output_ext = ".so";
#endif
link_settings = "-shared";
} else {
// TODO: Do I need anything here?
link_settings = "";
}
#if defined(GB_SYSTEM_OSX)
linker = "ld";
#else
// TODO(zangent): Figure out how to make ld work on Linux.
// It probably has to do with including the entire CRT, but
// that's quite a complicated issue to solve while remaining distro-agnostic.
// Clang can figure out linker flags for us, and that's good enough _for now_.
linker = "clang -Wno-unused-command-line-argument";
#endif
exit_code = system_exec_command_line_app("ld-link", true,
"%s \"%.*s\".o -o \"%.*s%s\" %s "
"-lc -lm "
" %.*s "
" %s "
#if defined(GB_SYSTEM_OSX)
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
// make sure to also change the `mtriple` param passed to `opt`
" -macosx_version_min 10.8.0 "
// This points the linker to where the entry point is
" -e _main "
#endif
, linker, LIT(output_base), LIT(output_base), output_ext,
lib_str, LIT(build_context.link_flags),
link_settings
);
if (exit_code != 0) {
return exit_code;
}
// timings_print_all(&timings);
if (run_output) {
system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base));
}
#endif #endif
#endif #endif
#endif #endif
+287 -198
View File
@@ -17,6 +17,8 @@ typedef enum ParseFileError {
typedef Array(AstNode *) AstNodeArray; typedef Array(AstNode *) AstNodeArray;
gb_global i32 global_file_id = 0;
typedef struct AstFile { typedef struct AstFile {
i32 id; i32 id;
gbArena arena; gbArena arena;
@@ -30,6 +32,8 @@ typedef struct AstFile {
// < 0: In Control Clause // < 0: In Control Clause
// NOTE(bill): Used to prevent type literals in control clauses // NOTE(bill): Used to prevent type literals in control clauses
isize expr_level; isize expr_level;
bool allow_range;
bool ignore_operand;
AstNodeArray decls; AstNodeArray decls;
bool is_global_scope; bool is_global_scope;
@@ -65,6 +69,8 @@ typedef enum ProcTag {
ProcTag_bounds_check = 1<<0, ProcTag_bounds_check = 1<<0,
ProcTag_no_bounds_check = 1<<1, ProcTag_no_bounds_check = 1<<1,
ProcTag_require_results = 1<<4,
ProcTag_foreign = 1<<10, ProcTag_foreign = 1<<10,
ProcTag_export = 1<<11, ProcTag_export = 1<<11,
ProcTag_link_name = 1<<12, ProcTag_link_name = 1<<12,
@@ -103,6 +109,7 @@ typedef enum FieldFlag {
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_immutable, FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_immutable,
} FieldListTag; } FieldListTag;
AstNodeArray make_ast_node_array(AstFile *f) { AstNodeArray make_ast_node_array(AstFile *f) {
AstNodeArray a; AstNodeArray a;
// array_init(&a, gb_arena_allocator(&f->arena)); // array_init(&a, gb_arena_allocator(&f->arena));
@@ -139,6 +146,10 @@ AstNodeArray make_ast_node_array(AstFile *f) {
AstNodeArray elems; \ AstNodeArray elems; \
Token open, close; \ Token open, close; \
}) \ }) \
AST_NODE_KIND(Alias, "alias", struct { \
Token token; \
AstNode *expr; \
}) \
AST_NODE_KIND(_ExprBegin, "", i32) \ AST_NODE_KIND(_ExprBegin, "", i32) \
AST_NODE_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \ AST_NODE_KIND(BadExpr, "bad expression", struct { Token begin, end; }) \
AST_NODE_KIND(TagExpr, "tag expression", struct { Token token, name; AstNode *expr; }) \ AST_NODE_KIND(TagExpr, "tag expression", struct { Token token, name; AstNode *expr; }) \
@@ -149,9 +160,11 @@ AST_NODE_KIND(_ExprBegin, "", i32) \
AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \ AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \
AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \ AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \
AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \ AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \
AST_NODE_KIND(SliceExpr, "slice expression", struct { \ AST_NODE_KIND(SliceExpr, "slice expression", struct { \
AstNode *expr; \ AstNode *expr; \
Token open, close; \ Token open, close; \
Token interval0; \
Token interval1; \
bool index3; \ bool index3; \
AstNode *low, *high, *max; \ AstNode *low, *high, *max; \
}) \ }) \
@@ -169,10 +182,9 @@ AST_NODE_KIND(_ExprBegin, "", i32) \
Token open; \ Token open; \
Token close; \ Token close; \
}) \ }) \
AST_NODE_KIND(CastExpr, "cast expression", struct { Token token; AstNode *type, *expr; Token open, close; }) \ AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \ AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \
AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \ AST_NODE_KIND(TypeAssertion, "type assertion", struct { AstNode *expr; Token dot; AstNode *type; }) \
AST_NODE_KIND(IntervalExpr, "interval expression", struct { Token op; AstNode *left, *right; }) \
AST_NODE_KIND(_ExprEnd, "", i32) \ AST_NODE_KIND(_ExprEnd, "", i32) \
AST_NODE_KIND(_StmtBegin, "", i32) \ AST_NODE_KIND(_StmtBegin, "", i32) \
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \ AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
@@ -215,6 +227,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
}) \ }) \
AST_NODE_KIND(ForStmt, "for statement", struct { \ AST_NODE_KIND(ForStmt, "for statement", struct { \
Token token; \ Token token; \
AstNode *label; \
AstNode *init; \ AstNode *init; \
AstNode *cond; \ AstNode *cond; \
AstNode *post; \ AstNode *post; \
@@ -222,6 +235,7 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
}) \ }) \
AST_NODE_KIND(RangeStmt, "range statement", struct { \ AST_NODE_KIND(RangeStmt, "range statement", struct { \
Token token; \ Token token; \
AstNode *label; \
AstNode *value; \ AstNode *value; \
AstNode *index; \ AstNode *index; \
Token in_token; \ Token in_token; \
@@ -235,20 +249,22 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
}) \ }) \
AST_NODE_KIND(MatchStmt, "match statement", struct { \ AST_NODE_KIND(MatchStmt, "match statement", struct { \
Token token; \ Token token; \
AstNode *label; \
AstNode *init; \ AstNode *init; \
AstNode *tag; \ AstNode *tag; \
AstNode *body; \ AstNode *body; \
}) \ }) \
AST_NODE_KIND(TypeMatchStmt, "type match statement", struct { \ AST_NODE_KIND(TypeMatchStmt, "type match statement", struct { \
Token token; \ Token token; \
AstNode *label; \
AstNode *tag; \ AstNode *tag; \
AstNode *body; \ AstNode *body; \
}) \ }) \
AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \ AST_NODE_KIND(DeferStmt, "defer statement", struct { Token token; AstNode *stmt; }) \
AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; }) \ AST_NODE_KIND(BranchStmt, "branch statement", struct { Token token; AstNode *label; }) \
AST_NODE_KIND(UsingStmt, "using statement", struct { \ AST_NODE_KIND(UsingStmt, "using statement", struct { \
Token token; \ Token token; \
AstNode *node; \ AstNodeArray list; \
}) \ }) \
AST_NODE_KIND(AsmOperand, "assembly operand", struct { \ AST_NODE_KIND(AsmOperand, "assembly operand", struct { \
Token string; \ Token string; \
@@ -301,6 +317,10 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
AstNode *cond; \ AstNode *cond; \
bool is_system; \ bool is_system; \
}) \ }) \
AST_NODE_KIND(Label, "label", struct { \
Token token; \
AstNode *name; \
}) \
AST_NODE_KIND(_DeclEnd, "", i32) \ AST_NODE_KIND(_DeclEnd, "", i32) \
AST_NODE_KIND(Field, "field", struct { \ AST_NODE_KIND(Field, "field", struct { \
AstNodeArray names; \ AstNodeArray names; \
@@ -331,6 +351,10 @@ AST_NODE_KIND(_TypeBegin, "", i32) \
Token token; \ Token token; \
AstNode *type; \ AstNode *type; \
}) \ }) \
AST_NODE_KIND(AtomicType, "atomic type", struct { \
Token token; \
AstNode *type; \
}) \
AST_NODE_KIND(ArrayType, "array type", struct { \ AST_NODE_KIND(ArrayType, "array type", struct { \
Token token; \ Token token; \
AstNode *count; \ AstNode *count; \
@@ -398,7 +422,6 @@ String const ast_node_strings[] = {
typedef struct AstNode { typedef struct AstNode {
AstNodeKind kind; AstNodeKind kind;
// AstNode *prev, *next; // NOTE(bill): allow for Linked list
u32 stmt_state_flags; u32 stmt_state_flags;
union { union {
#define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_; #define AST_NODE_KIND(_kind_name_, name, ...) GB_JOIN2(AstNode, _kind_name_) _kind_name_;
@@ -410,7 +433,9 @@ typedef struct AstNode {
#define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_)) #define ast_node(n_, Kind_, node_) GB_JOIN2(AstNode, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(AstNode_, Kind_))
#define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_); #define case_ast_node(n_, Kind_, node_) case GB_JOIN2(AstNode_, Kind_): { ast_node(n_, Kind_, node_);
#ifndef case_end
#define case_end } break; #define case_end } break;
#endif
gb_inline bool is_ast_node_expr(AstNode *node) { gb_inline bool is_ast_node_expr(AstNode *node) {
@@ -445,6 +470,8 @@ Token ast_node_token(AstNode *node) {
return ast_node_token(node->CompoundLit.type); return ast_node_token(node->CompoundLit.type);
} }
return node->CompoundLit.open; return node->CompoundLit.open;
case AstNode_Alias: return node->Alias.token;
case AstNode_TagExpr: return node->TagExpr.token; case AstNode_TagExpr: return node->TagExpr.token;
case AstNode_RunExpr: return node->RunExpr.token; case AstNode_RunExpr: return node->RunExpr.token;
case AstNode_BadExpr: return node->BadExpr.begin; case AstNode_BadExpr: return node->BadExpr.begin;
@@ -458,14 +485,13 @@ Token ast_node_token(AstNode *node) {
return ast_node_token(node->SelectorExpr.selector); return ast_node_token(node->SelectorExpr.selector);
} }
return node->SelectorExpr.token; return node->SelectorExpr.token;
case AstNode_IndexExpr: return node->IndexExpr.open; case AstNode_IndexExpr: return node->IndexExpr.open;
case AstNode_SliceExpr: return node->SliceExpr.open; case AstNode_SliceExpr: return node->SliceExpr.open;
case AstNode_Ellipsis: return node->Ellipsis.token; case AstNode_Ellipsis: return node->Ellipsis.token;
case AstNode_CastExpr: return node->CastExpr.token; case AstNode_FieldValue: return node->FieldValue.eq;
case AstNode_FieldValue: return node->FieldValue.eq; case AstNode_DerefExpr: return node->DerefExpr.op;
case AstNode_DerefExpr: return node->DerefExpr.op; case AstNode_TernaryExpr: return ast_node_token(node->TernaryExpr.cond);
case AstNode_TernaryExpr: return ast_node_token(node->TernaryExpr.cond); case AstNode_TypeAssertion: return ast_node_token(node->TypeAssertion.expr);
case AstNode_IntervalExpr: return ast_node_token(node->IntervalExpr.left);
case AstNode_BadStmt: return node->BadStmt.begin; case AstNode_BadStmt: return node->BadStmt.begin;
case AstNode_EmptyStmt: return node->EmptyStmt.token; case AstNode_EmptyStmt: return node->EmptyStmt.token;
@@ -492,6 +518,7 @@ Token ast_node_token(AstNode *node) {
case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names.e[0]); case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names.e[0]);
case AstNode_ImportDecl: return node->ImportDecl.token; case AstNode_ImportDecl: return node->ImportDecl.token;
case AstNode_ForeignLibrary: return node->ForeignLibrary.token; case AstNode_ForeignLibrary: return node->ForeignLibrary.token;
case AstNode_Label: return node->Label.token;
case AstNode_Field: case AstNode_Field:
@@ -507,6 +534,7 @@ Token ast_node_token(AstNode *node) {
case AstNode_HelperType: return node->HelperType.token; case AstNode_HelperType: return node->HelperType.token;
case AstNode_ProcType: return node->ProcType.token; case AstNode_ProcType: return node->ProcType.token;
case AstNode_PointerType: return node->PointerType.token; case AstNode_PointerType: return node->PointerType.token;
case AstNode_AtomicType: return node->AtomicType.token;
case AstNode_ArrayType: return node->ArrayType.token; case AstNode_ArrayType: return node->ArrayType.token;
case AstNode_DynamicArrayType: return node->DynamicArrayType.token; case AstNode_DynamicArrayType: return node->DynamicArrayType.token;
case AstNode_VectorType: return node->VectorType.token; case AstNode_VectorType: return node->VectorType.token;
@@ -668,11 +696,13 @@ AstNode *ast_index_expr(AstFile *f, AstNode *expr, AstNode *index, Token open, T
} }
AstNode *ast_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, bool index3, AstNode *low, AstNode *high, AstNode *max) { AstNode *ast_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, Token interval0, Token interval1, bool index3, AstNode *low, AstNode *high, AstNode *max) {
AstNode *result = make_ast_node(f, AstNode_SliceExpr); AstNode *result = make_ast_node(f, AstNode_SliceExpr);
result->SliceExpr.expr = expr; result->SliceExpr.expr = expr;
result->SliceExpr.open = open; result->SliceExpr.open = open;
result->SliceExpr.close = close; result->SliceExpr.close = close;
result->SliceExpr.interval0 = interval0;
result->SliceExpr.interval1 = interval1;
result->SliceExpr.index3 = index3; result->SliceExpr.index3 = index3;
result->SliceExpr.low = low; result->SliceExpr.low = low;
result->SliceExpr.high = high; result->SliceExpr.high = high;
@@ -687,16 +717,6 @@ AstNode *ast_deref_expr(AstFile *f, AstNode *expr, Token op) {
return result; return result;
} }
AstNode *ast_interval_expr(AstFile *f, Token op, AstNode *left, AstNode *right) {
AstNode *result = make_ast_node(f, AstNode_IntervalExpr);
result->IntervalExpr.op = op;
result->IntervalExpr.left = left;
result->IntervalExpr.right = right;
return result;
}
@@ -753,16 +773,6 @@ AstNode *ast_field_value(AstFile *f, AstNode *field, AstNode *value, Token eq) {
return result; return result;
} }
AstNode *ast_cast_expr(AstFile *f, Token token, AstNode *type, AstNode *expr, Token open, Token close) {
AstNode *result = make_ast_node(f, AstNode_CastExpr);
result->CastExpr.token = token;
result->CastExpr.type = type;
result->CastExpr.expr = expr;
result->CastExpr.open = open;
result->CastExpr.close = close;
return result;
}
AstNode *ast_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token open, Token close) { AstNode *ast_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token open, Token close) {
AstNode *result = make_ast_node(f, AstNode_CompoundLit); AstNode *result = make_ast_node(f, AstNode_CompoundLit);
result->CompoundLit.type = type; result->CompoundLit.type = type;
@@ -771,6 +781,13 @@ AstNode *ast_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token o
result->CompoundLit.close = close; result->CompoundLit.close = close;
return result; return result;
} }
AstNode *ast_alias(AstFile *f, Token token, AstNode *expr) {
AstNode *result = make_ast_node(f, AstNode_Alias);
result->Alias.token = token;
result->Alias.expr = expr;
return result;
}
AstNode *ast_ternary_expr(AstFile *f, AstNode *cond, AstNode *x, AstNode *y) { AstNode *ast_ternary_expr(AstFile *f, AstNode *cond, AstNode *x, AstNode *y) {
AstNode *result = make_ast_node(f, AstNode_TernaryExpr); AstNode *result = make_ast_node(f, AstNode_TernaryExpr);
@@ -779,6 +796,14 @@ AstNode *ast_ternary_expr(AstFile *f, AstNode *cond, AstNode *x, AstNode *y) {
result->TernaryExpr.y = y; result->TernaryExpr.y = y;
return result; return result;
} }
AstNode *ast_type_assertion(AstFile *f, AstNode *expr, Token dot, AstNode *type) {
AstNode *result = make_ast_node(f, AstNode_TypeAssertion);
result->TypeAssertion.expr = expr;
result->TypeAssertion.dot = dot;
result->TypeAssertion.type = type;
return result;
}
@@ -910,19 +935,21 @@ AstNode *ast_defer_stmt(AstFile *f, Token token, AstNode *stmt) {
return result; return result;
} }
AstNode *ast_branch_stmt(AstFile *f, Token token) { AstNode *ast_branch_stmt(AstFile *f, Token token, AstNode *label) {
AstNode *result = make_ast_node(f, AstNode_BranchStmt); AstNode *result = make_ast_node(f, AstNode_BranchStmt);
result->BranchStmt.token = token; result->BranchStmt.token = token;
result->BranchStmt.label = label;
return result; return result;
} }
AstNode *ast_using_stmt(AstFile *f, Token token, AstNode *node) { AstNode *ast_using_stmt(AstFile *f, Token token, AstNodeArray list) {
AstNode *result = make_ast_node(f, AstNode_UsingStmt); AstNode *result = make_ast_node(f, AstNode_UsingStmt);
result->UsingStmt.token = token; result->UsingStmt.token = token;
result->UsingStmt.node = node; result->UsingStmt.list = list;
return result; return result;
} }
AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) { AstNode *ast_asm_operand(AstFile *f, Token string, AstNode *operand) {
AstNode *result = make_ast_node(f, AstNode_AsmOperand); AstNode *result = make_ast_node(f, AstNode_AsmOperand);
result->AsmOperand.string = string; result->AsmOperand.string = string;
@@ -1022,6 +1049,13 @@ AstNode *ast_pointer_type(AstFile *f, Token token, AstNode *type) {
return result; return result;
} }
AstNode *ast_atomic_type(AstFile *f, Token token, AstNode *type) {
AstNode *result = make_ast_node(f, AstNode_AtomicType);
result->AtomicType.token = token;
result->AtomicType.type = type;
return result;
}
AstNode *ast_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem) { AstNode *ast_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem) {
AstNode *result = make_ast_node(f, AstNode_ArrayType); AstNode *result = make_ast_node(f, AstNode_ArrayType);
result->ArrayType.token = token; result->ArrayType.token = token;
@@ -1124,6 +1158,13 @@ AstNode *ast_foreign_library(AstFile *f, Token token, Token filepath, Token libr
return result; return result;
} }
AstNode *ast_label_decl(AstFile *f, Token token, AstNode *name) {
AstNode *result = make_ast_node(f, AstNode_Label);
result->Label.token = token;
result->Label.name = name;
return result;
}
bool next_token(AstFile *f) { bool next_token(AstFile *f) {
Token prev = f->curr_token; Token prev = f->curr_token;
@@ -1165,7 +1206,11 @@ Token expect_token(AstFile *f, TokenKind kind) {
syntax_error(f->curr_token, "Expected `%.*s`, got `%.*s`", syntax_error(f->curr_token, "Expected `%.*s`, got `%.*s`",
LIT(token_strings[kind]), LIT(token_strings[kind]),
LIT(token_strings[prev.kind])); LIT(token_strings[prev.kind]));
if (prev.kind == Token_EOF) {
gb_exit(1);
}
} }
next_token(f); next_token(f);
return prev; return prev;
} }
@@ -1189,6 +1234,9 @@ Token expect_operator(AstFile *f) {
if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) { if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
syntax_error(f->curr_token, "Expected an operator, got `%.*s`", syntax_error(f->curr_token, "Expected an operator, got `%.*s`",
LIT(token_strings[prev.kind])); LIT(token_strings[prev.kind]));
} else if (!f->allow_range && (prev.kind == Token_Ellipsis || prev.kind == Token_HalfClosed)) {
syntax_error(f->curr_token, "Expected an non-range operator, got `%.*s`",
LIT(token_strings[prev.kind]));
} }
next_token(f); next_token(f);
return prev; return prev;
@@ -1293,6 +1341,9 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) {
case AstNode_PointerType: case AstNode_PointerType:
return is_semicolon_optional_for_node(f, s->PointerType.type); return is_semicolon_optional_for_node(f, s->PointerType.type);
case AstNode_AtomicType:
return is_semicolon_optional_for_node(f, s->AtomicType.type);
case AstNode_StructType: case AstNode_StructType:
case AstNode_UnionType: case AstNode_UnionType:
case AstNode_RawUnionType: case AstNode_RawUnionType:
@@ -1534,6 +1585,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, AstNode **foreign_library_token, Str
expect_token(f, Token_String); expect_token(f, Token_String);
} }
} }
ELSE_IF_ADD_TAG(require_results)
ELSE_IF_ADD_TAG(export) ELSE_IF_ADD_TAG(export)
ELSE_IF_ADD_TAG(bounds_check) ELSE_IF_ADD_TAG(bounds_check)
ELSE_IF_ADD_TAG(no_bounds_check) ELSE_IF_ADD_TAG(no_bounds_check)
@@ -1692,17 +1744,14 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
AstNode *operand = NULL; // Operand AstNode *operand = NULL; // Operand
switch (f->curr_token.kind) { switch (f->curr_token.kind) {
case Token_Ident: case Token_Ident:
operand = parse_ident(f); return parse_ident(f);
if (!lhs) {
// TODO(bill): Handle?
}
return operand;
case Token_context: case Token_context:
return ast_implicit(f, expect_token(f, Token_context)); return ast_implicit(f, expect_token(f, Token_context));
case Token_Integer: case Token_Integer:
case Token_Float: case Token_Float:
case Token_Imag:
case Token_Rune: case Token_Rune:
operand = ast_basic_lit(f, f->curr_token); operand = ast_basic_lit(f, f->curr_token);
next_token(f); next_token(f);
@@ -1762,6 +1811,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
} else if (str_eq(name.string, str_lit("line"))) { return ast_basic_directive(f, token, name.string); } else if (str_eq(name.string, str_lit("line"))) { return ast_basic_directive(f, token, name.string);
} else if (str_eq(name.string, str_lit("procedure"))) { return ast_basic_directive(f, token, name.string); } else if (str_eq(name.string, str_lit("procedure"))) { return ast_basic_directive(f, token, name.string);
} else if (str_eq(name.string, str_lit("type"))) { return ast_helper_type(f, token, parse_type(f)); } else if (str_eq(name.string, str_lit("type"))) { return ast_helper_type(f, token, parse_type(f));
} else if (!lhs && str_eq(name.string, str_lit("alias"))) { return ast_alias(f, token, parse_expr(f, false));
} else { } else {
operand = ast_tag_expr(f, token, name, parse_expr(f, false)); operand = ast_tag_expr(f, token, name, parse_expr(f, false));
} }
@@ -1800,17 +1850,6 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
return type; return type;
} }
// case Token_if:
// if (!lhs && f->expr_level >= 0) {
// return parse_if_expr(f);
// }
// break;
// case Token_OpenBrace:
// if (!lhs && f->expr_level >= 0) {
// return parse_block_expr(f);
// }
// break;
default: { default: {
AstNode *type = parse_type_or_ident(f); AstNode *type = parse_type_or_ident(f);
if (type != NULL) { if (type != NULL) {
@@ -1824,10 +1863,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
} }
} }
Token begin = f->curr_token; return NULL;
syntax_error(begin, "Expected an operand");
fix_advance_to_next_stmt(f);
return ast_bad_expr(f, begin, f->curr_token);
} }
bool is_literal_type(AstNode *node) { bool is_literal_type(AstNode *node) {
@@ -1912,6 +1948,12 @@ AstNode *parse_macro_call_expr(AstFile *f, AstNode *operand) {
AstNode *parse_atom_expr(AstFile *f, bool lhs) { AstNode *parse_atom_expr(AstFile *f, bool lhs) {
AstNode *operand = parse_operand(f, lhs); AstNode *operand = parse_operand(f, lhs);
if (operand == NULL) {
Token begin = f->curr_token;
syntax_error(begin, "Expected an operand");
fix_advance_to_next_stmt(f);
operand = ast_bad_expr(f, begin, f->curr_token);
}
bool loop = true; bool loop = true;
while (loop) { while (loop) {
@@ -1933,6 +1975,13 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
// case Token_Integer: // case Token_Integer:
// operand = ast_selector_expr(f, token, operand, parse_expr(f, lhs)); // operand = ast_selector_expr(f, token, operand, parse_expr(f, lhs));
// break; // break;
case Token_OpenParen: {
Token open = expect_token(f, Token_OpenParen);
AstNode *type = parse_type(f);
Token close = expect_token(f, Token_CloseParen);
operand = ast_type_assertion(f, operand, token, type);
} break;
default: default:
syntax_error(f->curr_token, "Expected a selector"); syntax_error(f->curr_token, "Expected a selector");
next_token(f); next_token(f);
@@ -1946,6 +1995,9 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
if (lhs) { if (lhs) {
// TODO(bill): Handle this // TODO(bill): Handle this
} }
bool prev_allow_range = f->allow_range;
f->allow_range = false;
Token open = {0}, close = {0}, interval = {0}; Token open = {0}, close = {0}, interval = {0};
AstNode *indices[3] = {0}; AstNode *indices[3] = {0};
isize ellipsis_count = 0; isize ellipsis_count = 0;
@@ -1954,15 +2006,19 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
f->expr_level++; f->expr_level++;
open = expect_token(f, Token_OpenBracket); open = expect_token(f, Token_OpenBracket);
if (f->curr_token.kind != Token_Ellipsis) { if (f->curr_token.kind != Token_Ellipsis &&
f->curr_token.kind != Token_HalfClosed) {
indices[0] = parse_expr(f, false); indices[0] = parse_expr(f, false);
} }
bool is_index = true; bool is_index = true;
while (f->curr_token.kind == Token_Ellipsis && ellipsis_count < gb_count_of(ellipses)) { while ((f->curr_token.kind == Token_Ellipsis ||
f->curr_token.kind == Token_HalfClosed)
&& ellipsis_count < gb_count_of(ellipses)) {
ellipses[ellipsis_count++] = f->curr_token; ellipses[ellipsis_count++] = f->curr_token;
next_token(f); next_token(f);
if (f->curr_token.kind != Token_Ellipsis && if (f->curr_token.kind != Token_Ellipsis &&
f->curr_token.kind != Token_HalfClosed &&
f->curr_token.kind != Token_CloseBracket && f->curr_token.kind != Token_CloseBracket &&
f->curr_token.kind != Token_EOF) { f->curr_token.kind != Token_EOF) {
indices[ellipsis_count] = parse_expr(f, false); indices[ellipsis_count] = parse_expr(f, false);
@@ -1987,10 +2043,12 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
indices[2] = ast_bad_expr(f, ellipses[1], close); indices[2] = ast_bad_expr(f, ellipses[1], close);
} }
} }
operand = ast_slice_expr(f, operand, open, close, index3, indices[0], indices[1], indices[2]); operand = ast_slice_expr(f, operand, open, close, ellipses[0], ellipses[1], index3, indices[0], indices[1], indices[2]);
} else { } else {
operand = ast_index_expr(f, operand, indices[0], open, close); operand = ast_index_expr(f, operand, indices[0], open, close);
} }
f->allow_range = prev_allow_range;
} break; } break;
case Token_Pointer: // Deference case Token_Pointer: // Deference
@@ -2020,33 +2078,33 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
AstNode *parse_unary_expr(AstFile *f, bool lhs) { AstNode *parse_unary_expr(AstFile *f, bool lhs) {
switch (f->curr_token.kind) { switch (f->curr_token.kind) {
case Token_cast: // case Token_cast:
case Token_transmute: // case Token_transmute:
case Token_down_cast: // case Token_union_cast:
case Token_union_cast: // {
{ // Token token = f->curr_token; next_token(f);
Token token = f->curr_token; next_token(f); // Token open = expect_token(f, Token_OpenParen);
Token open = expect_token(f, Token_OpenParen); // AstNode *type = parse_type(f);
AstNode *type = parse_type(f); // Token close = expect_token(f, Token_CloseParen);
Token close = expect_token(f, Token_CloseParen); // AstNode *expr = parse_unary_expr(f, lhs);
AstNode *expr = parse_unary_expr(f, lhs); // return ast_cast_expr(f, token, type, expr, open, close);
return ast_cast_expr(f, token, type, expr, open, close); // } break;
} break;
// case Token_Pointer: {
// Token op = f->curr_token;
// next_token(f);
// AstNode *expr = parse_unary_expr(f, lhs);
// if (is_ast_node_type(expr)) {
// return ast_pointer_type(f, op, expr);
// }
// return ast_unary_expr(f, op, expr);
// } break;
case Token_Pointer: {
Token op = f->curr_token;
next_token(f);
AstNode *expr = parse_unary_expr(f, lhs);
if (is_ast_node_type(expr)) {
return ast_pointer_type(f, op, expr);
}
return ast_unary_expr(f, op, expr);
} break;
// case Token_Maybe:
case Token_Add: case Token_Add:
case Token_Sub: case Token_Sub:
case Token_Not: case Token_Not:
case Token_Xor: { case Token_Xor:
case Token_And: {
Token op = f->curr_token; Token op = f->curr_token;
next_token(f); next_token(f);
return ast_unary_expr(f, op, parse_unary_expr(f, lhs)); return ast_unary_expr(f, op, parse_unary_expr(f, lhs));
@@ -2056,27 +2114,49 @@ AstNode *parse_unary_expr(AstFile *f, bool lhs) {
return parse_atom_expr(f, lhs); return parse_atom_expr(f, lhs);
} }
bool is_ast_node_a_range(AstNode *expr) {
if (expr == NULL) {
return false;
}
if (expr->kind != AstNode_BinaryExpr) {
return false;
}
TokenKind op = expr->BinaryExpr.op.kind;
switch (op) {
case Token_Ellipsis:
case Token_HalfClosed:
return true;
}
return false;
}
// NOTE(bill): result == priority // NOTE(bill): result == priority
i32 token_precedence(TokenKind t) { i32 token_precedence(AstFile *f, TokenKind t) {
switch (t) { switch (t) {
case Token_Question: case Token_Question:
return 1; return 1;
case Token_Ellipsis:
case Token_HalfClosed:
if (f->allow_range) {
return 2;
}
return 0;
case Token_CmpOr: case Token_CmpOr:
return 2;
case Token_CmpAnd:
return 3; return 3;
case Token_CmpAnd:
return 4;
case Token_CmpEq: case Token_CmpEq:
case Token_NotEq: case Token_NotEq:
case Token_Lt: case Token_Lt:
case Token_Gt: case Token_Gt:
case Token_LtEq: case Token_LtEq:
case Token_GtEq: case Token_GtEq:
return 4; return 5;
case Token_Add: case Token_Add:
case Token_Sub: case Token_Sub:
case Token_Or: case Token_Or:
case Token_Xor: case Token_Xor:
return 5; return 6;
case Token_Mul: case Token_Mul:
case Token_Quo: case Token_Quo:
case Token_Mod: case Token_Mod:
@@ -2084,17 +2164,17 @@ i32 token_precedence(TokenKind t) {
case Token_AndNot: case Token_AndNot:
case Token_Shl: case Token_Shl:
case Token_Shr: case Token_Shr:
return 6; return 7;
} }
return 0; return 0;
} }
AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
AstNode *expr = parse_unary_expr(f, lhs); AstNode *expr = parse_unary_expr(f, lhs);
for (i32 prec = token_precedence(f->curr_token.kind); prec >= prec_in; prec--) { for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) {
for (;;) { for (;;) {
Token op = f->curr_token; Token op = f->curr_token;
i32 op_prec = token_precedence(op.kind); i32 op_prec = token_precedence(f, op.kind);
if (op_prec != prec) { if (op_prec != prec) {
// NOTE(bill): This will also catch operators that are not valid "binary" operators // NOTE(bill): This will also catch operators that are not valid "binary" operators
break; break;
@@ -2110,7 +2190,7 @@ AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
expr = ast_ternary_expr(f, cond, x, y); expr = ast_ternary_expr(f, cond, x, y);
} else { } else {
AstNode *right = parse_binary_expr(f, false, prec+1); AstNode *right = parse_binary_expr(f, false, prec+1);
if (!right) { if (right == NULL) {
syntax_error(op, "Expected expression on the right-hand side of the binary operator"); syntax_error(op, "Expected expression on the right-hand side of the binary operator");
} }
expr = ast_binary_expr(f, op, expr, right); expr = ast_binary_expr(f, op, expr, right);
@@ -2129,7 +2209,7 @@ AstNode *parse_expr(AstFile *f, bool lhs) {
AstNodeArray parse_expr_list(AstFile *f, bool lhs) { AstNodeArray parse_expr_list(AstFile *f, bool lhs) {
AstNodeArray list = make_ast_node_array(f); AstNodeArray list = make_ast_node_array(f);
do { for (;;) {
AstNode *e = parse_expr(f, lhs); AstNode *e = parse_expr(f, lhs);
array_add(&list, e); array_add(&list, e);
if (f->curr_token.kind != Token_Comma || if (f->curr_token.kind != Token_Comma ||
@@ -2137,7 +2217,7 @@ AstNodeArray parse_expr_list(AstFile *f, bool lhs) {
break; break;
} }
next_token(f); next_token(f);
} while (true); }
return list; return list;
} }
@@ -2238,6 +2318,7 @@ AstNode *parse_value_decl(AstFile *f, AstNodeArray lhs) {
} }
AstNode *parse_simple_stmt(AstFile *f, bool in_stmt_ok) { AstNode *parse_simple_stmt(AstFile *f, bool in_stmt_ok) {
AstNodeArray lhs = parse_lhs_expr_list(f); AstNodeArray lhs = parse_lhs_expr_list(f);
Token token = f->curr_token; Token token = f->curr_token;
@@ -2273,15 +2354,10 @@ AstNode *parse_simple_stmt(AstFile *f, bool in_stmt_ok) {
case Token_in: case Token_in:
if (in_stmt_ok) { if (in_stmt_ok) {
allow_token(f, Token_in); allow_token(f, Token_in);
bool prev_allow_range = f->allow_range;
f->allow_range = true;
AstNode *expr = parse_expr(f, false); AstNode *expr = parse_expr(f, false);
switch (f->curr_token.kind) { f->allow_range = prev_allow_range;
case Token_Ellipsis: {
Token op = f->curr_token;
next_token(f);
AstNode *right = parse_expr(f, false);
expr = ast_interval_expr(f, op, expr, right);
} break;
}
AstNodeArray rhs = {0}; AstNodeArray rhs = {0};
array_init_count(&rhs, heap_allocator(), 1); array_init_count(&rhs, heap_allocator(), 1);
@@ -2320,7 +2396,7 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
return parse_body(f); return parse_body(f);
} }
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow); AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow);
AstNode *parse_results(AstFile *f) { AstNode *parse_results(AstFile *f) {
@@ -2339,7 +2415,7 @@ AstNode *parse_results(AstFile *f) {
AstNode *list = NULL; AstNode *list = NULL;
expect_token(f, Token_OpenParen); expect_token(f, Token_OpenParen);
list = parse_field_list(f, NULL, 0, Token_Comma, Token_CloseParen); list = parse_field_list(f, NULL, 0, Token_CloseParen);
expect_token_after(f, Token_CloseParen, "parameter list"); expect_token_after(f, Token_CloseParen, "parameter list");
return list; return list;
} }
@@ -2350,7 +2426,7 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
Token proc_token = expect_token(f, Token_proc); Token proc_token = expect_token(f, Token_proc);
expect_token(f, Token_OpenParen); expect_token(f, Token_OpenParen);
params = parse_field_list(f, NULL, FieldFlag_Signature, Token_Comma, Token_CloseParen); params = parse_field_list(f, NULL, FieldFlag_Signature, Token_CloseParen);
expect_token_after(f, Token_CloseParen, "parameter list"); expect_token_after(f, Token_CloseParen, "parameter list");
results = parse_results(f); results = parse_results(f);
@@ -2369,17 +2445,6 @@ AstNode *parse_proc_type(AstFile *f, AstNode **foreign_library_, String *foreign
return ast_proc_type(f, proc_token, params, results, tags, cc); return ast_proc_type(f, proc_token, params, results, tags, cc);
} }
bool parse_expect_separator(AstFile *f, TokenKind separator, AstNode *param) {
if (separator == Token_Semicolon) {
expect_semicolon(f, param);
} else {
if (!allow_token(f, separator)) {
return true;
}
}
return false;
}
AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) { AstNode *parse_var_type(AstFile *f, bool allow_ellipsis) {
if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) { if (allow_ellipsis && f->curr_token.kind == Token_Ellipsis) {
Token tok = f->curr_token; Token tok = f->curr_token;
@@ -2490,7 +2555,22 @@ AstNodeArray convert_to_ident_list(AstFile *f, AstNodeAndFlagsArray list, bool i
return idents; return idents;
} }
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind separator, TokenKind follow) {
bool parse_expect_field_separator(AstFile *f, AstNode *param) {
Token token = f->curr_token;
if (allow_token(f, Token_Comma)) {
return true;
}
if (token.kind == Token_Semicolon) {
next_token(f);
error(f->curr_token, "Expected a comma, got a semicolon");
return true;
}
return false;
}
AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow) {
TokenKind separator = Token_Comma;
Token start_token = f->curr_token; Token start_token = f->curr_token;
AstNodeArray params = make_ast_node_array(f); AstNodeArray params = make_ast_node_array(f);
@@ -2528,7 +2608,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
AstNode *param = ast_field(f, names, type, set_flags); AstNode *param = ast_field(f, names, type, set_flags);
array_add(&params, param); array_add(&params, param);
parse_expect_separator(f, separator, type); parse_expect_field_separator(f, type);
while (f->curr_token.kind != follow && while (f->curr_token.kind != follow &&
f->curr_token.kind != Token_EOF) { f->curr_token.kind != Token_EOF) {
@@ -2546,7 +2626,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
AstNode *param = ast_field(f, names, type, set_flags); AstNode *param = ast_field(f, names, type, set_flags);
array_add(&params, param); array_add(&params, param);
if (parse_expect_separator(f, separator, param)) { if (!parse_expect_field_separator(f, param)) {
break; break;
} }
} }
@@ -2575,7 +2655,7 @@ AstNode *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, Tok
AstNode *parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) { AstNode *parse_record_fields(AstFile *f, isize *field_count_, u32 flags, String context) {
return parse_field_list(f, field_count_, flags, Token_Comma, Token_CloseBrace); return parse_field_list(f, field_count_, flags, Token_CloseBrace);
} }
AstNode *parse_type_or_ident(AstFile *f) { AstNode *parse_type_or_ident(AstFile *f) {
@@ -2589,10 +2669,11 @@ AstNode *parse_type_or_ident(AstFile *f) {
AstNode *sel = parse_ident(f); AstNode *sel = parse_ident(f);
e = ast_selector_expr(f, token, e, sel); e = ast_selector_expr(f, token, e, sel);
} }
if (f->curr_token.kind == Token_OpenParen) { // TODO(bill): Merge type_or_ident into the general parsing for expressions
// if (f->curr_token.kind == Token_OpenParen) {
// HACK NOTE(bill): For type_of_val(expr) et al. // HACK NOTE(bill): For type_of_val(expr) et al.
e = parse_call_expr(f, e); // e = parse_call_expr(f, e);
} // }
return e; return e;
} }
@@ -2614,6 +2695,12 @@ AstNode *parse_type_or_ident(AstFile *f) {
return ast_pointer_type(f, token, elem); return ast_pointer_type(f, token, elem);
} }
case Token_atomic: {
Token token = expect_token(f, Token_atomic);
AstNode *elem = parse_type(f);
return ast_atomic_type(f, token, elem);
}
case Token_OpenBracket: { case Token_OpenBracket: {
Token token = expect_token(f, Token_OpenBracket); Token token = expect_token(f, Token_OpenBracket);
AstNode *count_expr = NULL; AstNode *count_expr = NULL;
@@ -3041,46 +3128,6 @@ AstNode *parse_for_stmt(AstFile *f) {
cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression")); cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
return ast_for_stmt(f, token, init, cond, post, body); return ast_for_stmt(f, token, init, cond, post, body);
#if 0
Token token = expect_token(f, Token_for);
AstNodeArray names = parse_ident_list(f);
parse_check_name_list_for_reserves(f, names);
Token colon = expect_token_after(f, Token_in, "for name list");
isize prev_level = f->expr_level;
f->expr_level = -1;
AstNode *expr = parse_expr(f, false);
switch (f->curr_token.kind) {
case Token_HalfOpenRange:
case Token_Ellipsis: {
Token op = f->curr_token;
next_token(f);
AstNode *right = parse_expr(f, false);
expr = ast_interval_expr(f, op, expr, right);
} break;
}
f->expr_level = prev_level;
AstNode *value = NULL;
AstNode *index = NULL;
AstNode *body = parse_block_stmt(f, false);
switch (names.count) {
case 1:
value = names.e[0];
break;
case 2:
value = names.e[0];
index = names.e[1];
break;
default:
error(token, "Expected at 1 or 2 identifiers");
return ast_bad_stmt(f, token, f->curr_token);
}
return ast_range_stmt(f, token, value, index, expr, body);
#endif
} }
@@ -3088,7 +3135,10 @@ AstNode *parse_case_clause(AstFile *f) {
Token token = f->curr_token; Token token = f->curr_token;
AstNodeArray list = make_ast_node_array(f); AstNodeArray list = make_ast_node_array(f);
if (allow_token(f, Token_case)) { if (allow_token(f, Token_case)) {
bool prev_allow_range = f->allow_range;
f->allow_range = true;
list = parse_rhs_expr_list(f); list = parse_rhs_expr_list(f);
f->allow_range = prev_allow_range;
} else { } else {
expect_token(f, Token_default); expect_token(f, Token_default);
} }
@@ -3102,9 +3152,17 @@ AstNode *parse_case_clause(AstFile *f) {
AstNode *parse_type_case_clause(AstFile *f) { AstNode *parse_type_case_clause(AstFile *f) {
Token token = f->curr_token; Token token = f->curr_token;
AstNodeArray clause = make_ast_node_array(f); AstNodeArray list = make_ast_node_array(f);
if (allow_token(f, Token_case)) { if (allow_token(f, Token_case)) {
array_add(&clause, parse_type(f)); for (;;) {
AstNode *t = parse_type(f);
array_add(&list, t);
if (f->curr_token.kind != Token_Comma ||
f->curr_token.kind == Token_EOF) {
break;
}
next_token(f);
}
} else { } else {
expect_token(f, Token_default); expect_token(f, Token_default);
} }
@@ -3112,7 +3170,7 @@ AstNode *parse_type_case_clause(AstFile *f) {
// expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax? // expect_token(f, Token_ArrowRight); // TODO(bill): Is this the best syntax?
AstNodeArray stmts = parse_stmt_list(f); AstNodeArray stmts = parse_stmt_list(f);
return ast_case_clause(f, token, clause, stmts); return ast_case_clause(f, token, list, stmts);
} }
@@ -3233,6 +3291,7 @@ AstNode *parse_stmt(AstFile *f) {
case Token_Ident: case Token_Ident:
case Token_Integer: case Token_Integer:
case Token_Float: case Token_Float:
case Token_Imag:
case Token_Rune: case Token_Rune:
case Token_String: case Token_String:
case Token_OpenParen: case Token_OpenParen:
@@ -3256,43 +3315,51 @@ AstNode *parse_stmt(AstFile *f) {
case Token_break: case Token_break:
case Token_continue: case Token_continue:
case Token_fallthrough: case Token_fallthrough: {
AstNode *label = NULL;
next_token(f); next_token(f);
s = ast_branch_stmt(f, token); if (token.kind != Token_fallthrough &&
f->curr_token.kind == Token_Ident) {
label = parse_ident(f);
}
s = ast_branch_stmt(f, token, label);
expect_semicolon(f, s); expect_semicolon(f, s);
return s; return s;
}
case Token_using: { case Token_using: {
// TODO(bill): Make using statements better // TODO(bill): Make using statements better
Token token = expect_token(f, Token_using); Token token = expect_token(f, Token_using);
AstNode *node = parse_stmt(f); AstNodeArray list = parse_lhs_expr_list(f);
if (list.count == 0) {
syntax_error(token, "Illegal use of `using` statement");
expect_semicolon(f, NULL);
return ast_bad_stmt(f, token, f->curr_token);
}
switch (node->kind) { if (f->curr_token.kind != Token_Colon) {
case AstNode_ValueDecl: expect_semicolon(f, list.e[list.count-1]);
if (!node->ValueDecl.is_var) { return ast_using_stmt(f, token, list);
}
AstNode *decl = parse_value_decl(f, list);
expect_semicolon(f, decl);
if (decl->kind == AstNode_ValueDecl) {
#if 1
if (!decl->ValueDecl.is_var) {
syntax_error(token, "`using` may not be applied to constant declarations"); syntax_error(token, "`using` may not be applied to constant declarations");
return decl;
}
if (f->curr_proc == NULL) {
syntax_error(token, "`using` is not allowed at the file scope");
} else { } else {
if (f->curr_proc == NULL) { decl->ValueDecl.flags |= VarDeclFlag_using;
syntax_error(token, "`using` is not allowed at the file scope");
} else {
node->ValueDecl.flags |= VarDeclFlag_using;
}
} }
return node; #else
case AstNode_ExprStmt: { decl->ValueDecl.flags |= VarDeclFlag_using;
AstNode *e = unparen_expr(node->ExprStmt.expr); #endif
while (e->kind == AstNode_SelectorExpr) { return decl;
e = unparen_expr(e->SelectorExpr.selector);
}
if (e->kind == AstNode_Ident) {
return ast_using_stmt(f, token, node);
} else if (e->kind == AstNode_Implicit) {
syntax_error(token, "Illegal use of `using` statement with implicit value `%.*s`", LIT(e->Implicit.string));
return ast_bad_stmt(f, token, f->curr_token);
}
} break;
} }
syntax_error(token, "Illegal use of `using` statement"); syntax_error(token, "Illegal use of `using` statement");
@@ -3345,7 +3412,25 @@ AstNode *parse_stmt(AstFile *f) {
Token name = expect_token(f, Token_Ident); Token name = expect_token(f, Token_Ident);
String tag = name.string; String tag = name.string;
if (str_eq(tag, str_lit("import"))) { if (str_eq(tag, str_lit("label"))) {
AstNode *name = parse_ident(f);
AstNode *label = ast_label_decl(f, token, name);
AstNode *stmt = parse_stmt(f);
#define _SET_LABEL(Kind_, label_) case GB_JOIN2(AstNode_, Kind_): (stmt->Kind_).label = label_; break
switch (stmt->kind) {
_SET_LABEL(ForStmt, label);
_SET_LABEL(RangeStmt, label);
_SET_LABEL(MatchStmt, label);
_SET_LABEL(TypeMatchStmt, label);
default:
syntax_error(token, "#label cannot only be applied to a loop or match statement");
break;
}
#undef _SET_LABEL
return stmt;
} else if (str_eq(tag, str_lit("import"))) {
AstNode *cond = NULL; AstNode *cond = NULL;
Token import_name = {0}; Token import_name = {0};
@@ -3505,7 +3590,6 @@ AstNode *parse_stmt(AstFile *f) {
return s; return s;
} }
if (str_eq(tag, str_lit("include"))) { if (str_eq(tag, str_lit("include"))) {
syntax_error(token, "#include is not a valid import declaration kind. Use #load instead"); syntax_error(token, "#include is not a valid import declaration kind. Use #load instead");
s = ast_bad_stmt(f, token, f->curr_token); s = ast_bad_stmt(f, token, f->curr_token);
@@ -3589,6 +3673,7 @@ ParseFileError init_ast_file(AstFile *f, String fullpath) {
gb_arena_init_from_allocator(&f->arena, heap_allocator(), arena_size); gb_arena_init_from_allocator(&f->arena, heap_allocator(), arena_size);
f->curr_proc = NULL; f->curr_proc = NULL;
f->id = ++global_file_id;
return ParseFile_None; return ParseFile_None;
} }
@@ -3809,6 +3894,10 @@ ParseFileError parse_files(Parser *p, char *init_filename) {
if (err != ParseFile_None) { if (err != ParseFile_None) {
if (err == ParseFile_EmptyFile) { if (err == ParseFile_EmptyFile) {
if (str_eq(import_path, init_fullpath)) {
gb_printf_err("Initial file is empty - %.*s\n", LIT(init_fullpath));
gb_exit(1);
}
return ParseFile_None; return ParseFile_None;
} }
@@ -3827,7 +3916,7 @@ ParseFileError parse_files(Parser *p, char *init_filename) {
gb_printf_err("File permissions problem"); gb_printf_err("File permissions problem");
break; break;
case ParseFile_NotFound: case ParseFile_NotFound:
gb_printf_err("File cannot be found"); gb_printf_err("File cannot be found (`%.*s`)", LIT(import_path));
break; break;
case ParseFile_InvalidToken: case ParseFile_InvalidToken:
gb_printf_err("Invalid token found in file"); gb_printf_err("Invalid token found in file");
+2636
View File
File diff suppressed because it is too large Load Diff
+276
View File
@@ -0,0 +1,276 @@
#define SSA_OPS \
SSA_OP(Invalid)\
\
SSA_OP(Unknown)\
\
SSA_OP(Comment) /* Does nothing */\
\
SSA_OP(SP) /* Stack Pointer */\
SSA_OP(SB) /* Stack Base */\
\
SSA_OP(Local)\
SSA_OP(Global)\
SSA_OP(Proc)\
\
SSA_OP(Load)\
SSA_OP(Store)\
SSA_OP(Move)\
SSA_OP(LoadReg)\
SSA_OP(StoreReg)\
SSA_OP(Zero) /* Zero initialize */\
\
SSA_OP(ArrayIndex) /* Index for a fixed array */\
SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\
SSA_OP(PtrOffset)\
SSA_OP(ValueIndex) /* Extract for a value from a register */\
\
SSA_OP(Phi)\
SSA_OP(Copy)\
\
/* TODO(bill): calling conventions */\
SSA_OP(CallOdin)\
SSA_OP(CallC)\
SSA_OP(CallStd)\
SSA_OP(CallFast)\
\
SSA_OP(BoundsCheck)\
SSA_OP(SliceBoundsCheck)\
\
/* Built in operations/procedures */\
SSA_OP(Bswap16)\
SSA_OP(Bswap32)\
SSA_OP(Bswap64)\
\
SSA_OP(Assume)\
SSA_OP(DebugTrap)\
SSA_OP(Trap)\
SSA_OP(ReadCycleCounter)\
\
\
SSA_OP(ConstBool)\
SSA_OP(ConstString)\
SSA_OP(ConstSlice)\
SSA_OP(ConstNil)\
SSA_OP(Const8)\
SSA_OP(Const16)\
SSA_OP(Const32)\
SSA_OP(Const64)\
SSA_OP(Const32F)\
SSA_OP(Const64F)\
\
/* These should be all the operations I could possibly need for the mean time */\
SSA_OP(Add8)\
SSA_OP(Add16)\
SSA_OP(Add32)\
SSA_OP(Add64)\
SSA_OP(AddPtr)\
SSA_OP(Add32F)\
SSA_OP(Add64F)\
SSA_OP(Sub8)\
SSA_OP(Sub16)\
SSA_OP(Sub32)\
SSA_OP(Sub64)\
SSA_OP(SubPtr)\
SSA_OP(Sub32F)\
SSA_OP(Sub64F)\
SSA_OP(Mul8)\
SSA_OP(Mul16)\
SSA_OP(Mul32)\
SSA_OP(Mul64)\
SSA_OP(Mul32F)\
SSA_OP(Mul64F)\
SSA_OP(Div8)\
SSA_OP(Div8U)\
SSA_OP(Div16)\
SSA_OP(Div16U)\
SSA_OP(Div32)\
SSA_OP(Div32U)\
SSA_OP(Div64)\
SSA_OP(Div64U)\
SSA_OP(Div32F)\
SSA_OP(Div64F)\
SSA_OP(Mod8)\
SSA_OP(Mod8U)\
SSA_OP(Mod16)\
SSA_OP(Mod16U)\
SSA_OP(Mod32)\
SSA_OP(Mod32U)\
SSA_OP(Mod64)\
SSA_OP(Mod64U)\
\
SSA_OP(And8)\
SSA_OP(And16)\
SSA_OP(And32)\
SSA_OP(And64)\
SSA_OP(Or8)\
SSA_OP(Or16)\
SSA_OP(Or32)\
SSA_OP(Or64)\
SSA_OP(Xor8)\
SSA_OP(Xor16)\
SSA_OP(Xor32)\
SSA_OP(Xor64)\
SSA_OP(AndNot8)\
SSA_OP(AndNot16)\
SSA_OP(AndNot32)\
SSA_OP(AndNot64)\
\
SSA_OP(Lsh8x8)\
SSA_OP(Lsh8x16)\
SSA_OP(Lsh8x32)\
SSA_OP(Lsh8x64)\
SSA_OP(Lsh16x8)\
SSA_OP(Lsh16x16)\
SSA_OP(Lsh16x32)\
SSA_OP(Lsh16x64)\
SSA_OP(Lsh32x8)\
SSA_OP(Lsh32x16)\
SSA_OP(Lsh32x32)\
SSA_OP(Lsh32x64)\
SSA_OP(Lsh64x8)\
SSA_OP(Lsh64x16)\
SSA_OP(Lsh64x32)\
SSA_OP(Lsh64x64)\
SSA_OP(Rsh8x8)\
SSA_OP(Rsh8x16)\
SSA_OP(Rsh8x32)\
SSA_OP(Rsh8x64)\
SSA_OP(Rsh16x8)\
SSA_OP(Rsh16x16)\
SSA_OP(Rsh16x32)\
SSA_OP(Rsh16x64)\
SSA_OP(Rsh32x8)\
SSA_OP(Rsh32x16)\
SSA_OP(Rsh32x32)\
SSA_OP(Rsh32x64)\
SSA_OP(Rsh64x8)\
SSA_OP(Rsh64x16)\
SSA_OP(Rsh64x32)\
SSA_OP(Rsh64x64)\
SSA_OP(Rsh8Ux8)\
SSA_OP(Rsh8Ux16)\
SSA_OP(Rsh8Ux32)\
SSA_OP(Rsh8Ux64)\
SSA_OP(Rsh16Ux8)\
SSA_OP(Rsh16Ux16)\
SSA_OP(Rsh16Ux32)\
SSA_OP(Rsh16Ux64)\
SSA_OP(Rsh32Ux8)\
SSA_OP(Rsh32Ux16)\
SSA_OP(Rsh32Ux32)\
SSA_OP(Rsh32Ux64)\
SSA_OP(Rsh64Ux8)\
SSA_OP(Rsh64Ux16)\
SSA_OP(Rsh64Ux32)\
SSA_OP(Rsh64Ux64)\
\
SSA_OP(Eq8)\
SSA_OP(Eq16)\
SSA_OP(Eq32)\
SSA_OP(Eq64)\
SSA_OP(EqPtr)\
SSA_OP(Eq32F)\
SSA_OP(Eq64F)\
SSA_OP(Ne8)\
SSA_OP(Ne16)\
SSA_OP(Ne32)\
SSA_OP(Ne64)\
SSA_OP(NePtr)\
SSA_OP(Ne32F)\
SSA_OP(Ne64F)\
SSA_OP(Lt8)\
SSA_OP(Lt16)\
SSA_OP(Lt32)\
SSA_OP(Lt64)\
SSA_OP(LtPtr)\
SSA_OP(Lt32F)\
SSA_OP(Lt64F)\
SSA_OP(Gt8)\
SSA_OP(Gt16)\
SSA_OP(Gt32)\
SSA_OP(Gt64)\
SSA_OP(GtPtr)\
SSA_OP(Gt32F)\
SSA_OP(Gt64F)\
SSA_OP(Le8)\
SSA_OP(Le16)\
SSA_OP(Le32)\
SSA_OP(Le64)\
SSA_OP(LePtr)\
SSA_OP(Le32F)\
SSA_OP(Le64F)\
SSA_OP(Ge8)\
SSA_OP(Ge16)\
SSA_OP(Ge32)\
SSA_OP(Ge64)\
SSA_OP(GePtr)\
SSA_OP(Ge32F)\
SSA_OP(Ge64F)\
\
SSA_OP(NotB)\
SSA_OP(EqB)\
SSA_OP(NeB)\
\
SSA_OP(Neg8)\
SSA_OP(Neg16)\
SSA_OP(Neg32)\
SSA_OP(Neg64)\
SSA_OP(Neg32F)\
SSA_OP(Neg64F)\
\
SSA_OP(Not8)\
SSA_OP(Not16)\
SSA_OP(Not32)\
SSA_OP(Not64)\
\
SSA_OP(SignExt8to16)\
SSA_OP(SignExt8to32)\
SSA_OP(SignExt8to64)\
SSA_OP(SignExt16to32)\
SSA_OP(SignExt16to64)\
SSA_OP(SignExt32to64)\
SSA_OP(ZeroExt8to16)\
SSA_OP(ZeroExt8to32)\
SSA_OP(ZeroExt8to64)\
SSA_OP(ZeroExt16to32)\
SSA_OP(ZeroExt16to64)\
SSA_OP(ZeroExt32to64)\
SSA_OP(Trunc16to8)\
SSA_OP(Trunc32to8)\
SSA_OP(Trunc32to16)\
SSA_OP(Trunc64to8)\
SSA_OP(Trunc64to16)\
SSA_OP(Trunc64to32)\
\
SSA_OP(Cvt32to32F)\
SSA_OP(Cvt32to64F)\
SSA_OP(Cvt64to32F)\
SSA_OP(Cvt64to64F)\
SSA_OP(Cvt32Fto32)\
SSA_OP(Cvt32Fto64)\
SSA_OP(Cvt64Fto32)\
SSA_OP(Cvt64Fto64)\
SSA_OP(Cvt32Fto64F)\
SSA_OP(Cvt64Fto32F)\
SSA_OP(Cvt32Uto32F)\
SSA_OP(Cvt32Uto64F)\
SSA_OP(Cvt32Fto32U)\
SSA_OP(Cvt64Fto32U)\
SSA_OP(Cvt64Uto32F)\
SSA_OP(Cvt64Uto64F)\
SSA_OP(Cvt32Fto64U)\
SSA_OP(Cvt64Fto64U)\
enum ssaOp {
#define SSA_OP(k) GB_JOIN2(ssaOp_, k),
SSA_OPS
#undef SSA_OP
};
typedef enum ssaOp ssaOp;
String const ssa_op_strings[] = {
#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1},
SSA_OPS
#undef SSA_OP
};
+18 -2
View File
@@ -188,6 +188,22 @@ bool string_contains_char(String s, u8 c) {
return false; return false;
} }
String filename_from_path(String s) {
isize i = string_extension_position(s);
if (i > 0) {
isize j = 0;
s.len = i;
for (j = i-1; j >= 0; j--) {
if (s.text[j] == '/' ||
s.text[j] == '\\') {
break;
}
}
s.text += j+1;
s.len = i-j-1;
}
return make_string(NULL, 0);
}
@@ -207,7 +223,7 @@ bool string_contains_char(String s, u8 c) {
#include <iconv.h> #include <iconv.h>
int convert_multibyte_to_widechar(char *multibyte_input, int input_length, wchar_t *output, int output_size) { int convert_multibyte_to_widechar(char *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
iconv_t conv = iconv_open("WCHAR_T", "UTF-8"); 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); size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size);
iconv_close(conv); iconv_close(conv);
@@ -215,7 +231,7 @@ bool string_contains_char(String s, u8 c) {
return (int) result; return (int) result;
} }
int convert_widechar_to_multibyte(wchar_t* widechar_input, int input_length, char* output, int output_size) { int convert_widechar_to_multibyte(wchar_t* widechar_input, usize input_length, char* output, usize output_size) {
iconv_t conv = iconv_open("UTF-8", "WCHAR_T"); iconv_t conv = iconv_open("UTF-8", "WCHAR_T");
size_t result = iconv(conv, (char**) &widechar_input, &input_length, (char**) &output, &output_size); size_t result = iconv(conv, (char**) &widechar_input, &input_length, (char**) &output, &output_size);
iconv_close(conv); iconv_close(conv);
+73 -62
View File
@@ -7,6 +7,7 @@ TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \
TOKEN_KIND(Token_Ident, "identifier"), \ TOKEN_KIND(Token_Ident, "identifier"), \
TOKEN_KIND(Token_Integer, "integer"), \ TOKEN_KIND(Token_Integer, "integer"), \
TOKEN_KIND(Token_Float, "float"), \ TOKEN_KIND(Token_Float, "float"), \
TOKEN_KIND(Token_Imag, "imaginary"), \
TOKEN_KIND(Token_Rune, "rune"), \ TOKEN_KIND(Token_Rune, "rune"), \
TOKEN_KIND(Token_String, "string"), \ TOKEN_KIND(Token_String, "string"), \
TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \ TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \
@@ -63,18 +64,19 @@ TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
TOKEN_KIND(Token_GtEq, ">="), \ TOKEN_KIND(Token_GtEq, ">="), \
TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \ TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
\ \
TOKEN_KIND(Token_OpenParen, "("), \ TOKEN_KIND(Token_OpenParen, "("), \
TOKEN_KIND(Token_CloseParen, ")"), \ TOKEN_KIND(Token_CloseParen, ")"), \
TOKEN_KIND(Token_OpenBracket, "["), \ TOKEN_KIND(Token_OpenBracket, "["), \
TOKEN_KIND(Token_CloseBracket, "]"), \ TOKEN_KIND(Token_CloseBracket, "]"), \
TOKEN_KIND(Token_OpenBrace, "{"), \ TOKEN_KIND(Token_OpenBrace, "{"), \
TOKEN_KIND(Token_CloseBrace, "}"), \ TOKEN_KIND(Token_CloseBrace, "}"), \
TOKEN_KIND(Token_Colon, ":"), \ TOKEN_KIND(Token_Colon, ":"), \
TOKEN_KIND(Token_Semicolon, ";"), \ TOKEN_KIND(Token_Semicolon, ";"), \
TOKEN_KIND(Token_Period, "."), \ TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \ TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, ".."), \ TOKEN_KIND(Token_Ellipsis, ".."), \
/* TOKEN_KIND(Token_HalfOpenRange, "..<"), */ \ TOKEN_KIND(Token_HalfClosed, "..<"), \
TOKEN_KIND(Token_BackSlash, "\\"), \
TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \ TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
\ \
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
@@ -105,14 +107,13 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
TOKEN_KIND(Token_using, "using"), \ TOKEN_KIND(Token_using, "using"), \
TOKEN_KIND(Token_no_alias, "no_alias"), \ TOKEN_KIND(Token_no_alias, "no_alias"), \
TOKEN_KIND(Token_immutable, "immutable"), \ TOKEN_KIND(Token_immutable, "immutable"), \
TOKEN_KIND(Token_cast, "cast"), \
TOKEN_KIND(Token_transmute, "transmute"), \
TOKEN_KIND(Token_down_cast, "down_cast"), \
TOKEN_KIND(Token_union_cast, "union_cast"), \
TOKEN_KIND(Token_context, "context"), \ TOKEN_KIND(Token_context, "context"), \
TOKEN_KIND(Token_push_context, "push_context"), \ TOKEN_KIND(Token_push_context, "push_context"), \
TOKEN_KIND(Token_push_allocator, "push_allocator"), \ TOKEN_KIND(Token_push_allocator, "push_allocator"), \
TOKEN_KIND(Token_asm, "asm"), \ TOKEN_KIND(Token_asm, "asm"), \
TOKEN_KIND(Token_yield, "yield"), \
TOKEN_KIND(Token_await, "await"), \
TOKEN_KIND(Token_atomic, "atomic"), \
TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \ TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
TOKEN_KIND(Token_Count, "") TOKEN_KIND(Token_Count, "")
@@ -168,8 +169,8 @@ Token make_token_ident(String s) {
typedef struct ErrorCollector { typedef struct ErrorCollector {
TokenPos prev; TokenPos prev;
i64 count; i64 count;
i64 warning_count; i64 warning_count;
gbMutex mutex; gbMutex mutex;
} ErrorCollector; } ErrorCollector;
@@ -414,7 +415,7 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
TokenizerInitError err = TokenizerInit_None; TokenizerInitError err = TokenizerInit_None;
char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1); char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1);
memcpy(c_str, fullpath.text, fullpath.len); gb_memcopy(c_str, fullpath.text, fullpath.len);
c_str[fullpath.len] = '\0'; c_str[fullpath.len] = '\0';
// TODO(bill): Memory map rather than copy contents // TODO(bill): Memory map rather than copy contents
@@ -484,15 +485,9 @@ gb_inline i32 digit_value(Rune r) {
return 16; // NOTE(bill): Larger than highest possible return 16; // NOTE(bill): Larger than highest possible
} }
gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) { gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
if (allow_underscore) { while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') { advance_to_next_rune(t);
advance_to_next_rune(t);
}
} else {
while (digit_value(t->curr_rune) < base) {
advance_to_next_rune(t);
}
} }
} }
@@ -506,7 +501,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
if (seen_decimal_point) { if (seen_decimal_point) {
token.kind = Token_Float; token.kind = Token_Float;
scan_mantissa(t, 10, true); scan_mantissa(t, 10);
goto exponent; goto exponent;
} }
@@ -515,31 +510,37 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
advance_to_next_rune(t); advance_to_next_rune(t);
if (t->curr_rune == 'b') { // Binary if (t->curr_rune == 'b') { // Binary
advance_to_next_rune(t); advance_to_next_rune(t);
scan_mantissa(t, 2, true); scan_mantissa(t, 2);
if (t->curr - prev <= 2) { if (t->curr - prev <= 2) {
token.kind = Token_Invalid; token.kind = Token_Invalid;
} }
} else if (t->curr_rune == 'o') { // Octal } else if (t->curr_rune == 'o') { // Octal
advance_to_next_rune(t); advance_to_next_rune(t);
scan_mantissa(t, 8, true); scan_mantissa(t, 8);
if (t->curr - prev <= 2) { if (t->curr - prev <= 2) {
token.kind = Token_Invalid; token.kind = Token_Invalid;
} }
} else if (t->curr_rune == 'd') { // Decimal } else if (t->curr_rune == 'd') { // Decimal
advance_to_next_rune(t); advance_to_next_rune(t);
scan_mantissa(t, 10, true); scan_mantissa(t, 10);
if (t->curr - prev <= 2) {
token.kind = Token_Invalid;
}
} else if (t->curr_rune == 'z') { // Dozenal
advance_to_next_rune(t);
scan_mantissa(t, 12);
if (t->curr - prev <= 2) { if (t->curr - prev <= 2) {
token.kind = Token_Invalid; token.kind = Token_Invalid;
} }
} else if (t->curr_rune == 'x') { // Hexadecimal } else if (t->curr_rune == 'x') { // Hexadecimal
advance_to_next_rune(t); advance_to_next_rune(t);
scan_mantissa(t, 16, true); scan_mantissa(t, 16);
if (t->curr - prev <= 2) { if (t->curr - prev <= 2) {
token.kind = Token_Invalid; token.kind = Token_Invalid;
} }
} else { } else {
seen_decimal_point = false; seen_decimal_point = false;
scan_mantissa(t, 10, true); scan_mantissa(t, 10);
if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') { if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
seen_decimal_point = true; seen_decimal_point = true;
@@ -547,24 +548,24 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
} }
} }
token.string.len = t->curr - token.string.text; goto end;
return token;
} }
scan_mantissa(t, 10, true); scan_mantissa(t, 10);
fraction: fraction:
if (t->curr_rune == '.') { if (t->curr_rune == '.') {
// HACK(bill): This may be inefficient // HACK(bill): This may be inefficient
TokenizerState state = save_tokenizer_state(t); TokenizerState state = save_tokenizer_state(t);
advance_to_next_rune(t); advance_to_next_rune(t);
if (digit_value(t->curr_rune) >= 10) { if (t->curr_rune == '.') {
// TODO(bill): Clean up this shit // TODO(bill): Clean up this shit
restore_tokenizer_state(t, &state); restore_tokenizer_state(t, &state);
goto end; goto end;
} }
token.kind = Token_Float; token.kind = Token_Float;
scan_mantissa(t, 10, true); scan_mantissa(t, 10);
} }
exponent: exponent:
@@ -574,7 +575,14 @@ exponent:
if (t->curr_rune == '-' || t->curr_rune == '+') { if (t->curr_rune == '-' || t->curr_rune == '+') {
advance_to_next_rune(t); advance_to_next_rune(t);
} }
scan_mantissa(t, 10, false); scan_mantissa(t, 10);
}
switch (t->curr_rune) {
case 'i': case 'j': case 'k':
token.kind = Token_Imag;
advance_to_next_rune(t);
break;
} }
end: end:
@@ -611,20 +619,22 @@ bool scan_escape(Tokenizer *t, Rune quote) {
advance_to_next_rune(t); advance_to_next_rune(t);
len = 8; base = 16; max = GB_RUNE_MAX; len = 8; base = 16; max = GB_RUNE_MAX;
} else { } else {
if (t->curr_rune < 0) if (t->curr_rune < 0) {
tokenizer_err(t, "Escape sequence was not terminated"); tokenizer_err(t, "Escape sequence was not terminated");
else } else {
tokenizer_err(t, "Unknown escape sequence"); tokenizer_err(t, "Unknown escape sequence");
}
return false; return false;
} }
while (len --> 0) { while (len --> 0) {
u32 d = cast(u32)digit_value(t->curr_rune); u32 d = cast(u32)digit_value(t->curr_rune);
if (d >= base) { if (d >= base) {
if (t->curr_rune < 0) if (t->curr_rune < 0) {
tokenizer_err(t, "Escape sequence was not terminated"); tokenizer_err(t, "Escape sequence was not terminated");
else } else {
tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune); tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune);
}
return false; return false;
} }
@@ -859,27 +869,28 @@ Token tokenizer_get_token(Tokenizer *t) {
if (t->curr_rune == '.') { // Could be an ellipsis if (t->curr_rune == '.') { // Could be an ellipsis
advance_to_next_rune(t); advance_to_next_rune(t);
token.kind = Token_Ellipsis; token.kind = Token_Ellipsis;
// if (t->curr_rune == '<') { if (t->curr_rune == '<') {
// advance_to_next_rune(t); advance_to_next_rune(t);
// token.kind = Token_HalfOpenRange; token.kind = Token_HalfClosed;
// } }
} }
break; break;
case '#': token.kind = Token_Hash; break; case '#': token.kind = Token_Hash; break;
case '@': token.kind = Token_At; break; case '@': token.kind = Token_At; break;
case '$': token.kind = Token_Dollar; break; case '$': token.kind = Token_Dollar; break;
case '?': token.kind = Token_Question; break; case '?': token.kind = Token_Question; break;
case '^': token.kind = Token_Pointer; break; case '^': token.kind = Token_Pointer; break;
case ';': token.kind = Token_Semicolon; break; case ';': token.kind = Token_Semicolon; break;
case ',': token.kind = Token_Comma; break; case ',': token.kind = Token_Comma; break;
case ':': token.kind = Token_Colon; break; case ':': token.kind = Token_Colon; break;
case '(': token.kind = Token_OpenParen; break; case '(': token.kind = Token_OpenParen; break;
case ')': token.kind = Token_CloseParen; break; case ')': token.kind = Token_CloseParen; break;
case '[': token.kind = Token_OpenBracket; break; case '[': token.kind = Token_OpenBracket; break;
case ']': token.kind = Token_CloseBracket; break; case ']': token.kind = Token_CloseBracket; break;
case '{': token.kind = Token_OpenBrace; break; case '{': token.kind = Token_OpenBrace; break;
case '}': token.kind = Token_CloseBrace; break; case '}': token.kind = Token_CloseBrace; break;
case '\\': token.kind = Token_BackSlash; break;
case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break; case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break;
case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break; case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break;
+308 -258
View File
@@ -12,25 +12,15 @@ typedef enum BasicKind {
Basic_i64, Basic_i64,
Basic_u64, Basic_u64,
/* Basic_i16le,
Basic_i16be,
Basic_u16le,
Basic_u16be,
Basic_i32le,
Basic_i32be,
Basic_u32le,
Basic_u32be,
Basic_i64le,
Basic_i64be,
Basic_u64le,
Basic_u64be, */
// Basic_i128,
// Basic_u128,
// Basic_f16,
Basic_f32, Basic_f32,
Basic_f64, Basic_f64,
// Basic_f128,
Basic_complex64,
Basic_complex128,
Basic_quaternion128,
Basic_quaternion256,
Basic_int, Basic_int,
Basic_uint, Basic_uint,
Basic_rawptr, Basic_rawptr,
@@ -40,6 +30,8 @@ typedef enum BasicKind {
Basic_UntypedBool, Basic_UntypedBool,
Basic_UntypedInteger, Basic_UntypedInteger,
Basic_UntypedFloat, Basic_UntypedFloat,
Basic_UntypedComplex,
Basic_UntypedQuaternion,
Basic_UntypedString, Basic_UntypedString,
Basic_UntypedRune, Basic_UntypedRune,
Basic_UntypedNil, Basic_UntypedNil,
@@ -51,17 +43,19 @@ typedef enum BasicKind {
} BasicKind; } BasicKind;
typedef enum BasicFlag { typedef enum BasicFlag {
BasicFlag_Boolean = GB_BIT(0), BasicFlag_Boolean = GB_BIT(0),
BasicFlag_Integer = GB_BIT(1), BasicFlag_Integer = GB_BIT(1),
BasicFlag_Unsigned = GB_BIT(2), BasicFlag_Unsigned = GB_BIT(2),
BasicFlag_Float = GB_BIT(3), BasicFlag_Float = GB_BIT(3),
BasicFlag_Pointer = GB_BIT(4), BasicFlag_Complex = GB_BIT(4),
BasicFlag_String = GB_BIT(5), BasicFlag_Quaternion = GB_BIT(5),
BasicFlag_Rune = GB_BIT(6), BasicFlag_Pointer = GB_BIT(6),
BasicFlag_Untyped = GB_BIT(7), BasicFlag_String = GB_BIT(7),
BasicFlag_Rune = GB_BIT(8),
BasicFlag_Untyped = GB_BIT(9),
BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float, BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex | BasicFlag_Quaternion,
BasicFlag_Ordered = BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer, BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer,
BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_String | BasicFlag_Rune, BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_String | BasicFlag_Rune,
} BasicFlag; } BasicFlag;
@@ -116,6 +110,7 @@ typedef struct TypeRecord {
#define TYPE_KINDS \ #define TYPE_KINDS \
TYPE_KIND(Basic, BasicType) \ TYPE_KIND(Basic, BasicType) \
TYPE_KIND(Pointer, struct { Type *elem; }) \ TYPE_KIND(Pointer, struct { Type *elem; }) \
TYPE_KIND(Atomic, struct { Type *elem; }) \
TYPE_KIND(Array, struct { Type *elem; i64 count; }) \ TYPE_KIND(Array, struct { Type *elem; i64 count; }) \
TYPE_KIND(DynamicArray, struct { Type *elem; }) \ TYPE_KIND(DynamicArray, struct { Type *elem; }) \
TYPE_KIND(Vector, struct { Type *elem; i64 count; }) \ TYPE_KIND(Vector, struct { Type *elem; i64 count; }) \
@@ -138,7 +133,10 @@ typedef struct TypeRecord {
Type * results; /* Type_Tuple */ \ Type * results; /* Type_Tuple */ \
i32 param_count; \ i32 param_count; \
i32 result_count; \ i32 result_count; \
Type **abi_compat_params; \
Type **abi_compat_results; \
bool variadic; \ bool variadic; \
bool require_results; \
ProcCallingConvention calling_convention; \ ProcCallingConvention calling_convention; \
}) \ }) \
TYPE_KIND(Map, struct { \ TYPE_KIND(Map, struct { \
@@ -209,33 +207,43 @@ void selection_add_index(Selection *s, isize index) {
gb_global Type basic_types[] = { gb_global Type basic_types[] = {
{Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}}, {Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}},
{Type_Basic, {Basic_bool, BasicFlag_Boolean, 1, STR_LIT("bool")}},
{Type_Basic, {Basic_i8, BasicFlag_Integer, 1, STR_LIT("i8")}}, {Type_Basic, {Basic_bool, BasicFlag_Boolean, 1, STR_LIT("bool")}},
{Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("u8")}},
{Type_Basic, {Basic_i16, BasicFlag_Integer, 2, STR_LIT("i16")}}, {Type_Basic, {Basic_i8, BasicFlag_Integer, 1, STR_LIT("i8")}},
{Type_Basic, {Basic_u16, BasicFlag_Integer | BasicFlag_Unsigned, 2, STR_LIT("u16")}}, {Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("u8")}},
{Type_Basic, {Basic_i32, BasicFlag_Integer, 4, STR_LIT("i32")}}, {Type_Basic, {Basic_i16, BasicFlag_Integer, 2, STR_LIT("i16")}},
{Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}}, {Type_Basic, {Basic_u16, BasicFlag_Integer | BasicFlag_Unsigned, 2, STR_LIT("u16")}},
{Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}}, {Type_Basic, {Basic_i32, BasicFlag_Integer, 4, STR_LIT("i32")}},
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}}, {Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}},
// {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}}, {Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}},
// {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}}, {Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}},
// {Type_Basic, {Basic_f16, BasicFlag_Float, 2, STR_LIT("f16")}},
{Type_Basic, {Basic_f32, BasicFlag_Float, 4, STR_LIT("f32")}}, {Type_Basic, {Basic_f32, BasicFlag_Float, 4, STR_LIT("f32")}},
{Type_Basic, {Basic_f64, BasicFlag_Float, 8, STR_LIT("f64")}}, {Type_Basic, {Basic_f64, BasicFlag_Float, 8, STR_LIT("f64")}},
// {Type_Basic, {Basic_f128, BasicFlag_Float, 16, STR_LIT("f128")}},
{Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}}, {Type_Basic, {Basic_complex64, BasicFlag_Complex, 8, STR_LIT("complex64")}},
{Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}}, {Type_Basic, {Basic_complex128, BasicFlag_Complex, 16, STR_LIT("complex128")}},
{Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
{Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}}, {Type_Basic, {Basic_quaternion128, BasicFlag_Quaternion, 16, STR_LIT("quaternion128")}},
{Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}}, {Type_Basic, {Basic_quaternion256, BasicFlag_Quaternion, 32, STR_LIT("quaternion256")}},
{Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}},
{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}}, {Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}},
{Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}}, {Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}},
{Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}},
{Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}}, {Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
{Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}}, {Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}},
{Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}},
{Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}},
{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}},
{Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}},
{Type_Basic, {Basic_UntypedComplex, BasicFlag_Complex | BasicFlag_Untyped, 0, STR_LIT("untyped complex")}},
{Type_Basic, {Basic_UntypedQuaternion, BasicFlag_Quaternion | BasicFlag_Untyped, 0, STR_LIT("untyped quaternion")}},
{Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}},
{Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}},
{Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}},
}; };
gb_global Type basic_type_aliases[] = { gb_global Type basic_type_aliases[] = {
@@ -253,25 +261,33 @@ gb_global Type *t_i32 = &basic_types[Basic_i32];
gb_global Type *t_u32 = &basic_types[Basic_u32]; gb_global Type *t_u32 = &basic_types[Basic_u32];
gb_global Type *t_i64 = &basic_types[Basic_i64]; gb_global Type *t_i64 = &basic_types[Basic_i64];
gb_global Type *t_u64 = &basic_types[Basic_u64]; gb_global Type *t_u64 = &basic_types[Basic_u64];
// gb_global Type *t_i128 = &basic_types[Basic_i128];
// gb_global Type *t_u128 = &basic_types[Basic_u128];
// gb_global Type *t_f16 = &basic_types[Basic_f16];
gb_global Type *t_f32 = &basic_types[Basic_f32]; gb_global Type *t_f32 = &basic_types[Basic_f32];
gb_global Type *t_f64 = &basic_types[Basic_f64]; gb_global Type *t_f64 = &basic_types[Basic_f64];
// gb_global Type *t_f128 = &basic_types[Basic_f128];
gb_global Type *t_complex64 = &basic_types[Basic_complex64];
gb_global Type *t_complex128 = &basic_types[Basic_complex128];
gb_global Type *t_quaternion128 = &basic_types[Basic_quaternion128];
gb_global Type *t_quaternion256 = &basic_types[Basic_quaternion256];
gb_global Type *t_int = &basic_types[Basic_int]; gb_global Type *t_int = &basic_types[Basic_int];
gb_global Type *t_uint = &basic_types[Basic_uint]; gb_global Type *t_uint = &basic_types[Basic_uint];
gb_global Type *t_rawptr = &basic_types[Basic_rawptr]; gb_global Type *t_rawptr = &basic_types[Basic_rawptr];
gb_global Type *t_string = &basic_types[Basic_string]; gb_global Type *t_string = &basic_types[Basic_string];
gb_global Type *t_any = &basic_types[Basic_any]; gb_global Type *t_any = &basic_types[Basic_any];
gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool];
gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger]; gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool];
gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat]; gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger];
gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString]; gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat];
gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune]; gb_global Type *t_untyped_complex = &basic_types[Basic_UntypedComplex];
gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil]; gb_global Type *t_untyped_quaternion = &basic_types[Basic_UntypedQuaternion];
gb_global Type *t_byte = &basic_type_aliases[0]; gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString];
gb_global Type *t_rune = &basic_type_aliases[1]; gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune];
gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil];
gb_global Type *t_byte = &basic_type_aliases[0];
gb_global Type *t_rune = &basic_type_aliases[1];
gb_global Type *t_u8_ptr = NULL; gb_global Type *t_u8_ptr = NULL;
@@ -293,10 +309,13 @@ gb_global Type *t_type_info_enum_value_ptr = NULL;
gb_global Type *t_type_info_named = NULL; gb_global Type *t_type_info_named = NULL;
gb_global Type *t_type_info_integer = NULL; gb_global Type *t_type_info_integer = NULL;
gb_global Type *t_type_info_float = NULL; gb_global Type *t_type_info_float = NULL;
gb_global Type *t_type_info_complex = NULL;
gb_global Type *t_type_info_quaternion = NULL;
gb_global Type *t_type_info_any = NULL; gb_global Type *t_type_info_any = NULL;
gb_global Type *t_type_info_string = NULL; gb_global Type *t_type_info_string = NULL;
gb_global Type *t_type_info_boolean = NULL; gb_global Type *t_type_info_boolean = NULL;
gb_global Type *t_type_info_pointer = NULL; gb_global Type *t_type_info_pointer = NULL;
gb_global Type *t_type_info_atomic = NULL;
gb_global Type *t_type_info_procedure = NULL; gb_global Type *t_type_info_procedure = NULL;
gb_global Type *t_type_info_array = NULL; gb_global Type *t_type_info_array = NULL;
gb_global Type *t_type_info_dynamic_array = NULL; gb_global Type *t_type_info_dynamic_array = NULL;
@@ -312,10 +331,13 @@ gb_global Type *t_type_info_map = NULL;
gb_global Type *t_type_info_named_ptr = NULL; gb_global Type *t_type_info_named_ptr = NULL;
gb_global Type *t_type_info_integer_ptr = NULL; gb_global Type *t_type_info_integer_ptr = NULL;
gb_global Type *t_type_info_float_ptr = NULL; gb_global Type *t_type_info_float_ptr = NULL;
gb_global Type *t_type_info_complex_ptr = NULL;
gb_global Type *t_type_info_quaternion_ptr = NULL;
gb_global Type *t_type_info_any_ptr = NULL; gb_global Type *t_type_info_any_ptr = NULL;
gb_global Type *t_type_info_string_ptr = NULL; gb_global Type *t_type_info_string_ptr = NULL;
gb_global Type *t_type_info_boolean_ptr = NULL; gb_global Type *t_type_info_boolean_ptr = NULL;
gb_global Type *t_type_info_pointer_ptr = NULL; gb_global Type *t_type_info_pointer_ptr = NULL;
gb_global Type *t_type_info_atomic_ptr = NULL;
gb_global Type *t_type_info_procedure_ptr = NULL; gb_global Type *t_type_info_procedure_ptr = NULL;
gb_global Type *t_type_info_array_ptr = NULL; gb_global Type *t_type_info_array_ptr = NULL;
gb_global Type *t_type_info_dynamic_array_ptr = NULL; gb_global Type *t_type_info_dynamic_array_ptr = NULL;
@@ -333,8 +355,6 @@ gb_global Type *t_allocator_ptr = NULL;
gb_global Type *t_context = NULL; gb_global Type *t_context = NULL;
gb_global Type *t_context_ptr = NULL; gb_global Type *t_context_ptr = NULL;
gb_global Type *t_raw_dynamic_array = NULL;
gb_global Type *t_raw_dynamic_array_ptr = NULL;
gb_global Type *t_map_key = NULL; gb_global Type *t_map_key = NULL;
gb_global Type *t_map_header = NULL; gb_global Type *t_map_header = NULL;
@@ -375,6 +395,34 @@ Type *base_enum_type(Type *t) {
return t; return t;
} }
Type *core_type(Type *t) {
for (;;) {
if (t == NULL) {
break;
}
switch (t->kind) {
case Type_Named:
if (t == t->Named.base) {
return t_invalid;
}
t = t->Named.base;
continue;
case Type_Record:
if (t->Record.kind == TypeRecord_Enum) {
t = t->Record.enum_base_type;
continue;
}
break;
case Type_Atomic:
t = t->Atomic.elem;
continue;
}
break;
}
return t;
}
void set_base_type(Type *t, Type *base) { void set_base_type(Type *t, Type *base) {
if (t && t->kind == Type_Named) { if (t && t->kind == Type_Named) {
t->Named.base = base; t->Named.base = base;
@@ -384,6 +432,7 @@ void set_base_type(Type *t, Type *base) {
Type *alloc_type(gbAllocator a, TypeKind kind) { Type *alloc_type(gbAllocator a, TypeKind kind) {
Type *t = gb_alloc_item(a, Type); Type *t = gb_alloc_item(a, Type);
gb_zero_item(t);
t->kind = kind; t->kind = kind;
return t; return t;
} }
@@ -401,6 +450,12 @@ Type *make_type_pointer(gbAllocator a, Type *elem) {
return t; return t;
} }
Type *make_type_atomic(gbAllocator a, Type *elem) {
Type *t = alloc_type(a, Type_Atomic);
t->Atomic.elem = elem;
return t;
}
Type *make_type_array(gbAllocator a, Type *elem, i64 count) { Type *make_type_array(gbAllocator a, Type *elem, i64 count) {
Type *t = alloc_type(a, Type_Array); Type *t = alloc_type(a, Type_Array);
t->Array.elem = elem; t->Array.elem = elem;
@@ -510,8 +565,6 @@ Type *make_type_map(gbAllocator a, i64 count, Type *key, Type *value) {
Type *type_deref(Type *t) { Type *type_deref(Type *t) {
if (t != NULL) { if (t != NULL) {
Type *bt = base_type(t); Type *bt = base_type(t);
@@ -529,29 +582,43 @@ bool is_type_named(Type *t) {
} }
return t->kind == Type_Named; return t->kind == Type_Named;
} }
bool is_type_named_alias(Type *t) {
if (!is_type_named(t)) {
return false;
}
Entity *e = t->Named.type_name;
if (e == NULL) {
return false;
}
if (e->kind != Entity_TypeName) {
return false;
}
return e->TypeName.is_type_alias;
}
bool is_type_boolean(Type *t) { bool is_type_boolean(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Boolean) != 0; return (t->Basic.flags & BasicFlag_Boolean) != 0;
} }
return false; return false;
} }
bool is_type_integer(Type *t) { bool is_type_integer(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Integer) != 0; return (t->Basic.flags & BasicFlag_Integer) != 0;
} }
return false; return false;
} }
bool is_type_unsigned(Type *t) { bool is_type_unsigned(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Unsigned) != 0; return (t->Basic.flags & BasicFlag_Unsigned) != 0;
} }
return false; return false;
} }
bool is_type_numeric(Type *t) { bool is_type_numeric(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Numeric) != 0; return (t->Basic.flags & BasicFlag_Numeric) != 0;
} }
@@ -586,7 +653,7 @@ bool is_type_untyped(Type *t) {
return false; return false;
} }
bool is_type_ordered(Type *t) { bool is_type_ordered(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
switch (t->kind) { switch (t->kind) {
case Type_Basic: case Type_Basic:
return (t->Basic.flags & BasicFlag_Ordered) != 0; return (t->Basic.flags & BasicFlag_Ordered) != 0;
@@ -598,28 +665,42 @@ bool is_type_ordered(Type *t) {
return false; return false;
} }
bool is_type_constant_type(Type *t) { bool is_type_constant_type(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_ConstantType) != 0; return (t->Basic.flags & BasicFlag_ConstantType) != 0;
} }
return false; return false;
} }
bool is_type_float(Type *t) { bool is_type_float(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Float) != 0; return (t->Basic.flags & BasicFlag_Float) != 0;
} }
return false; return false;
} }
bool is_type_complex(Type *t) {
t = core_type(t);
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Complex) != 0;
}
return false;
}
bool is_type_quaternion(Type *t) {
t = core_type(t);
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Quaternion) != 0;
}
return false;
}
bool is_type_f32(Type *t) { bool is_type_f32(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return t->Basic.kind == Basic_f32; return t->Basic.kind == Basic_f32;
} }
return false; return false;
} }
bool is_type_f64(Type *t) { bool is_type_f64(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
if (t->kind == Type_Basic) { if (t->kind == Type_Basic) {
return t->Basic.kind == Basic_f64; return t->Basic.kind == Basic_f64;
} }
@@ -632,6 +713,10 @@ bool is_type_pointer(Type *t) {
} }
return t->kind == Type_Pointer; return t->kind == Type_Pointer;
} }
bool is_type_atomic(Type *t) {
t = base_type(t);
return t->kind == Type_Atomic;
}
bool is_type_tuple(Type *t) { bool is_type_tuple(Type *t) {
t = base_type(t); t = base_type(t);
return t->kind == Type_Tuple; return t->kind == Type_Tuple;
@@ -691,6 +776,31 @@ Type *base_vector_type(Type *t) {
return t; return t;
} }
Type *base_complex_elem_type(Type *t) {
t = core_type(t);
if (is_type_complex(t)) {
switch (t->Basic.kind) {
case Basic_complex64: return t_f32;
case Basic_complex128: return t_f64;
case Basic_UntypedComplex: return t_untyped_float;
}
}
GB_PANIC("Invalid complex type");
return t_invalid;
}
Type *base_quaternion_elem_type(Type *t) {
t = core_type(t);
if (is_type_quaternion(t)) {
switch (t->Basic.kind) {
case Basic_quaternion128: return t_f32;
case Basic_quaternion256: return t_f64;
case Basic_UntypedQuaternion: return t_untyped_float;
}
}
GB_PANIC("Invalid quaternion type");
return t_invalid;
}
bool is_type_struct(Type *t) { bool is_type_struct(Type *t) {
t = base_type(t); t = base_type(t);
@@ -737,7 +847,7 @@ bool is_type_untyped_nil(Type *t) {
bool is_type_valid_for_keys(Type *t) { bool is_type_valid_for_keys(Type *t) {
t = base_type(base_enum_type(t)); t = core_type(t);
if (is_type_untyped(t)) { if (is_type_untyped(t)) {
return false; return false;
} }
@@ -775,10 +885,17 @@ bool type_has_nil(Type *t) {
return false; return false;
} break; } break;
case Type_Slice: case Type_Slice:
case Type_DynamicArray:
case Type_Proc: case Type_Proc:
case Type_Pointer: case Type_Pointer:
case Type_DynamicArray:
case Type_Map:
return true; return true;
case Type_Record:
switch (t->Record.kind) {
case TypeRecord_Union:
return true;
}
return false;
} }
return false; return false;
} }
@@ -798,7 +915,7 @@ bool is_type_comparable(Type *t) {
return true; return true;
case Type_Record: { case Type_Record: {
if (is_type_enum(t)) { if (is_type_enum(t)) {
return is_type_comparable(base_enum_type(t)); return is_type_comparable(core_type(t));
} }
return false; return false;
} break; } break;
@@ -867,13 +984,21 @@ bool are_types_identical(Type *x, Type *y) {
x->Record.custom_align == y->Record.custom_align) { x->Record.custom_align == y->Record.custom_align) {
// TODO(bill); Fix the custom alignment rule // TODO(bill); Fix the custom alignment rule
for (isize i = 0; i < x->Record.field_count; i++) { for (isize i = 0; i < x->Record.field_count; i++) {
if (!are_types_identical(x->Record.fields[i]->type, y->Record.fields[i]->type)) { Entity *xf = x->Record.fields[i];
Entity *yf = y->Record.fields[i];
if (!are_types_identical(xf->type, yf->type)) {
return false; return false;
} }
if (str_ne(x->Record.fields[i]->token.string, y->Record.fields[i]->token.string)) { if (str_ne(xf->token.string, yf->token.string)) {
return false;
}
bool xf_is_using = (xf->flags&EntityFlag_Using) != 0;
bool yf_is_using = (yf->flags&EntityFlag_Using) != 0;
if (xf_is_using ^ yf_is_using) {
return false; return false;
} }
} }
// NOTE(bill): zeroth variant is NULL
for (isize i = 1; i < x->Record.variant_count; i++) { for (isize i = 1; i < x->Record.variant_count; i++) {
if (!are_types_identical(x->Record.variants[i]->type, y->Record.variants[i]->type)) { if (!are_types_identical(x->Record.variants[i]->type, y->Record.variants[i]->type)) {
return false; return false;
@@ -946,11 +1071,13 @@ Type *default_type(Type *type) {
} }
if (type->kind == Type_Basic) { if (type->kind == Type_Basic) {
switch (type->Basic.kind) { switch (type->Basic.kind) {
case Basic_UntypedBool: return t_bool; case Basic_UntypedBool: return t_bool;
case Basic_UntypedInteger: return t_int; case Basic_UntypedInteger: return t_int;
case Basic_UntypedFloat: return t_f64; case Basic_UntypedFloat: return t_f64;
case Basic_UntypedString: return t_string; case Basic_UntypedComplex: return t_complex128;
case Basic_UntypedRune: return t_rune; case Basic_UntypedQuaternion: return t_quaternion256;
case Basic_UntypedString: return t_string;
case Basic_UntypedRune: return t_rune;
} }
} }
return type; return type;
@@ -1031,10 +1158,13 @@ typedef enum ProcTypeOverloadKind {
} ProcTypeOverloadKind; } ProcTypeOverloadKind;
ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) { ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
if (!is_type_proc(x)) return ProcOverload_NotProcedure; if (x == NULL && y == NULL) return ProcOverload_NotProcedure;
if (!is_type_proc(y)) return ProcOverload_NotProcedure; if (x == NULL && y != NULL) return ProcOverload_NotProcedure;
if (x != NULL && y == NULL) return ProcOverload_NotProcedure;
if (!is_type_proc(x)) return ProcOverload_NotProcedure;
if (!is_type_proc(y)) return ProcOverload_NotProcedure;
TypeProc px = base_type(x)->Proc; TypeProc px = base_type(x)->Proc;
TypeProc py = base_type(y)->Proc; TypeProc py = base_type(y)->Proc;
@@ -1070,12 +1200,11 @@ ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
} }
} }
{ if (px.params != NULL && py.params != NULL) {
Entity *ex = px.params->Tuple.variables[0]; Entity *ex = px.params->Tuple.variables[0];
Entity *ey = py.params->Tuple.variables[0]; Entity *ey = py.params->Tuple.variables[0];
bool ok = are_types_identical(ex->type, ey->type); bool ok = are_types_identical(ex->type, ey->type);
if (ok) { if (ok) {
gb_printf_err("Here\n");
} }
} }
@@ -1085,20 +1214,6 @@ ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
gb_global Entity *entity__any_type_info = NULL;
gb_global Entity *entity__any_data = NULL;
gb_global Entity *entity__string_data = NULL;
gb_global Entity *entity__string_count = NULL;
gb_global Entity *entity__slice_count = NULL;
gb_global Entity *entity__slice_capacity = NULL;
gb_global Entity *entity__dynamic_array_count = NULL;
gb_global Entity *entity__dynamic_array_capacity = NULL;
gb_global Entity *entity__dynamic_array_allocator = NULL;
gb_global Entity *entity__dynamic_map_count = NULL;
gb_global Entity *entity__dynamic_map_capacity = NULL;
gb_global Entity *entity__dynamic_map_allocator = NULL;
Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel); Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel);
@@ -1151,6 +1266,10 @@ Selection lookup_field_from_index(gbAllocator a, Type *type, i64 index) {
return empty_selection; return empty_selection;
} }
gb_global Entity *entity__any_data = NULL;
gb_global Entity *entity__any_type_info = NULL;
Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel) { Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel) {
GB_ASSERT(type_ != NULL); GB_ASSERT(type_ != NULL);
@@ -1167,66 +1286,33 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
if (type->kind == Type_Basic) { if (type->kind == Type_Basic) {
switch (type->Basic.kind) { switch (type->Basic.kind) {
case Basic_any: { case Basic_any: {
#if 1
// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
// `Raw_Any` type?
String data_str = str_lit("data");
String type_info_str = str_lit("type_info"); String type_info_str = str_lit("type_info");
String data_str = str_lit("data");
if (entity__any_type_info == NULL) {
entity__any_type_info = make_entity_field(a, NULL, make_token_ident(type_info_str), t_type_info_ptr, false, 0);
}
if (entity__any_data == NULL) { if (entity__any_data == NULL) {
entity__any_data = make_entity_field(a, NULL, make_token_ident(data_str), t_rawptr, false, 1); entity__any_data = make_entity_field(a, NULL, make_token_ident(data_str), t_rawptr, false, 0);
} }
if (entity__any_type_info == NULL) {
if (str_eq(field_name, type_info_str)) { entity__any_type_info = make_entity_field(a, NULL, make_token_ident(type_info_str), t_type_info_ptr, false, 1);
selection_add_index(&sel, 0);
sel.entity = entity__any_type_info;
return sel;
} else if (str_eq(field_name, data_str)) {
selection_add_index(&sel, 1);
sel.entity = entity__any_data;
return sel;
}
} break;
case Basic_string: {
String data_str = str_lit("data");
String count_str = str_lit("count");
if (entity__string_data == NULL) {
entity__string_data = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, t_u8), false, 0);
}
if (entity__string_count == NULL) {
entity__string_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
} }
if (str_eq(field_name, data_str)) { if (str_eq(field_name, data_str)) {
selection_add_index(&sel, 0); selection_add_index(&sel, 0);
sel.entity = entity__string_data; sel.entity = entity__any_data;;
return sel; return sel;
} else if (str_eq(field_name, count_str)) { } else if (str_eq(field_name, type_info_str)) {
selection_add_index(&sel, 1); selection_add_index(&sel, 1);
sel.entity = entity__string_count; sel.entity = entity__any_type_info;
return sel; return sel;
} }
#endif
} break; } break;
} }
return sel; return sel;
} else if (type->kind == Type_Array) {
String count_str = str_lit("count");
// NOTE(bill): Underlying memory address cannot be changed
if (str_eq(field_name, count_str)) {
// HACK(bill): Memory leak
sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Array.count));
return sel;
}
} else if (type->kind == Type_Vector) { } else if (type->kind == Type_Vector) {
String count_str = str_lit("count");
// NOTE(bill): Vectors are not addressable
if (str_eq(field_name, count_str)) {
// HACK(bill): Memory leak
sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, make_exact_value_integer(type->Vector.count));
return sel;
}
if (type->Vector.count <= 4 && !is_type_boolean(type->Vector.elem)) { if (type->Vector.count <= 4 && !is_type_boolean(type->Vector.elem)) {
// HACK(bill): Memory leak // HACK(bill): Memory leak
switch (type->Vector.count) { switch (type->Vector.count) {
@@ -1248,98 +1334,6 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
#undef _VECTOR_FIELD_CASE #undef _VECTOR_FIELD_CASE
} }
} }
} else if (type->kind == Type_Slice) {
String data_str = str_lit("data");
String count_str = str_lit("count");
String capacity_str = str_lit("capacity");
if (str_eq(field_name, data_str)) {
selection_add_index(&sel, 0);
// HACK(bill): Memory leak
sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->Slice.elem), false, 0);
return sel;
} else if (str_eq(field_name, count_str)) {
selection_add_index(&sel, 1);
if (entity__slice_count == NULL) {
entity__slice_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
}
sel.entity = entity__slice_count;
return sel;
} else if (str_eq(field_name, capacity_str)) {
selection_add_index(&sel, 2);
if (entity__slice_capacity == NULL) {
entity__slice_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2);
}
sel.entity = entity__slice_capacity;
return sel;
}
} else if (type->kind == Type_DynamicArray) {
String data_str = str_lit("data");
String count_str = str_lit("count");
String capacity_str = str_lit("capacity");
String allocator_str = str_lit("allocator");
if (str_eq(field_name, data_str)) {
selection_add_index(&sel, 0);
// HACK(bill): Memory leak
sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->DynamicArray.elem), false, 0);
return sel;
} else if (str_eq(field_name, count_str)) {
selection_add_index(&sel, 1);
if (entity__dynamic_array_count == NULL) {
entity__dynamic_array_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
}
sel.entity = entity__dynamic_array_count;
return sel;
} else if (str_eq(field_name, capacity_str)) {
selection_add_index(&sel, 2);
if (entity__dynamic_array_capacity == NULL) {
entity__dynamic_array_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2);
}
sel.entity = entity__dynamic_array_capacity;
return sel;
} else if (str_eq(field_name, allocator_str)) {
selection_add_index(&sel, 3);
if (entity__dynamic_array_allocator == NULL) {
entity__dynamic_array_allocator = make_entity_field(a, NULL, make_token_ident(allocator_str), t_allocator, false, 3);
}
sel.entity = entity__dynamic_array_allocator;
return sel;
}
} else if (type->kind == Type_Map) {
String count_str = str_lit("count");
String capacity_str = str_lit("capacity");
String allocator_str = str_lit("allocator");
if (str_eq(field_name, count_str)) {
selection_add_index(&sel, 0);
if (entity__dynamic_map_count == NULL) {
entity__dynamic_map_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 0);
entity__dynamic_map_count->Variable.is_immutable = true;
}
sel.entity = entity__dynamic_map_count;
return sel;
} else if (str_eq(field_name, capacity_str)) {
selection_add_index(&sel, 1);
if (entity__dynamic_map_capacity == NULL) {
entity__dynamic_map_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 1);
entity__dynamic_map_capacity->Variable.is_immutable = true;
}
sel.entity = entity__dynamic_map_capacity;
return sel;
} else if (str_eq(field_name, allocator_str)) {
selection_add_index(&sel, 2);
if (entity__dynamic_map_allocator == NULL) {
entity__dynamic_map_allocator = make_entity_field(a, NULL, make_token_ident(allocator_str), t_allocator, false, 2);
entity__dynamic_map_allocator->Variable.is_immutable = true;
}
sel.entity = entity__dynamic_map_allocator;
return sel;
}
} }
if (is_type) { if (is_type) {
@@ -1405,7 +1399,7 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
return sel; return sel;
} }
if (f->flags & EntityFlag_Anonymous) { if (f->flags & EntityFlag_Using) {
isize prev_count = sel.index.count; isize prev_count = sel.index.count;
selection_add_index(&sel, i); // HACK(bill): Leaky memory selection_add_index(&sel, i); // HACK(bill): Leaky memory
@@ -1443,7 +1437,7 @@ void type_path_free(TypePath *tp) {
TypePath *type_path_push(TypePath *tp, Type *t) { TypePath *type_path_push(TypePath *tp, Type *t) {
GB_ASSERT(tp != NULL); GB_ASSERT(tp != NULL);
for_array(i, tp->path) { for (isize i = 0; i < tp->path.count; i++) {
if (tp->path.e[i] == t) { if (tp->path.e[i] == t) {
// TODO(bill): // TODO(bill):
GB_ASSERT(is_type_named(t)); GB_ASSERT(is_type_named(t));
@@ -1524,6 +1518,7 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
if (t->failure) { if (t->failure) {
return FAILURE_ALIGNMENT; return FAILURE_ALIGNMENT;
} }
t = base_type(t); t = base_type(t);
switch (t->kind) { switch (t->kind) {
@@ -1535,6 +1530,11 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
case Basic_int: case Basic_uint: case Basic_rawptr: case Basic_int: case Basic_uint: case Basic_rawptr:
return build_context.word_size; return build_context.word_size;
case Basic_complex64: case Basic_complex128:
return type_size_of_internal(allocator, t, path) / 2;
case Basic_quaternion128: case Basic_quaternion256:
return type_size_of_internal(allocator, t, path) / 4;
} }
} break; } break;
@@ -1623,6 +1623,18 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
break; break;
case TypeRecord_Union: { case TypeRecord_Union: {
i64 max = 1; i64 max = 1;
if (t->Record.field_count > 0) {
Type *field_type = t->Record.fields[0]->type;
type_path_push(path, field_type);
i64 align = type_align_of_internal(allocator, field_type, path);
if (path->failure) {
return FAILURE_ALIGNMENT;
}
type_path_pop(path);
if (max < align) {
max = align;
}
}
// NOTE(bill): field zero is null // NOTE(bill): field zero is null
for (isize i = 1; i < t->Record.variant_count; i++) { for (isize i = 1; i < t->Record.variant_count; i++) {
Type *variant = t->Record.variants[i]->type; Type *variant = t->Record.variants[i]->type;
@@ -1675,9 +1687,10 @@ i64 *type_set_offsets_of(gbAllocator allocator, Entity **fields, isize field_cou
} else { } else {
for (isize i = 0; i < field_count; i++) { for (isize i = 0; i < field_count; i++) {
i64 align = type_align_of(allocator, fields[i]->type); i64 align = type_align_of(allocator, fields[i]->type);
i64 size = type_size_of(allocator, fields[i]->type);
curr_offset = align_formula(curr_offset, align); curr_offset = align_formula(curr_offset, align);
offsets[i] = curr_offset; offsets[i] = curr_offset;
curr_offset += type_size_of(allocator, fields[i]->type); curr_offset += size;
} }
} }
return offsets; return offsets;
@@ -1713,8 +1726,18 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
if (t->failure) { if (t->failure) {
return FAILURE_SIZE; return FAILURE_SIZE;
} }
t = base_type(t);
switch (t->kind) { switch (t->kind) {
case Type_Named: {
type_path_push(path, t);
if (path->failure) {
return FAILURE_ALIGNMENT;
}
i64 size = type_size_of_internal(allocator, t->Named.base, path);
type_path_pop(path);
return size;
} break;
case Type_Basic: { case Type_Basic: {
GB_ASSERT(is_type_typed(t)); GB_ASSERT(is_type_typed(t));
BasicKind kind = t->Basic.kind; BasicKind kind = t->Basic.kind;
@@ -1747,7 +1770,8 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
} break; } break;
case Type_DynamicArray: case Type_DynamicArray:
return 3*build_context.word_size + type_size_of(allocator, t_allocator); // data + len + cap + allocator(procedure+data)
return 3*build_context.word_size + 2*build_context.word_size;
case Type_Vector: { case Type_Vector: {
#if 0 #if 0
@@ -1825,15 +1849,35 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
} break; } break;
case TypeRecord_Union: { case TypeRecord_Union: {
i64 count = t->Record.variant_count;
i64 align = type_align_of_internal(allocator, t, path); i64 align = type_align_of_internal(allocator, t, path);
if (path->failure) { if (path->failure) {
return FAILURE_SIZE; return FAILURE_SIZE;
} }
i64 max = 0; i64 max = 0;
isize field_count = t->Record.field_count;
isize variant_count = t->Record.variant_count;
// Check for recursive types
for (isize i = 0; i < field_count; i++) {
i64 size = type_size_of_internal(allocator, t->Record.fields[i]->type, path);
if (path->failure) {
return FAILURE_SIZE;
}
}
// NOTE(bill): Zeroth field is invalid // NOTE(bill): Zeroth field is invalid
for (isize i = 1; i < count; i++) { type_set_offsets(allocator, t);
i64 size = type_size_of_internal(allocator, t->Record.variants[i]->type, path);
if (field_count > 0) {
Type *end_type = t->Record.fields[field_count-1]->type;
i64 end_offset = t->Record.offsets[field_count-1];
i64 end_size = type_size_of_internal(allocator, end_type, path);
max = end_offset + end_size ;
}
for (isize i = 1; i < variant_count; i++) {
Type *variant_type = t->Record.variants[i]->type;
i64 size = type_size_of_internal(allocator, variant_type, path);
if (max < size) { if (max < size) {
max = size; max = size;
} }
@@ -1841,7 +1885,8 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
// NOTE(bill): Align to int // NOTE(bill): Align to int
i64 size = align_formula(max, build_context.word_size); i64 size = align_formula(max, build_context.word_size);
size += type_size_of_internal(allocator, t_int, path); size += type_size_of_internal(allocator, t_int, path);
return align_formula(size, align); size = align_formula(size, align);
return size;
} break; } break;
case TypeRecord_RawUnion: { case TypeRecord_RawUnion: {
@@ -1870,7 +1915,7 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
i64 type_offset_of(gbAllocator allocator, Type *t, i32 index) { i64 type_offset_of(gbAllocator allocator, Type *t, i32 index) {
t = base_type(t); t = base_type(t);
if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) { if (t->kind == Type_Record && (t->Record.kind == TypeRecord_Struct || t->Record.kind == TypeRecord_Union)) {
type_set_offsets(allocator, t); type_set_offsets(allocator, t);
if (gb_is_between(index, 0, t->Record.field_count-1)) { if (gb_is_between(index, 0, t->Record.field_count-1)) {
return t->Record.offsets[index]; return t->Record.offsets[index];
@@ -1975,13 +2020,18 @@ gbString write_type_to_string(gbString str, Type *type) {
str = write_type_to_string(str, type->Pointer.elem); str = write_type_to_string(str, type->Pointer.elem);
break; break;
case Type_Atomic:
str = gb_string_appendc(str, "atomic ");
str = write_type_to_string(str, type->Atomic.elem);
break;
case Type_Array: case Type_Array:
str = gb_string_appendc(str, gb_bprintf("[%lld]", type->Array.count)); str = gb_string_appendc(str, gb_bprintf("[%d]", cast(int)type->Array.count));
str = write_type_to_string(str, type->Array.elem); str = write_type_to_string(str, type->Array.elem);
break; break;
case Type_Vector: case Type_Vector:
str = gb_string_appendc(str, gb_bprintf("[vector %lld]", type->Vector.count)); str = gb_string_appendc(str, gb_bprintf("[vector %d]", cast(int)type->Vector.count));
str = write_type_to_string(str, type->Vector.elem); str = write_type_to_string(str, type->Vector.elem);
break; break;
@@ -2029,7 +2079,7 @@ gbString write_type_to_string(gbString str, Type *type) {
} }
str = gb_string_append_length(str, f->token.string.text, f->token.string.len); str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": "); str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, base_type(f->type)); str = write_type_to_string(str, f->type);
} }
for (isize i = 1; i < type->Record.variant_count; i++) { for (isize i = 1; i < type->Record.variant_count; i++) {
Entity *f = type->Record.variants[i]; Entity *f = type->Record.variants[i];
@@ -2095,7 +2145,7 @@ gbString write_type_to_string(gbString str, Type *type) {
case Type_Map: { case Type_Map: {
str = gb_string_appendc(str, "map["); str = gb_string_appendc(str, "map[");
if (type->Map.count > 0) { if (type->Map.count > 0) {
str = gb_string_appendc(str, gb_bprintf("%lld, ", type->Map.count)); str = gb_string_appendc(str, gb_bprintf("%d, ", cast(int)type->Map.count));
} }
str = write_type_to_string(str, type->Map.key); str = write_type_to_string(str, type->Map.key);
str = gb_string_appendc(str, "]"); str = gb_string_appendc(str, "]");