Compare commits

...

126 Commits

Author SHA1 Message Date
Ginger Bill 19bde275a3 Add files in core 2017-05-01 15:30:16 +01:00
Ginger Bill 634ee450f4 v0.2.1 2017-05-01 15:28:26 +01:00
Ginger Bill 750d7256fc Unary expression for vector (fix) 2017-05-01 15:27:21 +01:00
Ginger Bill fae5df2ed8 Fix IR vector arith conv bug 2017-05-01 15:05:56 +01:00
Ginger Bill 01d9161772 Fix value conversion with enum value on for in. 2017-05-01 10:10:07 +01:00
Ginger Bill aceabb2f2f for in iteration of Enum Type (request from issue #58) 2017-05-01 10:02:25 +01:00
Ginger Bill 04f5fff7fa Improve vector math; Make bprint* return string 2017-05-01 00:38:26 +01:00
Ginger Bill dc5587eae2 Fix statement parsing of unary: & and ^ 2017-04-30 17:20:37 +01:00
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
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
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
52 changed files with 7144 additions and 3373 deletions
+7
View File
@@ -251,6 +251,13 @@ paket-files/
# Project Specific
# - Windows
*.sln
builds/
bin/
*.exe
# - Linux/MacOS
odin
odin.dSYM
+21 -6
View File
@@ -10,6 +10,8 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
* metaprogramming
* designed for good programmers
Website: [https://odin.handmade.network/](https://odin.handmade.network/)
## Demonstrations:
* First Talk & Demo
- [Talk](https://youtu.be/TMCkT-uASaE?t=338)
@@ -19,15 +21,28 @@ The Odin programming language is fast, concise, readable, pragmatic and open sou
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
* [when, for, & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
## Requirements to build and run
* Windows
* x86-64
* MSVC 2015 installed (C99 support)
* Requires MSVC's link.exe as the linker
- run `vcvarsall.bat` to setup the path
* [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe`
- Windows
* x86-64
* MSVC 2015 installed (C99 support)
* [LLVM binaries](https://github.com/gingerBill/Odin/releases/tag/llvm-4.0-windows) for `opt.exe` and `llc.exe`
* Requires MSVC's link.exe as the linker
* run `vcvarsall.bat` to setup the path
- MacOS
* x86-64
* LLVM explicitly installed (`brew install llvm`)
* XCode installed (for the linker)
- GNU/Linux
* x86-64
* Build tools (ld)
* LLVM installed
* Clang installed (temporary - this is calling the linker for now)
## Warnings
+1 -1
View File
@@ -4,7 +4,7 @@
set exe_name=odin.exe
:: Debug = 0, Release = 1
set release_mode=1
set release_mode=0
set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
if %release_mode% EQU 0 ( rem Debug
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 -312
View File
@@ -1,320 +1,18 @@
#import "atomic.odin";
#import "hash.odin";
#import "mem.odin";
#import "opengl.odin";
#import "strconv.odin";
#import "sync.odin";
#import win32 "sys/windows.odin";
#import "fmt.odin";
#import "os.odin";
#import "math.odin";
main :: proc() {
when true {
/*
Added:
* Unexported entities and fields using an underscore prefix
- See `sync.odin` and explain
immutable program := "+ + * - /";
accumulator := 0;
Removed:
* Maybe/option types
* Remove `type` keyword and other "reserved" keywords
* ..< and ... removed and replace with .. (half-closed range)
Changed:
* `compile_assert` and `assert`return the value of the condition for semantic reasons
* 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
*/
{
Fruit :: enum {
APPLE,
BANANA,
COCONUT,
}
fmt.println(Fruit.names);
}
{
A :: struct {x, y: f32};
B :: struct #align 16 {x, y: f32};
fmt.println("align_of(A) =", align_of(A));
fmt.println("align_of(B) =", align_of(B));
}
{
// Removal of ..< and ...
for i in 0..16 {
}
// Is similar to
for _i := 0; _i < 16; _i++ { immutable i := _i;
for token in program {
match token {
case '+': accumulator += 1;
case '-': accumulator -= 1;
case '*': accumulator *= 2;
case '/': accumulator /= 2;
default: // Ignore everything else
}
}
{
#label thing
for i in 0..10 {
for j in i+1..10 {
if j == 2 {
fmt.println(i, j);
continue thing;
}
if j == 3 {
break thing;
}
}
}
// Works with, `for`, `for in`, `match`, `match in`
// NOTE(bill): This solves most of the problems I need `goto` for
}
{
t := type_info(int);
using Type_Info;
match i in t {
case Integer, Float:
fmt.println("It's a number");
}
x: any = 123;
#label foo
match i in x {
case int, f32:
fmt.println("It's an int or f32");
break foo;
}
}
{
cond := true;
x: int;
if cond {
x = 3;
} else {
x = 4;
}
// Ternary operator
y := cond ? 3 : 4;
FOO :: true ? 123 : 432; // Constant ternary expression
fmt.println("Ternary values:", y, FOO);
}
{
// Slices now store a capacity
buf: [256]byte;
s: []byte;
s = buf[..0]; // == buf[0..0];
fmt.println("count =", s.count);
fmt.println("capacity =", s.capacity);
append(s, 1, 2, 3);
fmt.println(s);
s = buf[1..2..3];
fmt.println("count =", s.count);
fmt.println("capacity =", s.capacity);
fmt.println(s);
clear(s); // Sets count to zero
s.count = 0; // Equivalent
}
{
Foo :: struct {
x, y, z: f32,
ok: bool,
flags: u32,
}
foo_array: [256]Foo;
foo_as_bytes: []byte = slice_to_bytes(foo_array[..]);
// Useful for things like
// os.write(handle, foo_as_bytes);
foo_slice := slice_ptr(cast(^Foo)foo_as_bytes.data, foo_as_bytes.count/size_of(Foo), foo_as_bytes.capacity/size_of(Foo));
// Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone?
// And if so what would the syntax be?
// slice_transmute([]Foo, foo_as_bytes);
}
{
Vec3 :: [vector 3]f32;
x := Vec3{1, 2, 3};
y := Vec3{4, 5, 6};
fmt.println(x < y);
fmt.println(x + y);
fmt.println(x - y);
fmt.println(x * y);
fmt.println(x / y);
for i in x {
fmt.println(i);
}
compile_assert(size_of([vector 7]bool) == size_of([7]bool));
compile_assert(size_of([vector 7]i32) == size_of([7]i32));
// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
}
{
// fmt.* changes
// bprint* returns `int` (bytes written)
// sprint* returns `string` (bytes written as a string)
data: [256]byte;
str := fmt.sprintf(data[..0], "Hellope %d %s %c", 123, "others", '!');
fmt.println(str);
buf := data[..0];
count := fmt.bprintf(^buf, "Hellope %d %s %c", 123, "others", '!');
fmt.println(cast(string)buf[..count]);
// NOTE(bill): We may change this but because this is a library feature, I am not that bothered yet
}
{
x: [dynamic]f64;
reserve(x, 16);
defer free(x); // `free` is overloaded for numerous types
// Number literals can have underscores in them for readability
append(x, 2_000_000.500_000, 3, 5, 7); // variadic append
for p, i in x {
if i > 0 { fmt.print(", "); }
fmt.print(p);
}
fmt.println();
}
{
// Dynamic array "literals"
x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
defer free(x);
fmt.println(x); // fmt.print* supports printing of dynamic types
clear(x);
fmt.println(x);
}
{
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);
delete(m, "c"); // deletes entry with key "c"
_, found := m["c"];
assert(!found);
fmt.println(m);
clear(m);
fmt.println(m);
// NOTE: Fixed size maps are planned but we have not yet implemented
// them as we have had no need for them as of yet
}
{
Vector3 :: struct{x, y, z: f32};
Quaternion :: struct{x, y, z, w: f32};
Entity :: union {
// Common Fields
id: u64,
name: string,
using position: Vector3,
orientation: Quaternion,
flags: u32,
// Variants
Frog{
ribbit_volume: f32,
jump_height: f32,
},
Door{
openness: f32,
},
Map{
width, height: f32,
place_positions: []Vector3,
place_names: []string,
},
}
entity: Entity;
// implicit conversion from variant to base type
entity = Entity.Frog{
id = 1337,
ribbit_volume = 0.5,
jump_height = 2.1,
/*other data */
};
entity.name = "Frank";
entity.position = Vector3{1, 4, 9};
using Entity;
match e in entity {
case Frog:
fmt.println("Ribbit");
case Door:
fmt.println("Creak");
case Map:
fmt.println("Rustle");
default:
fmt.println("Just a normal entity");
}
if frog, ok := union_cast(Frog)entity; ok {
fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position);
}
// Panics if not the correct type
frog: Frog;
frog = union_cast(Frog)entity;
frog, _ = union_cast(Frog)entity; // ignore error and force cast
}
fmt.printf("The program \"%s\" calculates the value %d\n", program, accumulator);
}
}
+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 "math.odin";
#import "os.odin";
@@ -12,11 +13,11 @@ time_now :: proc() -> f64 {
counter: i64;
win32.QueryPerformanceCounter(^counter);
result := counter as f64 / win32_perf_count_freq as f64;
result := cast(f64)counter / cast(f64)win32_perf_count_freq;
return result;
}
win32_print_last_error :: proc() {
err_code := win32.GetLastError() as int;
err_code := cast(int)win32.GetLastError();
if err_code != 0 {
fmt.println("GetLastError: %", err_code);
}
@@ -24,42 +25,42 @@ win32_print_last_error :: proc() {
// Yuk!
to_c_string :: proc(s: string) -> []u8 {
c_str := new_slice(u8, s.count+1);
copy(c_str, s as []byte);
c_str[s.count] = 0;
c_str := make([]u8, len(s)+1);
copy(c_str, cast([]byte)s);
c_str[len(s)] = 0;
return c_str;
}
Window :: struct {
width, height: int;
wc: win32.WNDCLASSEXA;
dc: win32.HDC;
hwnd: win32.HWND;
opengl_context, rc: win32.HGLRC;
c_title: []u8;
width, height: int,
wc: win32.WndClassExA,
dc: win32.Hdc,
hwnd: win32.Hwnd,
opengl_context, rc: wgl.Hglrc,
c_title: []u8,
}
make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC) -> (Window, bool) {
make_window :: proc(title: string, msg, height: int, window_proc: win32.Wnd_Proc) -> (Window, bool) {
using win32;
w: Window;
w.width, w.height = msg, height;
class_name := "Win32-Odin-Window\x00";
c_class_name := class_name.data;
if title[title.count-1] != 0 {
c_class_name := ^class_name[0];
if title[len(title)-1] != 0 {
w.c_title = to_c_string(title);
} else {
w.c_title = title as []u8;
w.c_title = cast([]u8)title;
}
instance := GetModuleHandleA(nil);
w.wc = WNDCLASSEXA{
size = size_of(WNDCLASSEXA) as u32,
w.wc = WndClassExA{
size = size_of(WndClassExA),
style = CS_VREDRAW | CS_HREDRAW,
instance = instance as HINSTANCE,
instance = cast(Hinstance)instance,
class_name = c_class_name,
wnd_proc = window_proc,
};
@@ -70,10 +71,10 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
}
w.hwnd = CreateWindowExA(0,
c_class_name, w.c_title.data,
c_class_name, ^w.c_title[0],
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
CW_USEDEFAULT, CW_USEDEFAULT,
w.width as i32, w.height as i32,
cast(i32)w.width, cast(i32)w.height,
nil, nil, instance, nil);
if w.hwnd == nil {
@@ -85,7 +86,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
{
pfd := PIXELFORMATDESCRIPTOR{
size = size_of(PIXELFORMATDESCRIPTOR) as u32,
size = size_of(PIXELFORMATDESCRIPTOR),
version = 1,
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
pixel_type = PFD_TYPE_RGBA,
@@ -97,19 +98,20 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
};
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil);
w.opengl_context = wglCreateContext(w.dc);
wglMakeCurrent(w.dc, w.opengl_context);
w.opengl_context = wgl.CreateContext(w.dc);
wgl.MakeCurrent(w.dc, w.opengl_context);
attribs := [8]i32{
WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
wgl.CONTEXT_MINOR_VERSION_ARB, 1,
wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
0, // NOTE(bill): tells the proc that this is the end of attribs
};
wglCreateContextAttribsARB := wglGetProcAddress(("wglCreateContextAttribsARB\x00" as string).data) as wglCreateContextAttribsARBType;
w.rc = wglCreateContextAttribsARB(w.dc, 0, ^attribs[0]);
wglMakeCurrent(w.dc, w.rc);
wgl_str := "wglCreateContextAttribsARB\x00";
wglCreateContextAttribsARB := cast(wgl.Create_Context_Attribs_ARB_Type)wgl.GetProcAddress(^wgl_str[0]);
w.rc = wglCreateContextAttribsARB(w.dc, nil, ^attribs[0]);
wgl.MakeCurrent(w.dc, w.rc);
SwapBuffers(w.dc);
}
@@ -117,7 +119,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
}
destroy_window :: proc(w: ^Window) {
free(w.c_title.data);
free(w.c_title);
}
display_window :: proc(w: ^Window) {
@@ -129,7 +131,7 @@ run :: proc() {
using win32;
using math;
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline {
win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
os.exit(0);
return 0;
@@ -137,7 +139,7 @@ run :: proc() {
return DefWindowProcA(hwnd, msg, wparam, lparam);
}
window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc);
window, window_success := make_window("Odin Language Demo", 854, 480, cast(Wnd_Proc)win32_proc);
if !window_success {
return;
}
@@ -153,10 +155,10 @@ run :: proc() {
for running {
curr_time := time_now();
dt := (curr_time - prev_time) as f32;
dt := cast(f32)(curr_time - prev_time);
prev_time = curr_time;
msg: MSG;
msg: Msg;
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
if msg.message == WM_QUIT {
running = false;
@@ -178,7 +180,7 @@ run :: proc() {
if is_key_down(Key_Code.UP) { v[1] += 1; }
if is_key_down(Key_Code.DOWN) { v[1] -= 1; }
v = vec2_norm0(v);
v = norm(v);
pos += v * Vec2{SPEED * dt};
}
@@ -188,8 +190,8 @@ run :: proc() {
gl.Clear(gl.COLOR_BUFFER_BIT);
gl.LoadIdentity();
gl.Ortho(0, window.width as f64,
0, window.height as f64, 0, 1);
gl.Ortho(0, cast(f64)window.width,
0, cast(f64)window.height, 0, 1);
draw_rect :: proc(x, y, w, h: f32) {
gl.Begin(gl.TRIANGLES);
@@ -207,9 +209,14 @@ run :: proc() {
draw_rect(pos.x, pos.y, 50, 50);
display_window(^window);
ms_to_sleep := (16 - 1000*dt) as i32;
ms_to_sleep := cast(i32)(16 - 1000*dt);
if ms_to_sleep > 0 {
win32.Sleep(ms_to_sleep);
}
}
}
main :: proc() {
run();
}
+8 -8
View File
@@ -136,7 +136,7 @@ bounds_checking :: proc() {
{
base: [10]int;
s := base[2:6];
s := base[2..6];
a, b := -1, 6;
#no_bounds_check {
@@ -164,7 +164,7 @@ type_introspection :: proc() {
info = type_info_of_val(x); // by value
// See: runtime.odin
match type i in info {
match i in info {
case Type_Info.Integer:
fmt.println("integer!");
case Type_Info.Float:
@@ -174,7 +174,7 @@ type_introspection :: proc() {
}
// Unsafe cast
integer_info := cast(^Type_Info.Integer)info;
integer_info := cast(^Type_Info.Integer)cast(rawptr)info;
}
{
@@ -263,12 +263,12 @@ crazy_introspection :: proc() {
}
fruit_ti := type_info(Fruit);
name := (cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
info := cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
name := (union_cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
info, _ := union_cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
fmt.printf("% :: enum % {\n", name, info.base);
for i := 0; i < info.values.count; i += 1 {
fmt.printf("\t%\t= %,\n", info.names[i], info.values[i]);
fmt.printf("%s :: enum %T {\n", name, info.base);
for i := 0; i < len(info.values); i++ {
fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
}
fmt.printf("}\n");
File diff suppressed because it is too large Load Diff
+17 -17
View File
@@ -1,14 +1,14 @@
#import "fmt.odin"
#import "utf8.odin"
#import "hash.odin"
#import "mem.odin"
#import "fmt.odin";
#import "utf8.odin";
#import "hash.odin";
#import "mem.odin";
main :: proc() {
{ // New Standard Library stuff
s := "Hello"
s := "Hello";
fmt.println(s,
utf8.valid_string(s),
hash.murmur64(s.data, s.count))
hash.murmur64(cast([]byte)s));
// utf8.odin
// hash.odin
@@ -19,15 +19,15 @@ main :: proc() {
}
{
arena: mem.Arena
mem.init_arena_from_context(^arena, mem.megabytes(16)) // Uses default allocator
defer mem.free_arena(^arena)
arena: mem.Arena;
mem.init_arena_from_context(^arena, mem.megabytes(16)); // Uses default allocator
defer mem.free_arena(^arena);
push_allocator mem.arena_allocator(^arena) {
x := new(int)
x^ = 1337
x := new(int);
x^ = 1337;
fmt.println(x^)
fmt.println(x^);
}
/*
@@ -48,14 +48,14 @@ main :: proc() {
// You can also "push" a context
c := current_context() // Create copy of the allocator
c.allocator = mem.arena_allocator(^arena)
c := context; // Create copy of the allocator
c.allocator = mem.arena_allocator(^arena);
push_context c {
x := new(int)
x^ = 365
x := new(int);
x^ = 365;
fmt.println(x^)
fmt.println(x^);
}
}
+15 -13
View File
@@ -42,12 +42,12 @@ syntax :: proc() {
};
Thing2 :: struct {x: f32, y: int, z: ^[]int};
// Slice interals are now just a `ptr+count`
slice: []int; compile_assert(size_of_val(slice) == 2*size_of(int));
// Slice interals are now just a `ptr+len+cap`
slice: []int; compile_assert(size_of_val(slice) == 3*size_of(int));
// Helper type - Help the reader understand what it is quicker
My_Int :: type int;
My_Proc :: type proc(int) -> f32;
My_Int :: #type int;
My_Proc :: #type proc(int) -> f32;
// All declarations with : are either variable or constant
@@ -59,6 +59,7 @@ syntax :: proc() {
c_proc :: proc() { /* code here */ };
/*
x += 1;
x -= 1;
// ++ and -- have been removed
@@ -67,7 +68,7 @@ syntax :: proc() {
// Question: Should they be added again?
// They were removed as they are redundant and statements, not expressions
// like in C/C++
*/
// You can now build files as a `.dll`
// `odin build_dll demo.odin`
@@ -85,7 +86,7 @@ syntax :: proc() {
Prefix_Type :: struct {x: int, y: f32, z: rawptr};
thread_local my_tls: Prefix_Type;
#thread_local my_tls: Prefix_Type;
prefixes :: proc() {
using var: Prefix_Type;
@@ -98,7 +99,7 @@ prefixes :: proc() {
foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
// int_ptr = nil; // Not valid
int_ptr^ = 123; // Is valid
// int_ptr^ = 123; // Not valid
}
@@ -154,6 +155,7 @@ foreign_procedures :: proc() {
}
special_expressions :: proc() {
/*
// Block expression
x := {
a: f32 = 123;
@@ -168,7 +170,7 @@ special_expressions :: proc() {
// TODO: Type cohesion is not yet finished
give 123;
}; // semicolon is required as it's an expression
*/
// This is allows for inline blocks of code and will be a useful feature to have when
// macros will be implemented into the language
@@ -191,17 +193,17 @@ loops :: proc() {
break;
}
for i in 0..<123 { // 123 exclusive
for i in 0..123 { // 123 exclusive
}
for i in 0...122 { // 122 inclusive
for i in 0..123-1 { // 122 inclusive
}
for val, idx in 12..<16 {
for val, idx in 12..16 {
fmt.println(val, idx);
}
primes := [...]int{2, 3, 5, 7, 11, 13, 17, 19};
primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19};
for p in primes {
fmt.println(p);
@@ -224,7 +226,7 @@ loops :: proc() {
when false {
for i, size := 0; i < name.count; i += size {
r: rune;
r, size = utf8.decode_rune(name[i:]);
r, size = utf8.decode_rune(name[i..]);
fmt.printf("%r\n", r);
}
}
+181 -125
View File
@@ -2,9 +2,8 @@
#import "os.odin";
#import "fmt.odin";
#import "mem.odin";
#import "utf8.odin";
#import "hash.odin";
#import "raw.odin";
// IMPORTANT NOTE(bill): `type_info` & `type_info_val` cannot be used within a
// #shared_global_scope due to the internals of the compiler.
@@ -29,24 +28,29 @@ Calling_Convention :: enum {
Type_Info_Record :: struct #ordered {
types: []^Type_Info,
names: []string,
offsets: []int, // offsets may not be used in tuples
size: int, // in bytes
align: int, // in bytes
offsets: []int, // offsets may not be used in tuples
usings: []bool, // usings may not be used in tuples
packed: bool,
ordered: bool,
custom_align: bool,
}
Type_Info :: union {
size: int,
align: int,
Named{name: string, base: ^Type_Info},
Integer{size: int, signed: bool},
Float{size: int},
Integer{signed: bool},
Float{},
Complex{},
Quaternion{},
String{},
Boolean{},
Any{},
Pointer{
elem: ^Type_Info, // nil -> rawptr
},
Atomic{elem: ^Type_Info},
Procedure{
params: ^Type_Info, // Type_Info.Tuple
results: ^Type_Info, // Type_Info.Tuple
@@ -60,20 +64,18 @@ Type_Info :: union {
},
Dynamic_Array{elem: ^Type_Info, elem_size: int},
Slice {elem: ^Type_Info, elem_size: int},
Vector {elem: ^Type_Info, elem_size, count, align: int},
Vector {elem: ^Type_Info, elem_size, count: int},
Tuple {using record: Type_Info_Record}, // Only really used for procedures
Struct {using record: Type_Info_Record},
Raw_Union {using record: Type_Info_Record},
Union{
common_fields: struct {
types: []^Type_Info,
names: []string,
offsets: []int, // offsets may not be used in tuples
types: []^Type_Info,
names: []string,
offsets: []int, // offsets may not be used in tuples
},
variant_names: []string,
variant_types: []^Type_Info,
size: int,
align: int,
},
Enum{
base: ^Type_Info,
@@ -93,6 +95,9 @@ Type_Info :: union {
// This will be set by the compiler
__type_table: []Type_Info;
__argv__: ^^byte;
__argc__: i32;
type_info_base :: proc(info: ^Type_Info) -> ^Type_Info {
if info == nil {
return nil;
@@ -129,8 +134,6 @@ __trap :: proc() #foreign __llvm_core "llvm.trap";
read_cycle_counter :: proc() -> u64 #foreign __llvm_core "llvm.readcyclecounter";
// IMPORTANT NOTE(bill): Must be in this order (as the compiler relies upon it)
Allocator_Mode :: enum u8 {
ALLOC,
@@ -146,6 +149,7 @@ Allocator :: struct #ordered {
data: rawptr,
}
Context :: struct #ordered {
thread_id: int,
@@ -162,7 +166,7 @@ DEFAULT_ALIGNMENT :: align_of([vector 4]f32);
__check_context :: proc() {
c := ^__context;
c := &__context;
if c.allocator.procedure == nil {
c.allocator = default_allocator();
@@ -230,7 +234,7 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
return nil;
}
mem.copy(new_memory, old_memory, min(old_size, new_size));;
__mem_copy(new_memory, old_memory, min(old_size, new_size));;
free(old_memory);
return new_memory;
}
@@ -276,20 +280,21 @@ default_allocator :: proc() -> Allocator {
__string_eq :: proc(a, b: string) -> bool {
if a.count != b.count {
if len(a) != len(b) {
return false;
}
if a.data == b.data {
if len(a) == 0 {
return true;
}
if &a[0] == &b[0] {
return true;
}
return __string_cmp(a, b) == 0;
}
__string_cmp :: proc(a, b: string) -> int {
return mem.compare(cast([]byte)a, cast([]byte)b);
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
}
__string_ne :: proc(a, b: string) -> bool #inline { return !__string_eq(a, b); }
@@ -299,6 +304,26 @@ __string_le :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) <=
__string_ge :: proc(a, b: string) -> bool #inline { return __string_cmp(a, b) >= 0; }
__complex64_eq :: proc(a, b: complex64) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
__complex64_ne :: proc(a, b: complex64) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
__complex128_eq :: proc(a, b: complex128) -> bool #inline { return real(a) == real(b) && imag(a) == imag(b); }
__complex128_ne :: proc(a, b: complex128) -> bool #inline { return real(a) != real(b) || imag(a) != imag(b); }
__quaternion128_eq :: proc(a, b: quaternion128) -> bool #inline {
return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
}
__quaternion128_ne :: proc(a, b: quaternion128) -> bool #inline {
return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
}
__quaternion256_eq :: proc(a, b: quaternion256) -> bool #inline {
return real(a) == real(b) && imag(a) == imag(b) && jmag(a) == jmag(b) && kmag(a) == kmag(b);
}
__quaternion256_ne :: proc(a, b: quaternion256) -> bool #inline {
return real(a) != real(b) || imag(a) != imag(b) || jmag(a) != jmag(b) || kmag(a) != kmag(b);
}
__assert :: proc(file: string, line, column: int, msg: string) #inline {
fmt.fprintf(os.stderr, "%s(%d:%d) Runtime assertion: %s\n",
file, line, column, msg);
@@ -313,7 +338,7 @@ __bounds_check_error :: proc(file: string, line, column: int, index, count: int)
if 0 <= index && index < count {
return;
}
fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..%d\n",
fmt.fprintf(os.stderr, "%s(%d:%d) Index %d is out of bounds range 0..<%d\n",
file, line, column, index, count);
__debug_trap();
}
@@ -322,21 +347,22 @@ __slice_expr_error :: proc(file: string, line, column: int, low, high, max: int)
if 0 <= low && low <= high && high <= max {
return;
}
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..%d..%d]\n",
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid slice indices: [%d..<%d..<%d]\n",
file, line, column, low, high, max);
__debug_trap();
}
__substring_expr_error :: proc(file: string, line, column: int, low, high: int) {
if 0 <= low && low <= high {
return;
}
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d..%d]\n",
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid substring indices: [%d..<%d]\n",
file, line, column, low, high);
__debug_trap();
}
__union_cast_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) {
__type_assertion_check :: proc(ok: bool, file: string, line, column: int, from, to: ^Type_Info) {
if !ok {
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid `union_cast` from %T to %T\n",
fmt.fprintf(os.stderr, "%s(%d:%d) Invalid type_assertion from %T to %T\n",
file, line, column, from, to);
__debug_trap();
}
@@ -349,7 +375,7 @@ __string_decode_rune :: proc(s: string) -> (rune, int) #inline {
__mem_set :: proc(data: rawptr, value: i32, len: int) -> rawptr {
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
llvm_memset_64bit(data, cast(byte)value, len, 1, false);
llvm_memset_64bit(data, byte(value), len, 1, false);
return data;
}
__mem_zero :: proc(data: rawptr, len: int) -> rawptr {
@@ -369,7 +395,7 @@ __mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
}
__mem_compare :: proc(a, b: ^byte, n: int) -> int {
for i in 0..n {
for i in 0..<n {
match {
case (a+i)^ < (b+i)^:
return -1;
@@ -380,41 +406,44 @@ __mem_compare :: proc(a, b: ^byte, n: int) -> int {
return 0;
}
Raw_Any :: struct #ordered {
type_info: ^Type_Info,
data: rawptr,
__sqrt_f32 :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
__sqrt_f64 :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
__abs_complex64 :: proc(x: complex64) -> f32 #inline {
r, i := real(x), imag(x);
return __sqrt_f32(r*r + i*i);
}
__abs_complex128 :: proc(x: complex128) -> f64 #inline {
r, i := real(x), imag(x);
return __sqrt_f64(r*r + i*i);
}
__abs_quaternion128 :: proc(x: quaternion128) -> f32 #inline {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return __sqrt_f32(r*r + i*i + j*j + k*k);
}
__abs_quaternion256 :: proc(x: quaternion256) -> f64 #inline {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return __sqrt_f64(r*r + i*i + j*j + k*k);
}
Raw_String :: struct #ordered {
data: ^byte,
count: int,
};
Raw_Slice :: struct #ordered {
data: rawptr,
count: int,
capacity: int,
};
Raw_Dynamic_Array :: struct #ordered {
data: rawptr,
count: int,
capacity: int,
allocator: Allocator,
};
Raw_Dynamic_Map :: struct #ordered {
hashes: [dynamic]int,
entries: Raw_Dynamic_Array,
};
__dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capacity: int) -> bool {
array := cast(^Raw_Dynamic_Array)array_;
__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 capacity <= array.capacity {
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;
}
@@ -424,8 +453,8 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capa
}
assert(array.allocator.procedure != nil);
old_size := array.capacity * elem_size;
new_size := capacity * elem_size;
old_size := array.cap * elem_size;
new_size := cap * elem_size;
allocator := array.allocator;
new_data := allocator.procedure(allocator.data, Allocator_Mode.RESIZE, new_size, elem_align, array.data, old_size, 0);
@@ -434,83 +463,102 @@ __dynamic_array_reserve :: proc(array_: rawptr, elem_size, elem_align: int, capa
}
array.data = new_data;
array.capacity = capacity;
array.cap = cap;
return true;
}
__dynamic_array_resize :: proc(array_: rawptr, elem_size, elem_align: int, len: int) -> bool {
array := ^raw.Dynamic_Array(array_);
ok := __dynamic_array_reserve(array_, elem_size, elem_align, len);
if ok {
array.len = len;
}
return ok;
}
__dynamic_array_append :: proc(array_: rawptr, elem_size, elem_align: int,
items: rawptr, item_count: int) -> int {
array := cast(^Raw_Dynamic_Array)array_;
array := ^raw.Dynamic_Array(array_);
if item_count <= 0 || items == nil {
return array.count;
return array.len;
}
ok := true;
if array.capacity <= array.count+item_count {
capacity := 2 * array.capacity + max(8, item_count);
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity);
if array.cap <= array.len+item_count {
cap := 2 * array.cap + max(8, item_count);
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap);
}
if !ok {
// TODO(bill): Better error handling for failed reservation
return array.count;
return array.len;
}
data := cast(^byte)array.data;
data := ^byte(array.data);
assert(data != nil);
mem.copy(data + (elem_size*array.count), items, elem_size * item_count);
array.count += item_count;
return array.count;
__mem_copy(data + (elem_size*array.len), items, elem_size * item_count);
array.len += item_count;
return array.len;
}
__dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: int) -> int {
array := cast(^Raw_Dynamic_Array)array_;
array := ^raw.Dynamic_Array(array_);
ok := true;
if array.capacity <= array.count+1 {
capacity := 2 * array.capacity + max(8, 1);
ok = __dynamic_array_reserve(array, elem_size, elem_align, capacity);
if array.cap <= array.len+1 {
cap := 2 * array.cap + max(8, 1);
ok = __dynamic_array_reserve(array, elem_size, elem_align, cap);
}
if !ok {
// TODO(bill): Better error handling for failed reservation
return array.count;
return array.len;
}
data := cast(^byte)array.data;
data := ^byte(array.data);
assert(data != nil);
mem.zero(data + (elem_size*array.count), elem_size);
array.count++;
return array.count;
__mem_zero(data + (elem_size*array.len), elem_size);
array.len++;
return array.len;
}
__slice_append :: proc(slice_: rawptr, elem_size, elem_align: int,
items: rawptr, item_count: int) -> int {
slice := cast(^Raw_Slice)slice_;
slice := ^raw.Slice(slice_);
if item_count <= 0 || items == nil {
return slice.count;
return slice.len;
}
item_count = min(slice.capacity-slice.count, item_count);
item_count = min(slice.cap-slice.len, item_count);
if item_count > 0 {
data := cast(^byte)slice.data;
data := ^byte(slice.data);
assert(data != nil);
mem.copy(data + (elem_size*slice.count), items, elem_size * item_count);
slice.count += item_count;
__mem_copy(data + (elem_size*slice.len), items, elem_size * item_count);
slice.len += item_count;
}
return slice.count;
return slice.len;
}
// Map stuff
__default_hash :: proc(data: []byte) -> u64 {
return hash.fnv64a(data);
fnv64a :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325;
for b in data {
h = (h ~ u64(b)) * 0x100000001b3;
}
return h;
}
return fnv64a(data);
}
__default_hash_string :: proc(s: string) -> u64 {
return __default_hash(cast([]byte)s);
return __default_hash([]byte(s));
}
__INITIAL_MAP_CAP :: 16;
__Map_Key :: struct #ordered {
hash: u64,
str: string,
@@ -531,39 +579,41 @@ __Map_Entry_Header :: struct #ordered {
}
__Map_Header :: struct #ordered {
m: ^Raw_Dynamic_Map,
m: ^raw.Dynamic_Map,
is_key_string: bool,
entry_size: int,
entry_align: int,
value_offset: int,
value_size: int,
}
__dynamic_map_reserve :: proc(using header: __Map_Header, capacity: int) -> bool {
h := __dynamic_array_reserve(^m.hashes, size_of(int), align_of(int), capacity);
e := __dynamic_array_reserve(^m.entries, entry_size, entry_align, capacity);
return h && e;
__dynamic_map_reserve :: proc(using header: __Map_Header, cap: int) {
__dynamic_array_reserve(&m.hashes, size_of(int), align_of(int), cap);
__dynamic_array_reserve(&m.entries, entry_size, entry_align, cap);
}
__dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
new_header := header;
nm: Raw_Dynamic_Map;
new_header.m = ^nm;
new_header: __Map_Header = header;
nm: raw.Dynamic_Map;
new_header.m = &nm;
reserve(nm.hashes, new_count);
nm.hashes.count = nm.hashes.capacity;
__dynamic_array_reserve(^nm.entries, entry_size, entry_align, m.entries.count);
for _, i in nm.hashes {
header_hashes := ^raw.Dynamic_Array(&header.m.hashes);
nm_hashes := ^raw.Dynamic_Array(&nm.hashes);
__dynamic_array_resize(nm_hashes, size_of(int), align_of(int), new_count);
__dynamic_array_reserve(&nm.entries, entry_size, entry_align, m.entries.len);
for i in 0..<new_count {
nm.hashes[i] = -1;
}
for i := 0; i < nm.entries.count; i++ {
entry_header := __dynamic_map_get_entry(new_header, i);
data := cast(^byte)entry_header;
if nm.hashes.count == 0 {
for i := 0; i < m.entries.len; i++ {
if len(nm.hashes) == 0 {
__dynamic_map_grow(new_header);
}
entry_header := __dynamic_map_get_entry(header, i);
data := ^byte(entry_header);
fr := __dynamic_map_find(new_header, entry_header.key);
j := __dynamic_map_add_entry(new_header, entry_header.key);
if fr.entry_prev < 0 {
@@ -575,13 +625,14 @@ __dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
e := __dynamic_map_get_entry(new_header, j);
e.next = fr.entry_index;
ndata := cast(^byte)e;
mem.copy(ndata+value_offset, data+value_offset, entry_size-value_offset);
ndata := ^byte(e);
__mem_copy(ndata+value_offset, data+value_offset, value_size);
if __dynamic_map_full(new_header) {
__dynamic_map_grow(new_header);
}
}
free_ptr_with_allocator(header.m.hashes.allocator, header.m.hashes.data);
free_ptr_with_allocator(header_hashes.allocator, header_hashes.data);
free_ptr_with_allocator(header.m.entries.allocator, header.m.entries.data);
header.m^ = nm;
}
@@ -589,7 +640,7 @@ __dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) {
__dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr {
index := __dynamic_map_find(h, key).entry_index;
if index >= 0 {
data := cast(^byte)__dynamic_map_get_entry(h, index);
data := ^byte(__dynamic_map_get_entry(h, index));
val := data + h.value_offset;
return val;
}
@@ -598,10 +649,14 @@ __dynamic_map_get :: proc(h: __Map_Header, key: __Map_Key) -> rawptr {
__dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr) {
index: int;
assert(value != nil);
if m.hashes.count == 0 {
if len(m.hashes) == 0 {
__dynamic_map_reserve(h, __INITIAL_MAP_CAP);
__dynamic_map_grow(h);
}
fr := __dynamic_map_find(h, key);
if fr.entry_index >= 0 {
index = fr.entry_index;
@@ -615,9 +670,10 @@ __dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr)
}
}
{
data := cast(^byte)__dynamic_map_get_entry(h, index);
val := data+value_offset;
mem.copy(val, value, entry_size-value_offset);
e := __dynamic_map_get_entry(h, index);
e.key = key;
val := ^byte(e) + value_offset;
__mem_copy(val, value, value_size);
}
if __dynamic_map_full(h) {
@@ -627,12 +683,12 @@ __dynamic_map_set :: proc(using h: __Map_Header, key: __Map_Key, value: rawptr)
__dynamic_map_grow :: proc(using h: __Map_Header) {
new_count := 2*m.entries.count + 8;
new_count := max(2*m.entries.cap + 8, __INITIAL_MAP_CAP);
__dynamic_map_rehash(h, new_count);
}
__dynamic_map_full :: proc(using h: __Map_Header) -> bool {
return cast(int)(0.75 * cast(f64)m.hashes.count) <= m.entries.count;
return int(0.75 * f64(len(m.hashes))) <= m.entries.cap;
}
@@ -648,8 +704,8 @@ __dynamic_map_hash_equal :: proc(h: __Map_Header, a, b: __Map_Key) -> bool {
__dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_Result {
fr := __Map_Find_Result{-1, -1, -1};
if m.hashes.count > 0 {
fr.hash_index = cast(int)(key.hash % cast(u64)m.hashes.count);
if len(m.hashes) > 0 {
fr.hash_index = int(key.hash % u64(len(m.hashes)));
fr.entry_index = m.hashes[fr.hash_index];
for fr.entry_index >= 0 {
entry := __dynamic_map_get_entry(h, fr.entry_index);
@@ -664,8 +720,8 @@ __dynamic_map_find :: proc(using h: __Map_Header, key: __Map_Key) -> __Map_Find_
}
__dynamic_map_add_entry :: proc(using h: __Map_Header, key: __Map_Key) -> int {
prev := m.entries.count;
c := __dynamic_array_append_nothing(^m.entries, entry_size, entry_align);
prev := m.entries.len;
c := __dynamic_array_append_nothing(&m.entries, entry_size, entry_align);
if c != prev {
end := __dynamic_map_get_entry(h, c-1);
end.key = key;
@@ -683,8 +739,8 @@ __dynamic_map_delete :: proc(using h: __Map_Header, key: __Map_Key) {
}
__dynamic_map_get_entry :: proc(using h: __Map_Header, index: int) -> ^__Map_Entry_Header {
data := cast(^byte)m.entries.data + index*entry_size;
return cast(^__Map_Entry_Header)data;
data := ^byte(m.entries.data) + index*entry_size;
return ^__Map_Entry_Header(data);
}
__dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
@@ -694,10 +750,10 @@ __dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) {
__dynamic_map_get_entry(h, fr.entry_prev).next = __dynamic_map_get_entry(h, fr.entry_index).next;
}
if fr.entry_index == m.entries.count-1 {
m.entries.count--;
if fr.entry_index == m.entries.len-1 {
m.entries.len--;
}
mem.copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.count-1), entry_size);
__mem_copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.len-1), entry_size);
last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key);
if last.entry_prev >= 0 {
__dynamic_map_get_entry(h, last.entry_prev).next = fr.entry_index;
+27 -27
View File
@@ -14,37 +14,37 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string {
for _, i in buf {
buf[i] = '0';
}
return buf.count;
return len(buf);
}
n := 10 + a.count + abs(a.decimal_point);
// TODO(bill): make this work with a buffer that's not big enough
assert(buf.count >= n);
buf = buf[..n];
assert(len(buf) >= n);
buf = buf[0..<n];
if a.count == 0 {
buf[0] = '0';
return cast(string)buf[0..1];
return string(buf[0..<1]);
}
w := 0;
if a.decimal_point <= 0 {
buf[w] = '0'; w++;
buf[w] = '.'; w++;
w += digit_zero(buf[w .. w-a.decimal_point]);
w += copy(buf[w..], a.digits[0..a.count]);
w += digit_zero(buf[w ..< w-a.decimal_point]);
w += copy(buf[w..], a.digits[0..<a.count]);
} else if a.decimal_point < a.count {
w += copy(buf[w..], a.digits[0..a.decimal_point]);
w += copy(buf[w..], a.digits[0..<a.decimal_point]);
buf[w] = '.'; w++;
w += copy(buf[w..], a.digits[a.decimal_point .. a.count]);
w += copy(buf[w..], a.digits[a.decimal_point ..< a.count]);
} else {
w += copy(buf[w..], a.digits[0..a.count]);
w += digit_zero(buf[w .. w+a.decimal_point-a.count]);
w += copy(buf[w..], a.digits[0..<a.count]);
w += digit_zero(buf[w ..< w+a.decimal_point-a.count]);
}
return cast(string)buf[0..w];
return string(buf[0..<w]);
}
// trim trailing zeros
@@ -64,7 +64,7 @@ assign :: proc(a: ^Decimal, i: u64) {
for i > 0 {
j := i/10;
i -= 10*j;
buf[n] = cast(byte)('0'+i);
buf[n] = byte('0'+i);
n++;
i = j;
}
@@ -99,7 +99,7 @@ shift_right :: proc(a: ^Decimal, k: uint) {
}
break;
}
c := cast(uint)a.digits[r];
c := uint(a.digits[r]);
n = n*10 + c - '0';
}
a.decimal_point -= r-1;
@@ -107,10 +107,10 @@ shift_right :: proc(a: ^Decimal, k: uint) {
mask: uint = (1<<k) - 1;
for ; r < a.count; r++ {
c := cast(uint)a.digits[r];
c := uint(a.digits[r]);
dig := n>>k;
n &= mask;
a.digits[w] = cast(byte)('0' + dig);
a.digits[w] = byte('0' + dig);
w++;
n = n*10 + c - '0';
}
@@ -118,8 +118,8 @@ shift_right :: proc(a: ^Decimal, k: uint) {
for n > 0 {
dig := n>>k;
n &= mask;
if w < a.digits.count {
a.digits[w] = cast(byte)('0' + dig);
if w < len(a.digits) {
a.digits[w] = byte('0' + dig);
w++;
} else if dig > 0 {
a.trunc = true;
@@ -133,19 +133,19 @@ shift_right :: proc(a: ^Decimal, k: uint) {
}
shift_left :: proc(a: ^Decimal, k: uint) {
delta := cast(int)(k/4);
delta := int(k/4);
r := a.count; // read index
w := a.count+delta; // write index
n: uint;
for r--; r >= 0; r-- {
n += (cast(uint)a.digits[r] - '0') << k;
n += (uint(a.digits[r]) - '0') << k;
quo := n/10;
rem := n - 10*quo;
w--;
if w < a.digits.count {
a.digits[w] = cast(byte)('0' + rem);
if w < len(a.digits) {
a.digits[w] = byte('0' + rem);
} else if rem != 0 {
a.trunc = true;
}
@@ -156,8 +156,8 @@ shift_left :: proc(a: ^Decimal, k: uint) {
quo := n/10;
rem := n - 10*quo;
w--;
if w < a.digits.count {
a.digits[w] = cast(byte)('0' + rem);
if 0 <= w && w < len(a.digits) {
a.digits[w] = byte('0' + rem);
} else if rem != 0 {
a.trunc = true;
}
@@ -165,7 +165,7 @@ shift_left :: proc(a: ^Decimal, k: uint) {
}
a.count += delta;
a.count = min(a.count, a.digits.count);
a.count = min(a.count, len(a.digits));
a.decimal_point += delta;
trim(a);
}
@@ -179,7 +179,7 @@ shift :: proc(a: ^Decimal, k: int) {
shift_left(a, max_shift);
k -= max_shift;
}
shift_left(a, cast(uint)k);
shift_left(a, uint(k));
case k < 0:
@@ -187,7 +187,7 @@ shift :: proc(a: ^Decimal, k: int) {
shift_right(a, max_shift);
k += max_shift;
}
shift_right(a, cast(uint)-k);
shift_right(a, uint(-k));
}
}
@@ -245,7 +245,7 @@ rounded_integer :: proc(a: ^Decimal) -> u64 {
n: u64 = 0;
m := min(a.decimal_point, a.count);
for i = 0; i < m; i++ {
n = n*10 + cast(u64)(a.digits[i]-'0');
n = n*10 + u64(a.digits[i]-'0');
}
for ; i < a.decimal_point; i++ {
n *= 10;
+362 -209
View File
@@ -3,24 +3,69 @@
#import "utf8.odin";
#import "types.odin";
#import "strconv.odin";
#import "raw.odin";
_BUFFER_SIZE :: 1<<12;
write_string :: proc(buf: ^[]byte, s: string) {
append(buf, ..cast([]byte)s);
String_Buffer :: struct {
is_dynamic: bool,
sa: []byte,
da: [dynamic]byte,
};
make_string_buffer_from_slice :: proc(b: []byte) -> String_Buffer {
return String_Buffer{
is_dynamic = false,
sa = b,
};
}
write_byte :: proc(buf: ^[]byte, b: byte) {
append(buf, b);
make_string_dynamic_buffer :: proc() -> String_Buffer {
return String_Buffer{
is_dynamic = true,
da = make([dynamic]byte),
};
}
write_rune :: proc(buf: ^[]byte, r: rune) {
string_buffer_data :: proc(buf: ^String_Buffer) -> []byte {
return string_buffer_data(buf^);
}
string_buffer_data :: proc(buf: String_Buffer) -> []byte {
if buf.is_dynamic {
return buf.da[..];
}
return buf.sa[..];
}
to_string :: proc(buf: String_Buffer) -> string {
return string(string_buffer_data(buf));
}
write_string :: proc(buf: ^String_Buffer, s: string) {
write_bytes(buf, []byte(s));
}
write_bytes :: proc(buf: ^String_Buffer, b: []byte) {
if buf.is_dynamic {
append(buf.da, ..b);
} else {
append(buf.sa, ..b);
}
}
write_byte :: proc(buf: ^String_Buffer, b: byte) {
if buf.is_dynamic {
append(buf.da, b);
} else {
append(buf.sa, b);
}
}
write_rune :: proc(buf: ^String_Buffer, r: rune) {
if r < utf8.RUNE_SELF {
write_byte(buf, cast(byte)r);
write_byte(buf, byte(r));
return;
}
b, n := utf8.encode_rune(r);
append(buf, ..b[..n]);
write_bytes(buf, b[0..<n]);
}
Fmt_Info :: struct {
@@ -38,7 +83,7 @@ Fmt_Info :: struct {
reordered: bool,
good_arg_index: bool,
buf: ^[]byte,
buf: ^String_Buffer,
arg: any, // Temporary
}
@@ -46,25 +91,28 @@ Fmt_Info :: struct {
fprint :: proc(fd: os.Handle, args: ..any) -> int {
data: [_BUFFER_SIZE]byte;
buf := data[..0];
bprint(^buf, ..args);
os.write(fd, buf[..buf.count]);
return buf.count;
buf := make_string_buffer_from_slice(data[0..<0]);
sbprint(&buf, ..args);
res := string_buffer_data(buf);
os.write(fd, res);
return len(res);
}
fprintln :: proc(fd: os.Handle, args: ..any) -> int {
data: [_BUFFER_SIZE]byte;
buf := data[..0];
bprintln(^buf, ..args);
os.write(fd, buf[..buf.count]);
return buf.count;
buf := make_string_buffer_from_slice(data[0..<0]);
sbprintln(&buf, ..args);
res := string_buffer_data(buf);
os.write(fd, res);
return len(res);
}
fprintf :: proc(fd: os.Handle, fmt: string, args: ..any) -> int {
data: [_BUFFER_SIZE]byte;
buf := data[..0];
bprintf(^buf, fmt, ..args);
os.write(fd, buf[..buf.count]);
return buf.count;
buf := make_string_buffer_from_slice(data[0..<0]);
sbprintf(&buf, fmt, ..args);
res := string_buffer_data(buf);
os.write(fd, res);
return len(res);
}
@@ -79,14 +127,56 @@ printf :: proc(fmt: string, args: ..any) -> int {
}
fprint_type :: proc(fd: os.Handle, info: ^Type_Info) {
data: [_BUFFER_SIZE]byte;
buf := data[..0];
write_type(^buf, info);
os.write(fd, buf[..buf.count]);
// aprint* procedures return a string that was allocated with the current context
// They must be freed accordingly
aprint :: proc(args: ..any) -> string {
buf := make_string_dynamic_buffer();
sbprint(&buf, ..args);
return to_string(buf);
}
aprintln :: proc(args: ..any) -> string {
buf := make_string_dynamic_buffer();
sbprintln(&buf, ..args);
return to_string(buf);
}
aprintf :: proc(fmt: string, args: ..any) -> string {
buf := make_string_dynamic_buffer();
sbprintf(&buf, fmt, ..args);
return to_string(buf);
}
write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
// bprint* procedures
// aprint* procedure return a string that was allocated with the current context
// They must be freed accordingly
bprint :: proc(buf: []byte, args: ..any) -> string {
sb := make_string_buffer_from_slice(buf[0..<0..<len(buf)]);
return sbprint(&sb, ..args);
}
bprintln :: proc(buf: []byte, args: ..any) -> string {
sb := make_string_buffer_from_slice(buf[0..<0..<len(buf)]);
return sbprintln(&sb, ..args);
}
bprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
sb := make_string_buffer_from_slice(buf[0..<0..<len(buf)]);
return sbprintf(&sb, fmt, ..args);
}
fprint_type :: proc(fd: os.Handle, info: ^Type_Info) {
data: [_BUFFER_SIZE]byte;
buf := make_string_buffer_from_slice(data[0..<0]);
write_type(&buf, info);
os.write(fd, string_buffer_data(buf));
}
write_type :: proc(buf: ^String_Buffer, ti: ^Type_Info) {
if ti == nil {
return;
}
@@ -102,7 +192,7 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
default:
write_string(buf, info.signed ? "i" : "u");
fi := Fmt_Info{buf = buf};
fmt_int(^fi, cast(u64)(8*info.size), false, 64, 'd');
fmt_int(&fi, u64(8*info.size), false, 64, 'd');
}
case Float:
@@ -110,8 +200,22 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
case 4: write_string(buf, "f32");
case 8: write_string(buf, "f64");
}
case Complex:
match info.size {
case 8: write_string(buf, "complex64");
case 16: write_string(buf, "complex128");
}
case Quaternion:
match info.size {
case 16: write_string(buf, "quaternion128");
case 32: write_string(buf, "quaternion");
}
case String: write_string(buf, "string");
case Boolean: write_string(buf, "bool");
case Atomic:
write_string(buf, "atomic ");
write_type(buf, info.elem);
case Pointer:
if info.elem == nil {
write_string(buf, "rawptr");
@@ -124,7 +228,7 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
if info.params == nil {
write_string(buf, "()");
} else {
t := union_cast(^Tuple)info.params;
t := info.params.(^Tuple);
write_string(buf, "(");
for type, i in t.types {
if i > 0 { write_string(buf, ", "); }
@@ -137,14 +241,14 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
write_type(buf, info.results);
}
case Tuple:
count := info.names.count;
count := len(info.names);
if count != 1 { write_string(buf, "("); }
for name, i in info.names {
if i > 0 { write_string(buf, ", "); }
type := info.types[i];
if name.count > 0 {
if len(name) > 0 {
write_string(buf, name);
write_string(buf, ": ");
}
@@ -155,20 +259,19 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
case Array:
write_string(buf, "[");
fi := Fmt_Info{buf = buf};
fmt_int(^fi, cast(u64)info.count, false, 64, 'd');
fmt_int(&fi, u64(info.count), false, 64, 'd');
write_string(buf, "]");
write_type(buf, info.elem);
case Dynamic_Array:
write_string(buf, "[..]");
write_string(buf, "[dynamic]");
write_type(buf, info.elem);
case Slice:
write_string(buf, "[");
write_string(buf, "]");
write_string(buf, "[]");
write_type(buf, info.elem);
case Vector:
write_string(buf, "[vector ");
fi := Fmt_Info{buf = buf};
fmt_int(^fi, cast(u64)info.count, false, 64, 'd');
fmt_int(&fi, u64(info.count), false, 64, 'd');
write_string(buf, "]");
write_type(buf, info.elem);
@@ -185,7 +288,7 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
if info.custom_align {
write_string(buf, "#align ");
fi := Fmt_Info{buf = buf};
fmt_int(^fi, cast(u64)info.align, false, 64, 'd');
fmt_int(&fi, u64(info.align), false, 64, 'd');
write_byte(buf, ' ');
}
write_byte(buf, '{');
@@ -221,14 +324,14 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
defer write_byte(buf, '}');
variant_type := type_info_base(info.variant_types[i]);
variant := union_cast(^Struct)variant_type;
variant := variant_type.(^Struct);
vc := variant.names.count-cf.names.count;
vc := len(variant.names)-len(cf.names);
for j in 0..vc {
if j > 0 {
write_string(buf, ", ");
}
index := j + cf.names.count;
index := j + len(cf.names);
write_string(buf, variant.names[index]);
write_string(buf, ": ");
write_type(buf, variant.types[index]);
@@ -263,53 +366,8 @@ write_type :: proc(buf: ^[]byte, ti: ^Type_Info) {
}
bprint :: proc(buf: ^[]byte, args: ..any) -> int {
fi: Fmt_Info;
fi.buf = buf;
prev_string := false;
for arg, i in args {
is_string := arg.data != nil && types.is_string(arg.type_info);
if i > 0 && !is_string && !prev_string {
write_byte(buf, ' ');
}
fmt_value(^fi, args[i], 'v');
prev_string = is_string;
}
return buf.count;
}
bprintln :: proc(buf: ^[]byte, args: ..any) -> int {
fi: Fmt_Info;
fi.buf = buf;
for arg, i in args {
if i > 0 {
write_byte(buf, ' ');
}
fmt_value(^fi, args[i], 'v');
}
write_byte(buf, '\n');
return buf.count;
}
sprint :: proc(buf: []byte, args: ..any) -> string {
count := bprint(^buf, ..args);
return cast(string)buf[..count];
}
sprintln :: proc(buf: []byte, args: ..any) -> string {
count := bprintln(^buf, ..args);
return cast(string)buf[..count];
}
sprintf :: proc(buf: []byte, fmt: string, args: ..any) -> string {
count := bprintf(^buf, fmt, ..args);
return cast(string)buf[..count];
}
parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: bool) {
_parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: bool) {
is_digit :: proc(r: rune) -> bool #inline {
return '0' <= r && r <= '9';
}
@@ -318,34 +376,29 @@ parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: bool
ok := true;
i := 0;
for o in offset..s.count {
c := cast(rune)s[offset+i];
for i < len(s[offset..]) {
c := rune(s[offset+i]);
if !is_digit(c) {
break;
}
i++;
result *= 10;
result += cast(int)(c - '0');
result += int(c)-'0';
}
return result, offset+i, i != 0;
}
_arg_number :: proc(fi: ^Fmt_Info,
arg_index: int,
format: string,
offset: int,
arg_count: int,
) -> (index: int, offset: int, ok: bool) {
_arg_number :: proc(fi: ^Fmt_Info, arg_index: int, format: string, offset, arg_count: int) -> (index, offset: int, ok: bool) {
parse_arg_number :: proc(format: string) -> (int, int, bool) {
if format.count < 3 {
if len(format) < 3 {
return 0, 1, false;
}
for i in 1..format.count {
for i in 1..len(format) {
if format[i] == ']' {
width, new_index, ok := parse_int(format, 1);
width, new_index, ok := _parse_int(format, 1);
if !ok || new_index != i {
return 0, i+1, false;
}
@@ -357,7 +410,7 @@ _arg_number :: proc(fi: ^Fmt_Info,
}
if format.count <= offset || format[offset] != '[' {
if len(format) <= offset || format[offset] != '[' {
return arg_index, offset, false;
}
fi.reordered = true;
@@ -373,19 +426,19 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
num := 0;
new_arg_index := arg_index;
ok := true;
if arg_index < args.count {
if arg_index < len(args) {
arg := args[arg_index];
arg.type_info = type_info_base(arg.type_info);
match i in arg {
case int: num = i;
case i8: num = cast(int)i;
case i16: num = cast(int)i;
case i32: num = cast(int)i;
case i64: num = cast(int)i;
case u8: num = cast(int)i;
case u16: num = cast(int)i;
case u32: num = cast(int)i;
case u64: num = cast(int)i;
case i8: num = int(i);
case i16: num = int(i);
case i32: num = int(i);
case i64: num = int(i);
case u8: num = int(i);
case u16: num = int(i);
case u32: num = int(i);
case u64: num = int(i);
default:
ok = false;
}
@@ -429,9 +482,10 @@ fmt_write_padding :: proc(fi: ^Fmt_Info, width: int) {
pad_byte = '0';
}
count := min(width, fi.buf.capacity-fi.buf.count);
data := string_buffer_data(fi.buf^);
count := min(width, cap(data)-len(data));
for _ in 0..count {
append(fi.buf, pad_byte);
write_byte(fi.buf, pad_byte);
}
}
@@ -441,25 +495,25 @@ is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned
if is_signed {
match bit_size {
case 8:
i := cast(i8)u;
i := i8(u);
neg = i < 0;
if neg { i = -i; }
u = cast(u64)i;
u = u64(i);
case 16:
i := cast(i16)u;
i := i16(u);
neg = i < 0;
if neg { i = -i; }
u = cast(u64)i;
u = u64(i);
case 32:
i := cast(i32)u;
i := i32(u);
neg = i < 0;
if neg { i = -i; }
u = cast(u64)i;
u = u64(i);
case 64:
i := cast(i64)u;
i := i64(u);
neg = i < 0;
if neg { i = -i; }
u = cast(u64)i;
u = u64(i);
default:
panic("is_integer_negative: Unknown integer size");
}
@@ -509,7 +563,7 @@ _write_int :: proc(fi: ^Fmt_Info, u: u64, base: int, is_signed: bool, bit_size:
if fi.hash { flags |= strconv.Int_Flag.PREFIX; }
if fi.plus { flags |= strconv.Int_Flag.PLUS; }
if fi.space { flags |= strconv.Int_Flag.SPACE; }
s := strconv.append_bits(buf[..0], u, base, is_signed, bit_size, digits, flags);
s := strconv.append_bits(buf[0..<0], u, base, is_signed, bit_size, digits, flags);
prev_zero := fi.zero;
defer fi.zero = prev_zero;
@@ -533,9 +587,9 @@ fmt_int :: proc(fi: ^Fmt_Info, u: u64, is_signed: bool, bit_size: int, verb: run
case 'x': _write_int(fi, u, 16, is_signed, bit_size, __DIGITS_LOWER);
case 'X': _write_int(fi, u, 16, is_signed, bit_size, __DIGITS_UPPER);
case 'c', 'r':
fmt_rune(fi, cast(rune)u);
fmt_rune(fi, rune(u));
case 'U':
r := cast(rune)u;
r := rune(u);
if r < 0 || r > utf8.MAX_RUNE {
fmt_bad_verb(fi, verb);
} else {
@@ -574,8 +628,8 @@ fmt_float :: proc(fi: ^Fmt_Info, v: f64, bit_size: int, verb: rune) {
prec = fi.prec;
}
buf: [128]byte;
str := strconv.append_float(buf[1..1], v, 'f', prec, bit_size);
str = cast(string)buf[..str.count+1];
str := strconv.append_float(buf[1..<1], v, 'f', prec, bit_size);
str = string(buf[0..<len(str)]);
if str[1] == '+' || str[1] == '-' {
str = str[1..];
} else {
@@ -586,16 +640,16 @@ fmt_float :: proc(fi: ^Fmt_Info, v: f64, bit_size: int, verb: rune) {
str[0] = ' ';
}
if str[1] == 'N' && str[1] == 'I' {
if len(str) > 1 && str[1] == 'N' && str[1] == 'I' {
write_string(fi.buf, str);
return;
}
if fi.plus || str[0] != '+' {
if fi.zero && fi.width_set && fi.width > str.count {
if fi.zero && fi.width_set && fi.width > len(str) {
write_byte(fi.buf, str[0]);
fmt_write_padding(fi, fi.width - str.count);
write_string(fi.buf, str[1..]);
fmt_write_padding(fi, fi.width - len(str));
write_string(fi.buf, str[1..<]);
} else {
_pad(fi, str);
}
@@ -612,6 +666,19 @@ fmt_string :: proc(fi: ^Fmt_Info, s: string, verb: rune) {
match verb {
case 's', 'v':
write_string(fi.buf, s);
case 'x', 'X':
space := fi.space;
fi.space = false;
defer fi.space = space;
for i in 0..<len(s) {
if i > 0 && space {
write_byte(fi.buf, ' ');
}
_write_int(fi, u64(s[i]), 16, false, 8, verb == 'x' ? __DIGITS_LOWER : __DIGITS_UPPER);
}
default:
fmt_bad_verb(fi, verb);
}
@@ -625,7 +692,7 @@ fmt_pointer :: proc(fi: ^Fmt_Info, p: rawptr, verb: rune) {
fmt_bad_verb(fi, verb);
return;
}
u := cast(u64)cast(uint)p;
u := u64(uint(p));
if !fi.hash || verb == 'v' {
write_string(fi.buf, "0x");
}
@@ -646,25 +713,25 @@ fmt_enum :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
case Enum:
match verb {
case 'd', 'f':
fmt_arg(fi, any{type_info_base(e.base), v.data}, verb);
fmt_arg(fi, any{v.data, type_info_base(e.base)}, verb);
case 's', 'v':
i: i64;
f: f64;
ok := false;
a := any{type_info_base(e.base), v.data};
a := any{v.data, type_info_base(e.base)};
match v in a {
case i8: i = cast(i64)v;
case i16: i = cast(i64)v;
case i32: i = cast(i64)v;
case i64: i = cast(i64)v;
case int: i = cast(i64)v;
case u8: i = cast(i64)v;
case u16: i = cast(i64)v;
case u32: i = cast(i64)v;
case u64: i = cast(i64)v;
case uint: i = cast(i64)v;
case f32: f = cast(f64)v; i = transmute(i64)f;
case f64: f = cast(f64)v; i = transmute(i64)f;
case i8: i = i64(v);
case i16: i = i64(v);
case i32: i = i64(v);
case i64: i = i64(v);
case int: i = i64(v);
case u8: i = i64(v);
case u16: i = i64(v);
case u32: i = i64(v);
case u64: i = i64(v);
case uint: i = i64(v);
case f32: f = f64(v); i = transmute(i64, f);
case f64: f = f64(v); i = transmute(i64, f);
}
if types.is_string(e.base) {
@@ -675,7 +742,7 @@ fmt_enum :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
break;
}
}
} else if e.values.count == 0 {
} else if len(e.values) == 0 {
write_string(fi.buf, "");
ok = true;
} else {
@@ -722,27 +789,32 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
}
write_string(fi.buf, b.names[i]);
write_string(fi.buf, " = ");
data := cast(^byte)v.data + b.offsets[i];
fmt_arg(fi, any{b.types[i], cast(rawptr)data}, 'v');
data := ^byte(v.data) + b.offsets[i];
fmt_arg(fi, any{rawptr(data), b.types[i]}, 'v');
}
write_byte(fi.buf, '}');
default:
fmt_value(fi, any{info.base, v.data}, verb);
fmt_value(fi, any{v.data, info.base}, verb);
}
case Boolean: fmt_arg(fi, v, verb);
case Float: fmt_arg(fi, v, verb);
case Integer: fmt_arg(fi, v, verb);
case String: fmt_arg(fi, v, verb);
case Boolean: fmt_arg(fi, v, verb);
case Integer: fmt_arg(fi, v, verb);
case Float: fmt_arg(fi, v, verb);
case Complex: fmt_arg(fi, v, verb);
case Quaternion: fmt_arg(fi, v, verb);
case String: fmt_arg(fi, v, verb);
case Pointer:
if v.type_info == type_info(^Type_Info) {
write_type(fi.buf, (cast(^^Type_Info)v.data)^);
write_type(fi.buf, (^^Type_Info)(v.data)^);
} else {
fmt_pointer(fi, (cast(^rawptr)v.data)^, verb);
fmt_pointer(fi, (^rawptr)(v.data)^, verb);
}
case Atomic:
fmt_arg(fi, any{v.data, info.elem}, verb);
case Array:
if verb != 'v' {
fmt_bad_verb(fi, verb);
@@ -751,12 +823,12 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
write_byte(fi.buf, '[');
defer write_byte(fi.buf, ']');
for i in 0..info.count {
for i in 0..<info.count {
if i > 0 {
write_string(fi.buf, ", ");
}
data := cast(^byte)v.data + i*info.elem_size;
fmt_arg(fi, any{info.elem, cast(rawptr)data}, 'v');
data := ^byte(v.data) + i*info.elem_size;
fmt_arg(fi, any{rawptr(data), info.elem}, 'v');
}
case Dynamic_Array:
@@ -767,13 +839,13 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
write_byte(fi.buf, '[');
defer write_byte(fi.buf, ']');
array := cast(^Raw_Dynamic_Array)v.data;
for i in 0..array.count {
array := (^raw.Dynamic_Array)(v.data);
for i in 0..<array.len {
if i > 0 {
write_string(fi.buf, ", ");
}
data := cast(^byte)array.data + i*info.elem_size;
fmt_arg(fi, any{info.elem, cast(rawptr)data}, 'v');
data := ^byte(array.data) + i*info.elem_size;
fmt_arg(fi, any{rawptr(data), info.elem}, 'v');
}
case Map:
@@ -784,30 +856,30 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
write_string(fi.buf, "map[");
defer write_byte(fi.buf, ']');
entries := ^(cast(^Raw_Dynamic_Map)v.data).entries;
gs := union_cast(^Struct)type_info_base(info.generated_struct);
ed := union_cast(^Dynamic_Array)type_info_base(gs.types[1]);
entries := &(^raw.Dynamic_Map(v.data).entries);
gs := type_info_base(info.generated_struct).(^Struct);
ed := type_info_base(gs.types[1]).(^Dynamic_Array);
entry_type := union_cast(^Struct)ed.elem;
entry_type := ed.elem.(^Struct);
entry_size := ed.elem_size;
for i in 0..entries.count {
for i in 0..<entries.len {
if i > 0 {
write_string(fi.buf, ", ");
}
data := cast(^byte)entries.data + i*entry_size;
data := ^byte(entries.data) + i*entry_size;
header := cast(^__Map_Entry_Header)data;
header := ^__Map_Entry_Header(data);
if types.is_string(info.key) {
write_string(fi.buf, header.key.str);
} else {
fi := Fmt_Info{buf = fi.buf};
fmt_arg(^fi, any{info.key, cast(rawptr)^header.key.hash}, 'v');
fmt_arg(&fi, any{rawptr(&header.key.hash), info.key}, 'v');
}
write_string(fi.buf, "=");
value := data + entry_type.offsets[2];
fmt_arg(fi, any{info.value, cast(rawptr)value}, 'v');
fmt_arg(fi, any{rawptr(value), info.value}, 'v');
}
case Slice:
@@ -818,26 +890,26 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
write_byte(fi.buf, '[');
defer write_byte(fi.buf, ']');
slice := cast(^[]byte)v.data;
for i in 0..slice.count {
slice := (^[]byte)(v.data);
for _, i in slice {
if i > 0 {
write_string(fi.buf, ", ");
}
data := slice.data + i*info.elem_size;
fmt_arg(fi, any{info.elem, cast(rawptr)data}, 'v');
data := &slice[0] + i*info.elem_size;
fmt_arg(fi, any{rawptr(data), info.elem}, 'v');
}
case Vector:
write_byte(fi.buf, '<');
defer write_byte(fi.buf, '>');
for i in 0..info.count {
for i in 0..<info.count {
if i > 0 {
write_string(fi.buf, ", ");
}
data := cast(^byte)v.data + i*info.elem_size;
fmt_value(fi, any{info.elem, cast(rawptr)data}, 'v');
data := ^byte(v.data) + i*info.elem_size;
fmt_value(fi, any{rawptr(data), info.elem}, 'v');
}
case Struct:
@@ -850,8 +922,8 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
}
write_string(fi.buf, info.names[i]);
write_string(fi.buf, " = ");
data := cast(^byte)v.data + info.offsets[i];
fmt_value(fi, any{info.types[i], cast(rawptr)data}, 'v');
data := ^byte(v.data) + info.offsets[i];
fmt_value(fi, any{rawptr(data), info.types[i]}, 'v');
}
case Union:
@@ -866,8 +938,8 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
}
write_string(fi.buf, cf.names[i]);
write_string(fi.buf, " = ");
data := cast(^byte)v.data + cf.offsets[i];
fmt_value(fi, any{cf.types[i], cast(rawptr)data}, 'v');
data := ^byte(v.data) + cf.offsets[i];
fmt_value(fi, any{rawptr(data), cf.types[i]}, 'v');
}
case Raw_Union:
@@ -879,12 +951,57 @@ fmt_value :: proc(fi: ^Fmt_Info, v: any, verb: rune) {
case Procedure:
write_type(fi.buf, v.type_info);
write_string(fi.buf, " @ ");
fmt_pointer(fi, (cast(^rawptr)v.data)^, 'p');
fmt_pointer(fi, (^rawptr)(v.data)^, 'p');
}
}
fmt_complex :: proc(fi: ^Fmt_Info, c: complex128, bits: int, verb: rune) {
match verb {
case 'f', 'F', 'v':
r := real(c);
i := imag(c);
fmt_float(fi, r, bits/2, verb);
if !fi.plus && i >= 0 {
write_rune(fi.buf, '+');
}
fmt_float(fi, i, bits/2, verb);
write_rune(fi.buf, 'i');
default:
fmt_bad_verb(fi, verb);
return;
}
}
fmt_quaternion :: proc(fi: ^Fmt_Info, c: quaternion256, bits: int, verb: rune) {
match verb {
case 'f', 'F', 'v':
r := real(c);
i := imag(c);
j := jmag(c);
k := kmag(c);
fmt_float(fi, r, bits/4, verb);
if !fi.plus && i >= 0 { write_rune(fi.buf, '+'); }
fmt_float(fi, i, bits/4, verb);
write_rune(fi.buf, 'i');
if !fi.plus && j >= 0 { write_rune(fi.buf, '+'); }
fmt_float(fi, j, bits/4, verb);
write_rune(fi.buf, 'j');
if !fi.plus && k >= 0 { write_rune(fi.buf, '+'); }
fmt_float(fi, k, bits/4, verb);
write_rune(fi.buf, 'k');
default:
fmt_bad_verb(fi, verb);
return;
}
}
fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
if arg.data == nil || arg.type_info == nil {
if arg == nil {
write_string(fi.buf, "<nil>");
return;
}
@@ -903,20 +1020,25 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
base_arg := arg;
base_arg.type_info = type_info_base(base_arg.type_info);
match a in base_arg {
case bool: fmt_bool(fi, a, verb);
case f32: fmt_float(fi, cast(f64)a, 32, verb);
case f64: fmt_float(fi, a, 64, verb);
case any: fmt_arg(fi, a, verb);
case bool: fmt_bool(fi, a, verb);
case f32: fmt_float(fi, f64(a), 32, verb);
case f64: fmt_float(fi, a, 64, verb);
case complex64: fmt_complex(fi, complex128(a), 64, verb);
case complex128: fmt_complex(fi, a, 128, verb);
case quaternion128: fmt_quaternion(fi, quaternion256(a), 128, verb);
case quaternion256: fmt_quaternion(fi, a, 256, verb);
case int: fmt_int(fi, cast(u64)a, true, 8*size_of(int), verb);
case i8: fmt_int(fi, cast(u64)a, true, 8, verb);
case i16: fmt_int(fi, cast(u64)a, true, 16, verb);
case i32: fmt_int(fi, cast(u64)a, true, 32, verb);
case i64: fmt_int(fi, cast(u64)a, true, 64, verb);
case uint: fmt_int(fi, cast(u64)a, false, 8*size_of(uint), verb);
case u8: fmt_int(fi, cast(u64)a, false, 8, verb);
case u16: fmt_int(fi, cast(u64)a, false, 16, verb);
case u32: fmt_int(fi, cast(u64)a, false, 32, verb);
case u64: fmt_int(fi, cast(u64)a, false, 64, verb);
case int: fmt_int(fi, u64(a), true, 8*size_of(int), verb);
case i8: fmt_int(fi, u64(a), true, 8, verb);
case i16: fmt_int(fi, u64(a), true, 16, verb);
case i32: fmt_int(fi, u64(a), true, 32, verb);
case i64: fmt_int(fi, u64(a), true, 64, verb);
case uint: fmt_int(fi, u64(a), false, 8*size_of(uint), verb);
case u8: fmt_int(fi, u64(a), false, 8, verb);
case u16: fmt_int(fi, u64(a), false, 16, verb);
case u32: fmt_int(fi, u64(a), false, 32, verb);
case u64: fmt_int(fi, u64(a), false, 64, verb);
case string: fmt_string(fi, a, verb);
default: fmt_value(fi, arg, verb);
}
@@ -924,9 +1046,40 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) {
}
bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
sbprint :: proc(buf: ^String_Buffer, args: ..any) -> string {
fi: Fmt_Info;
fi.buf = buf;
prev_string := false;
for arg, i in args {
is_string := arg != nil && types.is_string(arg.type_info);
if i > 0 && !is_string && !prev_string {
write_byte(buf, ' ');
}
fmt_value(&fi, args[i], 'v');
prev_string = is_string;
}
return to_string(buf^);
}
sbprintln :: proc(buf: ^String_Buffer, args: ..any) -> string {
fi: Fmt_Info;
fi.buf = buf;
for arg, i in args {
if i > 0 {
write_byte(buf, ' ');
}
fmt_value(&fi, args[i], 'v');
}
write_byte(buf, '\n');
return to_string(buf^);
}
sbprintf :: proc(b: ^String_Buffer, fmt: string, args: ..any) -> string {
fi := Fmt_Info{};
end := fmt.count;
end := len(fmt);
arg_index := 0;
was_prev_index := false;
for i := 0; i < end; {
@@ -937,7 +1090,7 @@ bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
i++;
}
if i > prev_i {
write_string(b, fmt[prev_i..i]);
write_string(b, fmt[prev_i..<i]);
}
if i >= end {
break;
@@ -965,7 +1118,7 @@ bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
}
}
arg_index, i, was_prev_index = _arg_number(^fi, arg_index, fmt, i, args.count);
arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args));
// Width
if i < end && fmt[i] == '*' {
@@ -982,7 +1135,7 @@ bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
}
was_prev_index = false;
} else {
fi.width, i, fi.width_set = parse_int(fmt, i);
fi.width, i, fi.width_set = _parse_int(fmt, i);
if was_prev_index && fi.width_set { // %[6]2d
fi.good_arg_index = false;
}
@@ -995,7 +1148,7 @@ bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
fi.good_arg_index = false;
}
if i < end && fmt[i] == '*' {
arg_index, i, was_prev_index = _arg_number(^fi, arg_index, fmt, i, args.count);
arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args));
i++;
fi.prec, arg_index, fi.prec_set = int_from_arg(args, arg_index);
if fi.prec < 0 {
@@ -1007,16 +1160,16 @@ bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
}
was_prev_index = false;
} else {
fi.prec, i, fi.prec_set = parse_int(fmt, i);
fi.prec, i, fi.prec_set = _parse_int(fmt, i);
if !fi.prec_set {
fi.prec_set = true;
fi.prec = 0;
// fi.prec_set = true;
// fi.prec = 0;
}
}
}
if !was_prev_index {
arg_index, i, was_prev_index = _arg_number(^fi, arg_index, fmt, i, args.count);
arg_index, i, was_prev_index = _arg_number(&fi, arg_index, fmt, i, len(args));
}
if i >= end {
@@ -1031,28 +1184,28 @@ bprintf :: proc(b: ^[]byte, fmt: string, args: ..any) -> int {
write_byte(b, '%');
} else if !fi.good_arg_index {
write_string(b, "%!(BAD ARGUMENT NUMBER)");
} else if arg_index >= args.count {
} else if arg_index >= len(args) {
write_string(b, "%!(MISSING ARGUMENT)");
} else {
fmt_arg(^fi, args[arg_index], verb);
fmt_arg(&fi, args[arg_index], verb);
arg_index++;
}
}
if !fi.reordered && arg_index < args.count {
if !fi.reordered && arg_index < len(args) {
write_string(b, "%!(EXTRA ");
for arg, index in args[arg_index..] {
if index > 0 {
write_string(b, ", ");
}
if arg.data == nil || arg.type_info == nil {
if arg == nil {
write_string(b, "<nil>");
} else {
fmt_arg(^fi, args[index], 'v');
fmt_arg(&fi, args[index], 'v');
}
}
write_string(b, ")");
}
return b.count;
return to_string(b^);
}
+36 -35
View File
@@ -1,14 +1,14 @@
crc32 :: proc(data: []byte) -> u32 {
result := ~cast(u32)0;
result := ~u32(0);
for b in data {
result = result>>8 ~ _crc32_table[(result ~ cast(u32)b) & 0xff];
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
}
return ~result;
}
crc64 :: proc(data: []byte) -> u64 {
result := ~cast(u64)0;
result := ~u64(0);
for b in data {
result = result>>8 ~ _crc64_table[(result ~ cast(u64)b) & 0xff];
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
}
return ~result;
}
@@ -16,7 +16,7 @@ crc64 :: proc(data: []byte) -> u64 {
fnv32 :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5;
for b in data {
h = (h * 0x01000193) ~ cast(u32)b;
h = (h * 0x01000193) ~ u32(b);
}
return h;
}
@@ -24,7 +24,7 @@ fnv32 :: proc(data: []byte) -> u32 {
fnv64 :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325;
for b in data {
h = (h * 0x100000001b3) ~ cast(u64)b;
h = (h * 0x100000001b3) ~ u64(b);
}
return h;
}
@@ -32,7 +32,7 @@ fnv64 :: proc(data: []byte) -> u64 {
fnv32a :: proc(data: []byte) -> u32 {
h: u32 = 0x811c9dc5;
for b in data {
h = (h ~ cast(u32)b) * 0x01000193;
h = (h ~ u32(b)) * 0x01000193;
}
return h;
}
@@ -40,7 +40,7 @@ fnv32a :: proc(data: []byte) -> u32 {
fnv64a :: proc(data: []byte) -> u64 {
h: u64 = 0xcbf29ce484222325;
for b in data {
h = (h ~ cast(u64)b) * 0x100000001b3;
h = (h ~ u64(b)) * 0x100000001b3;
}
return h;
}
@@ -50,12 +50,12 @@ murmur32 :: proc(data: []byte) -> u32 {
c2_32: u32 : 0x1b873593;
h1: u32 = 0;
nblocks := data.count/4;
p := data.data;
nblocks := len(data)/4;
p := &data[0];
p1 := p + 4*nblocks;
for ; p < p1; p += 4 {
k1 := (cast(^u32)p)^;
k1 := ^u32(p)^;
k1 *= c1_32;
k1 = (k1 << 15) | (k1 >> 17);
@@ -69,22 +69,22 @@ murmur32 :: proc(data: []byte) -> u32 {
tail := data[nblocks*4 ..];
k1: u32;
match tail.count&3 {
match len(tail)&3 {
case 3:
k1 ~= cast(u32)tail[2] << 16;
k1 ~= u32(tail[2]) << 16;
fallthrough;
case 2:
k1 ~= cast(u32)tail[2] << 8;
k1 ~= u32(tail[2]) << 8;
fallthrough;
case 1:
k1 ~= cast(u32)tail[0];
k1 ~= u32(tail[0]);
k1 *= c1_32;
k1 = (k1 << 15) | (k1 >> 17) ;
k1 *= c2_32;
h1 ~= k1;
}
h1 ~= cast(u32)data.count;
h1 ~= u32(len(data));
h1 ~= h1 >> 16;
h1 *= 0x85ebca6b;
@@ -98,12 +98,12 @@ murmur32 :: proc(data: []byte) -> u32 {
murmur64 :: proc(data: []byte) -> u64 {
SEED :: 0x9747b28c;
when false && size_of(int) == 8 {
when size_of(int) == 8 {
m :: 0xc6a4a7935bd1e995;
r :: 47;
h: u64 = SEED ~ (cast(u64)data.count * m);
data64 := slice_ptr(cast(^u64)^data[0], data.count/size_of(u64));
h: u64 = SEED ~ (u64(len(data)) * m);
data64 := slice_ptr(^u64(&data[0]), len(data)/size_of(u64));
for _, i in data64 {
k := data64[i];
@@ -116,15 +116,15 @@ murmur64 :: proc(data: []byte) -> u64 {
h *= m;
}
match data.count&7 {
case 7: h ~= cast(u64)data[6] << 48; fallthrough;
case 6: h ~= cast(u64)data[5] << 40; fallthrough;
case 5: h ~= cast(u64)data[4] << 32; fallthrough;
case 4: h ~= cast(u64)data[3] << 24; fallthrough;
case 3: h ~= cast(u64)data[2] << 16; fallthrough;
case 2: h ~= cast(u64)data[1] << 8; fallthrough;
match len(data)&7 {
case 7: h ~= u64(data[6]) << 48; fallthrough;
case 6: h ~= u64(data[5]) << 40; fallthrough;
case 5: h ~= u64(data[4]) << 32; fallthrough;
case 4: h ~= u64(data[3]) << 24; fallthrough;
case 3: h ~= u64(data[2]) << 16; fallthrough;
case 2: h ~= u64(data[1]) << 8; fallthrough;
case 1:
h ~= cast(u64)data[0];
h ~= u64(data[0]);
h *= m;
}
@@ -137,11 +137,11 @@ murmur64 :: proc(data: []byte) -> u64 {
m :: 0x5bd1e995;
r :: 24;
h1: u32 = cast(u32)SEED ~ cast(u32)data.count;
h2: u32 = SEED >> 32;
h1 := u32(SEED) ~ u32(len(data));
h2 := u32(SEED) >> 32;
data32 := slice_ptr(cast(^u32)^data[0], data.count/size_of(u32));
len := data.count;
data32 := slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
len := len(data);
i := 0;
for len >= 8 {
@@ -174,16 +174,17 @@ murmur64 :: proc(data: []byte) -> u64 {
len -= 4;
}
data8 := slice_to_bytes(data32[i..])[..3];
// TODO(bill): Fix this
#no_bounds_check data8 := slice_to_bytes(data32[i..])[0..<3];
match len {
case 3:
h2 ~= cast(u32)data8[2] << 16;
h2 ~= u32(data8[2]) << 16;
fallthrough;
case 2:
h2 ~= cast(u32)data8[1] << 8;
h2 ~= u32(data8[1]) << 8;
fallthrough;
case 1:
h2 ~= cast(u32)data8[0];
h2 ~= u32(data8[0]);
h2 *= m;
}
+42 -41
View File
@@ -20,21 +20,25 @@ Vec2 :: [vector 2]f32;
Vec3 :: [vector 3]f32;
Vec4 :: [vector 4]f32;
Mat2 :: [2]Vec2;
Mat3 :: [3]Vec3;
Mat4 :: [4]Vec4;
// Column major
Mat2 :: [2][2]f32;
Mat3 :: [3][3]f32;
Mat4 :: [4][4]f32;
Complex :: complex64;
Quat :: quaternion128;
sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
sin :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
sin :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
sin :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
sin :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
cos :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
cos :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
cos :: proc(θ: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
cos :: proc(θ: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
tan :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); }
tan :: proc(x: f64) -> f64 #inline { return sin(x)/cos(x); }
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";
@@ -43,8 +47,8 @@ pow :: proc(x, power: f64) -> f64 #foreign __llvm_core "llvm.pow.f64";
lerp :: proc(a, b, t: f32) -> f32 { return a*(1-t) + b*t; }
lerp :: proc(a, b, t: f64) -> f64 { return a*(1-t) + b*t; }
sign :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
sign :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; }
sign :: proc(x: f32) -> f32 { return x >= 0 ? +1 : -1; }
sign :: proc(x: f64) -> f64 { return x >= 0 ? +1 : -1; }
bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16";
bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32";
@@ -55,29 +59,29 @@ fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
copy_sign :: proc(x, y: f32) -> f32 {
ix := transmute(u32)x;
iy := transmute(u32)y;
ix := transmute(u32, x);
iy := transmute(u32, y);
ix &= 0x7fff_ffff;
ix |= iy & 0x8000_0000;
return transmute(f32)ix;
return transmute(f32, ix);
}
copy_sign :: proc(x, y: f64) -> f64 {
ix := transmute(u64)x;
iy := transmute(u64)y;
ix := transmute(u64, x);
iy := transmute(u64, y);
ix &= 0x7fff_ffff_ffff_ff;
ix |= iy & 0x8000_0000_0000_0000;
return transmute(f64)ix;
return transmute(f64, ix);
}
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 ? cast(f32)cast(i64)x : cast(f32)cast(i64)(x-0.5); } // TODO: Get accurate versions
floor :: proc(x: f64) -> f64 { return x >= 0 ? cast(f64)cast(i64)x : cast(f64)cast(i64)(x-0.5); } // TODO: Get accurate versions
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 ? cast(f32)cast(i64)x : cast(f32)cast(i64)(x+1); } // TODO: Get accurate versions
ceil :: proc(x: f64) -> f64 { return x < 0 ? cast(f64)cast(i64)x : cast(f64)cast(i64)(x+1); } // 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; }
@@ -120,32 +124,32 @@ mag :: proc(v: Vec2) -> f32 { return sqrt(dot(v, v)); }
mag :: proc(v: Vec3) -> f32 { return sqrt(dot(v, v)); }
mag :: proc(v: Vec4) -> f32 { return sqrt(dot(v, v)); }
norm :: proc(v: Vec2) -> Vec2 { return v / Vec2{mag(v)}; }
norm :: proc(v: Vec3) -> Vec3 { return v / Vec3{mag(v)}; }
norm :: proc(v: Vec4) -> Vec4 { return v / Vec4{mag(v)}; }
norm :: proc(v: Vec2) -> Vec2 { return v / mag(v); }
norm :: proc(v: Vec3) -> Vec3 { return v / mag(v); }
norm :: proc(v: Vec4) -> Vec4 { return v / mag(v); }
norm0 :: proc(v: Vec2) -> Vec2 {
m := mag(v);
if m == 0 {
return Vec2{0};
return 0;
}
return v / Vec2{m};
return v / m;
}
norm0 :: proc(v: Vec3) -> Vec3 {
m := mag(v);
if m == 0 {
return Vec3{0};
return 0;
}
return v / Vec3{m};
return v / m;
}
norm0 :: proc(v: Vec4) -> Vec4 {
m := mag(v);
if m == 0 {
return Vec4{0};
return 0;
}
return v / Vec4{m};
return v / m;
}
@@ -160,8 +164,8 @@ mat4_identity :: proc() -> Mat4 {
}
mat4_transpose :: proc(m: Mat4) -> Mat4 {
for j in 0..4 {
for i in 0..4 {
for j in 0..<4 {
for i in 0..<4 {
m[i][j], m[j][i] = m[j][i], m[i][j];
}
}
@@ -170,8 +174,8 @@ mat4_transpose :: proc(m: Mat4) -> Mat4 {
mul :: proc(a, b: Mat4) -> Mat4 {
c: Mat4;
for j in 0..4 {
for i in 0..4 {
for j in 0..<4 {
for i in 0..<4 {
c[j][i] = a[0][i]*b[j][0] +
a[1][i]*b[j][1] +
a[2][i]*b[j][2] +
@@ -317,10 +321,10 @@ look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
m: Mat4;
m[0] = Vec4{+s.x, +s.y, +s.z, 0};
m[1] = Vec4{+u.x, +u.y, +u.z, 0};
m[2] = Vec4{-f.x, -f.y, -f.z, 0};
m[3] = Vec4{dot(s, eye), dot(u, eye), dot(f, eye), 1};
m[0] = [4]f32{+s.x, +s.y, +s.z, 0};
m[1] = [4]f32{+u.x, +u.y, +u.z, 0};
m[2] = [4]f32{-f.x, -f.y, -f.z, 0};
m[3] = [4]f32{dot(s, eye), dot(u, eye), dot(f, eye), 1};
return m;
}
@@ -376,6 +380,3 @@ F64_MIN_10_EXP :: -307; // min decimal exponent
F64_MIN_EXP :: -1021; // min binary exponent
F64_RADIX :: 2; // exponent radix
F64_ROUNDS :: 1; // addition rounding: near
+17 -17
View File
@@ -19,7 +19,7 @@ copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr {
return __mem_copy_non_overlapping(dst, src, len);
}
compare :: proc(a, b: []byte) -> int {
return __mem_compare(a.data, b.data, min(a.count, b.count));
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
}
@@ -39,13 +39,13 @@ is_power_of_two :: proc(x: int) -> bool {
align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
assert(is_power_of_two(align));
a := cast(uint)align;
p := cast(uint)ptr;
a := uint(align);
p := uint(ptr);
modulo := p & (a-1);
if modulo != 0 {
p += a - modulo;
}
return cast(rawptr)p;
return rawptr(p);
}
@@ -56,9 +56,9 @@ Allocation_Header :: struct {
allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: int) {
header.size = size;
ptr := cast(^int)(header+1);
ptr := ^int(header+1);
for i := 0; cast(rawptr)ptr < data; i++ {
for i := 0; rawptr(ptr) < data; i++ {
(ptr+i)^ = -1;
}
}
@@ -66,11 +66,11 @@ allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
if data == nil {
return nil;
}
p := cast(^int)data;
p := ^int(data);
for (p-1)^ == -1 {
p = (p-1);
}
return cast(^Allocation_Header)p-1;
return ^Allocation_Header(p-1);
}
@@ -96,13 +96,13 @@ Arena_Temp_Memory :: struct {
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
backing = Allocator{};
memory = data[..0];
memory = data[0..<0];
temp_count = 0;
}
init_arena_from_context :: proc(using a: ^Arena, size: int) {
backing = context.allocator;
memory = new_slice(byte, size);
memory = make([]byte, size);
temp_count = 0;
}
@@ -127,18 +127,18 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
using Allocator_Mode;
arena := cast(^Arena)allocator_data;
arena := ^Arena(allocator_data);
match mode {
case ALLOC:
total_size := size + alignment;
if arena.offset + total_size > arena.memory.count {
if arena.offset + total_size > len(arena.memory) {
fmt.fprintln(os.stderr, "Arena out of memory");
return nil;
}
#no_bounds_check end := ^arena.memory[arena.offset];
#no_bounds_check end := &arena.memory[arena.offset];
ptr := align_forward(end, alignment);
arena.offset += total_size;
@@ -161,15 +161,15 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
tmp: Arena_Temp_Memory;
tmp.arena = a;
tmp.original_count = a.memory.count;
tmp.original_count = len(a.memory);
a.temp_count++;
return tmp;
}
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
assert(arena.memory.count >= original_count);
assert(len(arena.memory) >= original_count);
assert(arena.temp_count > 0);
arena.memory.count = original_count;
arena.memory = arena.memory[0..<original_count];
arena.temp_count--;
}
@@ -221,7 +221,7 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int {
return WORD_SIZE;
case Vector:
size := size_of_type_info(info.elem);
count := cast(int)max(prev_pow2(cast(i64)info.count), 1);
count := int(max(prev_pow2(i64(info.count)), 1));
total := size * count;
return clamp(total, 1, MAX_ALIGN);
case Tuple:
+55 -45
View File
@@ -1,4 +1,5 @@
#foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
#foreign_system_library lib "gl" when ODIN_OS == "linux";
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
#import "sys/wgl.odin" when ODIN_OS == "windows";
#load "opengl_constants.odin";
@@ -30,15 +31,19 @@ TexImage2D :: proc(target, level, internal_format,
format, type: i32, pixels: rawptr) #foreign lib "glTexImage2D";
string_data :: proc(s: string) -> ^u8 #inline { return ^s[0]; }
_string_data :: proc(s: string) -> ^u8 #inline { return &s[0]; }
_libgl := win32.LoadLibraryA(string_data("opengl32.dll\x00"));
_libgl := win32.LoadLibraryA(_string_data("opengl32.dll\x00"));
GetProcAddress :: proc(name: string) -> proc() #cc_c {
assert(name[name.count-1] == 0);
res := wgl.GetProcAddress(name.data);
if name[len(name)-1] == 0 {
name = name[0..<len(name)-1];
}
// NOTE(bill): null terminated
assert((&name[0] + len(name))^ == 0);
res := wgl.GetProcAddress(&name[0]);
if res == nil {
res = win32.GetProcAddress(_libgl, name.data);
res = win32.GetProcAddress(_libgl, &name[0]);
}
return res;
}
@@ -46,6 +51,7 @@ GetProcAddress :: proc(name: string) -> proc() #cc_c {
GenBuffers: proc(count: i32, buffers: ^u32) #cc_c;
GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c;
GenSamplers: proc(count: i32, buffers: ^u32) #cc_c;
DeleteBuffers: proc(count: i32, buffers: ^u32) #cc_c;
BindBuffer: proc(target: i32, buffer: u32) #cc_c;
BindVertexArray: proc(buffer: u32) #cc_c;
BindSampler: proc(position: i32, sampler: u32) #cc_c;
@@ -102,55 +108,59 @@ UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32
GetUniformLocation: proc(program: u32, name: ^byte) -> i32 #cc_c;
init :: proc() {
set_proc_address :: proc(p: rawptr, name: string) #inline { (cast(^(proc() #cc_c))p)^ = GetProcAddress(name); }
set_proc_address :: proc(p: rawptr, name: string) #inline {
x := ^(proc() #cc_c)(p);
x^ = GetProcAddress(name);
}
set_proc_address(^GenBuffers, "glGenBuffers\x00");
set_proc_address(^GenVertexArrays, "glGenVertexArrays\x00");
set_proc_address(^GenSamplers, "glGenSamplers\x00");
set_proc_address(^BindBuffer, "glBindBuffer\x00");
set_proc_address(^BindSampler, "glBindSampler\x00");
set_proc_address(^BindVertexArray, "glBindVertexArray\x00");
set_proc_address(^BufferData, "glBufferData\x00");
set_proc_address(^BufferSubData, "glBufferSubData\x00");
set_proc_address(&GenBuffers, "glGenBuffers\x00");
set_proc_address(&GenVertexArrays, "glGenVertexArrays\x00");
set_proc_address(&GenSamplers, "glGenSamplers\x00");
set_proc_address(&DeleteBuffers, "glDeleteBuffers\x00");
set_proc_address(&BindBuffer, "glBindBuffer\x00");
set_proc_address(&BindSampler, "glBindSampler\x00");
set_proc_address(&BindVertexArray, "glBindVertexArray\x00");
set_proc_address(&BufferData, "glBufferData\x00");
set_proc_address(&BufferSubData, "glBufferSubData\x00");
set_proc_address(^DrawArrays, "glDrawArrays\x00");
set_proc_address(^DrawElements, "glDrawElements\x00");
set_proc_address(&DrawArrays, "glDrawArrays\x00");
set_proc_address(&DrawElements, "glDrawElements\x00");
set_proc_address(^MapBuffer, "glMapBuffer\x00");
set_proc_address(^UnmapBuffer, "glUnmapBuffer\x00");
set_proc_address(&MapBuffer, "glMapBuffer\x00");
set_proc_address(&UnmapBuffer, "glUnmapBuffer\x00");
set_proc_address(^VertexAttribPointer, "glVertexAttribPointer\x00");
set_proc_address(^EnableVertexAttribArray, "glEnableVertexAttribArray\x00");
set_proc_address(&VertexAttribPointer, "glVertexAttribPointer\x00");
set_proc_address(&EnableVertexAttribArray, "glEnableVertexAttribArray\x00");
set_proc_address(^CreateShader, "glCreateShader\x00");
set_proc_address(^ShaderSource, "glShaderSource\x00");
set_proc_address(^CompileShader, "glCompileShader\x00");
set_proc_address(^CreateProgram, "glCreateProgram\x00");
set_proc_address(^AttachShader, "glAttachShader\x00");
set_proc_address(^DetachShader, "glDetachShader\x00");
set_proc_address(^DeleteShader, "glDeleteShader\x00");
set_proc_address(^LinkProgram, "glLinkProgram\x00");
set_proc_address(^UseProgram, "glUseProgram\x00");
set_proc_address(^DeleteProgram, "glDeleteProgram\x00");
set_proc_address(&CreateShader, "glCreateShader\x00");
set_proc_address(&ShaderSource, "glShaderSource\x00");
set_proc_address(&CompileShader, "glCompileShader\x00");
set_proc_address(&CreateProgram, "glCreateProgram\x00");
set_proc_address(&AttachShader, "glAttachShader\x00");
set_proc_address(&DetachShader, "glDetachShader\x00");
set_proc_address(&DeleteShader, "glDeleteShader\x00");
set_proc_address(&LinkProgram, "glLinkProgram\x00");
set_proc_address(&UseProgram, "glUseProgram\x00");
set_proc_address(&DeleteProgram, "glDeleteProgram\x00");
set_proc_address(^GetShaderiv, "glGetShaderiv\x00");
set_proc_address(^GetProgramiv, "glGetProgramiv\x00");
set_proc_address(^GetShaderInfoLog, "glGetShaderInfoLog\x00");
set_proc_address(^GetProgramInfoLog, "glGetProgramInfoLog\x00");
set_proc_address(&GetShaderiv, "glGetShaderiv\x00");
set_proc_address(&GetProgramiv, "glGetProgramiv\x00");
set_proc_address(&GetShaderInfoLog, "glGetShaderInfoLog\x00");
set_proc_address(&GetProgramInfoLog, "glGetProgramInfoLog\x00");
set_proc_address(^ActiveTexture, "glActiveTexture\x00");
set_proc_address(^GenerateMipmap, "glGenerateMipmap\x00");
set_proc_address(&ActiveTexture, "glActiveTexture\x00");
set_proc_address(&GenerateMipmap, "glGenerateMipmap\x00");
set_proc_address(^Uniform1i, "glUniform1i\x00");
set_proc_address(^UniformMatrix4fv, "glUniformMatrix4fv\x00");
set_proc_address(&Uniform1i, "glUniform1i\x00");
set_proc_address(&UniformMatrix4fv, "glUniformMatrix4fv\x00");
set_proc_address(^GetUniformLocation, "glGetUniformLocation\x00");
set_proc_address(&GetUniformLocation, "glGetUniformLocation\x00");
set_proc_address(^SamplerParameteri, "glSamplerParameteri\x00");
set_proc_address(^SamplerParameterf, "glSamplerParameterf\x00");
set_proc_address(^SamplerParameteriv, "glSamplerParameteriv\x00");
set_proc_address(^SamplerParameterfv, "glSamplerParameterfv\x00");
set_proc_address(^SamplerParameterIiv, "glSamplerParameterIiv\x00");
set_proc_address(^SamplerParameterIuiv, "glSamplerParameterIuiv\x00");
set_proc_address(&SamplerParameteri, "glSamplerParameteri\x00");
set_proc_address(&SamplerParameterf, "glSamplerParameterf\x00");
set_proc_address(&SamplerParameteriv, "glSamplerParameteriv\x00");
set_proc_address(&SamplerParameterfv, "glSamplerParameterfv\x00");
set_proc_address(&SamplerParameterIiv, "glSamplerParameterIiv\x00");
set_proc_address(&SamplerParameterIuiv, "glSamplerParameterIuiv\x00");
}
+1 -1
View File
@@ -1,3 +1,3 @@
#load "os_windows.odin" when ODIN_OS == "windows";
#load "os_x.odin" when ODIN_OS == "osx";
#load "os_linux.odin" when ODIN_OS == "linux";
+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;
}
+154 -82
View File
@@ -1,6 +1,5 @@
#import w "sys/windows.odin";
#import "fmt.odin";
#import win32 "sys/windows.odin";
#import fmt "fmt.odin";
Handle :: int;
File_Time :: u64;
@@ -50,117 +49,127 @@ WSAECONNRESET: Errno : 10054;
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
// "Argv" arguments converted to Odin strings
immutable args := _alloc_command_line_arguments();
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
if path.count == 0 {
if len(path) == 0 {
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
}
access: u32;
match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY: access = w.FILE_GENERIC_READ;
case O_WRONLY: access = w.FILE_GENERIC_WRITE;
case O_RDWR: access = w.FILE_GENERIC_READ | w.FILE_GENERIC_WRITE;
case O_RDONLY: access = win32.FILE_GENERIC_READ;
case O_WRONLY: access = win32.FILE_GENERIC_WRITE;
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE;
}
if mode&O_CREAT != 0 {
access |= w.FILE_GENERIC_WRITE;
access |= win32.FILE_GENERIC_WRITE;
}
if mode&O_APPEND != 0 {
access &~= w.FILE_GENERIC_WRITE;
access |= w.FILE_APPEND_DATA;
access &~= win32.FILE_GENERIC_WRITE;
access |= win32.FILE_APPEND_DATA;
}
share_mode := cast(u32)(w.FILE_SHARE_READ|w.FILE_SHARE_WRITE);
sa: ^w.Security_Attributes = nil;
sa_inherit := w.Security_Attributes{length = size_of(w.Security_Attributes), inherit_handle = 1};
share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
sa: ^win32.Security_Attributes = nil;
sa_inherit := win32.Security_Attributes{length = size_of(win32.Security_Attributes), inherit_handle = 1};
if mode&O_CLOEXEC == 0 {
sa = ^sa_inherit;
sa = &sa_inherit;
}
create_mode: u32;
match {
case mode&(O_CREAT|O_EXCL) == (O_CREAT | O_EXCL):
create_mode = w.CREATE_NEW;
create_mode = win32.CREATE_NEW;
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
create_mode = w.CREATE_ALWAYS;
create_mode = win32.CREATE_ALWAYS;
case mode&O_CREAT == O_CREAT:
create_mode = w.OPEN_ALWAYS;
create_mode = win32.OPEN_ALWAYS;
case mode&O_TRUNC == O_TRUNC:
create_mode = w.TRUNCATE_EXISTING;
create_mode = win32.TRUNCATE_EXISTING;
default:
create_mode = w.OPEN_EXISTING;
create_mode = win32.OPEN_EXISTING;
}
buf: [300]byte;
copy(buf[..], cast([]byte)path);
copy(buf[..], []byte(path));
handle := cast(Handle)w.CreateFileA(^buf[0], access, share_mode, sa, create_mode, w.FILE_ATTRIBUTE_NORMAL, nil);
handle := Handle(win32.CreateFileA(&buf[0], access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
if handle != INVALID_HANDLE {
return handle, ERROR_NONE;
}
err := w.GetLastError();
return INVALID_HANDLE, cast(Errno)err;
err := win32.GetLastError();
return INVALID_HANDLE, Errno(err);
}
close :: proc(fd: Handle) {
w.CloseHandle(cast(w.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) {
bytes_written: i32;
e := w.WriteFile(cast(w.Handle)fd, data.data, cast(i32)data.count, ^bytes_written, nil);
if e == w.FALSE {
err := w.GetLastError();
return 0, cast(Errno)err;
if len(data) == 0 {
return 0, ERROR_NONE;
}
return cast(int)bytes_written, ERROR_NONE;
bytes_written: i32;
e := win32.WriteFile(win32.Handle(fd), &data[0], i32(len(data)), &bytes_written, nil);
if e == win32.FALSE {
err := win32.GetLastError();
return 0, Errno(err);
}
return int(bytes_written), ERROR_NONE;
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
bytes_read: i32;
e := w.ReadFile(cast(w.Handle)fd, data.data, cast(u32)data.count, ^bytes_read, nil);
if e == w.FALSE {
err := w.GetLastError();
return 0, cast(Errno)err;
if len(data) == 0 {
return 0, ERROR_NONE;
}
return cast(int)bytes_read, ERROR_NONE;
bytes_read: i32;
e := win32.ReadFile(win32.Handle(fd), &data[0], u32(len(data)), &bytes_read, nil);
if e == win32.FALSE {
err := win32.GetLastError();
return 0, Errno(err);
}
return int(bytes_read), ERROR_NONE;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
using w;
w: u32;
match whence {
case 0: w = FILE_BEGIN;
case 1: w = FILE_CURRENT;
case 2: w = FILE_END;
case 0: w = win32.FILE_BEGIN;
case 1: w = win32.FILE_CURRENT;
case 2: w = win32.FILE_END;
}
hi := cast(i32)(offset>>32);
lo := cast(i32)(offset);
ft := GetFileType(cast(Handle)fd);
if ft == FILE_TYPE_PIPE {
hi := i32(offset>>32);
lo := i32(offset);
ft := win32.GetFileType(win32.Handle(fd));
if ft == win32.FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE;
}
dw_ptr := SetFilePointer(cast(Handle)fd, lo, ^hi, w);
if dw_ptr == INVALID_SET_FILE_POINTER {
err := GetLastError();
return 0, cast(Errno)err;
dw_ptr := win32.SetFilePointer(win32.Handle(fd), lo, &hi, w);
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
err := win32.GetLastError();
return 0, Errno(err);
}
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
}
// NOTE(bill): Uses startup to initialize it
stdin := get_std_handle(w.STD_INPUT_HANDLE);
stdout := get_std_handle(w.STD_OUTPUT_HANDLE);
stderr := get_std_handle(w.STD_ERROR_HANDLE);
stdin := get_std_handle(win32.STD_INPUT_HANDLE);
stdout := get_std_handle(win32.STD_OUTPUT_HANDLE);
stderr := get_std_handle(win32.STD_ERROR_HANDLE);
get_std_handle :: proc(h: int) -> Handle {
fd := w.GetStdHandle(cast(i32)h);
w.SetHandleInformation(fd, w.HANDLE_FLAG_INHERIT, 0);
return cast(Handle)fd;
fd := win32.GetStdHandle(i32(h));
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
return Handle(fd);
}
@@ -169,38 +178,35 @@ get_std_handle :: proc(h: int) -> Handle {
last_write_time :: proc(fd: Handle) -> File_Time {
file_info: w.By_Handle_File_Information;
w.GetFileInformationByHandle(cast(w.Handle)fd, ^file_info);
lo := cast(File_Time)file_info.last_write_time.lo;
hi := cast(File_Time)file_info.last_write_time.hi;
file_info: win32.By_Handle_File_Information;
win32.GetFileInformationByHandle(win32.Handle(fd), &file_info);
lo := File_Time(file_info.last_write_time.lo);
hi := File_Time(file_info.last_write_time.hi);
return lo | hi << 32;
}
last_write_time_by_name :: proc(name: string) -> File_Time {
last_write_time: w.Filetime;
data: w.File_Attribute_Data;
last_write_time: win32.Filetime;
data: win32.File_Attribute_Data;
buf: [1024]byte;
assert(buf.count > name.count);
assert(len(buf) > len(name));
copy(buf[..], cast([]byte)name);
copy(buf[..], []byte(name));
if w.GetFileAttributesExA(^buf[0], w.GetFileExInfoStandard, ^data) != 0 {
if win32.GetFileAttributesExA(&buf[0], win32.GetFileExInfoStandard, &data) != 0 {
last_write_time = data.last_write_time;
}
l := cast(File_Time)last_write_time.lo;
h := cast(File_Time)last_write_time.hi;
l := File_Time(last_write_time.lo);
h := File_Time(last_write_time.hi);
return l | h << 32;
}
read_entire_file :: proc(name: string) -> ([]byte, bool) {
buf: [300]byte;
copy(buf[..], cast([]byte)name);
copy(buf[..], []byte(name));
fd, err := open(name, O_RDONLY, 0);
if err != ERROR_NONE {
@@ -209,13 +215,16 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
defer close(fd);
length: i64;
file_size_ok := w.GetFileSizeEx(cast(w.Handle)fd, ^length) != 0;
if !file_size_ok {
if ok := win32.GetFileSizeEx(win32.Handle(fd), &length) != 0; !ok {
return nil, false;
}
data := new_slice(u8, length);
if data.data == nil {
if length == 0 {
return nil, true;
}
data := make([]byte, length);
if data == nil {
return nil, false;
}
@@ -227,18 +236,18 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
to_read: u32;
MAX :: 1<<32-1;
if remaining <= MAX {
to_read = cast(u32)remaining;
to_read = u32(remaining);
} else {
to_read = MAX;
}
w.ReadFile(cast(w.Handle)fd, ^data[total_read], to_read, ^single_read_length, nil);
win32.ReadFile(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil);
if single_read_length <= 0 {
free(data);
return nil, false;
}
total_read += cast(i64)single_read_length;
total_read += i64(single_read_length);
}
return data, true;
@@ -247,7 +256,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
heap_alloc :: proc(size: int) -> rawptr {
return w.HeapAlloc(w.GetProcessHeap(), w.HEAP_ZERO_MEMORY, size);
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
if new_size == 0 {
@@ -257,25 +266,88 @@ heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
if ptr == nil {
return heap_alloc(new_size);
}
return w.HeapReAlloc(w.GetProcessHeap(), w.HEAP_ZERO_MEMORY, ptr, new_size);
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
}
heap_free :: proc(ptr: rawptr) {
if ptr == nil {
return;
}
w.HeapFree(w.GetProcessHeap(), 0, ptr);
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
}
exit :: proc(code: int) {
w.ExitProcess(cast(u32)code);
win32.ExitProcess(u32(code));
}
current_thread_id :: proc() -> int {
return cast(int)w.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 "strings.odin";
Handle :: i32;
File_Time :: u64;
Errno :: int;
// INVALID_HANDLE: Handle : -1;
// TODO(zangent): Find out how to make this work on x64 and x32.
AddressSize :: i64;
// INVALID_HANDLE: Handle : -1;
O_RDONLY :: 0x00000;
O_WRONLY :: 0x00001;
@@ -19,91 +22,178 @@ O_APPEND :: 0x00400;
O_SYNC :: 0x01000;
O_ASYNC :: 0x02000;
O_CLOEXEC :: 0x80000;
SEEK_SET :: 0;
SEEK_CUR :: 1;
SEEK_END :: 2;
SEEK_DATA :: 3;
SEEK_HOLE :: 4;
SEEK_MAX :: SEEK_HOLE;
// ERROR_NONE: Errno : 0;
// ERROR_FILE_NOT_FOUND: Errno : 2;
// ERROR_PATH_NOT_FOUND: Errno : 3;
// ERROR_ACCESS_DENIED: Errno : 5;
// ERROR_NO_MORE_FILES: Errno : 18;
// ERROR_HANDLE_EOF: Errno : 38;
// ERROR_NETNAME_DELETED: Errno : 64;
// ERROR_FILE_EXISTS: Errno : 80;
// ERROR_BROKEN_PIPE: Errno : 109;
// ERROR_BUFFER_OVERFLOW: Errno : 111;
// ERROR_INSUFFICIENT_BUFFER: Errno : 122;
// ERROR_MOD_NOT_FOUND: Errno : 126;
// ERROR_PROC_NOT_FOUND: Errno : 127;
// ERROR_DIR_NOT_EMPTY: Errno : 145;
// ERROR_ALREADY_EXISTS: Errno : 183;
// ERROR_ENVVAR_NOT_FOUND: Errno : 203;
// ERROR_MORE_DATA: Errno : 234;
// ERROR_OPERATION_ABORTED: Errno : 995;
// ERROR_IO_PENDING: Errno : 997;
// ERROR_NOT_FOUND: Errno : 1168;
// ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
// WSAEACCES: Errno : 10013;
// WSAECONNRESET: Errno : 10054;
// NOTE(zangent): These are OS specific!
// Do not mix these up!
RTLD_LAZY :: 0x1;
RTLD_NOW :: 0x2;
RTLD_LOCAL :: 0x4;
RTLD_GLOBAL :: 0x8;
RTLD_NODELETE :: 0x80;
RTLD_NOLOAD :: 0x10;
RTLD_FIRST :: 0x100;
// Windows reserves errors >= 1<<29 for application use
// ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
args: [dynamic]string;
FileTime :: struct #ordered {
seconds: i64,
nanoseconds: i64
}
Stat :: struct #ordered {
device_id : i32, // ID of device containing file
mode : u16, // Mode of the file
nlink : u16, // Number of hard links
serial : u64, // File serial number
uid : u32, // User ID of the file's owner
gid : u32, // Group ID of the file's group
rdev : i32, // Device ID, if device
last_access : FileTime, // Time of last access
modified : FileTime, // Time of last modification
status_change : FileTime, // Time of last status change
created : FileTime, // Time of creation
size : i64, // Size of the file, in bytes
blocks : i64, // Number of blocks allocated for the file
block_size: i32, // Optimal blocksize for I/O
flags : u32, // User-defined flags for the file
gen_num : u32, // File generation number ...?
_spare : i32, // RESERVED
_reserve1,
_reserve2 : i64, // RESERVED
};
// File type
S_IFMT :: 0170000; // Type of file mask
S_IFIFO :: 0010000; // Named pipe (fifo)
S_IFCHR :: 0020000; // Character special
S_IFDIR :: 0040000; // Directory
S_IFBLK :: 0060000; // Block special
S_IFREG :: 0100000; // Regular
S_IFLNK :: 0120000; // Symbolic link
S_IFSOCK :: 0140000; // Socket
// File mode
// Read, write, execute/search by owner
S_IRWXU :: 0000700; // RWX mask for owner
S_IRUSR :: 0000400; // R for owner
S_IWUSR :: 0000200; // W for owner
S_IXUSR :: 0000100; // X for owner
// Read, write, execute/search by group
S_IRWXG :: 0000070; // RWX mask for group
S_IRGRP :: 0000040; // R for group
S_IWGRP :: 0000020; // W for group
S_IXGRP :: 0000010; // X for group
// Read, write, execute/search by others
S_IRWXO :: 0000007; // RWX mask for other
S_IROTH :: 0000004; // R for other
S_IWOTH :: 0000002; // W for other
S_IXOTH :: 0000001; // X for other
S_ISUID :: 0004000; // Set user id on execution
S_ISGID :: 0002000; // Set group id on execution
S_ISVTX :: 0001000; // Directory restrcted delete
S_ISLNK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFLNK; }
S_ISREG :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFREG; }
S_ISDIR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFDIR; }
S_ISCHR :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFCHR; }
S_ISBLK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFBLK; }
S_ISFIFO :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFIFO; }
S_ISSOCK :: proc(m: u32) -> bool #inline {return ((m) & S_IFMT) == S_IFSOCK;}
R_OK :: 4; // Test for read permission
W_OK :: 2; // Test for write permission
X_OK :: 1; // Test for execute permission
F_OK :: 0; // Test for file existance
#foreign_system_library dl "dl";
#foreign_system_library libc "c";
unix_open :: proc(path: ^u8, mode: int, perm: u32) -> Handle #foreign libc "open";
unix_close :: proc(handle: Handle) #foreign libc "close";
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "read";
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int #foreign libc "write";
unix_gettid :: proc() -> u64 #foreign libc "gettid";
unix_open :: proc(path: ^u8, mode: int) -> Handle #foreign libc "open";
unix_close :: proc(handle: Handle) #foreign libc "close";
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "read";
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> AddressSize #foreign libc "write";
unix_lseek :: proc(fs: Handle, offset: AddressSize, whence: int) -> AddressSize #foreign libc "lseek";
unix_gettid :: proc() -> u64 #foreign libc "gettid";
unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #foreign libc "stat";
unix_access :: proc(path: ^u8, mask: int) -> int #foreign libc "access";
unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
unix_free :: proc(ptr: rawptr) #foreign libc "free";
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
unix_malloc :: proc(size: int) -> rawptr #foreign libc "malloc";
unix_free :: proc(ptr: rawptr) #foreign libc "free";
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #foreign libc "realloc";
unix_getenv :: proc(^u8) -> ^u8 #foreign libc "getenv";
unix_exit :: proc(status: int) #foreign libc "exit";
unix_exit :: proc(status: int) #foreign libc "exit";
unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #foreign dl "dlopen";
unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #foreign dl "dlsym";
unix_dlclose :: proc(handle: rawptr) -> int #foreign dl "dlclose";
unix_dlerror :: proc() -> ^u8 #foreign dl "dlerror";
// TODO(zangent): Change this to just `open` when Bill fixes overloading.
open_simple :: proc(path: string, mode: int) -> (Handle, Errno) {
cstr := strings.new_c_string(path);
handle := unix_open(cstr, mode);
free(cstr);
if(handle == -1) {
return 0, 1;
}
return handle, 0;
}
// NOTE(zangent): This is here for compatability reasons. Should this be here?
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
return unix_open(path.data, mode, perm), 0;
return open_simple(path, mode);
}
close :: proc(fd: Handle) {
unix_close(fd);
}
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return unix_write(fd, data.data, data.count), 0;
write :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) {
assert(fd != -1);
bytes_written := unix_write(fd, &data[0], len(data));
if(bytes_written == -1) {
return 0, 1;
}
return bytes_written, 0;
}
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
return unix_read(fd, data.data, data.count), 0;
read :: proc(fd: Handle, data: []byte) -> (AddressSize, Errno) {
assert(fd != -1);
bytes_read := unix_read(fd, &data[0], len(data));
if(bytes_read == -1) {
return 0, 1;
}
return bytes_read, 0;
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
/*
using win32;
w: u32;
match whence {
case 0: w = FILE_BEGIN;
case 1: w = FILE_CURRENT;
case 2: w = FILE_END;
}
hi := cast(i32)(offset>>32);
lo := cast(i32)(offset);
ft := GetFileType(cast(HANDLE)fd);
if ft == FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE;
}
dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w);
if dw_ptr == INVALID_SET_FILE_POINTER {
err := GetLastError();
return 0, cast(Errno)err;
}
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
seek :: proc(fd: Handle, offset: AddressSize, whence: int) -> (AddressSize, Errno) {
assert(fd != -1);
*/
return 0, 0;
final_offset := unix_lseek(fd, offset, whence);
if(final_offset == -1) {
return 0, 1;
}
return final_offset, 0;
}
@@ -112,111 +202,85 @@ stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE);
stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE);
stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE);
/* TODO(zangent): Implement these!
last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {}
*/
/*
get_std_handle :: proc(h: int) -> Handle {
fd := win32.GetStdHandle(cast(i32)h);
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
return cast(Handle)fd;
stat :: proc(path: string) -> (Stat, bool) #inline {
s: Stat;
cstr := strings.new_c_string(path);
defer free(cstr);
ret_int := unix_stat(cstr, &s);
return s, ret_int==0;
}
last_write_time :: proc(fd: Handle) -> File_Time {
file_info: win32.BY_HANDLE_FILE_INFORMATION;
win32.GetFileInformationByHandle(cast(win32.HANDLE)fd, ^file_info);
lo := cast(File_Time)file_info.last_write_time.lo;
hi := cast(File_Time)file_info.last_write_time.hi;
return lo | hi << 32;
access :: proc(path: string, mask: int) -> bool #inline {
cstr := strings.new_c_string(path);
defer free(cstr);
return unix_access(cstr, mask) == 0;
}
last_write_time_by_name :: proc(name: string) -> File_Time {
last_write_time: win32.FILETIME;
data: win32.FILE_ATTRIBUTE_DATA;
buf: [1024]byte;
assert(buf.count > name.count);
copy(buf[:], cast([]byte)name);
if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
last_write_time = data.last_write_time;
}
l := cast(File_Time)last_write_time.lo;
h := cast(File_Time)last_write_time.hi;
return l | h << 32;
}
read_entire_file :: proc(name: string) -> ([]byte, bool) {
buf: [300]byte;
copy(buf[:], cast([]byte)name);
fd, err := open(name, O_RDONLY, 0);
if err != ERROR_NONE {
handle, err := open_simple(name, O_RDONLY);
if(err != 0) {
fmt.println("Failed to open file.");
return nil, false;
}
defer close(fd);
defer(close(handle));
length: i64;
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
if !file_size_ok {
// We have a file!
size: AddressSize;
size, err = seek(handle, 0, SEEK_END);
if(err != 0) {
fmt.println("Failed to seek to end of file.");
return nil, false;
}
data := new_slice(u8, length);
if data.data == nil {
_, err = seek(handle, 0, SEEK_SET);
if(err != 0) {
fmt.println("Failed to seek to beginning of file.");
return nil, false;
}
single_read_length: i32;
total_read: i64;
// We have a file size!
for total_read < length {
remaining := length - total_read;
to_read: u32;
MAX :: 1<<32-1;
if remaining <= MAX {
to_read = cast(u32)remaining;
} else {
to_read = MAX;
}
win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil);
if single_read_length <= 0 {
free(data);
return nil, false;
}
total_read += cast(i64)single_read_length;
data := make([]u8, size+1);
if data == nil {
fmt.println("Failed to allocate file buffer.");
return nil, false;
}
read(handle, data);
data[size] = 0;
return data, true;
}
*/
heap_alloc :: proc(size: int) -> rawptr {
heap_alloc :: proc(size: int) -> rawptr #inline {
assert(size > 0);
return unix_malloc(size);
}
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr #inline {
return unix_realloc(ptr, new_size);
}
heap_free :: proc(ptr: rawptr) {
heap_free :: proc(ptr: rawptr) #inline {
unix_free(ptr);
}
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.new_c_string(name);
cstr: ^u8 = unix_getenv(path_str);
free(path_str);
if(cstr == nil) {
return "", false;
}
return strings.to_odin_string(cstr), true;
}
exit :: proc(code: int) {
exit :: proc(code: int) #inline {
unix_exit(code);
}
@@ -226,5 +290,23 @@ current_thread_id :: proc() -> int {
return 0;
}
dlopen :: proc(filename: string, flags: int) -> rawptr #inline {
cstr := strings.new_c_string(filename);
handle := unix_dlopen(cstr, flags);
free(cstr);
return handle;
}
dlsym :: proc(handle: rawptr, symbol: string) -> (proc() #cc_c) #inline {
assert(handle != nil);
cstr := strings.new_c_string(symbol);
proc_handle := unix_dlsym(handle, cstr);
free(cstr);
return proc_handle;
}
dlclose :: proc(handle: rawptr) -> bool #inline {
assert(handle != nil);
return unix_dlclose(handle) == 0;
}
dlerror :: proc() -> string {
return strings.to_odin_string(unix_dlerror());
}
+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,
};
+88 -75
View File
@@ -18,22 +18,65 @@ parse_bool :: proc(s: string) -> (result: bool, ok: bool) {
return false, false;
}
_digit_value :: proc(r: rune) -> (int) {
ri := int(r);
v: int = 16;
match r {
case '0'..'9': v = ri-'0';
case 'a'..'z': v = ri-'a'+10;
case 'A'..'Z': v = ri-'A'+10;
}
return v;
}
parse_i64 :: proc(s: string, base: int) -> i64 {
result: i64;
for r in s {
v := _digit_value(r);
if v >= base {
break;
}
result *= i64(base);
result += i64(v);
}
return result;
}
parse_u64 :: proc(s: string, base: int) -> u64 {
result: u64;
for r in s {
v := _digit_value(r);
if v >= base {
break;
}
result *= u64(base);
result += u64(v);
}
return result;
}
parse_int :: proc(s: string, base: int) -> int {
return int(parse_i64(s, base));
}
parse_uint :: proc(s: string, base: int) -> uint {
return uint(parse_u64(s, base));
}
append_bool :: proc(buf: []byte, b: bool) -> string {
s := b ? "true" : "false";
append(buf, ..cast([]byte)s);
return cast(string)buf;
append(buf, ..[]byte(s));
return string(buf);
}
append_uint :: proc(buf: []byte, u: u64, base: int) -> string {
return append_bits(buf, u, base, false, 8*size_of(uint), digits, 0);
}
append_int :: proc(buf: []byte, i: i64, base: int) -> string {
return append_bits(buf, cast(u64)i, base, true, 8*size_of(int), 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 {
return cast(string)generic_ftoa(buf, f, fmt, prec, bit_size);
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
}
@@ -61,18 +104,18 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
flt: ^Float_Info;
match bit_size {
case 32:
bits = cast(u64)transmute(u32)cast(f32)val;
flt = ^f32_info;
bits = u64(transmute(u32, f32(val)));
flt = &f32_info;
case 64:
bits = transmute(u64)val;
flt = ^f64_info;
bits = transmute(u64, val);
flt = &f64_info;
default:
panic("strconv: invalid bit_size");
}
neg := bits>>(flt.expbits+flt.mantbits) != 0;
exp := cast(int)(bits>>flt.mantbits) & (1<<flt.expbits - 1);
mant := bits & (cast(u64)1 << flt.mantbits - 1);
exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1);
mant := bits & (u64(1) << flt.mantbits - 1);
match exp {
case 1<<flt.expbits - 1:
@@ -84,22 +127,22 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
} else {
s = "+Inf";
}
append(buf, ..cast([]byte)s);
append(buf, ..[]byte(s));
return buf;
case 0: // denormalized
exp++;
default:
mant |= cast(u64)1 << flt.mantbits;
mant |= u64(1) << flt.mantbits;
}
exp += flt.bias;
d_: Decimal;
d := ^d_;
d := &d_;
assign(d, mant);
shift(d, exp - cast(int)flt.mantbits);
shift(d, exp - int(flt.mantbits));
digs: Decimal_Slice;
shortest := prec < 0;
if shortest {
@@ -131,50 +174,33 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []byte {
match fmt {
case 'f', 'F':
add_bytes :: proc(dst: ^[]byte, w: ^int, bytes: ..byte) {
for b in bytes {
if dst.capacity <= w^ {
break;
}
dst.count++;
dst[w^] = b;
w^++;
}
}
dst := buf[..];
w := 0;
if neg {
add_bytes(^dst, ^w, '-');
} else {
add_bytes(^dst, ^w, '+');
}
append(buf, neg ? '-' : '+');
// integer, padded with zeros when needed
if digs.decimal_point > 0 {
m := min(digs.count, digs.decimal_point);
add_bytes(^dst, ^w, ..digs.digits[..m]);
append(buf, ..digs.digits[0..<m]);
for ; m < digs.decimal_point; m++ {
add_bytes(^dst, ^w, '0');
append(buf, '0');
}
} else {
add_bytes(^dst, ^w, '0');
append(buf, '0');
}
// fractional part
if prec > 0 {
add_bytes(^dst, ^w, '.');
append(buf, '.');
for i in 0..prec {
c: byte = '0';
if j := digs.decimal_point + i; 0 <= j && j < digs.count {
c = digs.digits[j];
}
add_bytes(^dst, ^w, c);
append(buf, c);
}
}
return buf[..w];
return buf;
case 'e', 'E':
panic("strconv: e/E float printing is not yet supported");
@@ -205,14 +231,14 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
332*(dp-nd) >= 100*(exp-mantbits)
*/
minexp := flt.bias+1;
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - cast(int)flt.mantbits) {
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
// Number is already its shortest
return;
}
upper_: Decimal; upper: = ^upper_;
upper_: Decimal; upper: = &upper_;
assign(upper, 2*mant - 1);
shift(upper, exp - cast(int)flt.mantbits - 1);
shift(upper, exp - int(flt.mantbits) - 1);
mantlo: u64;
explo: int;
@@ -223,13 +249,13 @@ round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
mantlo = 2*mant - 1;
explo = exp-1;
}
lower_: Decimal; lower: = ^lower_;
lower_: Decimal; lower: = &lower_;
assign(lower, 2*mantlo + 1);
shift(lower, explo - cast(int)flt.mantbits - 1);
shift(lower, explo - int(flt.mantbits) - 1);
inclusive := mant%2 == 0;
for i in 0..d.count {
for i in 0..<d.count {
l: byte = '0'; // lower digit
if i < lower.count {
l = lower.digits[i];
@@ -268,25 +294,25 @@ is_integer_negative :: proc(u: u64, is_signed: bool, bit_size: int) -> (unsigned
if is_signed {
match bit_size {
case 8:
i := cast(i8)u;
i := i8(u);
neg = i < 0;
if neg { i = -i; }
u = cast(u64)i;
u = u64(i);
case 16:
i := cast(i16)u;
i := i16(u);
neg = i < 0;
if neg { i = -i; }
u = cast(u64)i;
u = u64(i);
case 32:
i := cast(i32)u;
i := i32(u);
neg = i < 0;
if neg { i = -i; }
u = cast(u64)i;
u = u64(i);
case 64:
i := cast(i64)u;
i := i64(u);
neg = i < 0;
if neg { i = -i; }
u = cast(u64)i;
u = u64(i);
default:
panic("is_integer_negative: Unknown integer size");
}
@@ -308,34 +334,21 @@ append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: i
}
a: [65]byte;
i := a.count;
i := len(a);
neg: bool;
u, neg = is_integer_negative(u, is_signed, bit_size);
if is_pow2(cast(i64)base) {
b := cast(u64)base;
m := cast(uint)b - 1;
for u >= b {
i--;
a[i] = digits[cast(uint)u & m];
u >>= b;
}
for b := u64(base); 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];
q := u / b;
a[i] = digits[uint(u-q*b)];
u = q;
}
i--;
a[i] = digits[uint(u)];
if flags&Int_Flag.PREFIX != 0 {
ok := true;
match base {
@@ -362,6 +375,6 @@ append_bits :: proc(buf: []byte, u: u64, base: int, is_signed: bool, bit_size: 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 {
c := new_slice(byte, s.count+1);
copy(c, cast([]byte)s);
c[s.count] = 0;
return c.data;
c := make([]byte, len(s)+1);
copy(c, []byte(s));
c[len(s)] = 0;
return &c[0];
}
to_odin_string :: proc(c: ^byte) -> string {
s: string;
s.data = c;
for (c+s.count)^ != 0 {
s.count++;
len := 0;
for (c+len)^ != 0 {
len++;
}
return s;
return string(slice_ptr(c, len));
}
+2 -91
View File
@@ -1,91 +1,2 @@
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
#import "atomic.odin";
Semaphore :: struct {
_handle: win32.Handle,
}
Mutex :: struct {
_semaphore: Semaphore,
_counter: i32,
_owner: i32,
_recursion: i32,
}
current_thread_id :: proc() -> i32 {
return cast(i32)win32.GetCurrentThreadId();
}
semaphore_init :: proc(s: ^Semaphore) {
s._handle = win32.CreateSemaphoreA(nil, 0, 1<<31-1, nil);
}
semaphore_destroy :: proc(s: ^Semaphore) {
win32.CloseHandle(s._handle);
}
semaphore_post :: proc(s: ^Semaphore, count: int) {
win32.ReleaseSemaphore(s._handle, cast(i32)count, nil);
}
semaphore_release :: proc(s: ^Semaphore) #inline { semaphore_post(s, 1); }
semaphore_wait :: proc(s: ^Semaphore) {
win32.WaitForSingleObject(s._handle, win32.INFINITE);
}
mutex_init :: proc(m: ^Mutex) {
atomic.store(^m._counter, 0);
atomic.store(^m._owner, current_thread_id());
semaphore_init(^m._semaphore);
m._recursion = 0;
}
mutex_destroy :: proc(m: ^Mutex) {
semaphore_destroy(^m._semaphore);
}
mutex_lock :: proc(m: ^Mutex) {
thread_id := current_thread_id();
if atomic.fetch_add(^m._counter, 1) > 0 {
if thread_id != atomic.load(^m._owner) {
semaphore_wait(^m._semaphore);
}
}
atomic.store(^m._owner, thread_id);
m._recursion++;
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
thread_id := current_thread_id();
if atomic.load(^m._owner) == thread_id {
atomic.fetch_add(^m._counter, 1);
} else {
expected: i32 = 0;
if atomic.load(^m._counter) != 0 {
return false;
}
if atomic.compare_exchange(^m._counter, expected, 1) == 0 {
return false;
}
atomic.store(^m._owner, thread_id);
}
m._recursion++;
return true;
}
mutex_unlock :: proc(m: ^Mutex) {
recursion: i32;
thread_id := current_thread_id();
assert(thread_id == atomic.load(^m._owner));
m._recursion--;
recursion = m._recursion;
if recursion == 0 {
atomic.store(^m._owner, thread_id);
}
if atomic.fetch_add(^m._counter, -1) > 1 {
if recursion == 0 {
semaphore_release(^m._semaphore);
}
}
}
#load "sync_windows.odin" when ODIN_OS == "windows";
#load "sync_linux.odin" when ODIN_OS == "linux";
+93
View File
@@ -0,0 +1,93 @@
#import "atomics.odin";
#import "os.odin";
Semaphore :: struct {
// _handle: win32.Handle,
}
Mutex :: struct {
_semaphore: Semaphore,
_counter: i32,
_owner: i32,
_recursion: i32,
}
current_thread_id :: proc() -> i32 {
return i32(os.current_thread_id());
}
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) {
atomics.store(&m._counter, 0);
atomics.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 atomics.fetch_add(&m._counter, 1) > 0 {
if thread_id != atomics.load(&m._owner) {
semaphore_wait(&m._semaphore);
}
}
atomics.store(&m._owner, thread_id);
m._recursion++;
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
thread_id := current_thread_id();
if atomics.load(&m._owner) == thread_id {
atomics.fetch_add(&m._counter, 1);
} else {
expected: i32 = 0;
if atomics.load(&m._counter) != 0 {
return false;
}
if atomics.compare_exchange(&m._counter, expected, 1) == 0 {
return false;
}
atomics.store(&m._owner, thread_id);
}
m._recursion++;
return true;
}
mutex_unlock :: proc(m: ^Mutex) {
recursion: i32;
thread_id := current_thread_id();
assert(thread_id == atomics.load(&m._owner));
m._recursion--;
recursion = m._recursion;
if recursion == 0 {
atomics.store(&m._owner, thread_id);
}
if atomics.fetch_add(&m._counter, -1) > 1 {
if recursion == 0 {
semaphore_release(&m._semaphore);
}
}
}
+91
View File
@@ -0,0 +1,91 @@
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
#import "atomics.odin";
Semaphore :: struct {
_handle: win32.Handle,
}
Mutex :: struct {
_semaphore: Semaphore,
_counter: i32,
_owner: i32,
_recursion: i32,
}
current_thread_id :: proc() -> i32 {
return 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, 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) {
atomics.store(&m._counter, 0);
atomics.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 atomics.fetch_add(&m._counter, 1) > 0 {
if thread_id != atomics.load(&m._owner) {
semaphore_wait(&m._semaphore);
}
}
atomics.store(&m._owner, thread_id);
m._recursion++;
}
mutex_try_lock :: proc(m: ^Mutex) -> bool {
thread_id := current_thread_id();
if atomics.load(&m._owner) == thread_id {
atomics.fetch_add(&m._counter, 1);
} else {
expected: i32 = 0;
if atomics.load(&m._counter) != 0 {
return false;
}
if atomics.compare_exchange(&m._counter, expected, 1) == 0 {
return false;
}
atomics.store(&m._owner, thread_id);
}
m._recursion++;
return true;
}
mutex_unlock :: proc(m: ^Mutex) {
recursion: i32;
thread_id := current_thread_id();
assert(thread_id == atomics.load(&m._owner));
m._recursion--;
recursion = m._recursion;
if recursion == 0 {
atomics.store(&m._owner, thread_id);
}
if atomics.fetch_add(&m._counter, -1) > 1 {
if recursion == 0 {
semaphore_release(&m._semaphore);
}
}
}
+12 -2
View File
@@ -7,6 +7,7 @@ CONTEXT_FLAGS_ARB :: 0x2094;
CONTEXT_PROFILE_MASK_ARB :: 0x9126;
CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002;
CONTEXT_CORE_PROFILE_BIT_ARB :: 0x00000001;
CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x00000002;
Hglrc :: Handle;
Color_Ref :: u32;
@@ -50,8 +51,17 @@ Glyph_Metrics_Float :: struct #ordered {
cell_inc_y: f32,
}
Create_Context_Attribs_ARB_Type :: #type proc(hdc: Hdc, hshareContext: rawptr, attribList: ^i32) -> Hglrc;
Choose_Pixel_Format_ARB_Type :: #type proc(hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool #cc_c;
CreateContextAttribsARB_Type :: #type proc(hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc;
ChoosePixelFormatARB_Type :: #type proc(hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool #cc_c;
SwapIntervalEXT_Type :: #type proc(interval : i32) -> bool #cc_c;
GetExtensionsStringARB_Type :: #type proc(Hdc) -> ^byte #cc_c;
CreateContextAttribsARB: CreateContextAttribsARB_Type;
ChoosePixelFormatARB: ChoosePixelFormatARB_Type;
SwapIntervalEXT: SwapIntervalEXT_Type;
GetExtensionsStringARB: GetExtensionsStringARB_Type;
CreateContext :: proc(hdc: Hdc) -> Hglrc #foreign opengl32 "wglCreateContext";
+11 -11
View File
@@ -21,7 +21,7 @@ Bool :: i32;
Wnd_Proc :: #type proc(Hwnd, u32, Wparam, Lparam) -> Lresult #cc_c;
INVALID_HANDLE :: cast(Handle)~cast(int)0;
INVALID_HANDLE :: Handle(~int(0));
FALSE: Bool : 0;
TRUE: Bool : 1;
@@ -56,7 +56,7 @@ WM_CHAR :: 0x0102;
PM_REMOVE :: 1;
COLOR_BACKGROUND :: cast(Hbrush)(cast(int)1);
COLOR_BACKGROUND :: Hbrush(int(1));
BLACK_BRUSH :: 4;
SM_CXSCREEN :: 0;
@@ -178,7 +178,7 @@ DescribePixelFormat :: proc(dc: Hdc, pixel_format: i32, bytes : u32, pfd: ^PIXEL
GetQueryPerformanceFrequency :: proc() -> i64 {
r: i64;
QueryPerformanceFrequency(^r);
QueryPerformanceFrequency(&r);
return r;
}
@@ -260,7 +260,7 @@ FILE_TYPE_DISK :: 0x0001;
FILE_TYPE_CHAR :: 0x0002;
FILE_TYPE_PIPE :: 0x0003;
INVALID_SET_FILE_POINTER :: ~cast(u32)0;
INVALID_SET_FILE_POINTER :: ~u32(0);
@@ -313,7 +313,7 @@ Hmonitor :: Handle;
GWL_STYLE :: -16;
Hwnd_TOP :: cast(Hwnd)cast(uint)0;
Hwnd_TOP :: Hwnd(uint(0));
MONITOR_DEFAULTTONULL :: 0x00000000;
MONITOR_DEFAULTTOPRIMARY :: 0x00000001;
@@ -349,16 +349,17 @@ SetWindowPos :: proc(wnd: Hwnd, wndInsertAfter: Hwnd, x, y, width, height:
GetWindowPlacement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #foreign user32;
SetWindowPlacement :: proc(wnd: Hwnd, wndpl: ^Window_Placement) -> Bool #foreign user32;
GetWindowRect :: proc(wnd: Hwnd, rect: ^Rect) -> Bool #foreign user32;
GetWindowLongPtrA :: proc(wnd: Hwnd, index: i32) -> i64 #foreign user32;
SetWindowLongPtrA :: proc(wnd: Hwnd, index: i32, new: i64) -> i64 #foreign user32;
GetWindowText :: proc(wnd: Hwnd, str: ^byte, maxCount: i32) -> i32 #foreign user32;
HIWORD :: proc(wParam: Wparam) -> u16 { return cast(u16)((cast(u32)wParam >> 16) & 0xffff); }
HIWORD :: proc(lParam: Lparam) -> u16 { return cast(u16)((cast(u32)lParam >> 16) & 0xffff); }
LOWORD :: proc(wParam: Wparam) -> u16 { return cast(u16)wParam; }
LOWORD :: proc(lParam: Lparam) -> u16 { return cast(u16)lParam; }
HIWORD :: proc(wParam: Wparam) -> u16 { return u16((u32(wParam) >> 16) & 0xffff); }
HIWORD :: proc(lParam: Lparam) -> u16 { return u16((u32(lParam) >> 16) & 0xffff); }
LOWORD :: proc(wParam: Wparam) -> u16 { return u16(wParam); }
LOWORD :: proc(lParam: Lparam) -> u16 { return u16(lParam); }
@@ -476,7 +477,7 @@ Proc :: #type proc() #cc_c;
GetKeyState :: proc(v_key: i32) -> i16 #foreign user32;
GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign user32;
is_key_down :: proc(key: Key_Code) -> bool #inline { return GetAsyncKeyState(cast(i32)key) < 0; }
is_key_down :: proc(key: Key_Code) -> bool #inline { return GetAsyncKeyState(i32(key)) < 0; }
Key_Code :: enum i32 {
LBUTTON = 0x01,
@@ -624,4 +625,3 @@ Key_Code :: enum i32 {
PA1 = 0xFD,
OEM_CLEAR = 0xFE,
}
+43 -91
View File
@@ -1,146 +1,98 @@
is_signed :: proc(info: ^Type_Info) -> bool {
if is_integer(info) {
i := union_cast(^Type_Info.Integer)info;
return i.signed;
}
if is_float(info) {
return true;
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Integer: return i.signed;
case Type_Info.Float: return true;
}
return false;
}
is_integer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Integer: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Integer);
return ok;
}
is_float :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Float: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Float);
return ok;
}
is_complex :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
_, ok := type_info_base(info).(^Type_Info.Complex);
return ok;
}
is_any :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Any: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Any);
return ok;
}
is_string :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.String: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.String);
return ok;
}
is_boolean :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Boolean: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Boolean);
return ok;
}
is_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Pointer: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Pointer);
return ok;
}
is_procedure :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Procedure: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Procedure);
return ok;
}
is_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Array: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Array);
return ok;
}
is_dynamic_array :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Dynamic_Array: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Dynamic_Array);
return ok;
}
is_dynamic_map :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Map: return i.count == 0;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Map);
return ok;
}
is_slice :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Slice: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Slice);
return ok;
}
is_vector :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Vector: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Vector);
return ok;
}
is_tuple :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Tuple: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Tuple);
return ok;
}
is_struct :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Struct: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Struct);
return ok;
}
is_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Union: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Union);
return ok;
}
is_raw_union :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Raw_Union: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Raw_Union);
return ok;
}
is_enum :: proc(info: ^Type_Info) -> bool {
if info == nil { return false; }
match i in type_info_base(info) {
case Type_Info.Enum: return true;
}
return false;
_, ok := type_info_base(info).(^Type_Info.Enum);
return ok;
}
+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_SELF :: 0x80;
RUNE_BOM :: 0xfeff;
RUNE_EOF :: ~cast(rune)0;
RUNE_EOF :: ~rune(0);
MAX_RUNE :: '\U0010ffff';
UTF_MAX :: 4;
@@ -60,15 +60,15 @@ immutable accept_sizes := [256]byte{
encode_rune :: proc(r: rune) -> ([4]byte, int) {
buf: [4]byte;
i := cast(u32)r;
i := u32(r);
mask: byte : 0x3f;
if i <= 1<<7-1 {
buf[0] = cast(byte)r;
buf[0] = byte(r);
return buf, 1;
}
if i <= 1<<11-1 {
buf[0] = 0xc0 | cast(byte)(r>>6);
buf[1] = 0x80 | cast(byte)r & mask;
buf[0] = 0xc0 | byte(r>>6);
buf[1] = 0x80 | byte(r) & mask;
return buf, 2;
}
@@ -79,34 +79,34 @@ encode_rune :: proc(r: rune) -> ([4]byte, int) {
}
if i <= 1<<16-1 {
buf[0] = 0xe0 | cast(byte)(r>>12);
buf[1] = 0x80 | cast(byte)(r>>6) & mask;
buf[2] = 0x80 | cast(byte)r & mask;
buf[0] = 0xe0 | byte(r>>12);
buf[1] = 0x80 | byte(r>>6) & mask;
buf[2] = 0x80 | byte(r) & mask;
return buf, 3;
}
buf[0] = 0xf0 | cast(byte)(r>>18);
buf[1] = 0x80 | cast(byte)(r>>12) & mask;
buf[2] = 0x80 | cast(byte)(r>>6) & mask;
buf[3] = 0x80 | cast(byte)r & mask;
buf[0] = 0xf0 | byte(r>>18);
buf[1] = 0x80 | byte(r>>12) & mask;
buf[2] = 0x80 | byte(r>>6) & mask;
buf[3] = 0x80 | byte(r) & mask;
return buf, 4;
}
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune(cast([]byte)s); }
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune([]byte(s)); }
decode_rune :: proc(s: []byte) -> (rune, int) {
n := s.count;
n := len(s);
if n < 1 {
return RUNE_ERROR, 0;
}
s0 := s[0];
x := accept_sizes[s0];
if x >= 0xF0 {
mask := cast(rune)(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
return cast(rune)(s[0])&~mask | RUNE_ERROR&mask, 1;
mask := rune(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff.
return rune(s[0])&~mask | RUNE_ERROR&mask, 1;
}
sz := x & 7;
accept := accept_ranges[x>>4];
if n < cast(int)sz {
if n < int(sz) {
return RUNE_ERROR, 1;
}
b1 := s[1];
@@ -114,36 +114,36 @@ decode_rune :: proc(s: []byte) -> (rune, int) {
return RUNE_ERROR, 1;
}
if sz == 2 {
return cast(rune)(s0&MASK2)<<6 | cast(rune)(b1&MASKX), 2;
return rune(s0&MASK2)<<6 | rune(b1&MASKX), 2;
}
b2 := s[2];
if b2 < LOCB || HICB < b2 {
return RUNE_ERROR, 1;
}
if sz == 3 {
return cast(rune)(s0&MASK3)<<12 | cast(rune)(b1&MASKX)<<6 | cast(rune)(b2&MASKX), 3;
return rune(s0&MASK3)<<12 | rune(b1&MASKX)<<6 | rune(b2&MASKX), 3;
}
b3 := s[3];
if b3 < LOCB || HICB < b3 {
return RUNE_ERROR, 1;
}
return cast(rune)(s0&MASK4)<<18 | cast(rune)(b1&MASKX)<<12 | cast(rune)(b2&MASKX)<<6 | cast(rune)(b3&MASKX), 4;
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
}
decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune(cast([]byte)s); }
decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune([]byte(s)); }
decode_last_rune :: proc(s: []byte) -> (rune, int) {
r: rune;
size: int;
start, end, limit: int;
end = s.count;
end = len(s);
if end == 0 {
return RUNE_ERROR, 0;
}
start = end-1;
r = cast(rune)s[start];
r = rune(s[start]);
if r < RUNE_SELF {
return r, 1;
}
@@ -160,7 +160,7 @@ decode_last_rune :: proc(s: []byte) -> (rune, int) {
}
start = max(start, 0);
r, size = decode_rune(s[start..end]);
r, size = decode_rune(s[start..<end]);
if start+size != end {
return RUNE_ERROR, 1;
}
@@ -183,7 +183,7 @@ valid_rune :: proc(r: rune) -> bool {
}
valid_string :: proc(s: string) -> bool {
n := s.count;
n := len(s);
for i := 0; i < n; {
si := s[i];
if si < RUNE_SELF { // ascii
@@ -194,7 +194,7 @@ valid_string :: proc(s: string) -> bool {
if x == 0xf1 {
return false;
}
size := cast(int)(x & 7);
size := int(x & 7);
if i+size > n {
return false;
}
@@ -217,10 +217,10 @@ valid_string :: proc(s: string) -> bool {
rune_start :: proc(b: byte) -> bool #inline { return b&0xc0 != 0x80; }
rune_count :: proc(s: string) -> int #inline { return rune_count(cast([]byte)s); }
rune_count :: proc(s: string) -> int #inline { return rune_count([]byte(s)); }
rune_count :: proc(s: []byte) -> int {
count := 0;
n := s.count;
n := len(s);
for i := 0; i < n; {
defer count++;
@@ -234,7 +234,7 @@ rune_count :: proc(s: []byte) -> int {
i++;
continue;
}
size := cast(int)(x & 7);
size := int(x & 7);
if i+size > n {
i++;
continue;
+1 -2
View File
@@ -1,8 +1,7 @@
@echo off
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL
set _NO_DEBUG_HEAP=1
set path=w:\Odin\misc;%path%
+99 -9
View File
@@ -135,7 +135,62 @@ String odin_root_dir(void) {
return path;
}
#else
#error Implement system
// NOTE: Linux / Unix is unfinished and not tested very well.
#include <sys/stat.h>
String odin_root_dir(void) {
String path = global_module_path;
Array(char) path_buf;
isize len, i;
gbTempArenaMemory tmp;
u8 *text;
if (global_module_path_set) {
return global_module_path;
}
array_init_count(&path_buf, heap_allocator(), 300);
len = 0;
for (;;) {
// This is not a 100% reliable system, but for the purposes
// of this compiler, it should be _good enough_.
// That said, there's no solid 100% method on Linux to get the program's
// path without checking this link. Sorry.
len = readlink("/proc/self/exe", &path_buf.e[0], path_buf.count);
if(len == 0) {
return make_string(NULL, 0);
}
if (len < path_buf.count) {
break;
}
array_resize(&path_buf, 2*path_buf.count + 300);
}
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
gb_memmove(text, &path_buf.e[0], len);
path = make_string(text, len);
for (i = path.len-1; i >= 0; i--) {
u8 c = path.text[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
gb_temp_arena_memory_end(tmp);
array_free(&path_buf);
return path;
}
#endif
@@ -157,11 +212,10 @@ String path_to_fullpath(gbAllocator a, String s) {
}
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
String path_to_fullpath(gbAllocator a, String s) {
char* p = realpath(s.text, 0);
// GB_ASSERT(p && "file does not exist");
if(!p) return make_string_c("");
char *p = realpath(cast(char *)s.text, 0);
if(p == NULL) return make_string_c("");
return make_string(p, strlen(p));
return make_string_c(p);
}
#else
#error Implement system
@@ -209,7 +263,7 @@ String get_fullpath_core(gbAllocator a, String path) {
void init_build_context(void) {
BuildContext *bc = &build_context;
bc->ODIN_VENDOR = str_lit("odin");
bc->ODIN_VERSION = str_lit("0.1.3");
bc->ODIN_VERSION = str_lit("0.2.1");
bc->ODIN_ROOT = odin_root_dir();
#if defined(GB_SYSTEM_WINDOWS)
@@ -221,18 +275,54 @@ void init_build_context(void) {
bc->ODIN_ARCH = str_lit("amd64");
bc->ODIN_ENDIAN = str_lit("little");
#else
#error Implement system
bc->ODIN_OS = str_lit("linux");
bc->ODIN_ARCH = str_lit("amd64");
bc->ODIN_ENDIAN = str_lit("little");
#endif
// NOTE(zangent): The linker flags to set the build architecture are different
// across OSs. It doesn't make sense to allocate extra data on the heap
// here, so I just #defined the linker flags to keep things concise.
#if defined(GB_SYSTEM_WINDOWS)
#define LINK_FLAG_X64 "/machine:x64"
#define LINK_FLAG_X86 "/machine:x86"
#elif defined(GB_SYSTEM_OSX)
// NOTE(zangent): MacOS systems are x64 only, so ld doesn't have
// an architecture option. All compilation done on MacOS must be x64.
GB_ASSERT(str_eq(bc->ODIN_ARCH, str_lit("amd64")));
#define LINK_FLAG_X64 ""
#define LINK_FLAG_X86 ""
#else
// Linux, but also BSDs and the like.
// NOTE(zangent): When clang is swapped out with ld as the linker,
// the commented flags here should be used. Until then, we'll have
// to use alternative build flags made for clang.
/*
#define LINK_FLAG_X64 "-m elf_x86_64"
#define LINK_FLAG_X86 "-m elf_i386"
*/
#define LINK_FLAG_X64 "-arch x86-64"
#define LINK_FLAG_X86 "-arch x86"
#endif
if (str_eq(bc->ODIN_ARCH, str_lit("amd64"))) {
bc->word_size = 8;
bc->max_align = 16;
bc->llc_flags = str_lit("-march=x86-64 ");
bc->link_flags = str_lit("/machine:x64 ");
bc->link_flags = str_lit(LINK_FLAG_X64 " ");
} else if (str_eq(bc->ODIN_ARCH, str_lit("x86"))) {
bc->word_size = 4;
bc->max_align = 8;
bc->llc_flags = str_lit("-march=x86 ");
bc->link_flags = str_lit("/machine:x86 ");
bc->link_flags = str_lit(LINK_FLAG_X86 " ");
}
#undef LINK_FLAG_X64
#undef LINK_FLAG_X86
}
+86 -58
View File
@@ -1,12 +1,11 @@
bool check_is_terminating(AstNode *node);
void check_stmt (Checker *c, AstNode *node, u32 flags);
void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
// NOTE(bill): `content_name` is for debugging and error messages
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
e->type == t_invalid) {
operand->type == t_invalid ||
e->type == t_invalid) {
if (operand->mode == Addressing_Builtin) {
gbString expr_str = expr_to_string(operand->expr);
@@ -14,9 +13,9 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
// TODO(bill): is this a good enough error message?
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
error_node(operand->expr,
"Cannot assign builtin procedure `%s` in %.*s",
expr_str,
LIT(context_name));
"Cannot assign builtin procedure `%s` in %.*s",
expr_str,
LIT(context_name));
operand->mode = Addressing_Invalid;
@@ -86,8 +85,8 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArra
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
e->type == t_invalid) {
operand->type == t_invalid ||
e->type == t_invalid) {
if (e->type == NULL) {
e->type = t_invalid;
}
@@ -182,7 +181,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init,
check_init_constant(c, e, &operand);
if (operand.mode == Addressing_Invalid ||
base_type(operand.type) == t_invalid) {
base_type(operand.type) == t_invalid) {
error(e->token, "Invalid declaration type");
}
}
@@ -242,14 +241,15 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
check_open_scope(c, pd->type);
check_procedure_type(c, proc_type, pd->type);
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
bool is_link_name = (pd->tags & ProcTag_link_name) != 0;
bool is_export = (pd->tags & ProcTag_export) != 0;
bool is_inline = (pd->tags & ProcTag_inline) != 0;
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
bool is_link_name = (pd->tags & ProcTag_link_name) != 0;
bool is_export = (pd->tags & ProcTag_export) != 0;
bool is_inline = (pd->tags & ProcTag_inline) != 0;
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
bool is_require_results = (pd->tags & ProcTag_require_results) != 0;
if ((d->scope->is_file || d->scope->is_global) &&
str_eq(e->token.string, str_lit("main"))) {
if (d->scope->is_file && str_eq(e->token.string, str_lit("main"))) {
if (proc_type != NULL) {
TypeProc *pt = &proc_type->Proc;
if (pt->param_count != 0 ||
@@ -283,6 +283,16 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
}
if (proc_type != NULL && is_type_proc(proc_type)) {
TypeProc *tp = &proc_type->Proc;
if (tp->result_count == 0 && is_require_results) {
error_node(pd->type, "`#require_results` is not needed on a procedure with no results");
} else {
tp->require_results = is_require_results;
}
}
if (is_foreign) {
MapEntity *fp = &c->info.foreigns;
String name = e->token.string;
@@ -325,9 +335,9 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
Type *other_type = base_type(f->type);
if (!are_signatures_similar_enough(this_type, other_type)) {
error_node(d->proc_lit,
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
"\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
"\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
}
} else {
map_entity_set(fp, key, e);
@@ -350,9 +360,9 @@ void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
TokenPos pos = f->token.pos;
// TODO(bill): Better error message?
error_node(d->proc_lit,
"Non unique linking name for procedure `%.*s`\n"
"\tother at %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
"Non unique linking name for procedure `%.*s`\n"
"\tother at %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
} else {
map_entity_set(fp, key, e);
}
@@ -403,7 +413,7 @@ void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count
}
void check_alias_decl(Checker *c, Entity *e, AstNode *expr) {
void check_alias_decl(Checker *c, Entity *e, AstNode *expr, Type *named_type) {
GB_ASSERT(e->type == NULL);
GB_ASSERT(e->kind == Entity_Alias);
@@ -421,36 +431,47 @@ void check_alias_decl(Checker *c, Entity *e, AstNode *expr) {
return;
}
if (expr->kind == AstNode_Ident) {
Operand o = {0};
Entity *f = check_ident(c, &o, expr, NULL, NULL, true);
if (f != NULL) {
e->Alias.original = f;
e->type = f->type;
}
return;
} else if (expr->kind == AstNode_SelectorExpr) {
Operand o = {0};
Entity *f = check_selector(c, &o, expr, NULL);
if (f != NULL) {
e->Alias.original = f;
e->type = f->type;
}
return;
}
Operand o = {0};
check_expr_or_type(c, &o, expr);
if (o.mode == Addressing_Invalid) {
return;
}
switch (o.mode) {
case Addressing_Type:
e->type = o.type;
break;
default:
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) {
@@ -485,12 +506,12 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
case Entity_TypeName:
check_type_decl(c, e, d->type_expr, named_type);
break;
case Entity_Alias:
check_alias_decl(c, e, d->init_expr, named_type);
break;
case Entity_Procedure:
check_proc_lit(c, e, d);
break;
case Entity_Alias:
check_alias_decl(c, e, d->init_expr);
break;
}
c->context = prev;
@@ -514,14 +535,13 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
c->context.decl = decl;
c->context.proc_name = proc_name;
GB_ASSERT(type->kind == Type_Proc);
if (type->Proc.param_count > 0) {
TypeTuple *params = &type->Proc.params->Tuple;
for (isize i = 0; i < params->variable_count; i++) {
Entity *e = params->variables[i];
GB_ASSERT(e->kind == Entity_Variable);
if (!(e->flags & EntityFlag_Anonymous)) {
if (!(e->flags & EntityFlag_Using)) {
continue;
}
bool is_immutable = e->Variable.is_immutable;
@@ -552,7 +572,7 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
push_procedure(c, type);
{
ast_node(bs, BlockStmt, body);
check_stmt_list(c, bs->stmts, 0);
check_stmt_list(c, bs->stmts, Stmt_CheckScopeDecls);
if (type->Proc.result_count > 0) {
if (!check_is_terminating(body)) {
if (token.kind == Token_Ident) {
@@ -567,8 +587,16 @@ void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNod
check_scope_usage(c, c->context.scope);
c->context = old_context;
if (decl->parent != NULL) {
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
for_array(i, decl->deps.entries) {
HashKey key = decl->deps.entries.e[i].key;
Entity *e = cast(Entity *)key.ptr;
map_bool_set(&decl->parent->deps, key, true);
}
}
}
+984 -297
View File
File diff suppressed because it is too large Load Diff
+293 -175
View File
@@ -3,7 +3,9 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) {
return;
}
check_scope_decls(c, stmts, 1.2*stmts.count);
if (flags&Stmt_CheckScopeDecls) {
check_scope_decls(c, stmts, 1.2*stmts.count);
}
bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0;
flags &= ~Stmt_FallthroughAllowed;
@@ -123,15 +125,13 @@ bool check_is_terminating(AstNode *node) {
case_end;
case_ast_node(fs, ForStmt, node);
if (!check_has_break(fs->body, true)) {
if (fs->cond == NULL && !check_has_break(fs->body, true)) {
return check_is_terminating(fs->body);
}
case_end;
case_ast_node(rs, RangeStmt, node);
if (!check_has_break(rs->body, true)) {
return check_is_terminating(rs->body);
}
return false;
case_end;
case_ast_node(ms, MatchStmt, node);
@@ -362,6 +362,7 @@ typedef struct TypeAndToken {
#include "map.c"
void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
flags &= ~Stmt_CheckScopeDecls;
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, ws->cond);
if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
@@ -431,6 +432,134 @@ void check_label(Checker *c, AstNode *label) {
}
}
// Returns `true` for `continue`, `false` for `return`
bool check_using_stmt_entity(Checker *c, AstNodeUsingStmt *us, AstNode *expr, bool is_selector, Entity *e) {
if (e == NULL) {
error(us->token, "`using` applied to an unknown entity");
return true;
}
switch (e->kind) {
case Entity_Alias: {
if (e->Alias.original != NULL) {
check_using_stmt_entity(c, us, expr, is_selector, e->Alias.original);
} else {
error(us->token, "`using` cannot be applied to the alias `%.*s`", LIT(e->token.string));
return false;
}
} break;
case Entity_TypeName: {
Type *t = base_type(e->type);
if (is_type_union(t)) {
TokenPos pos = ast_node_token(expr).pos;
for (isize i = 1; i < t->Record.variant_count; i++) {
Entity *f = t->Record.variants[i];
// gb_printf_err("%s\n", type_to_string(f->type));
Entity *found = scope_insert_entity(c->context.scope, f);
if (found != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
gb_string_free(expr_str);
return false;
}
f->using_parent = e;
}
} else if (is_type_enum(t)) {
for (isize i = 0; i < t->Record.field_count; i++) {
Entity *f = t->Record.fields[i];
Entity *found = scope_insert_entity(c->context.scope, f);
if (found != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
gb_string_free(expr_str);
return false;
}
f->using_parent = e;
}
} else {
error(us->token, "`using` can be only applied to `union` or `enum` type entities");
}
} break;
case Entity_ImportName: {
Scope *scope = e->ImportName.scope;
for_array(i, scope->elements.entries) {
Entity *decl = scope->elements.entries.e[i].value;
Entity *found = scope_insert_entity(c->context.scope, decl);
if (found != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token,
"Namespace collision while `using` `%s` of: %.*s\n"
"\tat %.*s(%td:%td)\n"
"\tat %.*s(%td:%td)",
expr_str, LIT(found->token.string),
LIT(found->token.pos.file), found->token.pos.line, found->token.pos.column,
LIT(decl->token.pos.file), decl->token.pos.line, decl->token.pos.column
);
gb_string_free(expr_str);
return false;
}
}
} break;
case Entity_Variable: {
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
// TODO(bill): Make it work for unions too
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
if (is_selector) {
uvar->using_expr = expr;
}
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
gbString expr_str = expr_to_string(expr);
error(us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
gb_string_free(expr_str);
return false;
}
}
}
} else {
error(us->token, "`using` can only be applied to variables of type struct or raw_union");
return false;
}
} break;
case Entity_Constant:
error(us->token, "`using` cannot be applied to a constant");
break;
case Entity_Procedure:
case Entity_Builtin:
error(us->token, "`using` cannot be applied to a procedure");
break;
case Entity_Nil:
error(us->token, "`using` cannot be applied to `nil`");
break;
case Entity_Label:
error(us->token, "`using` cannot be applied to a label");
break;
case Entity_Invalid:
error(us->token, "`using` cannot be applied to an invalid entity");
break;
default:
GB_PANIC("TODO(bill): `using` other expressions?");
}
return true;
}
void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
switch (node->kind) {
@@ -442,9 +571,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
Operand operand = {Addressing_Invalid};
ExprKind kind = check_expr_base(c, &operand, es->expr, NULL);
switch (operand.mode) {
case Addressing_Type:
error_node(node, "Is not an expression");
break;
case Addressing_Type: {
gbString str = type_to_string(operand.type);
error_node(node, "`%s` is not an expression", str);
gb_string_free(str);
} break;
case Addressing_NoValue:
return;
default: {
@@ -452,6 +583,15 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
return;
}
if (operand.expr->kind == AstNode_CallExpr) {
AstNodeCallExpr *ce = &operand.expr->CallExpr;
Type *t = type_of_expr(&c->info, ce->proc);
if (is_type_proc(t)) {
if (t->Proc.require_results) {
gbString expr_str = expr_to_string(ce->proc);
error_node(node, "`%s` requires that its results must be handled", expr_str);
gb_string_free(expr_str);
}
}
return;
}
gbString expr_str = expr_to_string(operand.expr);
@@ -698,9 +838,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
Entity *entities[2] = {0};
isize entity_count = 0;
AstNode *expr = unparen_expr(rs->expr);
if (rs->expr != NULL && rs->expr->kind == AstNode_IntervalExpr) {
ast_node(ie, IntervalExpr, rs->expr);
if (is_ast_node_a_range(expr)) {
ast_node(ie, BinaryExpr, expr);
Operand x = {Addressing_Invalid};
Operand y = {Addressing_Invalid};
@@ -760,7 +902,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
TokenKind op = Token_Lt;
switch (ie->op.kind) {
case Token_Ellipsis: op = Token_Lt; break;
case Token_Ellipsis: op = Token_LtEq; break;
case Token_HalfClosed: op = Token_Lt; break;
default: error(ie->op, "Invalid range operator"); break;
}
bool ok = compare_exact_values(op, a, b);
@@ -777,9 +920,21 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
idx = t_int;
} else {
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, rs->expr);
check_expr_or_type(c, &operand, rs->expr);
if (operand.mode != Addressing_Invalid) {
if (operand.mode == Addressing_Type) {
if (!is_type_enum(operand.type)) {
gbString t = type_to_string(operand.type);
error_node(operand.expr, "Cannot iterate over the type `%s`", t);
gb_string_free(t);
goto skip_expr;
} else {
val = operand.type;
idx = t_int;
add_type_info_type(c, operand.type);
goto skip_expr;
}
} else if (operand.mode != Addressing_Invalid) {
Type *t = base_type(type_deref(operand.type));
switch (t->kind) {
case Type_Basic:
@@ -903,6 +1058,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
token.string = str_lit("true");
x.expr = ast_ident(c->curr_ast_file, token);
}
if (is_type_vector(x.type)) {
gbString str = type_to_string(x.type);
error_node(x.expr, "Invalid match expression type: %s", str);
gb_string_free(str);
break;
}
// NOTE(bill): Check for multiple defaults
AstNode *first_default = NULL;
@@ -944,66 +1106,120 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
ast_node(cc, CaseClause, stmt);
for_array(j, cc->list) {
AstNode *expr = cc->list.e[j];
Operand y = {0};
AstNode *expr = unparen_expr(cc->list.e[j]);
check_expr(c, &y, expr);
if (x.mode == Addressing_Invalid ||
y.mode == Addressing_Invalid) {
continue;
}
if (is_ast_node_a_range(expr)) {
ast_node(ie, BinaryExpr, expr);
Operand lhs = {0};
Operand rhs = {0};
check_expr(c, &lhs, ie->left);
if (x.mode == Addressing_Invalid) {
continue;
}
if (lhs.mode == Addressing_Invalid) {
continue;
}
check_expr(c, &rhs, ie->right);
if (rhs.mode == Addressing_Invalid) {
continue;
}
convert_to_typed(c, &y, x.type, 0);
if (y.mode == Addressing_Invalid) {
continue;
}
// NOTE(bill): the ordering here matters
Operand z = y;
check_comparison(c, &z, &x, Token_CmpEq);
if (z.mode == Addressing_Invalid) {
continue;
}
if (y.mode != Addressing_Constant) {
continue;
}
if (!is_type_ordered(x.type)) {
gbString str = type_to_string(x.type);
error_node(x.expr, "Unordered type `%s`, is invalid for an interval expression", str);
gb_string_free(str);
continue;
}
if (y.value.kind != ExactValue_Invalid) {
HashKey key = hash_exact_value(y.value);
TypeAndToken *found = map_type_and_token_get(&seen, key);
if (found != NULL) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
isize count = map_type_and_token_multi_count(&seen, key);
TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
TokenKind op = {0};
map_type_and_token_multi_get_all(&seen, key, taps);
bool continue_outer = false;
Operand a = lhs;
Operand b = rhs;
check_comparison(c, &a, &x, Token_LtEq);
if (a.mode == Addressing_Invalid) {
continue;
}
switch (ie->op.kind) {
case Token_Ellipsis: op = Token_GtEq; break;
case Token_HalfClosed: op = Token_Gt; break;
default: error(ie->op, "Invalid interval operator"); continue;
}
for (isize i = 0; i < count; i++) {
TypeAndToken tap = taps[i];
if (are_types_identical(y.type, tap.type)) {
TokenPos pos = tap.token.pos;
gbString expr_str = expr_to_string(y.expr);
error_node(y.expr,
"Duplicate case `%s`\n"
"\tprevious case at %.*s(%td:%td)",
expr_str,
LIT(pos.file), pos.line, pos.column);
gb_string_free(expr_str);
continue_outer = true;
break;
check_comparison(c, &b, &x, op);
if (b.mode == Addressing_Invalid) {
continue;
}
switch (ie->op.kind) {
case Token_Ellipsis: op = Token_LtEq; break;
case Token_HalfClosed: op = Token_Lt; break;
default: error(ie->op, "Invalid interval operator"); continue;
}
Operand a1 = lhs;
Operand b1 = rhs;
check_comparison(c, &a1, &b1, op);
} else {
Operand y = {0};
check_expr(c, &y, expr);
if (x.mode == Addressing_Invalid ||
y.mode == Addressing_Invalid) {
continue;
}
convert_to_typed(c, &y, x.type, 0);
if (y.mode == Addressing_Invalid) {
continue;
}
// NOTE(bill): the ordering here matters
Operand z = y;
check_comparison(c, &z, &x, Token_CmpEq);
if (z.mode == Addressing_Invalid) {
continue;
}
if (y.mode != Addressing_Constant) {
continue;
}
if (y.value.kind != ExactValue_Invalid) {
HashKey key = hash_exact_value(y.value);
TypeAndToken *found = map_type_and_token_get(&seen, key);
if (found != NULL) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
isize count = map_type_and_token_multi_count(&seen, key);
TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
map_type_and_token_multi_get_all(&seen, key, taps);
bool continue_outer = false;
for (isize i = 0; i < count; i++) {
TypeAndToken tap = taps[i];
if (are_types_identical(y.type, tap.type)) {
TokenPos pos = tap.token.pos;
gbString expr_str = expr_to_string(y.expr);
error_node(y.expr,
"Duplicate case `%s`\n"
"\tprevious case at %.*s(%td:%td)",
expr_str,
LIT(pos.file), pos.line, pos.column);
gb_string_free(expr_str);
continue_outer = true;
break;
}
}
gb_temp_arena_memory_end(tmp);
if (continue_outer) {
continue;
}
}
gb_temp_arena_memory_end(tmp);
if (continue_outer) {
continue;
}
TypeAndToken tap = {y.type, ast_node_token(y.expr)};
map_type_and_token_multi_insert(&seen, key, tap);
}
TypeAndToken tap = {y.type, ast_node_token(y.expr)};
map_type_and_token_multi_insert(&seen, key, tap);
}
}
@@ -1058,6 +1274,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
break;
}
bool is_ptr = is_type_pointer(x.type);
// NOTE(bill): Check for multiple defaults
AstNode *first_default = NULL;
ast_node(bs, BlockStmt, ms->body);
@@ -1153,6 +1371,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
}
}
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;
}
@@ -1163,8 +1388,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
check_open_scope(c, stmt);
{
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, case_type, true);
Entity *tag_var = make_entity_variable(c->allocator, c->context.scope, lhs->Ident, case_type, false);
tag_var->flags |= EntityFlag_Used;
tag_var->flags |= EntityFlag_Value;
add_entity(c, c->context.scope, lhs, tag_var);
add_entity_use(c, lhs, tag_var);
add_implicit_entity(c, stmt, tag_var);
@@ -1255,116 +1481,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
continue;
}
if (e == NULL) {
error(us->token, "`using` applied to an unknown entity");
continue;
}
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_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?");
if (!check_using_stmt_entity(c, us, expr, is_selector, e)) {
return;
}
}
case_end;
+137 -61
View File
@@ -11,7 +11,8 @@ typedef enum StmtFlag {
Stmt_BreakAllowed = 1<<0,
Stmt_ContinueAllowed = 1<<1,
Stmt_FallthroughAllowed = 1<<2,
Stmt_GiveAllowed = 1<<3,
Stmt_CheckScopeDecls = 1<<5,
} StmtFlag;
typedef struct BuiltinProc {
@@ -23,9 +24,12 @@ typedef struct BuiltinProc {
typedef enum BuiltinProcId {
BuiltinProc_Invalid,
BuiltinProc_len,
BuiltinProc_cap,
BuiltinProc_new,
BuiltinProc_new_slice,
BuiltinProc_free,
BuiltinProc_make,
BuiltinProc_reserve,
BuiltinProc_clear,
@@ -48,12 +52,17 @@ typedef enum BuiltinProcId {
BuiltinProc_panic,
BuiltinProc_copy,
// BuiltinProc_append,
BuiltinProc_swizzle,
// BuiltinProc_ptr_offset,
// BuiltinProc_ptr_sub,
BuiltinProc_complex,
BuiltinProc_quaternion,
BuiltinProc_real,
BuiltinProc_imag,
BuiltinProc_jmag,
BuiltinProc_kmag,
BuiltinProc_conj,
BuiltinProc_slice_ptr,
BuiltinProc_slice_to_bytes,
@@ -62,14 +71,19 @@ typedef enum BuiltinProcId {
BuiltinProc_abs,
BuiltinProc_clamp,
BuiltinProc_transmute,
BuiltinProc_Count,
} BuiltinProcId;
gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
{STR_LIT(""), 0, false, Expr_Stmt},
{STR_LIT("len"), 1, false, Expr_Expr},
{STR_LIT("cap"), 1, false, Expr_Expr},
{STR_LIT("new"), 1, false, Expr_Expr},
{STR_LIT("new_slice"), 2, true, Expr_Expr},
{STR_LIT("free"), 1, false, Expr_Stmt},
{STR_LIT("make"), 1, true, Expr_Expr},
{STR_LIT("reserve"), 2, false, Expr_Stmt},
{STR_LIT("clear"), 1, false, Expr_Stmt},
@@ -87,17 +101,22 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
{STR_LIT("type_info"), 1, false, Expr_Expr},
{STR_LIT("type_info_of_val"), 1, false, Expr_Expr},
{STR_LIT("compile_assert"), 1, false, Expr_Stmt},
{STR_LIT("assert"), 1, false, Expr_Stmt},
{STR_LIT("compile_assert"), 1, false, Expr_Expr},
{STR_LIT("assert"), 1, false, Expr_Expr},
{STR_LIT("panic"), 1, false, Expr_Stmt},
{STR_LIT("copy"), 2, false, Expr_Expr},
// {STR_LIT("append"), 2, false, Expr_Expr},
{STR_LIT("swizzle"), 1, true, Expr_Expr},
// {STR_LIT("ptr_offset"), 2, false, Expr_Expr},
// {STR_LIT("ptr_sub"), 2, false, Expr_Expr},
{STR_LIT("complex"), 2, false, Expr_Expr},
{STR_LIT("quaternion"), 4, false, Expr_Expr},
{STR_LIT("real"), 1, false, Expr_Expr},
{STR_LIT("imag"), 1, false, Expr_Expr},
{STR_LIT("jmag"), 1, false, Expr_Expr},
{STR_LIT("kmag"), 1, false, Expr_Expr},
{STR_LIT("conj"), 1, false, Expr_Expr},
{STR_LIT("slice_ptr"), 2, true, Expr_Expr},
{STR_LIT("slice_to_bytes"), 1, false, Expr_Stmt},
@@ -105,6 +124,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = {
{STR_LIT("max"), 2, false, Expr_Expr},
{STR_LIT("abs"), 1, false, Expr_Expr},
{STR_LIT("clamp"), 3, false, Expr_Expr},
{STR_LIT("transmute"), 2, false, Expr_Expr},
};
@@ -169,19 +190,21 @@ typedef struct BlockLabel {
} BlockLabel;
// DeclInfo is used to store information of certain declarations to allow for "any order" usage
typedef struct DeclInfo {
Scope *scope;
typedef struct DeclInfo DeclInfo;
struct DeclInfo {
DeclInfo * parent; // NOTE(bill): only used for procedure literals at the moment
Scope * scope;
Entity **entities;
isize entity_count;
Entity ** entities;
isize entity_count;
AstNode *type_expr;
AstNode *init_expr;
AstNode *proc_lit; // AstNode_ProcLit
AstNode * type_expr;
AstNode * init_expr;
AstNode * proc_lit; // AstNode_ProcLit
MapBool deps; // Key: Entity *
MapBool deps; // Key: Entity *
Array(BlockLabel) labels;
} DeclInfo;
};
// ProcedureInfo stores the information needed for checking a procedure
@@ -286,7 +309,7 @@ typedef struct CheckerInfo {
MapScope scopes; // Key: AstNode * | Node -> Scope
MapExprInfo untyped; // Key: AstNode * | Expression -> ExprInfo
MapDeclInfo entities; // Key: Entity *
MapEntity implicits; // Key: AstNode *
MapEntity implicits; // Key: AstNode *
MapEntity foreigns; // Key: String
MapAstFile files; // Key: String (full path)
MapIsize type_info_map; // Key: Type *
@@ -326,15 +349,16 @@ typedef Array(DelayedEntity) DelayedEntities;
void init_declaration_info(DeclInfo *d, Scope *scope) {
d->scope = scope;
void init_declaration_info(DeclInfo *d, Scope *scope, DeclInfo *parent) {
d->parent = parent;
d->scope = scope;
map_bool_init(&d->deps, heap_allocator());
array_init(&d->labels, heap_allocator());
}
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope) {
DeclInfo *make_declaration_info(gbAllocator a, Scope *scope, DeclInfo *parent) {
DeclInfo *d = gb_alloc_item(a, DeclInfo);
init_declaration_info(d, scope);
init_declaration_info(d, scope, parent);
return d;
}
@@ -533,6 +557,10 @@ Entity *scope_lookup_entity(Scope *s, String name) {
Entity *scope_insert_entity(Scope *s, Entity *entity) {
String name = entity->token.string;
if (str_eq(name, str_lit("output"))) {
gb_printf_err("Here! %.*s\n", LIT(name));
}
HashKey key = hash_string(name);
Entity **found = map_entity_get(&s->elements, key);
@@ -697,6 +725,10 @@ void destroy_checker_info(CheckerInfo *i) {
void init_checker(Checker *c, Parser *parser, BuildContext *bc) {
if (global_error_collector.count > 0) {
gb_exit(1);
}
gbAllocator a = heap_allocator();
c->parser = parser;
@@ -940,6 +972,24 @@ void add_type_info_type(Checker *c, Type *t) {
add_type_info_type(c, t_type_info_ptr);
add_type_info_type(c, t_rawptr);
break;
case Basic_complex64:
add_type_info_type(c, t_type_info_float);
add_type_info_type(c, t_f32);
break;
case Basic_complex128:
add_type_info_type(c, t_type_info_float);
add_type_info_type(c, t_f64);
break;
case Basic_quaternion128:
add_type_info_type(c, t_type_info_float);
add_type_info_type(c, t_f32);
break;
case Basic_quaternion256:
add_type_info_type(c, t_type_info_float);
add_type_info_type(c, t_f64);
break;
}
} break;
@@ -947,6 +997,10 @@ void add_type_info_type(Checker *c, Type *t) {
add_type_info_type(c, bt->Pointer.elem);
break;
case Type_Atomic:
add_type_info_type(c, bt->Atomic.elem);
break;
case Type_Array:
add_type_info_type(c, bt->Array.elem);
add_type_info_type(c, make_type_pointer(c->allocator, bt->Array.elem));
@@ -1128,35 +1182,41 @@ void init_preload(Checker *c) {
if (record->variant_count != 19) {
if (record->variant_count != 22) {
compiler_error("Invalid `Type_Info` layout");
}
t_type_info_named = record->variants[ 1]->type;
t_type_info_integer = record->variants[ 2]->type;
t_type_info_float = record->variants[ 3]->type;
t_type_info_string = record->variants[ 4]->type;
t_type_info_boolean = record->variants[ 5]->type;
t_type_info_any = record->variants[ 6]->type;
t_type_info_pointer = record->variants[ 7]->type;
t_type_info_procedure = record->variants[ 8]->type;
t_type_info_array = record->variants[ 9]->type;
t_type_info_dynamic_array = record->variants[10]->type;
t_type_info_slice = record->variants[11]->type;
t_type_info_vector = record->variants[12]->type;
t_type_info_tuple = record->variants[13]->type;
t_type_info_struct = record->variants[14]->type;
t_type_info_raw_union = record->variants[15]->type;
t_type_info_union = record->variants[16]->type;
t_type_info_enum = record->variants[17]->type;
t_type_info_map = record->variants[18]->type;
t_type_info_complex = record->variants[ 4]->type;
t_type_info_quaternion = record->variants[ 5]->type;
t_type_info_string = record->variants[ 6]->type;
t_type_info_boolean = record->variants[ 7]->type;
t_type_info_any = record->variants[ 8]->type;
t_type_info_pointer = record->variants[ 9]->type;
t_type_info_atomic = record->variants[10]->type;
t_type_info_procedure = record->variants[11]->type;
t_type_info_array = record->variants[12]->type;
t_type_info_dynamic_array = record->variants[13]->type;
t_type_info_slice = record->variants[14]->type;
t_type_info_vector = record->variants[15]->type;
t_type_info_tuple = record->variants[16]->type;
t_type_info_struct = record->variants[17]->type;
t_type_info_raw_union = record->variants[18]->type;
t_type_info_union = record->variants[19]->type;
t_type_info_enum = record->variants[20]->type;
t_type_info_map = record->variants[21]->type;
t_type_info_named_ptr = make_type_pointer(c->allocator, t_type_info_named);
t_type_info_integer_ptr = make_type_pointer(c->allocator, t_type_info_integer);
t_type_info_float_ptr = make_type_pointer(c->allocator, t_type_info_float);
t_type_info_complex_ptr = make_type_pointer(c->allocator, t_type_info_complex);
t_type_info_quaternion_ptr = make_type_pointer(c->allocator, t_type_info_quaternion);
t_type_info_string_ptr = make_type_pointer(c->allocator, t_type_info_string);
t_type_info_boolean_ptr = make_type_pointer(c->allocator, t_type_info_boolean);
t_type_info_any_ptr = make_type_pointer(c->allocator, t_type_info_any);
t_type_info_pointer_ptr = make_type_pointer(c->allocator, t_type_info_pointer);
t_type_info_atomic_ptr = make_type_pointer(c->allocator, t_type_info_atomic);
t_type_info_procedure_ptr = make_type_pointer(c->allocator, t_type_info_procedure);
t_type_info_array_ptr = make_type_pointer(c->allocator, t_type_info_array);
t_type_info_dynamic_array_ptr = make_type_pointer(c->allocator, t_type_info_dynamic_array);
@@ -1183,12 +1243,6 @@ void init_preload(Checker *c) {
t_context_ptr = make_type_pointer(c->allocator, t_context);
}
if (t_raw_dynamic_array == NULL) {
Entity *e = find_core_entity(c, str_lit("Raw_Dynamic_Array"));
t_raw_dynamic_array = e->type;
t_raw_dynamic_array = make_type_pointer(c->allocator, t_raw_dynamic_array);
}
if (t_map_key == NULL) {
Entity *e = find_core_entity(c, str_lit("__Map_Key"));
t_map_key = e->type;
@@ -1255,7 +1309,6 @@ void check_procedure_overloading(Checker *c, Entity *e) {
continue;
}
String name = p->token.string;
GB_ASSERT(p->kind == Entity_Procedure);
@@ -1418,7 +1471,7 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_cap);
DeclInfo *di = NULL;
if (vd->values.count > 0) {
di = make_declaration_info(heap_allocator(), c->context.scope);
di = make_declaration_info(heap_allocator(), c->context.scope, c->context.decl);
di->entities = entities;
di->type_expr = vd->type;
di->init_expr = vd->values.e[0];
@@ -1453,7 +1506,7 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
DeclInfo *d = di;
if (d == NULL) {
AstNode *init_expr = value;
d = make_declaration_info(heap_allocator(), e->scope);
d = make_declaration_info(heap_allocator(), e->scope, c->context.decl);
d->type_expr = vd->type;
d->init_expr = init_expr;
}
@@ -1479,21 +1532,26 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
init = vd->values.e[i];
}
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope);
DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);
Entity *e = NULL;
AstNode *up_init = unparen_expr(init);
if (up_init != NULL && is_ast_node_type(up_init)) {
AstNode *type = up_init;
e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
// TODO(bill): What if vd->type != NULL??? How to handle this case?
d->type_expr = init;
d->init_expr = init;
d->type_expr = type;
d->init_expr = type;
} else if (up_init != NULL && up_init->kind == AstNode_Alias) {
#if 1
error_node(up_init, "#alias declarations are not yet supported");
continue;
// e = make_entity_alias(c->allocator, d->scope, name->Ident, NULL, NULL);
// d->init_expr = init->Alias.expr;
}else if (init != NULL && up_init->kind == AstNode_ProcLit) {
#else
e = make_entity_alias(c->allocator, d->scope, name->Ident, NULL, EntityAlias_Invalid, NULL);
d->type_expr = vd->type;
d->init_expr = up_init->Alias.expr;
#endif
} else if (init != NULL && up_init->kind == AstNode_ProcLit) {
e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, up_init->ProcLit.tags);
d->proc_lit = up_init;
d->type_expr = vd->type;
@@ -1538,6 +1596,19 @@ void check_collect_entities(Checker *c, AstNodeArray nodes, bool is_file_scope)
continue;
}
if (fl->cond != NULL) {
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, fl->cond);
if (operand.mode != Addressing_Constant || !is_type_boolean(operand.type)) {
error_node(fl->cond, "Non-constant boolean `when` condition");
continue;
}
if (operand.value.kind == ExactValue_Bool &&
!operand.value.value_bool) {
continue;
}
}
DelayedDecl di = {c->context.scope, decl};
array_add(&c->delayed_foreign_libraries, di);
case_end;
@@ -1852,7 +1923,7 @@ void check_parsed_files(Checker *c) {
}
f->scope = scope;
f->decl_info = make_declaration_info(c->allocator, f->scope);
f->decl_info = make_declaration_info(c->allocator, f->scope, c->context.decl);
HashKey key = hash_string(f->tokenizer.fullpath);
map_scope_set(&file_scopes, key, scope);
map_ast_file_set(&c->info.files, key, f);
@@ -1929,17 +2000,25 @@ void check_parsed_files(Checker *c) {
}
}
// NOTE(bill): Check for illegal cyclic type declarations
for_array(i, c->info.definitions.entries) {
Entity *e = c->info.definitions.entries.e[i].value;
if (e->kind == Entity_TypeName) {
if (e->kind == Entity_TypeName ||
(e->kind == Entity_Alias && e->Alias.kind == EntityAlias_Type)) {
if (e->type != NULL) {
// i64 size = type_size_of(c->sizes, c->allocator, e->type);
i64 align = type_align_of(c->allocator, e->type);
if (align > 0) {
// add_type_info_type(c, e->type);
}
}
}
}
// gb_printf_err("Count: %td\n", c->info.type_info_count++);
for_array(i, file_scopes.entries) {
Scope *s = file_scopes.entries.e[i].value;
if (s->is_init) {
@@ -1964,6 +2043,3 @@ void check_parsed_files(Checker *c) {
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_IMPLEMENTATION
#include "gb/gb.h"
#include <math.h>
gbAllocator heap_allocator(void) {
return gb_heap_allocator();
}
@@ -24,6 +31,94 @@ gbAllocator scratch_allocator(void) {
return gb_scratch_allocator(&scratch_memory);
}
typedef struct DynamicArenaBlock DynamicArenaBlock;
typedef struct DynamicArena DynamicArena;
struct DynamicArenaBlock {
DynamicArenaBlock *prev;
DynamicArenaBlock *next;
u8 * start;
isize count;
isize capacity;
gbVirtualMemory vm;
};
struct DynamicArena {
DynamicArenaBlock *start_block;
DynamicArenaBlock *current_block;
isize block_size;
};
DynamicArenaBlock *add_dynamic_arena_block(DynamicArena *a) {
GB_ASSERT(a != NULL);
GB_ASSERT(a->block_size > 0);
gbVirtualMemory vm = gb_vm_alloc(NULL, a->block_size);
DynamicArenaBlock *block = cast(DynamicArenaBlock *)vm.data;
u8 *start = cast(u8 *)gb_align_forward(cast(u8 *)(block + 1), GB_DEFAULT_MEMORY_ALIGNMENT);
u8 *end = cast(u8 *)vm.data + vm.size;
block->vm = vm;
block->start = start;
block->count = 0;
block->capacity = end-start;
if (a->current_block != NULL) {
a->current_block->next = block;
block->prev = a->current_block;
}
a->current_block = block;
return block;
}
void init_dynamic_arena(DynamicArena *a, isize block_size) {
isize size = gb_size_of(DynamicArenaBlock) + block_size;
size = cast(isize)gb_align_forward(cast(void *)cast(uintptr)size, GB_DEFAULT_MEMORY_ALIGNMENT);
a->block_size = size;
a->start_block = add_dynamic_arena_block(a);
}
void destroy_dynamic_arena(DynamicArena *a) {
DynamicArenaBlock *b = a->current_block;
while (b != NULL) {
gbVirtualMemory vm = b->vm;
b = b->prev;
gb_vm_free(b->vm);
}
}
GB_ALLOCATOR_PROC(dynamic_arena_allocator_proc) {
DynamicArena *a = cast(DynamicArena *)allocator_data;
void *ptr = NULL;
switch (type) {
case gbAllocation_Alloc: {
} break;
case gbAllocation_Free: {
} break;
case gbAllocation_Resize: {
} break;
case gbAllocation_FreeAll:
GB_PANIC("free_all is not supported by this allocator");
break;
}
return ptr;
}
gbAllocator dynamic_arena_allocator(DynamicArena *a) {
gbAllocator allocator = {dynamic_arena_allocator_proc, a};
return allocator;
}
i64 next_pow2(i64 n) {
if (n <= 0) {
@@ -104,6 +199,10 @@ i16 f32_to_f16(f32 value) {
}
}
f64 gb_sqrt(f64 x) {
return sqrt(x);
}
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
+23 -9
View File
@@ -33,13 +33,14 @@ String const entity_strings[] = {
typedef enum EntityFlag {
EntityFlag_Visited = 1<<0,
EntityFlag_Used = 1<<1,
EntityFlag_Anonymous = 1<<2,
EntityFlag_Using = 1<<2,
EntityFlag_Field = 1<<3,
EntityFlag_Param = 1<<4,
EntityFlag_VectorElem = 1<<5,
EntityFlag_Ellipsis = 1<<6,
EntityFlag_NoAlias = 1<<7,
EntityFlag_TypeField = 1<<8,
EntityFlag_Value = 1<<9,
} EntityFlag;
// Zero value means the overloading process is not yet done
@@ -49,11 +50,18 @@ typedef enum OverloadKind {
Overload_Yes,
} OverloadKind;
typedef enum EntityAliasKind {
EntityAlias_Invalid,
EntityAlias_Type,
EntityAlias_Entity,
} EntityAliasKind;
// An Entity is a named "thing" in the language
typedef struct Entity Entity;
struct Entity {
EntityKind kind;
u64 id;
u32 flags;
Token token;
Scope * scope;
@@ -74,7 +82,9 @@ struct Entity {
bool is_immutable;
bool is_thread_local;
} Variable;
i32 TypeName;
struct {
bool is_type_alias;
} TypeName;
struct {
bool is_foreign;
String foreign_name;
@@ -98,7 +108,8 @@ struct Entity {
bool used;
} LibraryName;
struct {
Entity *original;
EntityAliasKind kind;
Entity * original;
} Alias;
i32 Nil;
struct {
@@ -135,6 +146,7 @@ bool is_entity_exported(Entity *e) {
return name.text[0] != '_';
}
gb_global u64 global_entity_id = 0;
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
Entity *entity = gb_alloc_item(a, Entity);
@@ -142,6 +154,7 @@ Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token,
entity->scope = scope;
entity->token = token;
entity->type = type;
entity->id = ++global_entity_id;
return entity;
}
@@ -156,7 +169,7 @@ Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, T
token.pos = parent->token.pos;
Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
entity->using_parent = parent;
entity->flags |= EntityFlag_Anonymous;
entity->flags |= EntityFlag_Using;
return entity;
}
@@ -172,20 +185,20 @@ Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *ty
return entity;
}
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, bool is_immutable) {
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, bool is_immutable) {
Entity *entity = make_entity_variable(a, scope, token, type, is_immutable);
entity->flags |= EntityFlag_Used;
if (anonymous) entity->flags |= EntityFlag_Anonymous;
if (is_using) entity->flags |= EntityFlag_Using;
entity->flags |= EntityFlag_Param;
return entity;
}
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) {
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool is_using, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type, false);
entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index;
if (is_using) entity->flags |= EntityFlag_Using;
entity->flags |= EntityFlag_Field;
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
return entity;
}
@@ -228,8 +241,9 @@ Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type
}
Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type,
Entity *original) {
EntityAliasKind kind, Entity *original) {
Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
entity->Alias.kind = kind;
entity->Alias.original = original;
return entity;
}
+318 -14
View File
@@ -5,6 +5,14 @@
typedef struct AstNode AstNode;
typedef struct Complex128 {
f64 real, imag;
} Complex128;
typedef struct Quaternion256 {
f64 real, imag, jmag, kmag;
} Quaternion256;
typedef enum ExactValueKind {
ExactValue_Invalid,
@@ -12,6 +20,8 @@ typedef enum ExactValueKind {
ExactValue_String,
ExactValue_Integer,
ExactValue_Float,
ExactValue_Complex,
ExactValue_Quaternion,
ExactValue_Pointer,
ExactValue_Compound, // TODO(bill): Is this good enough?
@@ -21,12 +31,14 @@ typedef enum ExactValueKind {
typedef struct ExactValue {
ExactValueKind kind;
union {
bool value_bool;
String value_string;
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
f64 value_float;
i64 value_pointer;
AstNode *value_compound;
bool value_bool;
String value_string;
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
f64 value_float;
i64 value_pointer;
Complex128 value_complex;
Quaternion256 value_quaternion;
AstNode * value_compound;
};
} ExactValue;
@@ -66,6 +78,23 @@ ExactValue exact_value_float(f64 f) {
return result;
}
ExactValue exact_value_complex(f64 real, f64 imag) {
ExactValue result = {ExactValue_Complex};
result.value_complex.real = real;
result.value_complex.imag = imag;
return result;
}
ExactValue exact_value_quaternion(f64 real, f64 imag, f64 jmag, f64 kmag) {
ExactValue result = {ExactValue_Quaternion};
result.value_quaternion.real = real;
result.value_quaternion.imag = imag;
result.value_quaternion.jmag = jmag;
result.value_quaternion.kmag = kmag;
return result;
}
ExactValue exact_value_pointer(i64 ptr) {
ExactValue result = {ExactValue_Pointer};
result.value_pointer = ptr;
@@ -113,9 +142,7 @@ ExactValue exact_value_integer_from_string(String string) {
return exact_value_integer(result);
}
ExactValue exact_value_float_from_string(String string) {
f64 float_from_string(String string) {
isize i = 0;
u8 *str = string.text;
isize len = string.len;
@@ -190,8 +217,11 @@ ExactValue exact_value_float_from_string(String string) {
while (exp > 0) { scale *= 10.0; exp -= 1; }
}
f64 result = sign * (frac ? (value / scale) : (value * scale));
return exact_value_float(result);
return sign * (frac ? (value / scale) : (value * scale));
}
ExactValue exact_value_float_from_string(String string) {
return exact_value_float(float_from_string(string));
}
@@ -200,6 +230,18 @@ ExactValue exact_value_from_basic_literal(Token token) {
case Token_String: return exact_value_string(token.string);
case Token_Integer: return exact_value_integer_from_string(token.string);
case Token_Float: return exact_value_float_from_string(token.string);
case Token_Imag: {
String str = token.string;
Rune last_rune = cast(Rune)str.text[str.len-1];
str.len--; // Ignore the `i|j|k`
f64 imag = float_from_string(str);
switch (last_rune) {
case 'i': return exact_value_complex(0, imag);
case 'j': return exact_value_quaternion(0, 0, imag, 0);
case 'k': return exact_value_quaternion(0, 0, 0, imag);
}
}
case Token_Rune: {
Rune r = GB_RUNE_INVALID;
gb_utf8_decode(token.string.text, token.string.len, &r);
@@ -245,6 +287,128 @@ ExactValue exact_value_to_float(ExactValue v) {
return r;
}
ExactValue exact_value_to_complex(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_complex(cast(i64)v.value_integer, 0);
case ExactValue_Float:
return exact_value_complex(v.value_float, 0);
case ExactValue_Complex:
return v;
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_to_quaternion(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_quaternion(cast(i64)v.value_integer, 0, 0, 0);
case ExactValue_Float:
return exact_value_quaternion(v.value_float, 0, 0, 0);
case ExactValue_Complex:
return exact_value_quaternion(v.value_complex.real, v.value_complex.imag, 0, 0);
case ExactValue_Quaternion:
return v;
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_real(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
return v;
case ExactValue_Complex:
return exact_value_float(v.value_complex.real);
case ExactValue_Quaternion:
return exact_value_float(v.value_quaternion.real);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_imag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
return exact_value_integer(0);
case ExactValue_Complex:
return exact_value_float(v.value_complex.imag);
case ExactValue_Quaternion:
return exact_value_float(v.value_quaternion.imag);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_jmag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
case ExactValue_Complex:
return exact_value_integer(0);
case ExactValue_Quaternion:
return exact_value_float(v.value_quaternion.jmag);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_kmag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
case ExactValue_Float:
case ExactValue_Complex:
return exact_value_integer(0);
case ExactValue_Quaternion:
return exact_value_float(v.value_quaternion.kmag);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_make_imag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_complex(0, exact_value_to_float(v).value_float);
case ExactValue_Float:
return exact_value_complex(0, v.value_float);
default:
GB_PANIC("Expected an integer or float type for `exact_value_make_imag`");
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_make_jmag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_quaternion(0, 0, exact_value_to_float(v).value_float, 0);
case ExactValue_Float:
return exact_value_quaternion(0, 0, v.value_float, 0);
default:
GB_PANIC("Expected an integer or float type for `exact_value_make_jmag`");
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_make_kmag(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return exact_value_quaternion(0, 0, 0, exact_value_to_float(v).value_float);
case ExactValue_Float:
return exact_value_quaternion(0, 0, 0, v.value_float);
default:
GB_PANIC("Expected an integer or float type for `exact_value_make_kmag`");
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
switch (op) {
@@ -253,6 +417,8 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
case ExactValue_Invalid:
case ExactValue_Integer:
case ExactValue_Float:
case ExactValue_Complex:
case ExactValue_Quaternion:
return v;
}
} break;
@@ -271,6 +437,18 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision)
i.value_float = -i.value_float;
return i;
}
case ExactValue_Complex: {
f64 real = v.value_complex.real;
f64 imag = v.value_complex.imag;
return exact_value_complex(-real, -imag);
}
case ExactValue_Quaternion: {
f64 real = v.value_quaternion.real;
f64 imag = v.value_quaternion.imag;
f64 jmag = v.value_quaternion.jmag;
f64 kmag = v.value_quaternion.kmag;
return exact_value_quaternion(-real, -imag, -jmag, -kmag);
}
}
} break;
@@ -324,8 +502,12 @@ i32 exact_value_order(ExactValue v) {
return 2;
case ExactValue_Float:
return 3;
case ExactValue_Pointer:
case ExactValue_Complex:
return 4;
case ExactValue_Quaternion:
return 5;
case ExactValue_Pointer:
return 6;
default:
GB_PANIC("How'd you get here? Invalid Value.kind");
@@ -346,6 +528,8 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
case ExactValue_Bool:
case ExactValue_String:
case ExactValue_Complex:
case ExactValue_Quaternion:
return;
case ExactValue_Integer:
@@ -356,16 +540,30 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
// TODO(bill): Is this good enough?
*x = exact_value_float(cast(f64)x->value_integer);
return;
case ExactValue_Complex:
*x = exact_value_complex(cast(f64)x->value_integer, 0);
return;
case ExactValue_Quaternion:
*x = exact_value_quaternion(cast(f64)x->value_integer, 0, 0, 0);
return;
}
break;
case ExactValue_Float:
if (y->kind == ExactValue_Float)
switch (y->kind) {
case ExactValue_Float:
return;
case ExactValue_Complex:
*x = exact_value_to_complex(*x);
return;
case ExactValue_Quaternion:
*x = exact_value_to_quaternion(*x);
return;
}
break;
}
compiler_error("How'd you get here? Invalid ExactValueKind");
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
}
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
@@ -420,6 +618,84 @@ ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y)
default: goto error;
}
} break;
case ExactValue_Complex: {
y = exact_value_to_complex(y);
f64 a = x.value_complex.real;
f64 b = x.value_complex.imag;
f64 c = y.value_complex.real;
f64 d = y.value_complex.imag;
f64 real = 0;
f64 imag = 0;
switch (op) {
case Token_Add:
real = a + c;
imag = b + d;
break;
case Token_Sub:
real = a - c;
imag = b - d;
break;
case Token_Mul:
real = (a*c - b*d);
imag = (b*c + a*d);
break;
case Token_Quo: {
f64 s = c*c + d*d;
real = (a*c + b*d)/s;
imag = (b*c - a*d)/s;
} break;
default: goto error;
}
return exact_value_complex(real, imag);
} break;
case ExactValue_Quaternion: {
y = exact_value_to_quaternion(y);
f64 a = x.value_quaternion.real;
f64 b = x.value_quaternion.imag;
f64 c = x.value_quaternion.jmag;
f64 d = x.value_quaternion.kmag;
f64 e = x.value_quaternion.real;
f64 f = x.value_quaternion.imag;
f64 g = x.value_quaternion.jmag;
f64 h = x.value_quaternion.kmag;
f64 real = 0;
f64 imag = 0;
f64 jmag = 0;
f64 kmag = 0;
switch (op) {
case Token_Add:
real = a + e;
imag = b + f;
jmag = c + g;
kmag = d + h;
break;
case Token_Sub:
real = a - e;
imag = b - f;
jmag = c - g;
kmag = d - h;
break;
case Token_Mul:
real = a*f + b*e + c*h - d*g;
imag = a*g - b*h + c*e + d*f;
jmag = a*h + b*g - c*f + d*e;
kmag = a*e - b*f - c*g - d*h;
break;
case Token_Quo: {
f64 s = e*e + f*f + g*g + h*h;
real = (+a*e + b*f + c*g + d*h)/s;
imag = (-a*f + b*e - c*h + d*h)/s;
jmag = (-a*g + b*h + c*e - d*f)/s;
kmag = (-a*h - b*g + c*f + d*e)/s;
} break;
default: goto error;
}
return exact_value_quaternion(real, imag, jmag, kmag);
} break;
}
error:
@@ -480,6 +756,34 @@ bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
}
} break;
case ExactValue_Complex: {
f64 a = x.value_complex.real;
f64 b = x.value_complex.imag;
f64 c = y.value_complex.real;
f64 d = y.value_complex.imag;
switch (op) {
case Token_CmpEq: return cmp_f64(a, c) == 0 && cmp_f64(b, d) == 0;
case Token_NotEq: return cmp_f64(a, c) != 0 || cmp_f64(b, d) != 0;
}
} break;
case ExactValue_Quaternion: {
f64 a = x.value_quaternion.real;
f64 b = x.value_quaternion.imag;
f64 c = x.value_quaternion.jmag;
f64 d = x.value_quaternion.kmag;
f64 e = y.value_quaternion.real;
f64 f = y.value_quaternion.imag;
f64 g = y.value_quaternion.jmag;
f64 h = y.value_quaternion.kmag;
switch (op) {
case Token_CmpEq: return cmp_f64(a, e) == 0 && cmp_f64(b, f) == 0 && cmp_f64(c, g) == 0 && cmp_f64(d, h) == 0;
case Token_NotEq: return cmp_f64(a, e) != 0 || cmp_f64(b, f) != 0 || cmp_f64(c, g) != 0 || cmp_f64(d, h) != 0;
}
} break;
case ExactValue_String: {
String a = x.value_string;
String b = y.value_string;
+116 -32
View File
@@ -275,8 +275,11 @@ extern "C" {
// TODO(bill): How many of these headers do I really need?
#include <stdarg.h>
#include <stddef.h>
// #include <stdarg.h>
#if !defined(GB_SYSTEM_WINDOWS)
#include <stddef.h>
#include <stdarg.h>
#endif
@@ -313,6 +316,10 @@ extern "C" {
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#if defined(GB_CPU_X86)
#include <xmmintrin.h>
#endif
#endif
#if defined(GB_SYSTEM_OSX)
@@ -425,7 +432,7 @@ typedef i32 b32; // NOTE(bill): Prefer this!!!
// NOTE(bill): Get true and false
#if !defined(__cplusplus)
#if (defined(_MSC_VER) && _MSC_VER <= 1800) || (!defined(_MSC_VER) && !defined(__STDC_VERSION__))
#if (defined(_MSC_VER) && _MSC_VER < 1800) || (!defined(_MSC_VER) && !defined(__STDC_VERSION__))
#ifndef true
#define true (0 == 0)
#endif
@@ -3615,14 +3622,11 @@ b32 gb_is_power_of_two(isize x) {
gb_inline void *gb_align_forward(void *ptr, isize alignment) {
uintptr p;
isize modulo;
GB_ASSERT(gb_is_power_of_two(alignment));
p = cast(uintptr)ptr;
modulo = p & (alignment-1);
if (modulo) p += (alignment - modulo);
return cast(void *)p;
return cast(void *)((p + (alignment-1)) &~ (alignment-1));
}
@@ -3642,15 +3646,33 @@ gb_inline void gb_zero_size(void *ptr, isize size) { gb_memset(ptr, 0, size); }
gb_inline void *gb_memcopy(void *dest, void const *source, isize n) {
#if defined(_MSC_VER)
if (dest == NULL) {
return NULL;
}
// TODO(bill): Is this good enough?
__movsb(cast(u8 *)dest, cast(u8 *)source, n);
// #elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
// NOTE(zangent): I assume there's a reason this isn't being used elsewhere,
// but casting pointers as arguments to an __asm__ call is considered an
// error on MacOS and (I think) Linux
// TODO(zangent): Figure out how to refactor the asm code so it works on MacOS,
// since this is probably not the way the author intended this to work.
// memcpy(dest, source, n);
#elif defined(GB_CPU_X86)
__asm__ __volatile__("rep movsb" : "+D"(cast(u8 *)dest), "+S"(cast(u8 *)source), "+c"(n) : : "memory");
if (dest == NULL) {
return NULL;
}
__asm__ __volatile__("rep movsb" : "+D"(dest), "+S"(source), "+c"(n) : : "memory");
#else
u8 *d = cast(u8 *)dest;
u8 const *s = cast(u8 const *)source;
u32 w, x;
if (dest == NULL) {
return NULL;
}
for (; cast(uintptr)s % 4 && n; n--) {
*d++ = *s++;
}
@@ -3782,10 +3804,16 @@ gb_inline void *gb_memmove(void *dest, void const *source, isize n) {
u8 *d = cast(u8 *)dest;
u8 const *s = cast(u8 const *)source;
if (d == s)
if (dest == NULL) {
return NULL;
}
if (d == s) {
return d;
if (s+n <= d || d+n <= s) // NOTE(bill): Non-overlapping
}
if (s+n <= d || d+n <= s) { // NOTE(bill): Non-overlapping
return gb_memcopy(d, s, n);
}
if (d < s) {
if (cast(uintptr)s % gb_size_of(isize) == cast(uintptr)d % gb_size_of(isize)) {
@@ -3824,6 +3852,10 @@ gb_inline void *gb_memset(void *dest, u8 c, isize n) {
isize k;
u32 c32 = ((u32)-1)/255 * c;
if (dest == NULL) {
return NULL;
}
if (n == 0)
return dest;
s[0] = s[n-1] = c;
@@ -3844,14 +3876,16 @@ gb_inline void *gb_memset(void *dest, u8 c, isize n) {
*cast(u32 *)(s+0) = c32;
*cast(u32 *)(s+n-4) = c32;
if (n < 9)
if (n < 9) {
return dest;
}
*cast(u32 *)(s + 4) = c32;
*cast(u32 *)(s + 8) = c32;
*cast(u32 *)(s+n-12) = c32;
*cast(u32 *)(s+n- 8) = c32;
if (n < 25)
if (n < 25) {
return dest;
}
*cast(u32 *)(s + 12) = c32;
*cast(u32 *)(s + 16) = c32;
*cast(u32 *)(s + 20) = c32;
@@ -3884,12 +3918,17 @@ gb_inline void *gb_memset(void *dest, u8 c, isize n) {
gb_inline i32 gb_memcompare(void const *s1, void const *s2, isize size) {
// TODO(bill): Heavily optimize
u8 const *s1p8 = cast(u8 const *)s1;
u8 const *s2p8 = cast(u8 const *)s2;
if (s1 == NULL || s2 == NULL) {
return 0;
}
while (size--) {
if (*s1p8 != *s2p8)
if (*s1p8 != *s2p8) {
return (*s1p8 - *s2p8);
}
s1p8++, s2p8++;
}
return 0;
@@ -3977,8 +4016,12 @@ gb_inline void gb_free_all (gbAllocator a)
gb_inline void *gb_resize (gbAllocator a, void *ptr, isize old_size, isize new_size) { return gb_resize_align(a, ptr, old_size, new_size, GB_DEFAULT_MEMORY_ALIGNMENT); }
gb_inline void *gb_resize_align(gbAllocator a, void *ptr, isize old_size, isize new_size, isize alignment) { return a.proc(a.data, gbAllocation_Resize, new_size, alignment, ptr, old_size, GB_DEFAULT_ALLOCATOR_FLAGS); }
gb_inline void *gb_alloc_copy (gbAllocator a, void const *src, isize size) { return gb_memcopy(gb_alloc(a, size), src, size); }
gb_inline void *gb_alloc_copy_align(gbAllocator a, void const *src, isize size, isize alignment) { return gb_memcopy(gb_alloc_align(a, size, alignment), src, size); }
gb_inline void *gb_alloc_copy (gbAllocator a, void const *src, isize size) {
return gb_memcopy(gb_alloc(a, size), src, size);
}
gb_inline void *gb_alloc_copy_align(gbAllocator a, void const *src, isize size, isize alignment) {
return gb_memcopy(gb_alloc_align(a, size, alignment), src, size);
}
gb_inline char *gb_alloc_str(gbAllocator a, char const *str) {
return gb_alloc_str_len(a, str, gb_strlen(str));
@@ -4695,7 +4738,7 @@ gb_inline u32 gb_thread_current_id(void) {
#elif defined(GB_ARCH_32_BIT) && defined(GB_CPU_X86)
__asm__("mov %%gs:0x08,%0" : "=r"(thread_id));
#elif defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
__asm__("mov %%gs:0x10,%0" : "=r"(thread_id));
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
#else
#error Unsupported architecture for gb_thread_current_id()
#endif
@@ -4840,6 +4883,26 @@ GB_ALLOCATOR_PROC(gb_heap_allocator_proc) {
case gbAllocation_Resize:
ptr = _aligned_realloc(old_memory, size, alignment);
break;
#elif defined(GB_SYSTEM_LINUX)
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
ptr = aligned_alloc(alignment, size);
// ptr = malloc(size+alignment);
if (flags & gbAllocatorFlag_ClearToZero) {
gb_zero_size(ptr, size);
}
} break;
case gbAllocation_Free: {
free(old_memory);
} break;
case gbAllocation_Resize: {
// ptr = realloc(old_memory, size);
ptr = gb_default_resize_align(gb_heap_allocator(), old_memory, old_size, size, alignment);
} break;
#else
// TODO(bill): *nix version that's decent
case gbAllocation_Alloc: {
@@ -4855,8 +4918,7 @@ GB_ALLOCATOR_PROC(gb_heap_allocator_proc) {
} break;
case gbAllocation_Resize: {
gbAllocator a = gb_heap_allocator();
ptr = gb_default_resize_align(a, old_memory, old_size, size, alignment);
ptr = gb_default_resize_align(gb_heap_allocator(), old_memory, old_size, size, alignment);
} break;
#endif
@@ -5019,7 +5081,10 @@ void gb_affinity_init(gbAffinity *a) {
// Parsing /proc/cpuinfo to get the number of threads per core.
// NOTE(zangent): This calls the CPU's threads "cores", although the wording
// is kind of weird. This should be right, though.
if (fopen("/proc/cpuinfo", "r") != NULL) {
FILE* cpu_info = fopen("/proc/cpuinfo", "r");
if (cpu_info != NULL) {
for (;;) {
// The 'temporary char'. Everything goes into this char,
// so that we can check against EOF at the end of this loop.
@@ -5050,6 +5115,8 @@ void gb_affinity_init(gbAffinity *a) {
}
#undef AF__CHECK
}
fclose(cpu_info);
}
if (threads == 0) {
@@ -5999,6 +6066,9 @@ gb_inline void gb_str_to_upper(char *str) {
gb_inline isize gb_strlen(char const *str) {
char const *begin = str;
isize const *w;
if (str == NULL) {
return 0;
}
while (cast(uintptr)str % sizeof(usize)) {
if (!*str)
return str - begin;
@@ -7552,10 +7622,14 @@ u64 gb_murmur64_seed(void const *data_, isize len, u64 seed) {
gbFileError gb_file_new(gbFile *f, gbFileDescriptor fd, gbFileOperations ops, char const *filename) {
gbFileError err = gbFileError_None;
isize len = gb_strlen(filename);
// gb_printf_err("gb_file_new: %s\n", filename);
f->ops = ops;
f->fd = fd;
f->filename = gb_alloc_str(gb_heap_allocator(), filename);
f->filename = gb_alloc_array(gb_heap_allocator(), char, len+1);
gb_memcopy(cast(char *)f->filename, cast(char *)filename, len+1);
f->last_write_time = gb_file_last_write_time(f->filename);
return err;
@@ -7577,11 +7651,17 @@ gbFileError gb_file_open_mode(gbFile *f, gbFileMode mode, char const *filename)
}
gbFileError gb_file_close(gbFile *f) {
if (!f) {
if (f == NULL) {
return gbFileError_Invalid;
}
if (f->filename) gb_free(gb_heap_allocator(), cast(char *)f->filename);
#if defined(GB_COMPILER_MSVC)
if (f->filename != NULL) {
gb_free(gb_heap_allocator(), cast(char *)f->filename);
}
#else
// TODO HACK(bill): Memory Leak!!!
#endif
#if defined(GB_SYSTEM_WINDOWS)
if (f->fd.p == INVALID_HANDLE_VALUE) {
@@ -7993,19 +8073,23 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
new_path[new_len] = 0;
return new_path;
#else
// TODO(bill): Make work on *nix, etc.
char* p = realpath(path, 0);
GB_ASSERT(p && "file does not exist");
char *p, *result, *fullpath = NULL;
isize len;
p = realpath(path, NULL);
fullpath = p;
if (p == NULL) {
// NOTE(bill): File does not exist
fullpath = cast(char *)path;
}
isize len = gb_strlen(p);
len = gb_strlen(fullpath);
// bill... gb_alloc_str_len refused to work for this...
char* ret = gb_alloc(a, sizeof(char) * len + 1);
gb_memmove(ret, p, len);
ret[len] = 0;
result = gb_alloc_array(a, char, len + 1);
gb_memmove(result, fullpath, len);
result[len] = 0;
free(p);
return ret;
return result;
#endif
}
+1216 -317
View File
File diff suppressed because it is too large Load Diff
+273 -82
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);
}
void ir_print_type(irFileBuffer *f, irModule *m, Type *t);
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
GB_ASSERT(is_type_proc(t));
t = base_type(t);
isize result_count = t->Proc.result_count;
if (result_count == 0) {
ir_fprintf(f, "void");
} else if (result_count == 1) {
ir_print_type(f, m, t->Proc.abi_compat_results[0]);
} else {
ir_fprintf(f, "{");
for (isize i = 0; i < result_count; i++) {
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, t->Proc.abi_compat_results[i]);
}
ir_fprintf(f, "}");
}
}
void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
i64 word_bits = 8*build_context.word_size;
@@ -145,32 +167,39 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
switch (t->kind) {
case Type_Basic:
switch (t->Basic.kind) {
case Basic_bool: ir_fprintf(f, "i1"); return;
case Basic_i8: ir_fprintf(f, "i8"); return;
case Basic_u8: ir_fprintf(f, "i8"); return;
case Basic_i16: ir_fprintf(f, "i16"); return;
case Basic_u16: ir_fprintf(f, "i16"); return;
case Basic_i32: ir_fprintf(f, "i32"); return;
case Basic_u32: ir_fprintf(f, "i32"); return;
case Basic_i64: ir_fprintf(f, "i64"); return;
case Basic_u64: ir_fprintf(f, "i64"); return;
// case Basic_i128: ir_fprintf(f, "i128"); return;
// case Basic_u128: ir_fprintf(f, "i128"); return;
// case Basic_f16: ir_fprintf(f, "half"); return;
case Basic_f32: ir_fprintf(f, "float"); return;
case Basic_f64: ir_fprintf(f, "double"); return;
// case Basic_f128: ir_fprintf(f, "fp128"); return;
case Basic_rawptr: ir_fprintf(f, "%%..rawptr"); return;
case Basic_string: ir_fprintf(f, "%%..string"); return;
case Basic_uint: ir_fprintf(f, "i%lld", word_bits); return;
case Basic_int: ir_fprintf(f, "i%lld", word_bits); return;
case Basic_any: ir_fprintf(f, "%%..any"); return;
case Basic_bool: ir_fprintf(f, "i1"); return;
case Basic_i8: ir_fprintf(f, "i8"); return;
case Basic_u8: ir_fprintf(f, "i8"); return;
case Basic_i16: ir_fprintf(f, "i16"); return;
case Basic_u16: ir_fprintf(f, "i16"); return;
case Basic_i32: ir_fprintf(f, "i32"); return;
case Basic_u32: ir_fprintf(f, "i32"); return;
case Basic_i64: ir_fprintf(f, "i64"); return;
case Basic_u64: ir_fprintf(f, "i64"); return;
case Basic_f32: ir_fprintf(f, "float"); return;
case Basic_f64: ir_fprintf(f, "double"); return;
case Basic_complex64: ir_fprintf(f, "%%..complex64"); return;
case Basic_complex128: ir_fprintf(f, "%%..complex128"); return;
case Basic_quaternion128: ir_fprintf(f, "%%..quaternion128"); return;
case Basic_quaternion256: ir_fprintf(f, "%%..quaternion256"); return;
case Basic_rawptr: ir_fprintf(f, "%%..rawptr"); return;
case Basic_string: ir_fprintf(f, "%%..string"); return;
case Basic_uint: ir_fprintf(f, "i%lld", word_bits); return;
case Basic_int: ir_fprintf(f, "i%lld", word_bits); return;
case Basic_any: ir_fprintf(f, "%%..any"); return;
}
break;
case Type_Pointer:
ir_print_type(f, m, t->Pointer.elem);
ir_fprintf(f, "*");
return;
case Type_Atomic:
ir_print_type(f, m, t->Atomic.elem);
return;
case Type_Array:
ir_fprintf(f, "[%lld x ", t->Array.count);
ir_print_type(f, m, t->Array.elem);
@@ -227,9 +256,30 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
case TypeRecord_Union: {
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
// LLVM takes the first element's alignment as the entire alignment (like C)
i64 size_of_union = type_size_of(heap_allocator(), t) - build_context.word_size;
i64 align_of_union = type_align_of(heap_allocator(), t);
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align_of_union, size_of_union, word_bits);
i64 align = type_align_of(heap_allocator(), t);
i64 total_size = type_size_of(heap_allocator(), t);
#if 1
i64 fields_size = 0;
if (t->Record.field_count > 0) {
type_set_offsets(m->allocator, t);
isize end_index = t->Record.field_count-1;
i64 end_offset = t->Record.offsets[end_index];
isize end_size = type_size_of(m->allocator, t->Record.fields[end_index]->type);
fields_size = align_formula(end_offset + end_size, build_context.word_size);
}
i64 block_size = total_size - fields_size - build_context.word_size;
ir_fprintf(f, "{[0 x <%lld x i8>], ", align);
for (isize i = 0; i < t->Record.field_count; i++) {
ir_print_type(f, m, t->Record.fields[i]->type);
ir_fprintf(f, ", ");
}
ir_fprintf(f, "[%lld x i8], ", block_size);
ir_fprintf(f, "i%lld}", word_bits);
#else
i64 block_size = total_size - build_context.word_size;
ir_fprintf(f, "{[0 x <%lld x i8>], [%lld x i8], i%lld}", align, block_size, word_bits);
#endif
} return;
case TypeRecord_RawUnion: {
// NOTE(bill): The zero size array is used to fix the alignment used in a structure as
@@ -247,7 +297,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
case Type_Named:
if (is_type_struct(t) || is_type_union(t)) {
String *name = map_string_get(&m->type_names, hash_pointer(t));
String *name = map_string_get(&m->entity_names, hash_pointer(t->Named.type_name));
GB_ASSERT_MSG(name != NULL, "%.*s", LIT(t->Named.name));
ir_print_encoded_local(f, *name);
} else {
@@ -269,18 +319,15 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
}
return;
case Type_Proc: {
if (t->Proc.result_count == 0) {
ir_fprintf(f, "void");
} else {
ir_print_type(f, m, t->Proc.results);
}
isize param_count = t->Proc.param_count;
isize result_count = t->Proc.result_count;
ir_print_proc_results(f, m, t);
ir_fprintf(f, " (");
TypeTuple *params = &t->Proc.params->Tuple;
for (isize i = 0; i < t->Proc.param_count; i++) {
for (isize i = 0; i < param_count; i++) {
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, params->variables[i]->type);
ir_print_type(f, m, t->Proc.abi_compat_params[i]);
}
ir_fprintf(f, ")*");
} return;
@@ -307,13 +354,7 @@ void ir_print_compound_element(irFileBuffer *f, irModule *m, ExactValue v, Type
void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *type) {
type = core_type(type);
if (is_type_float(type)) {
value = exact_value_to_float(value);
} else if (is_type_integer(type)) {
value = exact_value_to_integer(value);
} else if (is_type_pointer(type)) {
value = exact_value_to_integer(value);
}
value = convert_exact_value_for_type(value, type);
switch (value.kind) {
case ExactValue_Bool:
@@ -334,7 +375,6 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
// HACK NOTE(bill): This is a hack but it works because strings are created at the very end
// of the .ll file
irValue *str_array = ir_add_global_string_array(m, str);
ir_fprintf(f, "{i8* getelementptr inbounds (");
ir_print_type(f, m, str_array->Global.entity->type);
ir_fprintf(f, ", ");
@@ -365,7 +405,7 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
} break;
case ExactValue_Float: {
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;
switch (type->Basic.kind) {
case Basic_f32:
@@ -382,28 +422,52 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
switch (type->Basic.kind) {
case 0: break;
#if 0
case Basic_f16:
ir_fprintf(f, "bitcast (");
ir_print_type(f, m, t_u16);
ir_fprintf(f, " %u to ", cast(u16)f32_to_f16(cast(f32)value.value_float));
ir_print_type(f, m, t_f16);
ir_fprintf(f, ")");
break;
case Basic_f128:
ir_fprintf(f, "bitcast (");
ir_fprintf(f, "i128");
// TODO(bill): Actually support f128
ir_fprintf(f, " %llu to ", u);
ir_print_type(f, m, t_f128);
ir_fprintf(f, ")");
break;
#endif
default:
ir_fprintf(f, "0x%016llx", u);
break;
}
} break;
case ExactValue_Complex: {
type = core_type(type);
if (is_type_quaternion(type)) {
Type *ft = base_quaternion_elem_type(type);
ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(0), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(0), ft);
ir_fprintf(f, "}");
} else {
GB_ASSERT_MSG(is_type_complex(type), "%s", type_to_string(type));
Type *ft = base_complex_elem_type(type);
ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_complex.real), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_complex.imag), ft);
ir_fprintf(f, "}");
}
} break;
case ExactValue_Quaternion: {
GB_ASSERT_MSG(is_type_quaternion(type), "%s", type_to_string(type));
type = core_type(type);
Type *ft = base_quaternion_elem_type(type);
ir_fprintf(f, " {"); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.real), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.imag), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.jmag), ft);
ir_fprintf(f, ", "); ir_print_type(f, m, ft); ir_fprintf(f, " ");
ir_print_exact_value(f, m, exact_value_float(value.value_quaternion.kmag), ft);
ir_fprintf(f, "}");
} break;
case ExactValue_Pointer:
if (value.value_pointer == 0) {
ir_fprintf(f, "null");
@@ -515,12 +579,15 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type *
}
} else {
for (isize i = 0; i < value_count; i++) {
TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]);
GB_ASSERT(tav != NULL);
Entity *f = type->Record.fields_in_src_order[i];
values[f->Variable.field_index] = tav->value;
if (str_eq(f->token.string, str_lit("_"))) {
values[f->Variable.field_index] = (ExactValue){0};
} else {
TypeAndValue *tav = type_and_value_of_expression(m->info, cl->elems.e[i]);
GB_ASSERT(tav != NULL);
values[f->Variable.field_index] = tav->value;
}
}
}
@@ -616,6 +683,8 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
ir_print_type(f, m, t_int);
ir_fprintf(f, " 0, i32 0), ");
ir_print_type(f, m, t_int);
ir_fprintf(f, " %lld, ", cs->count);
ir_print_type(f, m, t_int);
ir_fprintf(f, " %lld}", cs->count);
}
} break;
@@ -631,9 +700,10 @@ void ir_print_value(irFileBuffer *f, irModule *m, irValue *value, Type *type_hin
Scope *scope = value->Global.entity->scope;
bool in_global_scope = false;
if (scope != NULL) {
// TODO(bill): Fix this rule. What should it be?
in_global_scope = scope->is_global || scope->is_init;
}
ir_print_encoded_global(f, value->Global.entity->token.string, in_global_scope);
ir_print_encoded_global(f, ir_get_global_name(m, value), in_global_scope);
} break;
case irValue_Param:
ir_print_encoded_local(f, value->Param.entity->token.string);
@@ -681,9 +751,13 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_Local: {
Type *type = instr->Local.entity->type;
i64 align = instr->Local.alignment;
if (align <= 0) {
align = type_align_of(m->allocator, type);
}
ir_fprintf(f, "%%%d = alloca ", value->index);
ir_print_type(f, m, type);
ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
ir_fprintf(f, ", align %lld\n", align);
} break;
case irInstr_ZeroInit: {
@@ -698,6 +772,9 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_Store: {
Type *type = type_deref(ir_type(instr->Store.address));
ir_fprintf(f, "store ");
if (instr->Store.atomic) {
ir_fprintf(f, "atomic ");
}
ir_print_type(f, m, type);
ir_fprintf(f, " ");
ir_print_value(f, m, instr->Store.value, type);
@@ -705,17 +782,29 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_print_type(f, m, type);
ir_fprintf(f, "* ");
ir_print_value(f, m, instr->Store.address, type);
if (is_type_atomic(type)) {
// TODO(bill): Do ordering
ir_fprintf(f, " unordered");
ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
}
ir_fprintf(f, "\n");
} break;
case irInstr_Load: {
Type *type = instr->Load.type;
ir_fprintf(f, "%%%d = load ", value->index);
if (is_type_atomic(type)) {
ir_fprintf(f, "atomic ");
}
ir_print_type(f, m, type);
ir_fprintf(f, ", ");
ir_print_type(f, m, type);
ir_fprintf(f, "* ");
ir_print_value(f, m, instr->Load.address, type);
if (is_type_atomic(type)) {
// TODO(bill): Do ordering
ir_fprintf(f, " unordered");
}
ir_fprintf(f, ", align %lld\n", type_align_of(m->allocator, type));
} break;
@@ -753,6 +842,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
if (st->Record.custom_align > 0) {
index += 1;
}
} else if (is_type_union(st)) {
index += 1;
}
ir_print_type(f, m, type_deref(et));
@@ -821,6 +912,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
if (st->Record.custom_align > 0) {
index += 1;
}
} else if (is_type_union(st)) {
index += 1;
}
@@ -833,6 +926,8 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_UnionTagPtr: {
Type *et = ir_type(instr->UnionTagPtr.address);
ir_fprintf(f, "%%%d = getelementptr inbounds ", value->index);
Type *t = base_type(type_deref(et));
GB_ASSERT(is_type_union(t));
ir_print_type(f, m, type_deref(et));
ir_fprintf(f, ", ");
@@ -843,7 +938,11 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_print_type(f, m, t_int);
ir_fprintf(f, " 0, ");
ir_print_type(f, m, t_i32);
#if 1
ir_fprintf(f, " %d", 2 + t->Record.field_count);
#else
ir_fprintf(f, " %d", 2);
#endif
ir_fprintf(f, " ; UnionTagPtr");
ir_fprintf(f, "\n");
} break;
@@ -851,11 +950,19 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case irInstr_UnionTagValue: {
Type *et = ir_type(instr->UnionTagValue.address);
ir_fprintf(f, "%%%d = extractvalue ", value->index);
Type *t = base_type(et);
GB_ASSERT(is_type_union(t));
ir_print_type(f, m, et);
ir_fprintf(f, " ");
ir_print_value(f, m, instr->UnionTagValue.address, et);
ir_fprintf(f, ", %d", 2);
ir_fprintf(f, ",");
#if 1
ir_fprintf(f, " %d", 2 + t->Record.field_count);
#else
ir_fprintf(f, " %d", 2);
#endif
ir_fprintf(f, ", %d", 2 + t->Record.field_count);
ir_fprintf(f, " ; UnionTagValue");
ir_fprintf(f, "\n");
} break;
@@ -963,9 +1070,6 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
Type *type = base_type(ir_type(bo->left));
Type *elem_type = type;
GB_ASSERT(!is_type_vector(elem_type));
while (elem_type->kind == Type_Vector) {
elem_type = base_type(elem_type->Vector.elem);
}
ir_fprintf(f, "%%%d = ", value->index);
@@ -1007,6 +1111,72 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
case Token_LtEq: ir_fprintf(f, "ole"); break;
case Token_GtEq: ir_fprintf(f, "oge"); break;
}
} else if (is_type_complex(elem_type)) {
ir_fprintf(f, "call ");
ir_print_calling_convention(f, m, ProcCC_Odin);
ir_print_type(f, m, t_bool);
char *runtime_proc = "";
i64 sz = 8*type_size_of(m->allocator, elem_type);
switch (sz) {
case 64:
switch (bo->op) {
case Token_CmpEq: runtime_proc = "__complex64_eq"; break;
case Token_NotEq: runtime_proc = "__complex64_ne"; break;
}
break;
case 128:
switch (bo->op) {
case Token_CmpEq: runtime_proc = "__complex128_eq"; break;
case Token_NotEq: runtime_proc = "__complex128_ne"; break;
}
break;
}
ir_fprintf(f, " ");
ir_print_encoded_global(f, make_string_c(runtime_proc), false);
ir_fprintf(f, "(");
ir_print_type(f, m, type);
ir_fprintf(f, " ");
ir_print_value(f, m, bo->left, type);
ir_fprintf(f, ", ");
ir_print_type(f, m, type);
ir_fprintf(f, " ");
ir_print_value(f, m, bo->right, type);
ir_fprintf(f, ")\n");
return;
} else if (is_type_quaternion(elem_type)) {
ir_fprintf(f, "call ");
ir_print_calling_convention(f, m, ProcCC_Odin);
ir_print_type(f, m, t_bool);
char *runtime_proc = "";
i64 sz = 8*type_size_of(m->allocator, elem_type);
switch (sz) {
case 128:
switch (bo->op) {
case Token_CmpEq: runtime_proc = "__quaternion128_eq"; break;
case Token_NotEq: runtime_proc = "__quaternion128_ne"; break;
}
break;
case 256:
switch (bo->op) {
case Token_CmpEq: runtime_proc = "__quaternion256_eq"; break;
case Token_NotEq: runtime_proc = "__quaternion256_ne"; break;
}
break;
}
ir_fprintf(f, " ");
ir_print_encoded_global(f, make_string_c(runtime_proc), false);
ir_fprintf(f, "(");
ir_print_type(f, m, type);
ir_fprintf(f, " ");
ir_print_value(f, m, bo->left, type);
ir_fprintf(f, ", ");
ir_print_type(f, m, type);
ir_fprintf(f, " ");
ir_print_value(f, m, bo->right, type);
ir_fprintf(f, ")\n");
return;
} else {
ir_fprintf(f, "icmp ");
if (bo->op != Token_CmpEq &&
@@ -1082,7 +1252,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
ir_fprintf(f, "call ");
ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
if (result_type) {
ir_print_type(f, m, result_type);
ir_print_proc_results(f, m, proc_type);
} else {
ir_fprintf(f, "void");
}
@@ -1098,7 +1268,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
for (isize i = 0; i < call->arg_count; i++) {
Entity *e = params->variables[i];
GB_ASSERT(e != NULL);
Type *t = e->type;
Type *t = proc_type->Proc.abi_compat_params[i];
if (i > 0) {
ir_fprintf(f, ", ");
}
@@ -1311,31 +1481,39 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
ir_print_calling_convention(f, m, proc_type->calling_convention);
if (proc_type->result_count == 0) {
ir_fprintf(f, "void");
} else {
ir_print_type(f, m, proc_type->results);
}
isize param_count = proc_type->param_count;
isize result_count = proc_type->result_count;
ir_print_proc_results(f, m, proc->type);
ir_fprintf(f, " ");
// #ifndef GB_SYSTEM_WINDOWS
#if 0
if(uses_args)
ir_fprintf(f, "@.nix_argpatch_main");
else
#endif
ir_print_encoded_global(f, proc->name, ir_print_is_proc_global(m, proc));
ir_fprintf(f, "(");
if (proc_type->param_count > 0) {
if (param_count > 0) {
TypeTuple *params = &proc_type->params->Tuple;
for (isize i = 0; i < params->variable_count; i++) {
Entity *e = params->variables[i];
Type *original_type = e->type;
Type *abi_type = proc_type->abi_compat_params[i];
if (i > 0) {
ir_fprintf(f, ", ");
}
ir_print_type(f, m, e->type);
ir_print_type(f, m, abi_type);
if (e->flags&EntityFlag_NoAlias) {
ir_fprintf(f, " noalias");
}
if (proc->body != NULL) {
if (!str_eq(e->token.string, str_lit("")) &&
!str_eq(e->token.string, str_lit("_"))) {
ir_fprintf(f, " %%%.*s", LIT(e->token.string));
ir_fprintf(f, " ");
ir_print_encoded_local(f, e->token.string);
} else {
ir_fprintf(f, " %%_.param_%td", i);
}
@@ -1415,11 +1593,21 @@ void print_llvm_ir(irGen *ir) {
ir_print_encoded_local(f, str_lit("..rawptr"));
ir_fprintf(f, " = type i8* ; Basic_rawptr\n");
ir_print_encoded_local(f, str_lit("..complex64"));
ir_fprintf(f, " = type {float, float} ; Basic_complex64\n");
ir_print_encoded_local(f, str_lit("..complex128"));
ir_fprintf(f, " = type {double, double} ; Basic_complex128\n");
ir_print_encoded_local(f, str_lit("..quaternion128"));
ir_fprintf(f, " = type {float, float, float, float} ; Basic_quaternion128\n");
ir_print_encoded_local(f, str_lit("..quaternion256"));
ir_fprintf(f, " = type {double, double, double, double} ; Basic_quaternion256\n");
ir_print_encoded_local(f, str_lit("..any"));
ir_fprintf(f, " = type {");
ir_print_type(f, m, t_type_info_ptr);
ir_fprintf(f, ", ");
ir_print_type(f, m, t_rawptr);
ir_fprintf(f, ", ");
ir_print_type(f, m, t_type_info_ptr);
ir_fprintf(f, "} ; Basic_any\n");
ir_fprintf(f, "declare void @llvm.dbg.declare(metadata, metadata, metadata) nounwind readnone \n");
@@ -1472,9 +1660,12 @@ void print_llvm_ir(irGen *ir) {
Scope *scope = g->entity->scope;
bool in_global_scope = false;
if (scope != NULL) {
// TODO(bill): Fix this rule. What should it be?
in_global_scope = scope->is_global || scope->is_init;
// in_global_scope = value->Global.name_is_not_mangled;
}
ir_print_encoded_global(f, g->entity->token.string, in_global_scope);
ir_print_encoded_global(f, ir_get_global_name(m, v), in_global_scope);
ir_fprintf(f, " = ");
if (g->is_foreign) {
ir_fprintf(f, "external ");
+137 -9
View File
@@ -102,6 +102,8 @@ i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
// }
// exit_code = status;
return exit_code;
}
#endif
@@ -242,17 +244,19 @@ int main(int argc, char **argv) {
#if 1
timings_start_section(&timings, str_lit("llvm-opt"));
char const *output_name = ir_gen.output_file.filename;
isize base_name_len = gb_path_extension(output_name)-1 - output_name;
String output = make_string(cast(u8 *)output_name, base_name_len);
String output_name = ir_gen.output_name;
String output_base = ir_gen.output_base;
int base_name_len = output_base.len;
i32 optimization_level = 0;
optimization_level = gb_clamp(optimization_level, 0, 3);
i32 exit_code = 0;
#if defined(GB_SYSTEM_WINDOWS)
// For more passes arguments: http://llvm.org/docs/Passes.html
exit_code = system_exec_command_line_app("llvm-opt", false,
"\"%.*sbin/opt\" \"%s\" -o \"%.*s\".bc "
"\"%.*sbin/opt\" \"%.*s\".ll -o \"%.*s\".bc "
"-mem2reg "
"-memcpyopt "
"-die "
@@ -261,10 +265,33 @@ int main(int argc, char **argv) {
// "-S "
"",
LIT(build_context.ODIN_ROOT),
output_name, LIT(output));
LIT(output_base), LIT(output_base));
if (exit_code != 0) {
return exit_code;
}
#else
// NOTE(zangent): This is separate because it seems that LLVM tools are packaged
// with the Windows version, while they will be system-provided on MacOS and GNU/Linux
exit_code = system_exec_command_line_app("llvm-opt", false,
"opt \"%.*s\".ll -o \"%.*s\".bc "
"-mem2reg "
"-memcpyopt "
"-die "
#if defined(GB_SYSTEM_OSX)
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
// make sure to also change the `macosx_version_min` param passed to `llc`
"-mtriple=x86_64-apple-macosx10.8 "
#endif
// "-dse "
// "-dce "
// "-S "
"",
LIT(output_base), LIT(output_base));
if (exit_code != 0) {
return exit_code;
}
#endif
#if defined(GB_SYSTEM_WINDOWS)
timings_start_section(&timings, str_lit("llvm-llc"));
@@ -275,7 +302,7 @@ int main(int argc, char **argv) {
// "-debug-pass=Arguments "
"",
LIT(build_context.ODIN_ROOT),
LIT(output),
LIT(output_base),
optimization_level,
LIT(build_context.llc_flags));
if (exit_code != 0) {
@@ -311,7 +338,7 @@ int main(int argc, char **argv) {
" %.*s "
" %s "
"",
LIT(output), LIT(output), output_ext,
LIT(output_base), LIT(output_base), output_ext,
lib_str, LIT(build_context.link_flags),
link_settings
);
@@ -322,11 +349,112 @@ int main(int argc, char **argv) {
// timings_print_all(&timings);
if (run_output) {
system_exec_command_line_app("odin run", false, "%.*s.exe", cast(int)base_name_len, output_name);
system_exec_command_line_app("odin run", false, "%.*s.exe", LIT(output_base));
}
#else
#error Implement build stuff for this platform
// NOTE(zangent): Linux / Unix is unfinished and not tested very well.
timings_start_section(&timings, str_lit("llvm-llc"));
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
exit_code = system_exec_command_line_app("llc", false,
"llc \"%.*s.bc\" -filetype=obj -O%d "
"%.*s "
// "-debug-pass=Arguments "
"",
LIT(output_base),
optimization_level,
LIT(build_context.llc_flags));
if (exit_code != 0) {
return exit_code;
}
timings_start_section(&timings, str_lit("ld-link"));
gbString lib_str = gb_string_make(heap_allocator(), "");
// defer (gb_string_free(lib_str));
char lib_str_buf[1024] = {0};
for_array(i, ir_gen.module.foreign_library_paths) {
String lib = ir_gen.module.foreign_library_paths.e[i];
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
// This allows you to specify '-f' in a #foreign_system_library,
// without having to implement any new syntax specifically for MacOS.
#if defined(GB_SYSTEM_OSX)
isize len;
if(lib.len > 2 && lib.text[0] == '-' && lib.text[1] == 'f') {
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" -framework %.*s ", (int)(lib.len) - 2, lib.text + 2);
} else {
len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" -l%.*s ", LIT(lib));
}
#else
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" -l%.*s ", LIT(lib));
#endif
lib_str = gb_string_appendc(lib_str, lib_str_buf);
}
// Unlike the Win32 linker code, the output_ext includes the dot, because
// typically executable files on *NIX systems don't have extensions.
char *output_ext = "";
char *link_settings = "";
char *linker;
if (build_context.is_dll) {
// Shared libraries are .dylib on MacOS and .so on Linux.
// TODO(zangent): Is that statement entirely truthful?
#if defined(GB_SYSTEM_OSX)
output_ext = ".dylib";
#else
output_ext = ".so";
#endif
link_settings = "-shared";
} else {
// TODO: Do I need anything here?
link_settings = "";
}
#if defined(GB_SYSTEM_OSX)
linker = "ld";
#else
// TODO(zangent): Figure out how to make ld work on Linux.
// It probably has to do with including the entire CRT, but
// that's quite a complicated issue to solve while remaining distro-agnostic.
// Clang can figure out linker flags for us, and that's good enough _for now_.
linker = "clang -Wno-unused-command-line-argument";
#endif
exit_code = system_exec_command_line_app("ld-link", true,
"%s \"%.*s\".o -o \"%.*s%s\" %s "
"-lc -lm "
" %.*s "
" %s "
#if defined(GB_SYSTEM_OSX)
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
// make sure to also change the `mtriple` param passed to `opt`
" -macosx_version_min 10.8.0 "
// This points the linker to where the entry point is
" -e _main "
#endif
, linker, LIT(output_base), LIT(output_base), output_ext,
lib_str, LIT(build_context.link_flags),
link_settings
);
if (exit_code != 0) {
return exit_code;
}
// timings_print_all(&timings);
if (run_output) {
system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base));
}
#endif
#endif
#endif
+145 -103
View File
@@ -17,6 +17,8 @@ typedef enum ParseFileError {
typedef Array(AstNode *) AstNodeArray;
gb_global i32 global_file_id = 0;
typedef struct AstFile {
i32 id;
gbArena arena;
@@ -30,6 +32,8 @@ typedef struct AstFile {
// < 0: In Control Clause
// NOTE(bill): Used to prevent type literals in control clauses
isize expr_level;
bool allow_range;
bool ignore_operand;
AstNodeArray decls;
bool is_global_scope;
@@ -65,6 +69,8 @@ typedef enum ProcTag {
ProcTag_bounds_check = 1<<0,
ProcTag_no_bounds_check = 1<<1,
ProcTag_require_results = 1<<4,
ProcTag_foreign = 1<<10,
ProcTag_export = 1<<11,
ProcTag_link_name = 1<<12,
@@ -103,6 +109,7 @@ typedef enum FieldFlag {
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_immutable,
} FieldListTag;
AstNodeArray make_ast_node_array(AstFile *f) {
AstNodeArray a;
// array_init(&a, gb_arena_allocator(&f->arena));
@@ -153,9 +160,11 @@ AST_NODE_KIND(_ExprBegin, "", i32) \
AST_NODE_KIND(SelectorExpr, "selector expression", struct { Token token; AstNode *expr, *selector; }) \
AST_NODE_KIND(IndexExpr, "index expression", struct { AstNode *expr, *index; Token open, close; }) \
AST_NODE_KIND(DerefExpr, "dereference expression", struct { Token op; AstNode *expr; }) \
AST_NODE_KIND(SliceExpr, "slice expression", struct { \
AST_NODE_KIND(SliceExpr, "slice expression", struct { \
AstNode *expr; \
Token open, close; \
Token interval0; \
Token interval1; \
bool index3; \
AstNode *low, *high, *max; \
}) \
@@ -173,10 +182,9 @@ AST_NODE_KIND(_ExprBegin, "", i32) \
Token open; \
Token close; \
}) \
AST_NODE_KIND(CastExpr, "cast expression", struct { Token token; AstNode *type, *expr; Token open, close; }) \
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \
AST_NODE_KIND(IntervalExpr, "interval expression", struct { Token op; AstNode *left, *right; }) \
AST_NODE_KIND(FieldValue, "field value", struct { Token eq; AstNode *field, *value; }) \
AST_NODE_KIND(TernaryExpr, "ternary expression", struct { AstNode *cond, *x, *y; }) \
AST_NODE_KIND(TypeAssertion, "type assertion", struct { AstNode *expr; Token dot; AstNode *type; }) \
AST_NODE_KIND(_ExprEnd, "", i32) \
AST_NODE_KIND(_StmtBegin, "", i32) \
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
@@ -343,6 +351,10 @@ AST_NODE_KIND(_TypeBegin, "", i32) \
Token token; \
AstNode *type; \
}) \
AST_NODE_KIND(AtomicType, "atomic type", struct { \
Token token; \
AstNode *type; \
}) \
AST_NODE_KIND(ArrayType, "array type", struct { \
Token token; \
AstNode *count; \
@@ -473,14 +485,13 @@ Token ast_node_token(AstNode *node) {
return ast_node_token(node->SelectorExpr.selector);
}
return node->SelectorExpr.token;
case AstNode_IndexExpr: return node->IndexExpr.open;
case AstNode_SliceExpr: return node->SliceExpr.open;
case AstNode_Ellipsis: return node->Ellipsis.token;
case AstNode_CastExpr: return node->CastExpr.token;
case AstNode_FieldValue: return node->FieldValue.eq;
case AstNode_DerefExpr: return node->DerefExpr.op;
case AstNode_TernaryExpr: return ast_node_token(node->TernaryExpr.cond);
case AstNode_IntervalExpr: return ast_node_token(node->IntervalExpr.left);
case AstNode_IndexExpr: return node->IndexExpr.open;
case AstNode_SliceExpr: return node->SliceExpr.open;
case AstNode_Ellipsis: return node->Ellipsis.token;
case AstNode_FieldValue: return node->FieldValue.eq;
case AstNode_DerefExpr: return node->DerefExpr.op;
case AstNode_TernaryExpr: return ast_node_token(node->TernaryExpr.cond);
case AstNode_TypeAssertion: return ast_node_token(node->TypeAssertion.expr);
case AstNode_BadStmt: return node->BadStmt.begin;
case AstNode_EmptyStmt: return node->EmptyStmt.token;
@@ -523,6 +534,7 @@ Token ast_node_token(AstNode *node) {
case AstNode_HelperType: return node->HelperType.token;
case AstNode_ProcType: return node->ProcType.token;
case AstNode_PointerType: return node->PointerType.token;
case AstNode_AtomicType: return node->AtomicType.token;
case AstNode_ArrayType: return node->ArrayType.token;
case AstNode_DynamicArrayType: return node->DynamicArrayType.token;
case AstNode_VectorType: return node->VectorType.token;
@@ -684,11 +696,13 @@ AstNode *ast_index_expr(AstFile *f, AstNode *expr, AstNode *index, Token open, T
}
AstNode *ast_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, bool index3, AstNode *low, AstNode *high, AstNode *max) {
AstNode *ast_slice_expr(AstFile *f, AstNode *expr, Token open, Token close, Token interval0, Token interval1, bool index3, AstNode *low, AstNode *high, AstNode *max) {
AstNode *result = make_ast_node(f, AstNode_SliceExpr);
result->SliceExpr.expr = expr;
result->SliceExpr.open = open;
result->SliceExpr.close = close;
result->SliceExpr.interval0 = interval0;
result->SliceExpr.interval1 = interval1;
result->SliceExpr.index3 = index3;
result->SliceExpr.low = low;
result->SliceExpr.high = high;
@@ -703,16 +717,6 @@ AstNode *ast_deref_expr(AstFile *f, AstNode *expr, Token op) {
return result;
}
AstNode *ast_interval_expr(AstFile *f, Token op, AstNode *left, AstNode *right) {
AstNode *result = make_ast_node(f, AstNode_IntervalExpr);
result->IntervalExpr.op = op;
result->IntervalExpr.left = left;
result->IntervalExpr.right = right;
return result;
}
@@ -769,16 +773,6 @@ AstNode *ast_field_value(AstFile *f, AstNode *field, AstNode *value, Token eq) {
return result;
}
AstNode *ast_cast_expr(AstFile *f, Token token, AstNode *type, AstNode *expr, Token open, Token close) {
AstNode *result = make_ast_node(f, AstNode_CastExpr);
result->CastExpr.token = token;
result->CastExpr.type = type;
result->CastExpr.expr = expr;
result->CastExpr.open = open;
result->CastExpr.close = close;
return result;
}
AstNode *ast_compound_lit(AstFile *f, AstNode *type, AstNodeArray elems, Token open, Token close) {
AstNode *result = make_ast_node(f, AstNode_CompoundLit);
result->CompoundLit.type = type;
@@ -802,6 +796,14 @@ AstNode *ast_ternary_expr(AstFile *f, AstNode *cond, AstNode *x, AstNode *y) {
result->TernaryExpr.y = y;
return result;
}
AstNode *ast_type_assertion(AstFile *f, AstNode *expr, Token dot, AstNode *type) {
AstNode *result = make_ast_node(f, AstNode_TypeAssertion);
result->TypeAssertion.expr = expr;
result->TypeAssertion.dot = dot;
result->TypeAssertion.type = type;
return result;
}
@@ -1047,6 +1049,13 @@ AstNode *ast_pointer_type(AstFile *f, Token token, AstNode *type) {
return result;
}
AstNode *ast_atomic_type(AstFile *f, Token token, AstNode *type) {
AstNode *result = make_ast_node(f, AstNode_AtomicType);
result->AtomicType.token = token;
result->AtomicType.type = type;
return result;
}
AstNode *ast_array_type(AstFile *f, Token token, AstNode *count, AstNode *elem) {
AstNode *result = make_ast_node(f, AstNode_ArrayType);
result->ArrayType.token = token;
@@ -1197,7 +1206,11 @@ Token expect_token(AstFile *f, TokenKind kind) {
syntax_error(f->curr_token, "Expected `%.*s`, got `%.*s`",
LIT(token_strings[kind]),
LIT(token_strings[prev.kind]));
if (prev.kind == Token_EOF) {
gb_exit(1);
}
}
next_token(f);
return prev;
}
@@ -1221,6 +1234,9 @@ Token expect_operator(AstFile *f) {
if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
syntax_error(f->curr_token, "Expected an operator, got `%.*s`",
LIT(token_strings[prev.kind]));
} else if (!f->allow_range && (prev.kind == Token_Ellipsis || prev.kind == Token_HalfClosed)) {
syntax_error(f->curr_token, "Expected an non-range operator, got `%.*s`",
LIT(token_strings[prev.kind]));
}
next_token(f);
return prev;
@@ -1325,6 +1341,9 @@ bool is_semicolon_optional_for_node(AstFile *f, AstNode *s) {
case AstNode_PointerType:
return is_semicolon_optional_for_node(f, s->PointerType.type);
case AstNode_AtomicType:
return is_semicolon_optional_for_node(f, s->AtomicType.type);
case AstNode_StructType:
case AstNode_UnionType:
case AstNode_RawUnionType:
@@ -1566,6 +1585,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, AstNode **foreign_library_token, Str
expect_token(f, Token_String);
}
}
ELSE_IF_ADD_TAG(require_results)
ELSE_IF_ADD_TAG(export)
ELSE_IF_ADD_TAG(bounds_check)
ELSE_IF_ADD_TAG(no_bounds_check)
@@ -1724,17 +1744,14 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
AstNode *operand = NULL; // Operand
switch (f->curr_token.kind) {
case Token_Ident:
operand = parse_ident(f);
if (!lhs) {
// TODO(bill): Handle?
}
return operand;
return parse_ident(f);
case Token_context:
return ast_implicit(f, expect_token(f, Token_context));
case Token_Integer:
case Token_Float:
case Token_Imag:
case Token_Rune:
operand = ast_basic_lit(f, f->curr_token);
next_token(f);
@@ -1833,17 +1850,6 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
return type;
}
// case Token_if:
// if (!lhs && f->expr_level >= 0) {
// return parse_if_expr(f);
// }
// break;
// case Token_OpenBrace:
// if (!lhs && f->expr_level >= 0) {
// return parse_block_expr(f);
// }
// break;
default: {
AstNode *type = parse_type_or_ident(f);
if (type != NULL) {
@@ -1857,10 +1863,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
}
}
Token begin = f->curr_token;
syntax_error(begin, "Expected an operand");
fix_advance_to_next_stmt(f);
return ast_bad_expr(f, begin, f->curr_token);
return NULL;
}
bool is_literal_type(AstNode *node) {
@@ -1945,6 +1948,12 @@ AstNode *parse_macro_call_expr(AstFile *f, AstNode *operand) {
AstNode *parse_atom_expr(AstFile *f, bool lhs) {
AstNode *operand = parse_operand(f, lhs);
if (operand == NULL) {
Token begin = f->curr_token;
syntax_error(begin, "Expected an operand");
fix_advance_to_next_stmt(f);
operand = ast_bad_expr(f, begin, f->curr_token);
}
bool loop = true;
while (loop) {
@@ -1966,6 +1975,13 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
// case Token_Integer:
// operand = ast_selector_expr(f, token, operand, parse_expr(f, lhs));
// break;
case Token_OpenParen: {
Token open = expect_token(f, Token_OpenParen);
AstNode *type = parse_type(f);
Token close = expect_token(f, Token_CloseParen);
operand = ast_type_assertion(f, operand, token, type);
} break;
default:
syntax_error(f->curr_token, "Expected a selector");
next_token(f);
@@ -1979,6 +1995,9 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
if (lhs) {
// TODO(bill): Handle this
}
bool prev_allow_range = f->allow_range;
f->allow_range = false;
Token open = {0}, close = {0}, interval = {0};
AstNode *indices[3] = {0};
isize ellipsis_count = 0;
@@ -1987,15 +2006,19 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
f->expr_level++;
open = expect_token(f, Token_OpenBracket);
if (f->curr_token.kind != Token_Ellipsis) {
if (f->curr_token.kind != Token_Ellipsis &&
f->curr_token.kind != Token_HalfClosed) {
indices[0] = parse_expr(f, false);
}
bool is_index = true;
while (f->curr_token.kind == Token_Ellipsis && ellipsis_count < gb_count_of(ellipses)) {
while ((f->curr_token.kind == Token_Ellipsis ||
f->curr_token.kind == Token_HalfClosed)
&& ellipsis_count < gb_count_of(ellipses)) {
ellipses[ellipsis_count++] = f->curr_token;
next_token(f);
if (f->curr_token.kind != Token_Ellipsis &&
f->curr_token.kind != Token_HalfClosed &&
f->curr_token.kind != Token_CloseBracket &&
f->curr_token.kind != Token_EOF) {
indices[ellipsis_count] = parse_expr(f, false);
@@ -2020,10 +2043,12 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
indices[2] = ast_bad_expr(f, ellipses[1], close);
}
}
operand = ast_slice_expr(f, operand, open, close, index3, indices[0], indices[1], indices[2]);
operand = ast_slice_expr(f, operand, open, close, ellipses[0], ellipses[1], index3, indices[0], indices[1], indices[2]);
} else {
operand = ast_index_expr(f, operand, indices[0], open, close);
}
f->allow_range = prev_allow_range;
} break;
case Token_Pointer: // Deference
@@ -2052,34 +2077,11 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
AstNode *parse_unary_expr(AstFile *f, bool lhs) {
switch (f->curr_token.kind) {
case Token_cast:
case Token_transmute:
case Token_down_cast:
case Token_union_cast:
{
Token token = f->curr_token; next_token(f);
Token open = expect_token(f, Token_OpenParen);
AstNode *type = parse_type(f);
Token close = expect_token(f, Token_CloseParen);
AstNode *expr = parse_unary_expr(f, lhs);
return ast_cast_expr(f, token, type, expr, open, close);
} break;
case Token_Pointer: {
Token op = f->curr_token;
next_token(f);
AstNode *expr = parse_unary_expr(f, lhs);
if (is_ast_node_type(expr)) {
return ast_pointer_type(f, op, expr);
}
return ast_unary_expr(f, op, expr);
} break;
// case Token_Maybe:
case Token_Add:
case Token_Sub:
case Token_Not:
case Token_Xor: {
case Token_Xor:
case Token_And: {
Token op = f->curr_token;
next_token(f);
return ast_unary_expr(f, op, parse_unary_expr(f, lhs));
@@ -2089,27 +2091,49 @@ AstNode *parse_unary_expr(AstFile *f, bool lhs) {
return parse_atom_expr(f, lhs);
}
bool is_ast_node_a_range(AstNode *expr) {
if (expr == NULL) {
return false;
}
if (expr->kind != AstNode_BinaryExpr) {
return false;
}
TokenKind op = expr->BinaryExpr.op.kind;
switch (op) {
case Token_Ellipsis:
case Token_HalfClosed:
return true;
}
return false;
}
// NOTE(bill): result == priority
i32 token_precedence(TokenKind t) {
i32 token_precedence(AstFile *f, TokenKind t) {
switch (t) {
case Token_Question:
return 1;
case Token_Ellipsis:
case Token_HalfClosed:
if (f->allow_range) {
return 2;
}
return 0;
case Token_CmpOr:
return 2;
case Token_CmpAnd:
return 3;
case Token_CmpAnd:
return 4;
case Token_CmpEq:
case Token_NotEq:
case Token_Lt:
case Token_Gt:
case Token_LtEq:
case Token_GtEq:
return 4;
return 5;
case Token_Add:
case Token_Sub:
case Token_Or:
case Token_Xor:
return 5;
return 6;
case Token_Mul:
case Token_Quo:
case Token_Mod:
@@ -2117,17 +2141,17 @@ i32 token_precedence(TokenKind t) {
case Token_AndNot:
case Token_Shl:
case Token_Shr:
return 6;
return 7;
}
return 0;
}
AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
AstNode *expr = parse_unary_expr(f, lhs);
for (i32 prec = token_precedence(f->curr_token.kind); prec >= prec_in; prec--) {
for (i32 prec = token_precedence(f, f->curr_token.kind); prec >= prec_in; prec--) {
for (;;) {
Token op = f->curr_token;
i32 op_prec = token_precedence(op.kind);
i32 op_prec = token_precedence(f, op.kind);
if (op_prec != prec) {
// NOTE(bill): This will also catch operators that are not valid "binary" operators
break;
@@ -2143,7 +2167,7 @@ AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
expr = ast_ternary_expr(f, cond, x, y);
} else {
AstNode *right = parse_binary_expr(f, false, prec+1);
if (!right) {
if (right == NULL) {
syntax_error(op, "Expected expression on the right-hand side of the binary operator");
}
expr = ast_binary_expr(f, op, expr, right);
@@ -2271,6 +2295,7 @@ AstNode *parse_value_decl(AstFile *f, AstNodeArray lhs) {
}
AstNode *parse_simple_stmt(AstFile *f, bool in_stmt_ok) {
AstNodeArray lhs = parse_lhs_expr_list(f);
Token token = f->curr_token;
@@ -2306,15 +2331,10 @@ AstNode *parse_simple_stmt(AstFile *f, bool in_stmt_ok) {
case Token_in:
if (in_stmt_ok) {
allow_token(f, Token_in);
bool prev_allow_range = f->allow_range;
f->allow_range = true;
AstNode *expr = parse_expr(f, false);
switch (f->curr_token.kind) {
case Token_Ellipsis: {
Token op = f->curr_token;
next_token(f);
AstNode *right = parse_expr(f, false);
expr = ast_interval_expr(f, op, expr, right);
} break;
}
f->allow_range = prev_allow_range;
AstNodeArray rhs = {0};
array_init_count(&rhs, heap_allocator(), 1);
@@ -2626,10 +2646,11 @@ AstNode *parse_type_or_ident(AstFile *f) {
AstNode *sel = parse_ident(f);
e = ast_selector_expr(f, token, e, sel);
}
if (f->curr_token.kind == Token_OpenParen) {
// TODO(bill): Merge type_or_ident into the general parsing for expressions
// if (f->curr_token.kind == Token_OpenParen) {
// HACK NOTE(bill): For type_of_val(expr) et al.
e = parse_call_expr(f, e);
}
// e = parse_call_expr(f, e);
// }
return e;
}
@@ -2651,6 +2672,12 @@ AstNode *parse_type_or_ident(AstFile *f) {
return ast_pointer_type(f, token, elem);
}
case Token_atomic: {
Token token = expect_token(f, Token_atomic);
AstNode *elem = parse_type(f);
return ast_atomic_type(f, token, elem);
}
case Token_OpenBracket: {
Token token = expect_token(f, Token_OpenBracket);
AstNode *count_expr = NULL;
@@ -3085,7 +3112,10 @@ AstNode *parse_case_clause(AstFile *f) {
Token token = f->curr_token;
AstNodeArray list = make_ast_node_array(f);
if (allow_token(f, Token_case)) {
bool prev_allow_range = f->allow_range;
f->allow_range = true;
list = parse_rhs_expr_list(f);
f->allow_range = prev_allow_range;
} else {
expect_token(f, Token_default);
}
@@ -3238,14 +3268,17 @@ AstNode *parse_stmt(AstFile *f) {
case Token_Ident:
case Token_Integer:
case Token_Float:
case Token_Imag:
case Token_Rune:
case Token_String:
case Token_OpenParen:
case Token_Pointer:
// Unary Operators
case Token_Add:
case Token_Sub:
case Token_Xor:
case Token_Not:
case Token_And:
s = parse_simple_stmt(f, false);
expect_semicolon(f, s);
return s;
@@ -3288,10 +3321,11 @@ AstNode *parse_stmt(AstFile *f) {
return ast_using_stmt(f, token, list);
}
AstNode *decl = parse_simple_stmt(f, false);
AstNode *decl = parse_value_decl(f, list);
expect_semicolon(f, decl);
if (decl->kind == AstNode_ValueDecl) {
#if 1
if (!decl->ValueDecl.is_var) {
syntax_error(token, "`using` may not be applied to constant declarations");
return decl;
@@ -3301,6 +3335,9 @@ AstNode *parse_stmt(AstFile *f) {
} else {
decl->ValueDecl.flags |= VarDeclFlag_using;
}
#else
decl->ValueDecl.flags |= VarDeclFlag_using;
#endif
return decl;
}
@@ -3615,6 +3652,7 @@ ParseFileError init_ast_file(AstFile *f, String fullpath) {
gb_arena_init_from_allocator(&f->arena, heap_allocator(), arena_size);
f->curr_proc = NULL;
f->id = ++global_file_id;
return ParseFile_None;
}
@@ -3835,6 +3873,10 @@ ParseFileError parse_files(Parser *p, char *init_filename) {
if (err != ParseFile_None) {
if (err == ParseFile_EmptyFile) {
if (str_eq(import_path, init_fullpath)) {
gb_printf_err("Initial file is empty - %.*s\n", LIT(init_fullpath));
gb_exit(1);
}
return ParseFile_None;
}
+480 -113
View File
@@ -1,6 +1,7 @@
typedef struct ssaModule ssaModule;
typedef struct ssaValue ssaValue;
typedef struct ssaValueArgs ssaValueArgs;
typedef struct ssaDefer ssaDefer;
typedef struct ssaBlock ssaBlock;
typedef struct ssaProc ssaProc;
typedef struct ssaEdge ssaEdge;
@@ -8,6 +9,8 @@ typedef struct ssaRegister ssaRegister;
typedef struct ssaTargetList ssaTargetList;
typedef enum ssaBlockKind ssaBlockKind;
typedef enum ssaBranchPrediction ssaBranchPrediction;
typedef enum ssaDeferExitKind ssaDeferExitKind;
String ssa_mangle_name(ssaModule *m, String path, Entity *e);
@@ -49,6 +52,7 @@ enum ssaBlockKind {
// architectures, these could become conditions blocks like amd64 LT or EQ
ssaBlock_Entry, // Entry point
ssaBlock_Plain,
ssaBlock_Defer, // Similar to a plain block but generated by a `defer` statement
ssaBlock_If,
ssaBlock_Ret,
ssaBlock_RetJmp, // Stores return value and jumps to Ret block
@@ -63,6 +67,27 @@ enum ssaBranchPrediction {
ssaBranch_Unlikely = -1,
};
typedef enum ssaDeferKind {
ssaDefer_Node,
ssaDefer_Instr,
} ssaDeferKind;
struct ssaDefer {
ssaDeferKind kind;
i32 scope_level;
ssaBlock * block;
union {
AstNode * stmt;
ssaValue * instr;
};
};
enum ssaDeferExitKind {
ssaDeferExit_Default,
ssaDeferExit_Return,
ssaDeferExit_Branch,
};
// ssaEdge represents a control flow graph (CFG) edge
struct ssaEdge {
// Succs array: Block To
@@ -79,6 +104,7 @@ struct ssaBlock {
ssaBlockKind kind;
ssaProc * proc; // Containing procedure
String name; // Optional
i32 scope_level;
// Likely branch direction
ssaBranchPrediction likeliness;
@@ -118,6 +144,9 @@ struct ssaProc {
i32 block_id;
i32 value_id;
MapSsaValue values; // Key: Entity *
Array(ssaDefer) defer_stmts;
i32 scope_level;
};
struct ssaRegister {
@@ -147,6 +176,17 @@ struct ssaModule {
ssaValueArray procs_to_generate;
};
typedef enum ssaAddrKind {
ssaAddr_Default,
ssaAddr_Map,
} ssaAddrKind;
typedef struct ssaAddr {
ssaValue * addr;
ssaAddrKind kind;
} ssaAddr;
void ssa_push_target_list(ssaProc *p, ssaBlock *break_, ssaBlock *continue_, ssaBlock *fallthrough_) {
ssaTargetList *tl = gb_alloc_item(p->allocator, ssaTargetList);
@@ -167,6 +207,7 @@ ssaBlock *ssa_new_block(ssaProc *p, ssaBlockKind kind, char *name) {
b->id = p->block_id++;
b->kind = kind;
b->proc = p;
p->scope_level = p->scope_level;
if (name != NULL || name[0] != 0) {
b->name = make_string_c(name);
}
@@ -352,6 +393,17 @@ ssaValue *ssa_const_int(ssaProc *p, Type *t, i64 c) {
return NULL;
}
ssaAddr ssa_build_addr (ssaProc *p, AstNode *expr);
ssaValue *ssa_build_expr (ssaProc *p, AstNode *expr);
void ssa_build_stmt (ssaProc *p, AstNode *node);
void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes);
ssaValue *ssa_emit_deep_field_ptr_index(ssaProc *p, ssaValue *e, Selection sel);
void ssa_reset_value_args(ssaValue *v) {
for_array(i, v->args) {
v->args.e[i]->uses--;
@@ -365,6 +417,85 @@ void ssa_reset(ssaValue *v, ssaOp op) {
ssa_reset_value_args(v);
}
ssaValue *ssa_get_last_value(ssaBlock *b) {
if (b == NULL) {
return NULL;
}
isize len = b->values.count;
if (len <= 0) {
return 0;
}
ssaValue *v = b->values.e[len-1];
return v;
}
void ssa_emit_comment(ssaProc *p, String s) {
// ssa_new_value0v(p, ssaOp_Comment, NULL, exact_value_string(s));
}
void ssa_build_defer_stmt(ssaProc *p, ssaDefer d) {
// ssaValue *last_instr = ssa_get_last_value(p->curr_block);
ssaBlock *b = ssa_new_block(p, ssaBlock_Plain, "defer");
ssa_emit_jump(p, b);
ssa_start_block(p, b);
ssa_emit_comment(p, str_lit("defer"));
if (d.kind == ssaDefer_Node) {
ssa_build_stmt(p, d.stmt);
} else if (d.kind == ssaDefer_Instr) {
// NOTE(bill): Need to make a new copy
ssaValue *v = cast(ssaValue *)gb_alloc_copy(p->allocator, d.instr, gb_size_of(ssaValue));
array_add(&p->curr_block->values, v);
}
}
void ssa_emit_defer_stmts(ssaProc *p, ssaDeferExitKind kind, ssaBlock *b) {
isize count = p->defer_stmts.count;
for (isize i = count-1; i >= 0; i--) {
ssaDefer d = p->defer_stmts.e[i];
if (kind == ssaDeferExit_Default) {
gb_printf_err("scope_level %d %d\n", p->scope_level, d.scope_level);
if (p->scope_level == d.scope_level &&
d.scope_level > 1) {
ssa_build_defer_stmt(p, d);
array_pop(&p->defer_stmts);
continue;
} else {
break;
}
} else if (kind == ssaDeferExit_Return) {
ssa_build_defer_stmt(p, d);
} else if (kind == ssaDeferExit_Branch) {
GB_ASSERT(b != NULL);
i32 lower_limit = b->scope_level+1;
if (lower_limit < d.scope_level) {
ssa_build_defer_stmt(p, d);
}
}
}
}
ssaDefer ssa_add_defer_node(ssaProc *p, i32 scope_level, AstNode *stmt) {
ssaDefer d = {ssaDefer_Node};
d.scope_level = scope_level;
d.block = p->curr_block;
d.stmt = stmt;
array_add(&p->defer_stmts, d);
return d;
}
void ssa_open_scope(ssaProc *p) {
p->scope_level++;
}
void ssa_close_scope(ssaProc *p, ssaDeferExitKind kind, ssaBlock *b) {
ssa_emit_defer_stmts(p, kind, b);
GB_ASSERT(p->scope_level > 0);
p->scope_level--;
}
ssaValue *ssa_emit_load(ssaProc *p, ssaValue *v) {
GB_ASSERT(is_type_pointer(v->type));
return ssa_new_value1(p, ssaOp_Load, type_deref(v->type), v);
@@ -411,15 +542,6 @@ bool ssa_is_blank_ident(AstNode *node) {
}
typedef enum ssaAddrKind {
ssaAddr_Default,
ssaAddr_Map,
} ssaAddrKind;
typedef struct ssaAddr {
ssaValue * addr;
ssaAddrKind kind;
} ssaAddr;
ssaAddr ssa_addr(ssaValue *v) {
if (v != NULL) {
@@ -456,6 +578,7 @@ ssaProc *ssa_new_proc(ssaModule *m, String name, Entity *entity, DeclInfo *decl_
p->decl_info = decl_info;
array_init(&p->blocks, heap_allocator());
array_init(&p->defer_stmts, heap_allocator());
map_ssa_value_init(&p->values, heap_allocator());
return p;
@@ -472,10 +595,8 @@ ssaAddr ssa_add_local(ssaProc *p, Entity *e, AstNode *expr) {
map_ssa_value_set(&p->values, hash_pointer(e), local);
map_ssa_value_set(&p->module->values, hash_pointer(e), local);
local->comment_string = e->token.string;
ssaValue *addr = ssa_new_value1(p, ssaOp_Addr, local->type, local);
ssa_new_value1(p, ssaOp_Zero, t, addr);
return ssa_addr(addr);
ssa_new_value1(p, ssaOp_Zero, t, local);
return ssa_addr(local);
}
ssaAddr ssa_add_local_for_ident(ssaProc *p, AstNode *name) {
Entity **found = map_entity_get(&p->module->info->definitions, hash_pointer(name));
@@ -498,9 +619,7 @@ ssaAddr ssa_add_local_generated(ssaProc *p, Type *t) {
return ssa_add_local(p, e, NULL);
}
void ssa_emit_comment(ssaProc *p, String s) {
// ssa_new_value0v(p, ssaOp_Comment, NULL, exact_value_string(s));
}
#define SSA_MAX_STRUCT_FIELD_COUNT 4
@@ -549,12 +668,6 @@ bool can_ssa_type(Type *t) {
return true;
}
ssaAddr ssa_build_addr (ssaProc *p, AstNode *expr);
ssaValue *ssa_build_expr (ssaProc *p, AstNode *expr);
void ssa_build_stmt (ssaProc *p, AstNode *node);
void ssa_build_stmt_list(ssaProc *p, AstNodeArray nodes);
ssaValue *ssa_emit_deep_field_ptr_index(ssaProc *p, ssaValue *e, Selection sel);
void ssa_addr_store(ssaProc *p, ssaAddr addr, ssaValue *value) {
if (addr.addr == NULL) {
return;
@@ -587,7 +700,7 @@ ssaValue *ssa_addr_load(ssaProc *p, ssaAddr addr) {
}
ssaValue *ssa_get_using_variable(ssaProc *p, Entity *e) {
GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous);
GB_ASSERT(e->kind == Entity_Variable && e->flags & EntityFlag_Using);
String name = e->token.string;
Entity *parent = e->using_parent;
Selection sel = lookup_field(p->allocator, parent->type, name, false);
@@ -611,7 +724,7 @@ ssaAddr ssa_build_addr_from_entity(ssaProc *p, Entity *e, AstNode *expr) {
ssaValue **found = map_ssa_value_get(&p->module->values, hash_pointer(e));
if (found) {
v = *found;
} else if (e->kind == Entity_Variable && e->flags & EntityFlag_Anonymous) {
} else if (e->kind == Entity_Variable && e->flags & EntityFlag_Using) {
// NOTE(bill): Calculate the using variable every time
v = ssa_get_using_variable(p, e);
}
@@ -706,10 +819,6 @@ ssaValue *ssa_emit_ptr_index(ssaProc *p, ssaValue *s, i64 index) {
GB_ASSERT(t->Record.field_count > 0);
GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
result_type = make_type_pointer(a, t->Record.fields[index]->type);
i64 offset = t->Record.offsets[index];
ssaValue *ptr = ssa_emit_conv(p, s, t_u8_ptr);
ptr = ssa_new_value2(p, ssaOp_PtrOffset, ptr->type, ptr, ssa_const_int(p, t_int, offset));
return ssa_emit_conv(p, ptr, result_type);
} else if (is_type_tuple(t)) {
GB_ASSERT(t->Tuple.variable_count > 0);
GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
@@ -772,13 +881,7 @@ ssaValue *ssa_emit_value_index(ssaProc *p, ssaValue *s, i64 index) {
type_set_offsets(a, t);
GB_ASSERT(t->Record.field_count > 0);
GB_ASSERT(gb_is_between(index, 0, t->Record.field_count-1));
Type *ptr_type = make_type_pointer(a, t->Record.fields[index]->type);
i64 offset = t->Record.offsets[index];
ssaValue *ptr = ssa_address_from_load_or_generate_local(p, s);
ptr = ssa_emit_conv(p, s, t_u8_ptr);
ptr = ssa_new_value2(p, ssaOp_PtrOffset, ptr->type, ptr, ssa_const_int(p, t_int, offset));
ptr = ssa_emit_conv(p, ptr, ptr_type);
return ssa_emit_load(p, ptr);
result_type = t->Record.fields[index]->type;
} else if (is_type_tuple(t)) {
GB_ASSERT(t->Tuple.variable_count > 0);
GB_ASSERT(gb_is_between(index, 0, t->Tuple.variable_count-1));
@@ -1007,9 +1110,54 @@ ssaAddr ssa_build_addr(ssaProc *p, AstNode *expr) {
return ssa_addr(a);
}
case_end;
case_ast_node(ue, UnaryExpr, expr);
switch (ue->op.kind) {
case Token_Pointer: {
return ssa_build_addr(p, ue->expr);
}
default:
GB_PANIC("Invalid unary expression for ssa_build_addr");
}
case_end;
case_ast_node(be, BinaryExpr, expr);
GB_PANIC("Invalid binary expression for ssa_build_addr: %.*s\n", LIT(be->op.string));
case_end;
case_ast_node(ie, IndexExpr, expr);
GB_PANIC("TODO(bill): ssa_build_addr IndexExpr");
case_end;
case_ast_node(se, SliceExpr, expr);
GB_PANIC("TODO(bill): ssa_build_addr SliceExpr");
case_end;
case_ast_node(de, DerefExpr, expr);
ssaValue *addr = ssa_build_expr(p, de->expr);
return ssa_addr(addr);
case_end;
case_ast_node(ce, CallExpr, expr);
ssaValue *e = ssa_build_expr(p, expr);
ssaValue *v = ssa_address_from_load_or_generate_local(p, e);
return ssa_addr(v);
case_end;
case_ast_node(cl, CompoundLit, expr);
GB_PANIC("TODO(bill): ssa_build_addr CompoundLit");
case_end;
}
GB_PANIC("Cannot get entity's address");
TokenPos token_pos = ast_node_token(expr).pos;
GB_PANIC("Unexpected address expression\n"
"\tAstNode: %.*s @ "
"%.*s(%td:%td)\n",
LIT(ast_node_strings[expr->kind]),
LIT(token_pos.file), token_pos.line, token_pos.column);
return ssa_addr(NULL);
}
@@ -1234,6 +1382,7 @@ ssaOp ssa_determine_op(TokenKind op, Type *t) {
return ssaOp_Invalid;
}
ssaValue *ssa_emit_comp(ssaProc *p, TokenKind op, ssaValue *x, ssaValue *y) {
GB_ASSERT(x != NULL && y != NULL);
Type *a = core_type(x->type);
@@ -1278,6 +1427,130 @@ ssaValue *ssa_emit_comp(ssaProc *p, TokenKind op, ssaValue *x, ssaValue *y) {
ssaValue *ssa_emit_unary_arith(ssaProc *p, TokenKind op, ssaValue *x, Type *type) {
if (is_type_vector(x->type)) {
ssa_emit_comment(p, str_lit("vector.arith.begin"));
// IMPORTANT TODO(bill): This is very wasteful with regards to stack memory
Type *tl = base_type(x->type);
ssaValue *val = ssa_address_from_load_or_generate_local(p, x);
GB_ASSERT(is_type_vector(type));
Type *elem_type = base_type(type)->Vector.elem;
ssaAddr res = ssa_add_local_generated(p, type);
for (i64 i = 0; i < tl->Vector.count; i++) {
ssaValue *index = ssa_const_int(p, t_int, i);
ssaValue *e = ssa_emit_load(p, ssa_emit_array_index(p, val, index));
ssaValue *z = ssa_emit_unary_arith(p, op, e, elem_type);
ssa_emit_store(p, ssa_emit_array_index(p, res.addr, index), z);
}
ssa_emit_comment(p, str_lit("vector.arith.end"));
return ssa_addr_load(p, res);
}
switch (op) {
case Token_Pointer: {
GB_PANIC("Token_Pointer should be handled elsewhere");
} break;
case Token_Add:
return x;
case Token_Not: // Boolean not
return ssa_new_value1(p, ssaOp_NotB, type, x);
case Token_Xor: { // Bitwise not
isize bits = 8*type_size_of(p->allocator, x->type);
switch (bits) {
case 8: return ssa_new_value1(p, ssaOp_Not8, type, x);
case 16: return ssa_new_value1(p, ssaOp_Not16, type, x);
case 32: return ssa_new_value1(p, ssaOp_Not32, type, x);
case 64: return ssa_new_value1(p, ssaOp_Not64, type, x);
}
GB_PANIC("unknown integer size");
} break;
case Token_Sub: { // 0-x
isize bits = 8*type_size_of(p->allocator, x->type);
if (is_type_integer(x->type)) {
switch (bits) {
case 8: return ssa_new_value1(p, ssaOp_Neg8, type, x);
case 16: return ssa_new_value1(p, ssaOp_Neg16, type, x);
case 32: return ssa_new_value1(p, ssaOp_Neg32, type, x);
case 64: return ssa_new_value1(p, ssaOp_Neg64, type, x);
}
} else if (is_type_float(x->type)) {
switch (bits) {
case 32: return ssa_new_value1(p, ssaOp_Neg32F, type, x);
case 64: return ssa_new_value1(p, ssaOp_Neg64F, type, x);
}
}
GB_PANIC("unknown type for -x");
} break;
}
return NULL;
}
ssaValue *ssa_emit_arith(ssaProc *p, TokenKind op, ssaValue *x, ssaValue *y, Type *type) {
if (is_type_vector(x->type)) {
GB_PANIC("TODO(bill): ssa_emit_arith vector");
} else if (is_type_complex(x->type)) {
GB_PANIC("TODO(bill): ssa_emit_arith complex");
} else if (is_type_quaternion(x->type)) {
GB_PANIC("TODO(bill): ssa_emit_arith quaternion");
}
if (op == Token_Add) {
if (is_type_pointer(x->type)) {
GB_PANIC("TODO(bill): Ptr arith");
ssaValue *ptr = ssa_emit_conv(p, x, type);
ssaValue *offset = y;
// return ssa_emit_ptr_offset(p, ptr, offset);
} else if (is_type_pointer(y->type)) {
GB_PANIC("TODO(bill): Ptr arith");
ssaValue *ptr = ssa_emit_conv(p, y, type);
ssaValue *offset = x;
// return ssa_emit_ptr_offset(p, ptr, offset);
}
} else if (op == Token_Sub) {
if (is_type_pointer(x->type) && is_type_integer(y->type)) {
GB_PANIC("TODO(bill): Ptr arith");
// ptr - int
ssaValue *ptr = ssa_emit_conv(p, x, type);
ssaValue *offset = y;
// return ssa_emit_ptr_offset(p, ptr, offset);
} else if (is_type_pointer(x->type) && is_type_pointer(y->type)) {
GB_ASSERT(is_type_integer(type));
Type *ptr_type = base_type(x->type);
GB_ASSERT(!is_type_rawptr(ptr_type));
ssaValue *elem_size = ssa_const_int(p, t_int, type_size_of(p->allocator, ptr_type->Pointer.elem));
ssaValue *a = ssa_emit_conv(p, x, type);
ssaValue *b = ssa_emit_conv(p, y, type);
ssaValue *diff = ssa_emit_arith(p, op, a, b, type);
return ssa_emit_arith(p, Token_Quo, diff, elem_size, type);
}
}
switch (op) {
case Token_Add:
case Token_Sub:
case Token_Mul:
case Token_Quo:
case Token_Mod:
case Token_And:
case Token_Or:
case Token_Xor:
case Token_AndNot:
GB_ASSERT(x != NULL && y != NULL);
return ssa_new_value2(p, ssa_determine_op(op, x->type), type, x, y);
}
return NULL;
}
ssaValue *ssa_build_cond(ssaProc *p, AstNode *cond, ssaBlock *yes, ssaBlock *no) {
switch (cond->kind) {
case_ast_node(pe, ParenExpr, cond);
@@ -1439,47 +1712,12 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
case_end;
case_ast_node(ue, UnaryExpr, expr);
switch (ue->op.kind) {
case Token_Pointer: {
if (ue->op.kind == Token_Pointer) {
return ssa_build_addr(p, ue->expr).addr;
} break;
case Token_Add:
return ssa_build_expr(p, ue->expr);
case Token_Not: // Boolean not
return ssa_new_value1(p, ssaOp_NotB, tv->type, ssa_build_expr(p, ue->expr));
case Token_Xor: { // Bitwise not
ssaValue *x = ssa_build_expr(p, ue->expr);
isize bits = 8*type_size_of(p->allocator, x->type);
switch (bits) {
case 8: return ssa_new_value1(p, ssaOp_Not8, tv->type, x);
case 16: return ssa_new_value1(p, ssaOp_Not16, tv->type, x);
case 32: return ssa_new_value1(p, ssaOp_Not32, tv->type, x);
case 64: return ssa_new_value1(p, ssaOp_Not64, tv->type, x);
}
GB_PANIC("unknown integer size");
} break;
case Token_Sub: { // 0-x
ssaValue *x = ssa_build_expr(p, ue->expr);
isize bits = 8*type_size_of(p->allocator, x->type);
if (is_type_integer(x->type)) {
switch (bits) {
case 8: return ssa_new_value1(p, ssaOp_Neg8, tv->type, x);
case 16: return ssa_new_value1(p, ssaOp_Neg16, tv->type, x);
case 32: return ssa_new_value1(p, ssaOp_Neg32, tv->type, x);
case 64: return ssa_new_value1(p, ssaOp_Neg64, tv->type, x);
}
} else if (is_type_float(x->type)) {
switch (bits) {
case 32: return ssa_new_value1(p, ssaOp_Neg32F, tv->type, x);
case 64: return ssa_new_value1(p, ssaOp_Neg64F, tv->type, x);
}
}
GB_PANIC("unknown type for -x");
} break;
}
ssaValue *x = ssa_build_expr(p, ue->expr);
return ssa_emit_unary_arith(p, ue->op.kind, x, tv->type);
case_end;
case_ast_node(be, BinaryExpr, expr);
@@ -1497,8 +1735,7 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
case Token_AndNot: {
ssaValue *x = ssa_build_expr(p, be->left);
ssaValue *y = ssa_build_expr(p, be->right);
GB_ASSERT(x != NULL && y != NULL);
return ssa_new_value2(p, ssa_determine_op(be->op.kind, x->type), tv->type, x, y);
return ssa_emit_arith(p, be->op.kind, x, y, type);
}
case Token_Shl:
@@ -1527,8 +1764,99 @@ ssaValue *ssa_build_expr(ssaProc *p, AstNode *expr) {
break;
}
case_end;
case_ast_node(de, DerefExpr, expr);
return ssa_addr_load(p, ssa_build_addr(p, expr));
case_end;
case_ast_node(se, SelectorExpr, expr);
return ssa_addr_load(p, ssa_build_addr(p, expr));
case_end;
case_ast_node(te, TernaryExpr, expr);
ssa_emit_comment(p, str_lit("TernaryExpr"));
ssaValue *yes = NULL;
ssaValue *no = NULL;
GB_ASSERT(te->y != NULL);
ssaBlock *then = ssa_new_block(p, ssaBlock_Plain, "if.then");
ssaBlock *done = ssa_new_block(p, ssaBlock_Plain, "if.done"); // NOTE(bill): Append later
ssaBlock *else_ = ssa_new_block(p, ssaBlock_Plain, "if.else");
ssaBlock *v = NULL;
ssa_build_cond(p, te->cond, then, else_);
ssa_start_block(p, then);
// ssa_open_scope(p);
yes = ssa_build_expr(p, te->x);
// ssa_close_scope(p, ssaDeferExit_Default, NULL);
ssa_emit_jump(p, done);
ssa_start_block(p, else_);
// ssa_open_scope(p);
no = ssa_build_expr(p, te->y);
// ssa_close_scope(p, ssaDeferExit_Default, NULL);
ssa_emit_jump(p, done);
ssa_start_block(p, done);
return ssa_new_value2(p, ssaOp_Phi, tv->type, yes, no);
case_end;
case_ast_node(pl, ProcLit, expr);
GB_PANIC("TODO(bill): ssa_build_expr ProcLit");
#if 0
// NOTE(bill): Generate a new name
// parent$count
isize name_len = proc->name.len + 1 + 8 + 1;
u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(proc->name), cast(i32)proc->children.count);
String name = make_string(name_text, name_len-1);
Type *type = type_of_expr(proc->module->info, expr);
irValue *value = ir_value_procedure(proc->module->allocator,
proc->module, NULL, type, pl->type, pl->body, name);
value->Proc.tags = pl->tags;
value->Proc.parent = proc;
array_add(&proc->children, &value->Proc);
array_add(&proc->module->procs_to_generate, value);
return value;
#endif
case_end;
case_ast_node(cl, CompoundLit, expr);
return ssa_addr_load(p, ssa_build_addr(p, expr));
case_end;
case_ast_node(ce, CallExpr, expr);
if (map_tav_get(&p->module->info->types, hash_pointer(ce->proc))->mode == Addressing_Type) {
GB_ASSERT(ce->args.count == 1);
ssaValue *x = ssa_build_expr(p, ce->args.e[0]);
return ssa_emit_conv(p, x, tv->type);
}
AstNode *p = unparen_expr(ce->proc);
GB_PANIC("TODO(bill): ssa_build_expr CallExpr");
case_end;
case_ast_node(se, SliceExpr, expr);
return ssa_addr_load(p, ssa_build_addr(p, expr));
case_end;
case_ast_node(ie, IndexExpr, expr);
return ssa_addr_load(p, ssa_build_addr(p, expr));
case_end;
}
GB_PANIC("Unexpected expression: %.*s", LIT(ast_node_strings[expr->kind]));
return NULL;
}
@@ -1566,21 +1894,43 @@ void ssa_build_when_stmt(ssaProc *p, AstNodeWhenStmt *ws) {
}
void ssa_build_assign_op(ssaProc *p, ssaAddr lhs, ssaValue *value, TokenKind op) {
// ssaValue *old_value = ssa_addr_load(p, lhs);
// Type *type = old_value->type;
ssaValue *old_value = ssa_addr_load(p, lhs);
Type *type = old_value->type;
// ssaValue *change = value;
// if (is_type_pointer(type) && is_type_integer(value->type)) {
// change = ssa_emit_conv(p, value, default_type(value->type));
// } else {
// change = ssa_emit_conv(p, value, type);
// }
// ssaValue *new_value = ssa_emit_arith(p, op, old_value, change, type);
// ssa_addr_store(p, lhs, new_value);
ssaValue *change = value;
if (is_type_pointer(type) && is_type_integer(value->type)) {
change = ssa_emit_conv(p, value, default_type(value->type));
} else {
change = ssa_emit_conv(p, value, type);
}
ssaValue *new_value = ssa_emit_arith(p, op, old_value, change, type);
ssa_addr_store(p, lhs, new_value);
}
void ssa_build_stmt_internal(ssaProc *p, AstNode *node);
void ssa_build_stmt(ssaProc *p, AstNode *node) {
u32 prev_stmt_state_flags = p->module->stmt_state_flags;
if (node->stmt_state_flags != 0) {
u32 in = node->stmt_state_flags;
u32 out = p->module->stmt_state_flags;
if (in & StmtStateFlag_bounds_check) {
out |= StmtStateFlag_bounds_check;
out &= ~StmtStateFlag_no_bounds_check;
} else if (in & StmtStateFlag_no_bounds_check) {
out |= StmtStateFlag_no_bounds_check;
out &= ~StmtStateFlag_bounds_check;
}
p->module->stmt_state_flags = out;
}
ssa_build_stmt_internal(p, node);
p->module->stmt_state_flags = prev_stmt_state_flags;
}
void ssa_build_stmt_internal(ssaProc *p, AstNode *node) {
if (p->curr_block == NULL) {
ssaBlock *dead_block = ssa_new_block(p, ssaBlock_Plain, "");
ssa_start_block(p, dead_block);
@@ -1591,7 +1941,9 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
case_end;
case_ast_node(bs, BlockStmt, node);
ssa_open_scope(p);
ssa_build_stmt_list(p, bs->stmts);
ssa_close_scope(p, ssaDeferExit_Default, NULL);
case_end;
case_ast_node(us, UsingStmt, node);
@@ -1649,11 +2001,11 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
if (init == NULL) { // TODO(bill): remove this
continue;
}
Type *t = type_deref(init->type);
if (init->op == ssaOp_Addr && t->kind == Type_Tuple) {
Type *t = base_type(init->type);
if (t->kind == Type_Tuple) {
for (isize i = 0; i < t->Tuple.variable_count; i++) {
Entity *e = t->Tuple.variables[i];
ssaValue *v = ssa_emit_ptr_index(p, init, i);
// Entity *e = t->Tuple.variables[i];
ssaValue *v = ssa_emit_value_index(p, init, i);
array_add(&inits, v);
}
} else {
@@ -1667,6 +2019,8 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
}
gb_temp_arena_memory_end(tmp);
} else {
GB_PANIC("TODO(bill): ssa_build_stmt Type/Proc Entities");
}
case_end;
@@ -1714,12 +2068,12 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
for_array(i, as->rhs) {
ssaValue *init = ssa_build_expr(p, as->rhs.e[i]);
Type *t = type_deref(init->type);
Type *t = base_type(init->type);
// TODO(bill): refactor for code reuse as this is repeated a bit
if (init->op == ssaOp_Addr && t->kind == Type_Tuple) {
if (t->kind == Type_Tuple) {
for (isize i = 0; i < t->Tuple.variable_count; i++) {
Entity *e = t->Tuple.variables[i];
ssaValue *v = ssa_emit_ptr_index(p, init, i);
ssaValue *v = ssa_emit_value_index(p, init, i);
array_add(&inits, v);
}
} else {
@@ -1734,14 +2088,14 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
} break;
default: {
GB_PANIC("TODO(bill): assign operations");
// GB_PANIC("TODO(bill): assign operations");
// NOTE(bill): Only 1 += 1 is allowed, no tuples
// +=, -=, etc
// i32 op = cast(i32)as->op.kind;
// op += Token_Add - Token_AddEq; // Convert += to +
// ssaAddr lhs = ssa_build_addr(p, as->lhs.e[0]);
// ssaValue *value = ssa_build_expr(p, as->rhs.e[0]);
// ssa_build_assign_op(p, lhs, value, cast(TokenKind)op);
i32 op = cast(i32)as->op.kind;
op += Token_Add - Token_AddEq; // Convert += to +
ssaAddr lhs = ssa_build_addr(p, as->lhs.e[0]);
ssaValue *value = ssa_build_expr(p, as->rhs.e[0]);
ssa_build_assign_op(p, lhs, value, cast(TokenKind)op);
} break;
}
@@ -1754,7 +2108,13 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
case_end;
case_ast_node(ds, DeferStmt, node);
GB_PANIC("TODO: DeferStmt");
// GB_PANIC("TODO: DeferStmt");
ssa_emit_comment(p, str_lit("DeferStmt"));
i32 scope_level = p->scope_level;
if (ds->stmt->kind == AstNode_BlockStmt) {
scope_level--;
}
ssa_add_defer_node(p, scope_level, ds->stmt);
case_end;
case_ast_node(rs, ReturnStmt, node);
@@ -1780,18 +2140,18 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
ssa_build_cond(p, is->cond, then, else_);
ssa_start_block(p, then);
// ssa_open_scope(p);
ssa_open_scope(p);
ssa_build_stmt(p, is->body);
// ssa_close_scope(p, ssaDeferExit_Default, NULL);
ssa_close_scope(p, ssaDeferExit_Default, NULL);
ssa_emit_jump(p, done);
if (is->else_stmt != NULL) {
ssa_start_block(p, else_);
// ssa_open_scope(p);
ssa_open_scope(p);
ssa_build_stmt(p, is->else_stmt);
// ssa_close_scope(p, ssaDeferExit_Default, NULL);
ssa_close_scope(p, ssaDeferExit_Default, NULL);
ssa_emit_jump(p, done);
}
@@ -1829,9 +2189,9 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
}
ssa_push_target_list(p, done, post, NULL);
// ssa_open_scope(p);
ssa_open_scope(p);
ssa_build_stmt(p, fs->body);
// ssa_close_scope(p, ssaDeferExit_Default, NULL);
ssa_close_scope(p, ssaDeferExit_Default, NULL);
ssa_pop_target_list(p);
ssa_emit_jump(p, post);
@@ -1877,7 +2237,7 @@ void ssa_build_stmt(ssaProc *p, AstNode *node) {
break;
}
if (b != NULL) {
// ssa_emit_defer_stmts(p, irDeferExit_Branch, b);
ssa_emit_defer_stmts(p, ssaDeferExit_Branch, b);
}
switch (bs->token.kind) {
case Token_break: ssa_emit_comment(p, str_lit("break")); break;
@@ -1916,9 +2276,9 @@ void ssa_print_exact_value(gbFile *f, ssaValue *v) {
break;
case ExactValue_Integer:
if (is_type_unsigned(t)) {
gb_fprintf(f, " [%llu]", ev.value_integer);
gb_fprintf(f, " [%llu]", cast(unsigned long long)ev.value_integer);
} else {
gb_fprintf(f, " [%lld]", ev.value_integer);
gb_fprintf(f, " [%lld]", cast(long long)ev.value_integer);
}
break;
case ExactValue_Float:
@@ -1929,7 +2289,7 @@ void ssa_print_exact_value(gbFile *f, ssaValue *v) {
} else if (is_type_f64(t)) {
f64 fp = cast(f64)ev.value_float;
u64 x = *cast(u64 *)&fp;
gb_fprintf(f, " [0x%llx]", x);
gb_fprintf(f, " [0x%llx]", cast(unsigned long long)x);
} else {
GB_PANIC("unhandled integer");
}
@@ -1938,7 +2298,7 @@ void ssa_print_exact_value(gbFile *f, ssaValue *v) {
gb_fprintf(f, " [%.*s]", LIT(ev.value_string));
break;
case ExactValue_Pointer:
gb_fprintf(f, " [0x%llx]", ev.value_pointer);
gb_fprintf(f, " [0x%llx]", cast(unsigned long long)cast(uintptr)ev.value_pointer);
break;
}
}
@@ -1986,6 +2346,9 @@ void ssa_print_proc(gbFile *f, ssaProc *p) {
gb_fprintf(f, " b%d", pred->id);
}
}
if (b->name.len > 0) {
gb_fprintf(f, " ; %.*s", LIT(b->name));
}
gb_fprintf(f, "\n");
isize n = 0;
@@ -2087,6 +2450,10 @@ void ssa_build_proc(ssaModule *m, ssaProc *p) {
ssa_start_block(p, p->entry);
ssa_build_stmt(p, pl->body);
if (p->entity->type->Proc.result_count == 0) {
ssa_emit_defer_stmts(p, ssaDeferExit_Return, NULL);
}
p->exit = ssa_new_block(p, ssaBlock_Exit, "exit");
ssa_emit_jump(p, p->exit);
-1
View File
@@ -7,7 +7,6 @@
\
SSA_OP(SP) /* Stack Pointer */\
SSA_OP(SB) /* Stack Base */\
SSA_OP(Addr) /* Address of something - special rules for certain types when loading and storing (e.g. Maps) */\
\
SSA_OP(Local)\
SSA_OP(Global)\
+18 -2
View File
@@ -188,6 +188,22 @@ bool string_contains_char(String s, u8 c) {
return false;
}
String filename_from_path(String s) {
isize i = string_extension_position(s);
if (i > 0) {
isize j = 0;
s.len = i;
for (j = i-1; j >= 0; j--) {
if (s.text[j] == '/' ||
s.text[j] == '\\') {
break;
}
}
s.text += j+1;
s.len = i-j-1;
}
return make_string(NULL, 0);
}
@@ -207,7 +223,7 @@ bool string_contains_char(String s, u8 c) {
#include <iconv.h>
int convert_multibyte_to_widechar(char *multibyte_input, int input_length, wchar_t *output, int output_size) {
int convert_multibyte_to_widechar(char *multibyte_input, usize input_length, wchar_t *output, usize output_size) {
iconv_t conv = iconv_open("WCHAR_T", "UTF-8");
size_t result = iconv(conv, cast(char **)&multibyte_input, &input_length, cast(char **)&output, &output_size);
iconv_close(conv);
@@ -215,7 +231,7 @@ bool string_contains_char(String s, u8 c) {
return (int) result;
}
int convert_widechar_to_multibyte(wchar_t* widechar_input, int input_length, char* output, int output_size) {
int convert_widechar_to_multibyte(wchar_t* widechar_input, usize input_length, char* output, usize output_size) {
iconv_t conv = iconv_open("UTF-8", "WCHAR_T");
size_t result = iconv(conv, (char**) &widechar_input, &input_length, (char**) &output, &output_size);
iconv_close(conv);
+55 -44
View File
@@ -7,6 +7,7 @@ TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \
TOKEN_KIND(Token_Ident, "identifier"), \
TOKEN_KIND(Token_Integer, "integer"), \
TOKEN_KIND(Token_Float, "float"), \
TOKEN_KIND(Token_Imag, "imaginary"), \
TOKEN_KIND(Token_Rune, "rune"), \
TOKEN_KIND(Token_String, "string"), \
TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \
@@ -63,18 +64,19 @@ TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
TOKEN_KIND(Token_GtEq, ">="), \
TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
\
TOKEN_KIND(Token_OpenParen, "("), \
TOKEN_KIND(Token_CloseParen, ")"), \
TOKEN_KIND(Token_OpenBracket, "["), \
TOKEN_KIND(Token_CloseBracket, "]"), \
TOKEN_KIND(Token_OpenBrace, "{"), \
TOKEN_KIND(Token_CloseBrace, "}"), \
TOKEN_KIND(Token_Colon, ":"), \
TOKEN_KIND(Token_Semicolon, ";"), \
TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, ".."), \
/* TOKEN_KIND(Token_HalfOpenRange, "..<"), */ \
TOKEN_KIND(Token_OpenParen, "("), \
TOKEN_KIND(Token_CloseParen, ")"), \
TOKEN_KIND(Token_OpenBracket, "["), \
TOKEN_KIND(Token_CloseBracket, "]"), \
TOKEN_KIND(Token_OpenBrace, "{"), \
TOKEN_KIND(Token_CloseBrace, "}"), \
TOKEN_KIND(Token_Colon, ":"), \
TOKEN_KIND(Token_Semicolon, ";"), \
TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, ".."), \
TOKEN_KIND(Token_HalfClosed, "..<"), \
TOKEN_KIND(Token_BackSlash, "\\"), \
TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
\
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
@@ -105,14 +107,13 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
TOKEN_KIND(Token_using, "using"), \
TOKEN_KIND(Token_no_alias, "no_alias"), \
TOKEN_KIND(Token_immutable, "immutable"), \
TOKEN_KIND(Token_cast, "cast"), \
TOKEN_KIND(Token_transmute, "transmute"), \
TOKEN_KIND(Token_down_cast, "down_cast"), \
TOKEN_KIND(Token_union_cast, "union_cast"), \
TOKEN_KIND(Token_context, "context"), \
TOKEN_KIND(Token_push_context, "push_context"), \
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
TOKEN_KIND(Token_asm, "asm"), \
TOKEN_KIND(Token_yield, "yield"), \
TOKEN_KIND(Token_await, "await"), \
TOKEN_KIND(Token_atomic, "atomic"), \
TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
TOKEN_KIND(Token_Count, "")
@@ -168,8 +169,8 @@ Token make_token_ident(String s) {
typedef struct ErrorCollector {
TokenPos prev;
i64 count;
i64 warning_count;
i64 count;
i64 warning_count;
gbMutex mutex;
} ErrorCollector;
@@ -414,7 +415,7 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
TokenizerInitError err = TokenizerInit_None;
char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1);
memcpy(c_str, fullpath.text, fullpath.len);
gb_memcopy(c_str, fullpath.text, fullpath.len);
c_str[fullpath.len] = '\0';
// TODO(bill): Memory map rather than copy contents
@@ -547,18 +548,18 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
}
}
token.string.len = t->curr - token.string.text;
return token;
goto end;
}
scan_mantissa(t, 10);
fraction:
if (t->curr_rune == '.') {
// HACK(bill): This may be inefficient
TokenizerState state = save_tokenizer_state(t);
advance_to_next_rune(t);
if (digit_value(t->curr_rune) >= 10) {
if (t->curr_rune == '.') {
// TODO(bill): Clean up this shit
restore_tokenizer_state(t, &state);
goto end;
@@ -577,6 +578,13 @@ exponent:
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:
token.string.len = t->curr - token.string.text;
return token;
@@ -611,20 +619,22 @@ bool scan_escape(Tokenizer *t, Rune quote) {
advance_to_next_rune(t);
len = 8; base = 16; max = GB_RUNE_MAX;
} else {
if (t->curr_rune < 0)
if (t->curr_rune < 0) {
tokenizer_err(t, "Escape sequence was not terminated");
else
} else {
tokenizer_err(t, "Unknown escape sequence");
}
return false;
}
while (len --> 0) {
u32 d = cast(u32)digit_value(t->curr_rune);
if (d >= base) {
if (t->curr_rune < 0)
if (t->curr_rune < 0) {
tokenizer_err(t, "Escape sequence was not terminated");
else
} else {
tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune);
}
return false;
}
@@ -859,27 +869,28 @@ Token tokenizer_get_token(Tokenizer *t) {
if (t->curr_rune == '.') { // Could be an ellipsis
advance_to_next_rune(t);
token.kind = Token_Ellipsis;
// if (t->curr_rune == '<') {
// advance_to_next_rune(t);
// token.kind = Token_HalfOpenRange;
// }
if (t->curr_rune == '<') {
advance_to_next_rune(t);
token.kind = Token_HalfClosed;
}
}
break;
case '#': token.kind = Token_Hash; break;
case '@': token.kind = Token_At; break;
case '$': token.kind = Token_Dollar; break;
case '?': token.kind = Token_Question; break;
case '^': token.kind = Token_Pointer; break;
case ';': token.kind = Token_Semicolon; break;
case ',': token.kind = Token_Comma; break;
case ':': token.kind = Token_Colon; break;
case '(': token.kind = Token_OpenParen; break;
case ')': token.kind = Token_CloseParen; break;
case '[': token.kind = Token_OpenBracket; break;
case ']': token.kind = Token_CloseBracket; break;
case '{': token.kind = Token_OpenBrace; break;
case '}': token.kind = Token_CloseBrace; break;
case '#': token.kind = Token_Hash; break;
case '@': token.kind = Token_At; break;
case '$': token.kind = Token_Dollar; break;
case '?': token.kind = Token_Question; break;
case '^': token.kind = Token_Pointer; break;
case ';': token.kind = Token_Semicolon; break;
case ',': token.kind = Token_Comma; break;
case ':': token.kind = Token_Colon; break;
case '(': token.kind = Token_OpenParen; break;
case ')': token.kind = Token_CloseParen; break;
case '[': token.kind = Token_OpenBracket; break;
case ']': token.kind = Token_CloseBracket; break;
case '{': token.kind = Token_OpenBrace; break;
case '}': token.kind = Token_CloseBrace; break;
case '\\': token.kind = Token_BackSlash; break;
case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break;
case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break;
+293 -251
View File
@@ -12,25 +12,15 @@ typedef enum BasicKind {
Basic_i64,
Basic_u64,
/* Basic_i16le,
Basic_i16be,
Basic_u16le,
Basic_u16be,
Basic_i32le,
Basic_i32be,
Basic_u32le,
Basic_u32be,
Basic_i64le,
Basic_i64be,
Basic_u64le,
Basic_u64be, */
// Basic_i128,
// Basic_u128,
// Basic_f16,
Basic_f32,
Basic_f64,
// Basic_f128,
Basic_complex64,
Basic_complex128,
Basic_quaternion128,
Basic_quaternion256,
Basic_int,
Basic_uint,
Basic_rawptr,
@@ -40,6 +30,8 @@ typedef enum BasicKind {
Basic_UntypedBool,
Basic_UntypedInteger,
Basic_UntypedFloat,
Basic_UntypedComplex,
Basic_UntypedQuaternion,
Basic_UntypedString,
Basic_UntypedRune,
Basic_UntypedNil,
@@ -51,17 +43,19 @@ typedef enum BasicKind {
} BasicKind;
typedef enum BasicFlag {
BasicFlag_Boolean = GB_BIT(0),
BasicFlag_Integer = GB_BIT(1),
BasicFlag_Unsigned = GB_BIT(2),
BasicFlag_Float = GB_BIT(3),
BasicFlag_Pointer = GB_BIT(4),
BasicFlag_String = GB_BIT(5),
BasicFlag_Rune = GB_BIT(6),
BasicFlag_Untyped = GB_BIT(7),
BasicFlag_Boolean = GB_BIT(0),
BasicFlag_Integer = GB_BIT(1),
BasicFlag_Unsigned = GB_BIT(2),
BasicFlag_Float = GB_BIT(3),
BasicFlag_Complex = GB_BIT(4),
BasicFlag_Quaternion = GB_BIT(5),
BasicFlag_Pointer = GB_BIT(6),
BasicFlag_String = GB_BIT(7),
BasicFlag_Rune = GB_BIT(8),
BasicFlag_Untyped = GB_BIT(9),
BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float,
BasicFlag_Ordered = BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer,
BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float | BasicFlag_Complex | BasicFlag_Quaternion,
BasicFlag_Ordered = BasicFlag_Integer | BasicFlag_Float | BasicFlag_String | BasicFlag_Pointer,
BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_Pointer | BasicFlag_String | BasicFlag_Rune,
} BasicFlag;
@@ -116,6 +110,7 @@ typedef struct TypeRecord {
#define TYPE_KINDS \
TYPE_KIND(Basic, BasicType) \
TYPE_KIND(Pointer, struct { Type *elem; }) \
TYPE_KIND(Atomic, struct { Type *elem; }) \
TYPE_KIND(Array, struct { Type *elem; i64 count; }) \
TYPE_KIND(DynamicArray, struct { Type *elem; }) \
TYPE_KIND(Vector, struct { Type *elem; i64 count; }) \
@@ -138,7 +133,10 @@ typedef struct TypeRecord {
Type * results; /* Type_Tuple */ \
i32 param_count; \
i32 result_count; \
Type **abi_compat_params; \
Type **abi_compat_results; \
bool variadic; \
bool require_results; \
ProcCallingConvention calling_convention; \
}) \
TYPE_KIND(Map, struct { \
@@ -209,33 +207,43 @@ void selection_add_index(Selection *s, isize index) {
gb_global Type basic_types[] = {
{Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}},
{Type_Basic, {Basic_bool, BasicFlag_Boolean, 1, STR_LIT("bool")}},
{Type_Basic, {Basic_i8, BasicFlag_Integer, 1, STR_LIT("i8")}},
{Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("u8")}},
{Type_Basic, {Basic_i16, BasicFlag_Integer, 2, STR_LIT("i16")}},
{Type_Basic, {Basic_u16, BasicFlag_Integer | BasicFlag_Unsigned, 2, STR_LIT("u16")}},
{Type_Basic, {Basic_i32, BasicFlag_Integer, 4, STR_LIT("i32")}},
{Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}},
{Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}},
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}},
// {Type_Basic, {Basic_i128, BasicFlag_Integer, 16, STR_LIT("i128")}},
// {Type_Basic, {Basic_u128, BasicFlag_Integer | BasicFlag_Unsigned, 16, STR_LIT("u128")}},
// {Type_Basic, {Basic_f16, BasicFlag_Float, 2, STR_LIT("f16")}},
{Type_Basic, {Basic_f32, BasicFlag_Float, 4, STR_LIT("f32")}},
{Type_Basic, {Basic_f64, BasicFlag_Float, 8, STR_LIT("f64")}},
// {Type_Basic, {Basic_f128, BasicFlag_Float, 16, STR_LIT("f128")}},
{Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}},
{Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}},
{Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
{Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}},
{Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}},
{Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}},
{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}},
{Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}},
{Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}},
{Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}},
{Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}},
{Type_Basic, {Basic_Invalid, 0, 0, STR_LIT("invalid type")}},
{Type_Basic, {Basic_bool, BasicFlag_Boolean, 1, STR_LIT("bool")}},
{Type_Basic, {Basic_i8, BasicFlag_Integer, 1, STR_LIT("i8")}},
{Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, 1, STR_LIT("u8")}},
{Type_Basic, {Basic_i16, BasicFlag_Integer, 2, STR_LIT("i16")}},
{Type_Basic, {Basic_u16, BasicFlag_Integer | BasicFlag_Unsigned, 2, STR_LIT("u16")}},
{Type_Basic, {Basic_i32, BasicFlag_Integer, 4, STR_LIT("i32")}},
{Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, 4, STR_LIT("u32")}},
{Type_Basic, {Basic_i64, BasicFlag_Integer, 8, STR_LIT("i64")}},
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, 8, STR_LIT("u64")}},
{Type_Basic, {Basic_f32, BasicFlag_Float, 4, STR_LIT("f32")}},
{Type_Basic, {Basic_f64, BasicFlag_Float, 8, STR_LIT("f64")}},
{Type_Basic, {Basic_complex64, BasicFlag_Complex, 8, STR_LIT("complex64")}},
{Type_Basic, {Basic_complex128, BasicFlag_Complex, 16, STR_LIT("complex128")}},
{Type_Basic, {Basic_quaternion128, BasicFlag_Quaternion, 16, STR_LIT("quaternion128")}},
{Type_Basic, {Basic_quaternion256, BasicFlag_Quaternion, 32, STR_LIT("quaternion256")}},
{Type_Basic, {Basic_int, BasicFlag_Integer, -1, STR_LIT("int")}},
{Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, -1, STR_LIT("uint")}},
{Type_Basic, {Basic_rawptr, BasicFlag_Pointer, -1, STR_LIT("rawptr")}},
{Type_Basic, {Basic_string, BasicFlag_String, -1, STR_LIT("string")}},
{Type_Basic, {Basic_any, 0, -1, STR_LIT("any")}},
{Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, 0, STR_LIT("untyped bool")}},
{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped integer")}},
{Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, 0, STR_LIT("untyped float")}},
{Type_Basic, {Basic_UntypedComplex, BasicFlag_Complex | BasicFlag_Untyped, 0, STR_LIT("untyped complex")}},
{Type_Basic, {Basic_UntypedQuaternion, BasicFlag_Quaternion | BasicFlag_Untyped, 0, STR_LIT("untyped quaternion")}},
{Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, 0, STR_LIT("untyped string")}},
{Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, 0, STR_LIT("untyped rune")}},
{Type_Basic, {Basic_UntypedNil, BasicFlag_Untyped, 0, STR_LIT("untyped nil")}},
};
gb_global Type basic_type_aliases[] = {
@@ -253,25 +261,33 @@ gb_global Type *t_i32 = &basic_types[Basic_i32];
gb_global Type *t_u32 = &basic_types[Basic_u32];
gb_global Type *t_i64 = &basic_types[Basic_i64];
gb_global Type *t_u64 = &basic_types[Basic_u64];
// gb_global Type *t_i128 = &basic_types[Basic_i128];
// gb_global Type *t_u128 = &basic_types[Basic_u128];
// gb_global Type *t_f16 = &basic_types[Basic_f16];
gb_global Type *t_f32 = &basic_types[Basic_f32];
gb_global Type *t_f64 = &basic_types[Basic_f64];
// gb_global Type *t_f128 = &basic_types[Basic_f128];
gb_global Type *t_complex64 = &basic_types[Basic_complex64];
gb_global Type *t_complex128 = &basic_types[Basic_complex128];
gb_global Type *t_quaternion128 = &basic_types[Basic_quaternion128];
gb_global Type *t_quaternion256 = &basic_types[Basic_quaternion256];
gb_global Type *t_int = &basic_types[Basic_int];
gb_global Type *t_uint = &basic_types[Basic_uint];
gb_global Type *t_rawptr = &basic_types[Basic_rawptr];
gb_global Type *t_string = &basic_types[Basic_string];
gb_global Type *t_any = &basic_types[Basic_any];
gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool];
gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger];
gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat];
gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString];
gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune];
gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil];
gb_global Type *t_byte = &basic_type_aliases[0];
gb_global Type *t_rune = &basic_type_aliases[1];
gb_global Type *t_untyped_bool = &basic_types[Basic_UntypedBool];
gb_global Type *t_untyped_integer = &basic_types[Basic_UntypedInteger];
gb_global Type *t_untyped_float = &basic_types[Basic_UntypedFloat];
gb_global Type *t_untyped_complex = &basic_types[Basic_UntypedComplex];
gb_global Type *t_untyped_quaternion = &basic_types[Basic_UntypedQuaternion];
gb_global Type *t_untyped_string = &basic_types[Basic_UntypedString];
gb_global Type *t_untyped_rune = &basic_types[Basic_UntypedRune];
gb_global Type *t_untyped_nil = &basic_types[Basic_UntypedNil];
gb_global Type *t_byte = &basic_type_aliases[0];
gb_global Type *t_rune = &basic_type_aliases[1];
gb_global Type *t_u8_ptr = NULL;
@@ -293,10 +309,13 @@ gb_global Type *t_type_info_enum_value_ptr = NULL;
gb_global Type *t_type_info_named = NULL;
gb_global Type *t_type_info_integer = NULL;
gb_global Type *t_type_info_float = NULL;
gb_global Type *t_type_info_complex = NULL;
gb_global Type *t_type_info_quaternion = NULL;
gb_global Type *t_type_info_any = NULL;
gb_global Type *t_type_info_string = NULL;
gb_global Type *t_type_info_boolean = NULL;
gb_global Type *t_type_info_pointer = NULL;
gb_global Type *t_type_info_atomic = NULL;
gb_global Type *t_type_info_procedure = NULL;
gb_global Type *t_type_info_array = NULL;
gb_global Type *t_type_info_dynamic_array = NULL;
@@ -312,10 +331,13 @@ gb_global Type *t_type_info_map = NULL;
gb_global Type *t_type_info_named_ptr = NULL;
gb_global Type *t_type_info_integer_ptr = NULL;
gb_global Type *t_type_info_float_ptr = NULL;
gb_global Type *t_type_info_complex_ptr = NULL;
gb_global Type *t_type_info_quaternion_ptr = NULL;
gb_global Type *t_type_info_any_ptr = NULL;
gb_global Type *t_type_info_string_ptr = NULL;
gb_global Type *t_type_info_boolean_ptr = NULL;
gb_global Type *t_type_info_pointer_ptr = NULL;
gb_global Type *t_type_info_atomic_ptr = NULL;
gb_global Type *t_type_info_procedure_ptr = NULL;
gb_global Type *t_type_info_array_ptr = NULL;
gb_global Type *t_type_info_dynamic_array_ptr = NULL;
@@ -333,8 +355,6 @@ gb_global Type *t_allocator_ptr = NULL;
gb_global Type *t_context = NULL;
gb_global Type *t_context_ptr = NULL;
gb_global Type *t_raw_dynamic_array = NULL;
gb_global Type *t_raw_dynamic_array_ptr = NULL;
gb_global Type *t_map_key = NULL;
gb_global Type *t_map_header = NULL;
@@ -376,7 +396,31 @@ Type *base_enum_type(Type *t) {
}
Type *core_type(Type *t) {
return base_type(base_enum_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) {
@@ -388,6 +432,7 @@ void set_base_type(Type *t, Type *base) {
Type *alloc_type(gbAllocator a, TypeKind kind) {
Type *t = gb_alloc_item(a, Type);
gb_zero_item(t);
t->kind = kind;
return t;
}
@@ -405,6 +450,12 @@ Type *make_type_pointer(gbAllocator a, Type *elem) {
return t;
}
Type *make_type_atomic(gbAllocator a, Type *elem) {
Type *t = alloc_type(a, Type_Atomic);
t->Atomic.elem = elem;
return t;
}
Type *make_type_array(gbAllocator a, Type *elem, i64 count) {
Type *t = alloc_type(a, Type_Array);
t->Array.elem = elem;
@@ -514,8 +565,6 @@ Type *make_type_map(gbAllocator a, i64 count, Type *key, Type *value) {
Type *type_deref(Type *t) {
if (t != NULL) {
Type *bt = base_type(t);
@@ -533,6 +582,20 @@ bool is_type_named(Type *t) {
}
return t->kind == Type_Named;
}
bool is_type_named_alias(Type *t) {
if (!is_type_named(t)) {
return false;
}
Entity *e = t->Named.type_name;
if (e == NULL) {
return false;
}
if (e->kind != Entity_TypeName) {
return false;
}
return e->TypeName.is_type_alias;
}
bool is_type_boolean(Type *t) {
t = core_type(t);
if (t->kind == Type_Basic) {
@@ -615,6 +678,20 @@ bool is_type_float(Type *t) {
}
return false;
}
bool is_type_complex(Type *t) {
t = core_type(t);
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Complex) != 0;
}
return false;
}
bool is_type_quaternion(Type *t) {
t = core_type(t);
if (t->kind == Type_Basic) {
return (t->Basic.flags & BasicFlag_Quaternion) != 0;
}
return false;
}
bool is_type_f32(Type *t) {
t = core_type(t);
if (t->kind == Type_Basic) {
@@ -636,6 +713,10 @@ bool is_type_pointer(Type *t) {
}
return t->kind == Type_Pointer;
}
bool is_type_atomic(Type *t) {
t = base_type(t);
return t->kind == Type_Atomic;
}
bool is_type_tuple(Type *t) {
t = base_type(t);
return t->kind == Type_Tuple;
@@ -695,6 +776,31 @@ Type *base_vector_type(Type *t) {
return t;
}
Type *base_complex_elem_type(Type *t) {
t = core_type(t);
if (is_type_complex(t)) {
switch (t->Basic.kind) {
case Basic_complex64: return t_f32;
case Basic_complex128: return t_f64;
case Basic_UntypedComplex: return t_untyped_float;
}
}
GB_PANIC("Invalid complex type");
return t_invalid;
}
Type *base_quaternion_elem_type(Type *t) {
t = core_type(t);
if (is_type_quaternion(t)) {
switch (t->Basic.kind) {
case Basic_quaternion128: return t_f32;
case Basic_quaternion256: return t_f64;
case Basic_UntypedQuaternion: return t_untyped_float;
}
}
GB_PANIC("Invalid quaternion type");
return t_invalid;
}
bool is_type_struct(Type *t) {
t = base_type(t);
@@ -779,10 +885,17 @@ bool type_has_nil(Type *t) {
return false;
} break;
case Type_Slice:
case Type_DynamicArray:
case Type_Proc:
case Type_Pointer:
case Type_DynamicArray:
case Type_Map:
return true;
case Type_Record:
switch (t->Record.kind) {
case TypeRecord_Union:
return true;
}
return false;
}
return false;
}
@@ -871,13 +984,21 @@ bool are_types_identical(Type *x, Type *y) {
x->Record.custom_align == y->Record.custom_align) {
// TODO(bill); Fix the custom alignment rule
for (isize i = 0; i < x->Record.field_count; i++) {
if (!are_types_identical(x->Record.fields[i]->type, y->Record.fields[i]->type)) {
Entity *xf = x->Record.fields[i];
Entity *yf = y->Record.fields[i];
if (!are_types_identical(xf->type, yf->type)) {
return false;
}
if (str_ne(x->Record.fields[i]->token.string, y->Record.fields[i]->token.string)) {
if (str_ne(xf->token.string, yf->token.string)) {
return false;
}
bool xf_is_using = (xf->flags&EntityFlag_Using) != 0;
bool yf_is_using = (yf->flags&EntityFlag_Using) != 0;
if (xf_is_using ^ yf_is_using) {
return false;
}
}
// NOTE(bill): zeroth variant is NULL
for (isize i = 1; i < x->Record.variant_count; i++) {
if (!are_types_identical(x->Record.variants[i]->type, y->Record.variants[i]->type)) {
return false;
@@ -950,11 +1071,13 @@ Type *default_type(Type *type) {
}
if (type->kind == Type_Basic) {
switch (type->Basic.kind) {
case Basic_UntypedBool: return t_bool;
case Basic_UntypedInteger: return t_int;
case Basic_UntypedFloat: return t_f64;
case Basic_UntypedString: return t_string;
case Basic_UntypedRune: return t_rune;
case Basic_UntypedBool: return t_bool;
case Basic_UntypedInteger: return t_int;
case Basic_UntypedFloat: return t_f64;
case Basic_UntypedComplex: return t_complex128;
case Basic_UntypedQuaternion: return t_quaternion256;
case Basic_UntypedString: return t_string;
case Basic_UntypedRune: return t_rune;
}
}
return type;
@@ -1035,7 +1158,6 @@ typedef enum ProcTypeOverloadKind {
} ProcTypeOverloadKind;
ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
if (x == NULL && y == NULL) return ProcOverload_NotProcedure;
if (x == NULL && y != NULL) return ProcOverload_NotProcedure;
@@ -1078,12 +1200,11 @@ ProcTypeOverloadKind are_proc_types_overload_safe(Type *x, Type *y) {
}
}
{
if (px.params != NULL && py.params != NULL) {
Entity *ex = px.params->Tuple.variables[0];
Entity *ey = py.params->Tuple.variables[0];
bool ok = are_types_identical(ex->type, ey->type);
if (ok) {
gb_printf_err("Here\n");
}
}
@@ -1093,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);
@@ -1159,6 +1266,10 @@ Selection lookup_field_from_index(gbAllocator a, Type *type, i64 index) {
return empty_selection;
}
gb_global Entity *entity__any_data = NULL;
gb_global Entity *entity__any_type_info = NULL;
Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_name, bool is_type, Selection sel) {
GB_ASSERT(type_ != NULL);
@@ -1175,66 +1286,33 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
if (type->kind == Type_Basic) {
switch (type->Basic.kind) {
case Basic_any: {
#if 1
// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
// `Raw_Any` type?
String data_str = str_lit("data");
String type_info_str = str_lit("type_info");
String data_str = str_lit("data");
if (entity__any_type_info == NULL) {
entity__any_type_info = make_entity_field(a, NULL, make_token_ident(type_info_str), t_type_info_ptr, false, 0);
}
if (entity__any_data == NULL) {
entity__any_data = make_entity_field(a, NULL, make_token_ident(data_str), t_rawptr, false, 1);
entity__any_data = make_entity_field(a, NULL, make_token_ident(data_str), t_rawptr, false, 0);
}
if (str_eq(field_name, type_info_str)) {
selection_add_index(&sel, 0);
sel.entity = entity__any_type_info;
return sel;
} else if (str_eq(field_name, data_str)) {
selection_add_index(&sel, 1);
sel.entity = entity__any_data;
return sel;
}
} break;
case Basic_string: {
String data_str = str_lit("data");
String count_str = str_lit("count");
if (entity__string_data == NULL) {
entity__string_data = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, t_u8), false, 0);
}
if (entity__string_count == NULL) {
entity__string_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
if (entity__any_type_info == NULL) {
entity__any_type_info = make_entity_field(a, NULL, make_token_ident(type_info_str), t_type_info_ptr, false, 1);
}
if (str_eq(field_name, data_str)) {
selection_add_index(&sel, 0);
sel.entity = entity__string_data;
sel.entity = entity__any_data;;
return sel;
} else if (str_eq(field_name, count_str)) {
} else if (str_eq(field_name, type_info_str)) {
selection_add_index(&sel, 1);
sel.entity = entity__string_count;
sel.entity = entity__any_type_info;
return sel;
}
#endif
} break;
}
return sel;
} else if (type->kind == Type_Array) {
String count_str = str_lit("count");
// NOTE(bill): Underlying memory address cannot be changed
if (str_eq(field_name, count_str)) {
// HACK(bill): Memory leak
sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, exact_value_integer(type->Array.count));
return sel;
}
} else if (type->kind == Type_Vector) {
String count_str = str_lit("count");
// NOTE(bill): Vectors are not addressable
if (str_eq(field_name, count_str)) {
// HACK(bill): Memory leak
sel.entity = make_entity_constant(a, NULL, make_token_ident(count_str), t_int, exact_value_integer(type->Vector.count));
return sel;
}
if (type->Vector.count <= 4 && !is_type_boolean(type->Vector.elem)) {
// HACK(bill): Memory leak
switch (type->Vector.count) {
@@ -1256,98 +1334,6 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
#undef _VECTOR_FIELD_CASE
}
}
} else if (type->kind == Type_Slice) {
String data_str = str_lit("data");
String count_str = str_lit("count");
String capacity_str = str_lit("capacity");
if (str_eq(field_name, data_str)) {
selection_add_index(&sel, 0);
// HACK(bill): Memory leak
sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->Slice.elem), false, 0);
return sel;
} else if (str_eq(field_name, count_str)) {
selection_add_index(&sel, 1);
if (entity__slice_count == NULL) {
entity__slice_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
}
sel.entity = entity__slice_count;
return sel;
} else if (str_eq(field_name, capacity_str)) {
selection_add_index(&sel, 2);
if (entity__slice_capacity == NULL) {
entity__slice_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2);
}
sel.entity = entity__slice_capacity;
return sel;
}
} else if (type->kind == Type_DynamicArray) {
String data_str = str_lit("data");
String count_str = str_lit("count");
String capacity_str = str_lit("capacity");
String allocator_str = str_lit("allocator");
if (str_eq(field_name, data_str)) {
selection_add_index(&sel, 0);
// HACK(bill): Memory leak
sel.entity = make_entity_field(a, NULL, make_token_ident(data_str), make_type_pointer(a, type->DynamicArray.elem), false, 0);
return sel;
} else if (str_eq(field_name, count_str)) {
selection_add_index(&sel, 1);
if (entity__dynamic_array_count == NULL) {
entity__dynamic_array_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 1);
}
sel.entity = entity__dynamic_array_count;
return sel;
} else if (str_eq(field_name, capacity_str)) {
selection_add_index(&sel, 2);
if (entity__dynamic_array_capacity == NULL) {
entity__dynamic_array_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 2);
}
sel.entity = entity__dynamic_array_capacity;
return sel;
} else if (str_eq(field_name, allocator_str)) {
selection_add_index(&sel, 3);
if (entity__dynamic_array_allocator == NULL) {
entity__dynamic_array_allocator = make_entity_field(a, NULL, make_token_ident(allocator_str), t_allocator, false, 3);
}
sel.entity = entity__dynamic_array_allocator;
return sel;
}
} else if (type->kind == Type_Map) {
String count_str = str_lit("count");
String capacity_str = str_lit("capacity");
String allocator_str = str_lit("allocator");
if (str_eq(field_name, count_str)) {
selection_add_index(&sel, 0);
if (entity__dynamic_map_count == NULL) {
entity__dynamic_map_count = make_entity_field(a, NULL, make_token_ident(count_str), t_int, false, 0);
entity__dynamic_map_count->Variable.is_immutable = true;
}
sel.entity = entity__dynamic_map_count;
return sel;
} else if (str_eq(field_name, capacity_str)) {
selection_add_index(&sel, 1);
if (entity__dynamic_map_capacity == NULL) {
entity__dynamic_map_capacity = make_entity_field(a, NULL, make_token_ident(capacity_str), t_int, false, 1);
entity__dynamic_map_capacity->Variable.is_immutable = true;
}
sel.entity = entity__dynamic_map_capacity;
return sel;
} else if (str_eq(field_name, allocator_str)) {
selection_add_index(&sel, 2);
if (entity__dynamic_map_allocator == NULL) {
entity__dynamic_map_allocator = make_entity_field(a, NULL, make_token_ident(allocator_str), t_allocator, false, 2);
entity__dynamic_map_allocator->Variable.is_immutable = true;
}
sel.entity = entity__dynamic_map_allocator;
return sel;
}
}
if (is_type) {
@@ -1413,7 +1399,7 @@ Selection lookup_field_with_selection(gbAllocator a, Type *type_, String field_n
return sel;
}
if (f->flags & EntityFlag_Anonymous) {
if (f->flags & EntityFlag_Using) {
isize prev_count = sel.index.count;
selection_add_index(&sel, i); // HACK(bill): Leaky memory
@@ -1451,16 +1437,16 @@ void type_path_free(TypePath *tp) {
TypePath *type_path_push(TypePath *tp, Type *t) {
GB_ASSERT(tp != NULL);
for_array(i, tp->path) {
for (isize i = 1; i < tp->path.count; i++) {
if (tp->path.e[i] == t) {
// TODO(bill):
GB_ASSERT(is_type_named(t));
GB_ASSERT_MSG(is_type_named(t), "%s", type_to_string(t));
Entity *e = t->Named.type_name;
error(e->token, "Illegal declaration cycle of `%.*s`", LIT(t->Named.name));
// NOTE(bill): Print cycle, if it's deep enough
for (isize j = 0; j < tp->path.count; j++) {
for (isize j = i; j < tp->path.count; j++) {
Type *t = tp->path.e[j];
GB_ASSERT(is_type_named(t));
GB_ASSERT_MSG(is_type_named(t), "%s", type_to_string(t));
Entity *e = t->Named.type_name;
error(e->token, "\t%.*s refers to", LIT(t->Named.name));
}
@@ -1475,14 +1461,14 @@ TypePath *type_path_push(TypePath *tp, Type *t) {
}
}
if (!tp->failure) {
if (!tp->failure && is_type_named(t)) {
array_add(&tp->path, t);
}
return tp;
}
void type_path_pop(TypePath *tp) {
if (tp != NULL) {
if (tp != NULL && tp->path.count > 0) {
array_pop(&tp->path);
}
}
@@ -1532,6 +1518,7 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
if (t->failure) {
return FAILURE_ALIGNMENT;
}
t = base_type(t);
switch (t->kind) {
@@ -1543,6 +1530,11 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
case Basic_int: case Basic_uint: case Basic_rawptr:
return build_context.word_size;
case Basic_complex64: case Basic_complex128:
return type_size_of_internal(allocator, t, path) / 2;
case Basic_quaternion128: case Basic_quaternion256:
return type_size_of_internal(allocator, t, path) / 4;
}
} break;
@@ -1631,6 +1623,18 @@ i64 type_align_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
break;
case TypeRecord_Union: {
i64 max = 1;
if (t->Record.field_count > 0) {
Type *field_type = t->Record.fields[0]->type;
type_path_push(path, field_type);
i64 align = type_align_of_internal(allocator, field_type, path);
if (path->failure) {
return FAILURE_ALIGNMENT;
}
type_path_pop(path);
if (max < align) {
max = align;
}
}
// NOTE(bill): field zero is null
for (isize i = 1; i < t->Record.variant_count; i++) {
Type *variant = t->Record.variants[i]->type;
@@ -1683,9 +1687,10 @@ i64 *type_set_offsets_of(gbAllocator allocator, Entity **fields, isize field_cou
} else {
for (isize i = 0; i < field_count; i++) {
i64 align = type_align_of(allocator, fields[i]->type);
i64 size = type_size_of(allocator, fields[i]->type);
curr_offset = align_formula(curr_offset, align);
offsets[i] = curr_offset;
curr_offset += type_size_of(allocator, fields[i]->type);
curr_offset += size;
}
}
return offsets;
@@ -1721,8 +1726,18 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
if (t->failure) {
return FAILURE_SIZE;
}
t = base_type(t);
switch (t->kind) {
case Type_Named: {
type_path_push(path, t);
if (path->failure) {
return FAILURE_ALIGNMENT;
}
i64 size = type_size_of_internal(allocator, t->Named.base, path);
type_path_pop(path);
return size;
} break;
case Type_Basic: {
GB_ASSERT(is_type_typed(t));
BasicKind kind = t->Basic.kind;
@@ -1755,7 +1770,8 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
} break;
case Type_DynamicArray:
return 3*build_context.word_size + type_size_of(allocator, t_allocator);
// data + len + cap + allocator(procedure+data)
return 3*build_context.word_size + 2*build_context.word_size;
case Type_Vector: {
#if 0
@@ -1833,15 +1849,35 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
} break;
case TypeRecord_Union: {
i64 count = t->Record.variant_count;
i64 align = type_align_of_internal(allocator, t, path);
if (path->failure) {
return FAILURE_SIZE;
}
i64 max = 0;
isize field_count = t->Record.field_count;
isize variant_count = t->Record.variant_count;
// Check for recursive types
for (isize i = 0; i < field_count; i++) {
i64 size = type_size_of_internal(allocator, t->Record.fields[i]->type, path);
if (path->failure) {
return FAILURE_SIZE;
}
}
// NOTE(bill): Zeroth field is invalid
for (isize i = 1; i < count; i++) {
i64 size = type_size_of_internal(allocator, t->Record.variants[i]->type, path);
type_set_offsets(allocator, t);
if (field_count > 0) {
Type *end_type = t->Record.fields[field_count-1]->type;
i64 end_offset = t->Record.offsets[field_count-1];
i64 end_size = type_size_of_internal(allocator, end_type, path);
max = end_offset + end_size ;
}
for (isize i = 1; i < variant_count; i++) {
Type *variant_type = t->Record.variants[i]->type;
i64 size = type_size_of_internal(allocator, variant_type, path);
if (max < size) {
max = size;
}
@@ -1849,7 +1885,8 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
// NOTE(bill): Align to int
i64 size = align_formula(max, build_context.word_size);
size += type_size_of_internal(allocator, t_int, path);
return align_formula(size, align);
size = align_formula(size, align);
return size;
} break;
case TypeRecord_RawUnion: {
@@ -1878,7 +1915,7 @@ i64 type_size_of_internal(gbAllocator allocator, Type *t, TypePath *path) {
i64 type_offset_of(gbAllocator allocator, Type *t, i32 index) {
t = base_type(t);
if (t->kind == Type_Record && t->Record.kind == TypeRecord_Struct) {
if (t->kind == Type_Record && (t->Record.kind == TypeRecord_Struct || t->Record.kind == TypeRecord_Union)) {
type_set_offsets(allocator, t);
if (gb_is_between(index, 0, t->Record.field_count-1)) {
return t->Record.offsets[index];
@@ -1983,13 +2020,18 @@ gbString write_type_to_string(gbString str, Type *type) {
str = write_type_to_string(str, type->Pointer.elem);
break;
case Type_Atomic:
str = gb_string_appendc(str, "atomic ");
str = write_type_to_string(str, type->Atomic.elem);
break;
case Type_Array:
str = gb_string_appendc(str, gb_bprintf("[%lld]", type->Array.count));
str = gb_string_appendc(str, gb_bprintf("[%d]", cast(int)type->Array.count));
str = write_type_to_string(str, type->Array.elem);
break;
case Type_Vector:
str = gb_string_appendc(str, gb_bprintf("[vector %lld]", type->Vector.count));
str = gb_string_appendc(str, gb_bprintf("[vector %d]", cast(int)type->Vector.count));
str = write_type_to_string(str, type->Vector.elem);
break;
@@ -2037,7 +2079,7 @@ gbString write_type_to_string(gbString str, Type *type) {
}
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, base_type(f->type));
str = write_type_to_string(str, f->type);
}
for (isize i = 1; i < type->Record.variant_count; i++) {
Entity *f = type->Record.variants[i];
@@ -2103,7 +2145,7 @@ gbString write_type_to_string(gbString str, Type *type) {
case Type_Map: {
str = gb_string_appendc(str, "map[");
if (type->Map.count > 0) {
str = gb_string_appendc(str, gb_bprintf("%lld, ", type->Map.count));
str = gb_string_appendc(str, gb_bprintf("%d, ", cast(int)type->Map.count));
}
str = write_type_to_string(str, type->Map.key);
str = gb_string_appendc(str, "]");