mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-05 11:11:37 -07:00
Compare commits
432 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c454ede184 | |||
| d854c5003c | |||
| 66d8776b83 | |||
| ba6ecf35cf | |||
| 10cc9cf661 | |||
| 2db971eedd | |||
| 1775e80b41 | |||
| e4a93619db | |||
| 4d14b3bcb4 | |||
| 9f4f5f9346 | |||
| 0fae31fb54 | |||
| 8987a6630c | |||
| 10ff8e0426 | |||
| a0ae02168a | |||
| a3c1ac2030 | |||
| 629b248f53 | |||
| 62a72f0163 | |||
| 655931f0ea | |||
| ca36fabfc0 | |||
| 7bd62481ad | |||
| fbd27d7c45 | |||
| 3546391311 | |||
| 24c812115e | |||
| 28be0ad69b | |||
| f0980c0a98 | |||
| 1df4aa90ce | |||
| 6b3cf051f8 | |||
| 4ecd6e592b | |||
| dbddec33c8 | |||
| 401a5955a4 | |||
| 9a3b4167bb | |||
| 13bc6eeea4 | |||
| 2da18b6d33 | |||
| 6d37ed12d2 | |||
| eab23cd5b7 | |||
| d233706a2d | |||
| f1ab17ed4e | |||
| 6113164211 | |||
| 4db462a703 | |||
| a22c6d6c0c | |||
| 59fb7b020a | |||
| 65f079ebc4 | |||
| d16aa79492 | |||
| 5af0acc4af | |||
| a459364de3 | |||
| 277ef1a68f | |||
| 193c7c82c8 | |||
| f7d8ba408c | |||
| 9a8759efef | |||
| 054948e701 | |||
| 1c5ddd65b4 | |||
| b8697fb4ed | |||
| 03570275c1 | |||
| b5587f1937 | |||
| c4c6975f1b | |||
| 0be0fb2a57 | |||
| 115e6e7f9e | |||
| 3868a9a0f0 | |||
| ba5050ac7c | |||
| d936ca1ea0 | |||
| fd8c4d58bb | |||
| ce4b7b8b7d | |||
| 069a47220e | |||
| 66e4aaffc5 | |||
| 81336b58cb | |||
| b201670f7a | |||
| 4b051a0d3b | |||
| 45353465a6 | |||
| c63cb98019 | |||
| 773cf5ca08 | |||
| 2db03cb4a5 | |||
| eed873c6ec | |||
| 3d2d461867 | |||
| 36392d658e | |||
| 82696179e8 | |||
| 188bc28f6a | |||
| 240da5c8e0 | |||
| 689a0c0b49 | |||
| bc16b290ba | |||
| 96d32680fe | |||
| d782b3d21d | |||
| ed089b44b9 | |||
| 33f4af2e19 | |||
| 69f7382eec | |||
| 7e3293fc20 | |||
| e4a8283327 | |||
| 001baf4419 | |||
| d167290b28 | |||
| f4879d4723 | |||
| fd81c06c35 | |||
| 94afcec757 | |||
| 4f28e9e1fb | |||
| 0622509807 | |||
| 9ca2246bac | |||
| 647e2cafd7 | |||
| 5df854fcef | |||
| 260089431e | |||
| d0d8da8c08 | |||
| d1365b3466 | |||
| c949ca2a5c | |||
| d974b29f67 | |||
| cc7316bb35 | |||
| a0d8dcd974 | |||
| c642e326ce | |||
| 362a118782 | |||
| 3ab481df17 | |||
| 4e7150b470 | |||
| 1ced92be47 | |||
| 15dbea6899 | |||
| c4081393c1 | |||
| 1d81b73df9 | |||
| 18f885efab | |||
| bba088bee7 | |||
| 6cbb6bef0b | |||
| 8744c60563 | |||
| 8197c02dcf | |||
| 9faf0020cc | |||
| 53075e2570 | |||
| 264ca00db7 | |||
| 6b65ef6d88 | |||
| 5957d7f7be | |||
| 35c102137f | |||
| 5427d14416 | |||
| 178236d1ff | |||
| 736c880ba9 | |||
| 126f7aa892 | |||
| 2957f007e3 | |||
| 04501c93fe | |||
| 4236519b84 | |||
| e4944b4f2e | |||
| 2deb2f8eeb | |||
| 3fa398ec43 | |||
| 1851674b50 | |||
| c5ef5279d4 | |||
| d3c24d159f | |||
| 23f9f9064e | |||
| a134307dcd | |||
| c3b510c2d9 | |||
| e7fc24e48c | |||
| 6a88dc322a | |||
| 6b464e3558 | |||
| 76b0c7b765 | |||
| 91857e8f16 | |||
| ccda456c0a | |||
| 83bad13e9e | |||
| e6a206a430 | |||
| f52a1e4ded | |||
| a8e458339b | |||
| 6b5e9aec8e | |||
| 2ab0d97573 | |||
| 0c05fc1432 | |||
| 33eeb58521 | |||
| 8fafdb185c | |||
| c2c935ba81 | |||
| 2d73c8868b | |||
| b95bb1286b | |||
| 4237c8ec30 | |||
| 49b4b39055 | |||
| bf15fea135 | |||
| 47c03e376d | |||
| 1cabfac36c | |||
| 8e32276283 | |||
| 366b306df0 | |||
| 4bf1f798f5 | |||
| b2fdb69b4d | |||
| af2736daec | |||
| 5cad7d44a6 | |||
| 2b96be0ae8 | |||
| 2a89d8021c | |||
| 13deb4706c | |||
| 9b61adb97d | |||
| 333924cce1 | |||
| 574b82c0c7 | |||
| f60c772c11 | |||
| 107740ca5e | |||
| 88b990eb63 | |||
| d2e7d730ac | |||
| 817e4b663e | |||
| 214bb73454 | |||
| eba2c74bff | |||
| 7c5e6c808b | |||
| ebe5beaafd | |||
| 029a6095d9 | |||
| 2c0e59ae06 | |||
| 9d1a4c304a | |||
| 13b8a1e348 | |||
| 0d4945dc87 | |||
| e0b9c4a275 | |||
| fec6df65b3 | |||
| 78494e84d5 | |||
| 60d7c833c0 | |||
| 98dbbf11f3 | |||
| f4924e39d4 | |||
| 826e05c96e | |||
| d3f63e5903 | |||
| 80c034ec7c | |||
| b41f09b730 | |||
| 06185e1769 | |||
| f8fa7fe380 | |||
| 45dbe8d354 | |||
| ddb99dd638 | |||
| 41aa4e606b | |||
| e025a828ca | |||
| 807e17207a | |||
| 3e18f5f057 | |||
| 9637cc5690 | |||
| ded99a2cab | |||
| 45eecc0905 | |||
| 87f1a62ca4 | |||
| c6d531df95 | |||
| 8677c81da7 | |||
| 5595daf5a3 | |||
| 64b5afd820 | |||
| 7692061eef | |||
| f7f2272c50 | |||
| 03fbdc3f75 | |||
| ea6a4859ed | |||
| 615fa82d1f | |||
| b60b310121 | |||
| c7f7e562a0 | |||
| a317237404 | |||
| 51ea59d76a | |||
| 789b297f32 | |||
| 3b25f924cb | |||
| cc6282a6e3 | |||
| 206a3e093c | |||
| 19bde275a3 | |||
| 634ee450f4 | |||
| 750d7256fc | |||
| fae5df2ed8 | |||
| 01d9161772 | |||
| aceabb2f2f | |||
| 04f5fff7fa | |||
| dc5587eae2 | |||
| 7057034b75 | |||
| 1430ca30a3 | |||
| e63393e394 | |||
| 784f3ecf7e | |||
| 54ea70df98 | |||
| d05ec5e484 | |||
| c7575164cc | |||
| 99125dc743 | |||
| b78e970698 | |||
| 5b8be25938 | |||
| 29efdc5fc1 | |||
| a80872b60d | |||
| 822bb51b55 | |||
| c2fa79012e | |||
| 3fd37c6dc5 | |||
| 0ea815db49 | |||
| 91ed51ff5c | |||
| 4d0afc55c3 | |||
| 9a1566d665 | |||
| a713e33007 | |||
| c5411a25a9 | |||
| 95692fda52 | |||
| 813a028ed0 | |||
| 0c22081e5f | |||
| 6d9fadf351 | |||
| a213061f33 | |||
| d1a0a46141 | |||
| 187b186112 | |||
| 5041a35b95 | |||
| 92d4fcedee | |||
| c69df7cd3a | |||
| 67d8f48553 | |||
| b4a339f2e3 | |||
| 0d7bf58b60 | |||
| abb9930725 | |||
| 169310a9f6 | |||
| 23a0a6de4b | |||
| 0d2dbee84e | |||
| d8d22e34dd | |||
| 627ee002e8 | |||
| 8e73d1ce1f | |||
| b53d16d1d5 | |||
| f5819eafa9 | |||
| 5916e71d4f | |||
| 913b9b6447 | |||
| 8e55bb2a6c | |||
| 98d493504b | |||
| 3a3202fbc6 | |||
| aaf355e750 | |||
| 0683d2b4f4 | |||
| d7fdd3d7b8 | |||
| 83ebb24015 | |||
| 70f9cacdce | |||
| 6b33b254e9 | |||
| c0019cc305 | |||
| c067a1f0ec | |||
| 63345cd0d8 | |||
| e41d6261c2 | |||
| 3e80411d37 | |||
| f952c7c747 | |||
| 642256f9ba | |||
| c9c82da1f3 | |||
| 382a5ca6a2 | |||
| 96e8bb5b6f | |||
| 22afac2b90 | |||
| 01da0d1377 | |||
| 8ce58573df | |||
| ce0d874efd | |||
| 2c8b99337b | |||
| 5008e2c88b | |||
| 90fc9abeae | |||
| dc303cde21 | |||
| 24b33374b7 | |||
| 3315dc7f25 | |||
| 77b3295de5 | |||
| 1349aa6f2c | |||
| a75ccb6fbc | |||
| 7a28827602 | |||
| c61015b1fe | |||
| e935f8e2ff | |||
| 690c682847 | |||
| f541dd40db | |||
| c7bb861d3c | |||
| 188b290dd5 | |||
| c6ff961088 | |||
| c26990c22d | |||
| c34d839f9f | |||
| 5562364a98 | |||
| 32150e401e | |||
| aaec8bf423 | |||
| 0fcbda951a | |||
| e2734a2dc6 | |||
| 5adfbec847 | |||
| 4ef4605d6d | |||
| 2aa402f462 | |||
| 00f6bee454 | |||
| 6e1864d21c | |||
| fb2d611dcd | |||
| d890731716 | |||
| 9e8c9be1ea | |||
| 231ea8b026 | |||
| 9bc37f4400 | |||
| f29e303ce7 | |||
| 3c9143957c | |||
| 18b3c0b2fc | |||
| c59f6b7d0b | |||
| 5bbdb3a3a3 | |||
| 67ed8a9a4a | |||
| 27aa07307b | |||
| 4cc4d604bc | |||
| eec709c545 | |||
| 9b2f5c359a | |||
| a982c51c30 | |||
| 20b9f1ff59 | |||
| 561c583b3f | |||
| 047c0e4bcc | |||
| 8d5896ab7e | |||
| a94dfdf21d | |||
| c0d5237b75 | |||
| 6fdcbefe5d | |||
| 3cec2550d9 | |||
| 758dd9ba16 | |||
| 0c37aa9ea0 | |||
| 9ff474f387 | |||
| d2f9d20833 | |||
| 802b1a70f8 | |||
| aaa4dd5c36 | |||
| 71100ed427 | |||
| 3ecf3505fd | |||
| daa1cd55a1 | |||
| 2722de65b7 | |||
| 8b5e3428a1 | |||
| d1f65097c4 | |||
| 74d15ab84b | |||
| 763cd2649d | |||
| 9d19ee7e4c | |||
| 8df3175f10 | |||
| ebb10e5597 | |||
| 047f883078 | |||
| 320c22e08a | |||
| bd27c24fab | |||
| 282f8bb06f | |||
| b9ed546ce0 | |||
| a9398bf30f | |||
| 7829421085 | |||
| c50aabd916 | |||
| 3f3122bccc | |||
| fc1a006de1 | |||
| e1fdd675ce | |||
| 754b368140 | |||
| a49e888ce6 | |||
| 4306345ff1 | |||
| 346aa5f71c | |||
| 99c663d9f3 | |||
| afac95e092 | |||
| 05486f9fa3 | |||
| cad46ae51c | |||
| 3424b2badd | |||
| 73d6a55f5c | |||
| f18ae89931 | |||
| 3445a28c4a | |||
| 7f6b83d50c | |||
| 72d4bfb32a | |||
| 37f7630a9e | |||
| 73c5c5d5d3 | |||
| 584869730a | |||
| 90ab448bca | |||
| 454d0b5cf5 | |||
| 8becbdc1b2 | |||
| eeeb90c441 | |||
| 219ca0ac46 | |||
| 5796c41357 | |||
| 8cfae17535 | |||
| 6efd400c98 | |||
| df78b8ad3e | |||
| f11d73ffaa | |||
| c126339090 | |||
| 5cfa4ba580 | |||
| 9f2d9b596d | |||
| 00c7489157 | |||
| b1562edccf | |||
| 2a5b674d33 | |||
| 7944b7714f | |||
| 205f4664f8 | |||
| c6133587d1 | |||
| 5516e80ab7 | |||
| 864310e3da | |||
| 4e7082a68d | |||
| 502e63b9c5 | |||
| 34150385d8 | |||
| 0ca1b4612c | |||
| 9e143a38ce | |||
| 43be91bca3 | |||
| 984e36a151 | |||
| ec9c8fb8a4 | |||
| 3e79ec4aef | |||
| 3e257ef8d0 | |||
| 626f91f307 |
+9
-3
@@ -251,7 +251,13 @@ paket-files/
|
||||
|
||||
|
||||
# Project Specific
|
||||
|
||||
# - Windows
|
||||
*.sln
|
||||
!misc/llvm-bim/lli.exe
|
||||
!misc/llvm-bim/opt.exe
|
||||
builds
|
||||
builds/
|
||||
bin/
|
||||
*.exe
|
||||
|
||||
# - Linux/MacOS
|
||||
odin
|
||||
odin.dSYM
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2016 Ginger Bill. All rights reserved.
|
||||
Copyright (c) 2016-2017 Ginger Bill. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
<img src="logo-slim.png" alt="Odin logo" height="74">
|
||||
<img src="misc/logo-slim.png" alt="Odin logo" height="74">
|
||||
|
||||
# The Odin Programming Language
|
||||
|
||||
Odin is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals:
|
||||
The Odin programming language is fast, concise, readable, pragmatic and open sourced. It is designed with the intent of replacing C with the following goals:
|
||||
* simplicity
|
||||
* high performance
|
||||
* built for modern systems
|
||||
* joy of programming
|
||||
* metaprogramming
|
||||
* designed for good programmers
|
||||
|
||||
Website: [https://odin.handmade.network/](https://odin.handmade.network/)
|
||||
|
||||
## Demonstrations:
|
||||
* First Talk & Demo
|
||||
@@ -18,39 +19,52 @@ Odin is fast, concise, readable, pragmatic and open sourced. It is designed with
|
||||
* [Composition & Refactorability](https://www.youtube.com/watch?v=n1wemZfcbXM)
|
||||
* [Introspection, Modules, and Record Layout](https://www.youtube.com/watch?v=UFq8rhWhx4s)
|
||||
* [push_allocator & Minimal Dependency Building](https://www.youtube.com/watch?v=f_LGVOAMb78)
|
||||
* [when, for & procedure overloading](https://www.youtube.com/watch?v=OzeOekzyZK8)
|
||||
* [Context Types, Unexported Entities, Labelled Branches](https://www.youtube.com/watch?v=CkHVwT1Qk-g)
|
||||
* [Bit Fields, i128 & u128, Syntax Changes](https://www.youtube.com/watch?v=NlTutcLyF64)
|
||||
|
||||
## 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
|
||||
- Windows
|
||||
* x86-64
|
||||
* MSVC 2015 installed (C++11 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
|
||||
|
||||
* This is still highly in development and the language's design is quite volatile.
|
||||
* Syntax is definitely not fixed
|
||||
* Syntax is not fixed.
|
||||
|
||||
## Roadmap
|
||||
|
||||
Not in any particular order
|
||||
Not in any particular order and not be implemented
|
||||
|
||||
* Custom backend to replace LLVM
|
||||
- Improve SSA design to accommodate for lowering to a "bytecode"
|
||||
- SSA optimizations
|
||||
- COFF generation
|
||||
- linker
|
||||
* Type safe "macros"
|
||||
* Documentation generator for "Entities"
|
||||
* Multiple architecture support
|
||||
* Inline assembly
|
||||
* Linking options
|
||||
- Executable
|
||||
- Static/Dynamic Library
|
||||
* Debug information
|
||||
* Compile Time Execution (CTE)
|
||||
- More metaprogramming madness
|
||||
- Compiler as a library
|
||||
- AST inspection and modification
|
||||
* CTE-based build system
|
||||
* Replace LLVM backend with my own custom backend
|
||||
* Improve SSA design to accommodate for lowering to a "bytecode"
|
||||
* SSA optimizations
|
||||
* Documentation Generator for "Entities"
|
||||
* Multiple Architecture support
|
||||
* Debug Information
|
||||
- pdb format too
|
||||
* Command line tooling
|
||||
* Compiler internals:
|
||||
* Command Line Tooling
|
||||
* Compiler Internals:
|
||||
- Big numbers library
|
||||
|
||||
- Multithreading for performance increase
|
||||
|
||||
@@ -1,200 +0,0 @@
|
||||
This file is a list of the people responsible for ensuring that patches for a
|
||||
particular part of LLVM are reviewed, either by themself or by someone else.
|
||||
They are also the gatekeepers for their part of LLVM, with the final word on
|
||||
what goes in or not.
|
||||
|
||||
The list is sorted by surname and formatted to allow easy grepping and
|
||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
||||
(W), PGP key ID and fingerprint (P), description (D), and snail-mail address
|
||||
(S). Each entry should contain at least the (N), (E) and (D) fields.
|
||||
|
||||
N: Joe Abbey
|
||||
E: jabbey@arxan.com
|
||||
D: LLVM Bitcode (lib/Bitcode/* include/llvm/Bitcode/*)
|
||||
|
||||
N: Owen Anderson
|
||||
E: resistor@mac.com
|
||||
D: SelectionDAG (lib/CodeGen/SelectionDAG/*)
|
||||
|
||||
N: Rafael Avila de Espindola
|
||||
E: rafael.espindola@gmail.com
|
||||
D: Gold plugin (tools/gold/*)
|
||||
|
||||
N: Justin Bogner
|
||||
E: mail@justinbogner.com
|
||||
D: InstrProfiling and related parts of ProfileData
|
||||
|
||||
N: Chandler Carruth
|
||||
E: chandlerc@gmail.com
|
||||
E: chandlerc@google.com
|
||||
D: Config, ADT, Support, inlining & related passes, SROA/mem2reg & related passes, CMake, library layering
|
||||
|
||||
N: Evan Cheng
|
||||
E: evan.cheng@apple.com
|
||||
D: parts of code generator not covered by someone else
|
||||
|
||||
N: Eric Christopher
|
||||
E: echristo@gmail.com
|
||||
D: Debug Information, autotools/configure/make build, inline assembly
|
||||
|
||||
N: Greg Clayton
|
||||
E: gclayton@apple.com
|
||||
D: LLDB
|
||||
|
||||
N: Marshall Clow
|
||||
E: mclow.lists@gmail.com
|
||||
D: libc++
|
||||
|
||||
N: Peter Collingbourne
|
||||
E: peter@pcc.me.uk
|
||||
D: llgo
|
||||
|
||||
N: Quentin Colombet
|
||||
E: qcolombet@apple.com
|
||||
D: Register allocators
|
||||
|
||||
N: Duncan P. N. Exon Smith
|
||||
E: dexonsmith@apple.com
|
||||
D: Branch weights and BlockFrequencyInfo
|
||||
|
||||
N: Hal Finkel
|
||||
E: hfinkel@anl.gov
|
||||
D: BBVectorize, the loop reroller, alias analysis and the PowerPC target
|
||||
|
||||
N: Dan Gohman
|
||||
E: sunfish@mozilla.com
|
||||
D: WebAssembly Backend (lib/Target/WebAssembly/*)
|
||||
|
||||
N: Renato Golin
|
||||
E: renato.golin@linaro.org
|
||||
D: ARM Linux support
|
||||
|
||||
N: Venkatraman Govindaraju
|
||||
E: venkatra@cs.wisc.edu
|
||||
D: Sparc Backend (lib/Target/Sparc/*)
|
||||
|
||||
N: Tobias Grosser
|
||||
E: tobias@grosser.es
|
||||
D: Polly
|
||||
|
||||
N: James Grosbach
|
||||
E: grosbach@apple.com
|
||||
D: MC layer
|
||||
|
||||
N: Justin Holewinski
|
||||
E: jholewinski@nvidia.com
|
||||
D: NVPTX Target (lib/Target/NVPTX/*)
|
||||
|
||||
N: Lang Hames
|
||||
E: lhames@gmail.com
|
||||
D: MCJIT, RuntimeDyld and JIT event listeners
|
||||
|
||||
N: Galina Kistanova
|
||||
E: gkistanova@gmail.com
|
||||
D: LLVM Buildbot
|
||||
|
||||
N: Anton Korobeynikov
|
||||
E: anton@korobeynikov.info
|
||||
D: Exception handling, Windows codegen, ARM EABI
|
||||
|
||||
N: Benjamin Kramer
|
||||
E: benny.kra@gmail.com
|
||||
D: DWARF Parser
|
||||
|
||||
N: Sergei Larin
|
||||
E: slarin@codeaurora.org
|
||||
D: VLIW Instruction Scheduling, Packetization
|
||||
|
||||
N: Chris Lattner
|
||||
E: sabre@nondot.org
|
||||
W: http://nondot.org/~sabre/
|
||||
D: Everything not covered by someone else
|
||||
|
||||
N: David Majnemer
|
||||
E: david.majnemer@gmail.com
|
||||
D: IR Constant Folder, InstCombine
|
||||
|
||||
N: Dylan McKay
|
||||
E: dylanmckay34@gmail.com
|
||||
D: AVR Backend
|
||||
|
||||
N: Tim Northover
|
||||
E: t.p.northover@gmail.com
|
||||
D: AArch64 backend, misc ARM backend
|
||||
|
||||
N: Diego Novillo
|
||||
E: dnovillo@google.com
|
||||
D: SampleProfile and related parts of ProfileData
|
||||
|
||||
N: Jakob Olesen
|
||||
E: stoklund@2pi.dk
|
||||
D: TableGen
|
||||
|
||||
N: Richard Osborne
|
||||
E: richard@xmos.com
|
||||
D: XCore Backend
|
||||
|
||||
N: Krzysztof Parzyszek
|
||||
E: kparzysz@codeaurora.org
|
||||
D: Hexagon Backend
|
||||
|
||||
N: Paul Robinson
|
||||
E: paul_robinson@playstation.sony.com
|
||||
D: Sony PlayStation®4 support
|
||||
|
||||
N: Chad Rosier
|
||||
E: mcrosier@codeaurora.org
|
||||
D: Fast-Isel
|
||||
|
||||
N: Nadav Rotem
|
||||
E: nrotem@apple.com
|
||||
D: X86 Backend, Loop Vectorizer
|
||||
|
||||
N: Daniel Sanders
|
||||
E: daniel.sanders@imgtec.com
|
||||
D: MIPS Backend (lib/Target/Mips/*)
|
||||
|
||||
N: Duncan Sands
|
||||
E: baldrick@free.fr
|
||||
D: DragonEgg
|
||||
|
||||
N: Kostya Serebryany
|
||||
E: kcc@google.com
|
||||
D: AddressSanitizer, ThreadSanitizer (LLVM parts)
|
||||
|
||||
N: Michael Spencer
|
||||
E: bigcheesegs@gmail.com
|
||||
D: Windows parts of Support, Object, ar, nm, objdump, ranlib, size
|
||||
|
||||
N: Alexei Starovoitov
|
||||
E: alexei.starovoitov@gmail.com
|
||||
D: BPF backend
|
||||
|
||||
N: Tom Stellard
|
||||
E: thomas.stellard@amd.com
|
||||
E: mesa-dev@lists.freedesktop.org
|
||||
D: Release manager for the 3.5 and 3.6 branches, R600 Backend, libclc
|
||||
|
||||
N: Evgeniy Stepanov
|
||||
E: eugenis@google.com
|
||||
D: MemorySanitizer (LLVM part)
|
||||
|
||||
N: Andrew Trick
|
||||
E: atrick@apple.com
|
||||
D: IndVar Simplify, Loop Strength Reduction, Instruction Scheduling
|
||||
|
||||
N: Ulrich Weigand
|
||||
E: uweigand@de.ibm.com
|
||||
D: SystemZ Backend
|
||||
|
||||
N: Bill Wendling
|
||||
E: isanbard@gmail.com
|
||||
D: libLTO, IR Linker
|
||||
|
||||
N: Peter Zotov
|
||||
E: whitequark@whitequark.org
|
||||
D: OCaml bindings
|
||||
|
||||
N: Andrey Churbanov
|
||||
E: andrey.churbanov@intel.com
|
||||
D: OpenMP runtime library
|
||||
-467
@@ -1,467 +0,0 @@
|
||||
This file is a partial list of people who have contributed to the LLVM
|
||||
project. If you have contributed a patch or made some other contribution to
|
||||
LLVM, please submit a patch to this file to add yourself, and it will be
|
||||
done!
|
||||
|
||||
The list is sorted by surname and formatted to allow easy grepping and
|
||||
beautification by scripts. The fields are: name (N), email (E), web-address
|
||||
(W), PGP key ID and fingerprint (P), description (D), snail-mail address
|
||||
(S), and (I) IRC handle.
|
||||
|
||||
|
||||
N: Vikram Adve
|
||||
E: vadve@cs.uiuc.edu
|
||||
W: http://www.cs.uiuc.edu/~vadve/
|
||||
D: The Sparc64 backend, provider of much wisdom, and motivator for LLVM
|
||||
|
||||
N: Owen Anderson
|
||||
E: resistor@mac.com
|
||||
D: LCSSA pass and related LoopUnswitch work
|
||||
D: GVNPRE pass, DataLayout refactoring, random improvements
|
||||
|
||||
N: Henrik Bach
|
||||
D: MingW Win32 API portability layer
|
||||
|
||||
N: Aaron Ballman
|
||||
E: aaron@aaronballman.com
|
||||
D: __declspec attributes, Windows support, general bug fixing
|
||||
|
||||
N: Nate Begeman
|
||||
E: natebegeman@mac.com
|
||||
D: PowerPC backend developer
|
||||
D: Target-independent code generator and analysis improvements
|
||||
|
||||
N: Daniel Berlin
|
||||
E: dberlin@dberlin.org
|
||||
D: ET-Forest implementation.
|
||||
D: Sparse bitmap
|
||||
|
||||
N: David Blaikie
|
||||
E: dblaikie@gmail.com
|
||||
D: General bug fixing/fit & finish, mostly in Clang
|
||||
|
||||
N: Neil Booth
|
||||
E: neil@daikokuya.co.uk
|
||||
D: APFloat implementation.
|
||||
|
||||
N: Misha Brukman
|
||||
E: brukman+llvm@uiuc.edu
|
||||
W: http://misha.brukman.net
|
||||
D: Portions of X86 and Sparc JIT compilers, PowerPC backend
|
||||
D: Incremental bitcode loader
|
||||
|
||||
N: Cameron Buschardt
|
||||
E: buschard@uiuc.edu
|
||||
D: The `mem2reg' pass - promotes values stored in memory to registers
|
||||
|
||||
N: Brendon Cahoon
|
||||
E: bcahoon@codeaurora.org
|
||||
D: Loop unrolling with run-time trip counts.
|
||||
|
||||
N: Chandler Carruth
|
||||
E: chandlerc@gmail.com
|
||||
E: chandlerc@google.com
|
||||
D: Hashing algorithms and interfaces
|
||||
D: Inline cost analysis
|
||||
D: Machine block placement pass
|
||||
D: SROA
|
||||
|
||||
N: Casey Carter
|
||||
E: ccarter@uiuc.edu
|
||||
D: Fixes to the Reassociation pass, various improvement patches
|
||||
|
||||
N: Evan Cheng
|
||||
E: evan.cheng@apple.com
|
||||
D: ARM and X86 backends
|
||||
D: Instruction scheduler improvements
|
||||
D: Register allocator improvements
|
||||
D: Loop optimizer improvements
|
||||
D: Target-independent code generator improvements
|
||||
|
||||
N: Dan Villiom Podlaski Christiansen
|
||||
E: danchr@gmail.com
|
||||
E: danchr@cs.au.dk
|
||||
W: http://villiom.dk
|
||||
D: LLVM Makefile improvements
|
||||
D: Clang diagnostic & driver tweaks
|
||||
S: Aarhus, Denmark
|
||||
|
||||
N: Jeff Cohen
|
||||
E: jeffc@jolt-lang.org
|
||||
W: http://jolt-lang.org
|
||||
D: Native Win32 API portability layer
|
||||
|
||||
N: John T. Criswell
|
||||
E: criswell@uiuc.edu
|
||||
D: Original Autoconf support, documentation improvements, bug fixes
|
||||
|
||||
N: Anshuman Dasgupta
|
||||
E: adasgupt@codeaurora.org
|
||||
D: Deterministic finite automaton based infrastructure for VLIW packetization
|
||||
|
||||
N: Stefanus Du Toit
|
||||
E: stefanus.du.toit@intel.com
|
||||
D: Bug fixes and minor improvements
|
||||
|
||||
N: Rafael Avila de Espindola
|
||||
E: rafael.espindola@gmail.com
|
||||
D: The ARM backend
|
||||
|
||||
N: Dave Estes
|
||||
E: cestes@codeaurora.org
|
||||
D: AArch64 machine description for Cortex-A53
|
||||
|
||||
N: Alkis Evlogimenos
|
||||
E: alkis@evlogimenos.com
|
||||
D: Linear scan register allocator, many codegen improvements, Java frontend
|
||||
|
||||
N: Hal Finkel
|
||||
E: hfinkel@anl.gov
|
||||
D: Basic-block autovectorization, PowerPC backend improvements
|
||||
|
||||
N: Eric Fiselier
|
||||
E: eric@efcs.ca
|
||||
D: LIT patches and documentation.
|
||||
|
||||
N: Ryan Flynn
|
||||
E: pizza@parseerror.com
|
||||
D: Miscellaneous bug fixes
|
||||
|
||||
N: Brian Gaeke
|
||||
E: gaeke@uiuc.edu
|
||||
W: http://www.students.uiuc.edu/~gaeke/
|
||||
D: Portions of X86 static and JIT compilers; initial SparcV8 backend
|
||||
D: Dynamic trace optimizer
|
||||
D: FreeBSD/X86 compatibility fixes, the llvm-nm tool
|
||||
|
||||
N: Nicolas Geoffray
|
||||
E: nicolas.geoffray@lip6.fr
|
||||
W: http://www-src.lip6.fr/homepages/Nicolas.Geoffray/
|
||||
D: PPC backend fixes for Linux
|
||||
|
||||
N: Louis Gerbarg
|
||||
E: lgg@apple.com
|
||||
D: Portions of the PowerPC backend
|
||||
|
||||
N: Saem Ghani
|
||||
E: saemghani@gmail.com
|
||||
D: Callgraph class cleanups
|
||||
|
||||
N: Mikhail Glushenkov
|
||||
E: foldr@codedgers.com
|
||||
D: Author of llvmc2
|
||||
|
||||
N: Dan Gohman
|
||||
E: sunfish@mozilla.com
|
||||
D: Miscellaneous bug fixes
|
||||
D: WebAssembly Backend
|
||||
|
||||
N: David Goodwin
|
||||
E: david@goodwinz.net
|
||||
D: Thumb-2 code generator
|
||||
|
||||
N: David Greene
|
||||
E: greened@obbligato.org
|
||||
D: Miscellaneous bug fixes
|
||||
D: Register allocation refactoring
|
||||
|
||||
N: Gabor Greif
|
||||
E: ggreif@gmail.com
|
||||
D: Improvements for space efficiency
|
||||
|
||||
N: James Grosbach
|
||||
E: grosbach@apple.com
|
||||
I: grosbach
|
||||
D: SjLj exception handling support
|
||||
D: General fixes and improvements for the ARM back-end
|
||||
D: MCJIT
|
||||
D: ARM integrated assembler and assembly parser
|
||||
D: Led effort for the backend formerly known as ARM64
|
||||
|
||||
N: Lang Hames
|
||||
E: lhames@gmail.com
|
||||
D: PBQP-based register allocator
|
||||
|
||||
N: Gordon Henriksen
|
||||
E: gordonhenriksen@mac.com
|
||||
D: Pluggable GC support
|
||||
D: C interface
|
||||
D: Ocaml bindings
|
||||
|
||||
N: Raul Fernandes Herbster
|
||||
E: raul@dsc.ufcg.edu.br
|
||||
D: JIT support for ARM
|
||||
|
||||
N: Paolo Invernizzi
|
||||
E: arathorn@fastwebnet.it
|
||||
D: Visual C++ compatibility fixes
|
||||
|
||||
N: Patrick Jenkins
|
||||
E: patjenk@wam.umd.edu
|
||||
D: Nightly Tester
|
||||
|
||||
N: Dale Johannesen
|
||||
E: dalej@apple.com
|
||||
D: ARM constant islands improvements
|
||||
D: Tail merging improvements
|
||||
D: Rewrite X87 back end
|
||||
D: Use APFloat for floating point constants widely throughout compiler
|
||||
D: Implement X87 long double
|
||||
|
||||
N: Brad Jones
|
||||
E: kungfoomaster@nondot.org
|
||||
D: Support for packed types
|
||||
|
||||
N: Rod Kay
|
||||
E: rkay@auroraux.org
|
||||
D: Author of LLVM Ada bindings
|
||||
|
||||
N: Eric Kidd
|
||||
W: http://randomhacks.net/
|
||||
D: llvm-config script
|
||||
|
||||
N: Anton Korobeynikov
|
||||
E: asl@math.spbu.ru
|
||||
D: Mingw32 fixes, cross-compiling support, stdcall/fastcall calling conv.
|
||||
D: x86/linux PIC codegen, aliases, regparm/visibility attributes
|
||||
D: Switch lowering refactoring
|
||||
|
||||
N: Sumant Kowshik
|
||||
E: kowshik@uiuc.edu
|
||||
D: Author of the original C backend
|
||||
|
||||
N: Benjamin Kramer
|
||||
E: benny.kra@gmail.com
|
||||
D: Miscellaneous bug fixes
|
||||
|
||||
N: Sundeep Kushwaha
|
||||
E: sundeepk@codeaurora.org
|
||||
D: Implemented DFA-based target independent VLIW packetizer
|
||||
|
||||
N: Christopher Lamb
|
||||
E: christopher.lamb@gmail.com
|
||||
D: aligned load/store support, parts of noalias and restrict support
|
||||
D: vreg subreg infrastructure, X86 codegen improvements based on subregs
|
||||
D: address spaces
|
||||
|
||||
N: Jim Laskey
|
||||
E: jlaskey@apple.com
|
||||
D: Improvements to the PPC backend, instruction scheduling
|
||||
D: Debug and Dwarf implementation
|
||||
D: Auto upgrade mangler
|
||||
D: llvm-gcc4 svn wrangler
|
||||
|
||||
N: Chris Lattner
|
||||
E: sabre@nondot.org
|
||||
W: http://nondot.org/~sabre/
|
||||
D: Primary architect of LLVM
|
||||
|
||||
N: Tanya Lattner (Tanya Brethour)
|
||||
E: tonic@nondot.org
|
||||
W: http://nondot.org/~tonic/
|
||||
D: The initial llvm-ar tool, converted regression testsuite to dejagnu
|
||||
D: Modulo scheduling in the SparcV9 backend
|
||||
D: Release manager (1.7+)
|
||||
|
||||
N: Sylvestre Ledru
|
||||
E: sylvestre@debian.org
|
||||
W: http://sylvestre.ledru.info/
|
||||
W: http://llvm.org/apt/
|
||||
D: Debian and Ubuntu packaging
|
||||
D: Continuous integration with jenkins
|
||||
|
||||
N: Andrew Lenharth
|
||||
E: alenhar2@cs.uiuc.edu
|
||||
W: http://www.lenharth.org/~andrewl/
|
||||
D: Alpha backend
|
||||
D: Sampling based profiling
|
||||
|
||||
N: Nick Lewycky
|
||||
E: nicholas@mxc.ca
|
||||
D: PredicateSimplifier pass
|
||||
|
||||
N: Tony Linthicum, et. al.
|
||||
E: tlinth@codeaurora.org
|
||||
D: Backend for Qualcomm's Hexagon VLIW processor.
|
||||
|
||||
N: Bruno Cardoso Lopes
|
||||
E: bruno.cardoso@gmail.com
|
||||
I: bruno
|
||||
W: http://brunocardoso.cc
|
||||
D: Mips backend
|
||||
D: Random ARM integrated assembler and assembly parser improvements
|
||||
D: General X86 AVX1 support
|
||||
|
||||
N: Duraid Madina
|
||||
E: duraid@octopus.com.au
|
||||
W: http://kinoko.c.u-tokyo.ac.jp/~duraid/
|
||||
D: IA64 backend, BigBlock register allocator
|
||||
|
||||
N: John McCall
|
||||
E: rjmccall@apple.com
|
||||
D: Clang semantic analysis and IR generation
|
||||
|
||||
N: Michael McCracken
|
||||
E: michael.mccracken@gmail.com
|
||||
D: Line number support for llvmgcc
|
||||
|
||||
N: Vladimir Merzliakov
|
||||
E: wanderer@rsu.ru
|
||||
D: Test suite fixes for FreeBSD
|
||||
|
||||
N: Scott Michel
|
||||
E: scottm@aero.org
|
||||
D: Added STI Cell SPU backend.
|
||||
|
||||
N: Kai Nacke
|
||||
E: kai@redstar.de
|
||||
D: Support for implicit TLS model used with MS VC runtime
|
||||
D: Dumping of Win64 EH structures
|
||||
|
||||
N: Takumi Nakamura
|
||||
E: geek4civic@gmail.com
|
||||
E: chapuni@hf.rim.or.jp
|
||||
D: Cygwin and MinGW support.
|
||||
D: Win32 tweaks.
|
||||
S: Yokohama, Japan
|
||||
|
||||
N: Edward O'Callaghan
|
||||
E: eocallaghan@auroraux.org
|
||||
W: http://www.auroraux.org
|
||||
D: Add Clang support with various other improvements to utils/NewNightlyTest.pl
|
||||
D: Fix and maintain Solaris & AuroraUX support for llvm, various build warnings
|
||||
D: and error clean ups.
|
||||
|
||||
N: Morten Ofstad
|
||||
E: morten@hue.no
|
||||
D: Visual C++ compatibility fixes
|
||||
|
||||
N: Jakob Stoklund Olesen
|
||||
E: stoklund@2pi.dk
|
||||
D: Machine code verifier
|
||||
D: Blackfin backend
|
||||
D: Fast register allocator
|
||||
D: Greedy register allocator
|
||||
|
||||
N: Richard Osborne
|
||||
E: richard@xmos.com
|
||||
D: XCore backend
|
||||
|
||||
N: Devang Patel
|
||||
E: dpatel@apple.com
|
||||
D: LTO tool, PassManager rewrite, Loop Pass Manager, Loop Rotate
|
||||
D: GCC PCH Integration (llvm-gcc), llvm-gcc improvements
|
||||
D: Optimizer improvements, Loop Index Split
|
||||
|
||||
N: Ana Pazos
|
||||
E: apazos@codeaurora.org
|
||||
D: Fixes and improvements to the AArch64 backend
|
||||
|
||||
N: Wesley Peck
|
||||
E: peckw@wesleypeck.com
|
||||
W: http://wesleypeck.com/
|
||||
D: MicroBlaze backend
|
||||
|
||||
N: Francois Pichet
|
||||
E: pichet2000@gmail.com
|
||||
D: MSVC support
|
||||
|
||||
N: Vladimir Prus
|
||||
W: http://vladimir_prus.blogspot.com
|
||||
E: ghost@cs.msu.su
|
||||
D: Made inst_iterator behave like a proper iterator, LowerConstantExprs pass
|
||||
|
||||
N: Kalle Raiskila
|
||||
E: kalle.rasikila@nokia.com
|
||||
D: Some bugfixes to CellSPU
|
||||
|
||||
N: Xerxes Ranby
|
||||
E: xerxes@zafena.se
|
||||
D: Cmake dependency chain and various bug fixes
|
||||
|
||||
N: Alex Rosenberg
|
||||
E: alexr@leftfield.org
|
||||
I: arosenberg
|
||||
D: ARM calling conventions rewrite, hard float support
|
||||
|
||||
N: Chad Rosier
|
||||
E: mcrosier@codeaurora.org
|
||||
I: mcrosier
|
||||
D: AArch64 fast instruction selection pass
|
||||
D: Fixes and improvements to the ARM fast-isel pass
|
||||
D: Fixes and improvements to the AArch64 backend
|
||||
|
||||
N: Nadav Rotem
|
||||
E: nrotem@apple.com
|
||||
D: X86 code generation improvements, Loop Vectorizer.
|
||||
|
||||
N: Roman Samoilov
|
||||
E: roman@codedgers.com
|
||||
D: MSIL backend
|
||||
|
||||
N: Duncan Sands
|
||||
E: baldrick@free.fr
|
||||
I: baldrick
|
||||
D: Ada support in llvm-gcc
|
||||
D: Dragonegg plugin
|
||||
D: Exception handling improvements
|
||||
D: Type legalizer rewrite
|
||||
|
||||
N: Ruchira Sasanka
|
||||
E: sasanka@uiuc.edu
|
||||
D: Graph coloring register allocator for the Sparc64 backend
|
||||
|
||||
N: Arnold Schwaighofer
|
||||
E: arnold.schwaighofer@gmail.com
|
||||
D: Tail call optimization for the x86 backend
|
||||
|
||||
N: Shantonu Sen
|
||||
E: ssen@apple.com
|
||||
D: Miscellaneous bug fixes
|
||||
|
||||
N: Anand Shukla
|
||||
E: ashukla@cs.uiuc.edu
|
||||
D: The `paths' pass
|
||||
|
||||
N: Michael J. Spencer
|
||||
E: bigcheesegs@gmail.com
|
||||
D: Shepherding Windows COFF support into MC.
|
||||
D: Lots of Windows stuff.
|
||||
|
||||
N: Reid Spencer
|
||||
E: rspencer@reidspencer.com
|
||||
W: http://reidspencer.com/
|
||||
D: Lots of stuff, see: http://wiki.llvm.org/index.php/User:Reid
|
||||
|
||||
N: Alp Toker
|
||||
E: alp@nuanti.com
|
||||
W: http://atoker.com/
|
||||
D: C++ frontend next generation standards implementation
|
||||
|
||||
N: Craig Topper
|
||||
E: craig.topper@gmail.com
|
||||
D: X86 codegen and disassembler improvements. AVX2 support.
|
||||
|
||||
N: Edwin Torok
|
||||
E: edwintorok@gmail.com
|
||||
D: Miscellaneous bug fixes
|
||||
|
||||
N: Adam Treat
|
||||
E: manyoso@yahoo.com
|
||||
D: C++ bugs filed, and C++ front-end bug fixes.
|
||||
|
||||
N: Lauro Ramos Venancio
|
||||
E: lauro.venancio@indt.org.br
|
||||
D: ARM backend improvements
|
||||
D: Thread Local Storage implementation
|
||||
|
||||
N: Bill Wendling
|
||||
I: wendling
|
||||
E: isanbard@gmail.com
|
||||
D: Release manager, IR Linker, LTO
|
||||
D: Bunches of stuff
|
||||
|
||||
N: Bob Wilson
|
||||
E: bob.wilson@acm.org
|
||||
D: Advanced SIMD (NEON) support in the ARM backend.
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
==============================================================================
|
||||
LLVM Release License
|
||||
==============================================================================
|
||||
University of Illinois/NCSA
|
||||
Open Source License
|
||||
|
||||
Copyright (c) 2003-2015 University of Illinois at Urbana-Champaign.
|
||||
All rights reserved.
|
||||
|
||||
Developed by:
|
||||
|
||||
LLVM Team
|
||||
|
||||
University of Illinois at Urbana-Champaign
|
||||
|
||||
http://llvm.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal with
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimers.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimers in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the names of the LLVM Team, University of Illinois at
|
||||
Urbana-Champaign, nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this Software without specific
|
||||
prior written permission.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
|
||||
SOFTWARE.
|
||||
|
||||
==============================================================================
|
||||
Copyrights and Licenses for Third Party Software Distributed with LLVM:
|
||||
==============================================================================
|
||||
The LLVM software contains code written by third parties. Such software will
|
||||
have its own individual LICENSE.TXT file in the directory in which it appears.
|
||||
This file will describe the copyrights, license, and restrictions which apply
|
||||
to that code.
|
||||
|
||||
The disclaimer of warranty in the University of Illinois Open Source License
|
||||
applies to all code in the LLVM Distribution, and nothing in any of the
|
||||
other licenses gives permission to use the names of the LLVM Team or the
|
||||
University of Illinois to endorse or promote products derived from this
|
||||
Software.
|
||||
|
||||
The following pieces of software have additional or alternate copyrights,
|
||||
licenses, and/or restrictions:
|
||||
|
||||
Program Directory
|
||||
------- ---------
|
||||
Autoconf llvm/autoconf
|
||||
llvm/projects/ModuleMaker/autoconf
|
||||
Google Test llvm/utils/unittest/googletest
|
||||
OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex}
|
||||
pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT}
|
||||
ARM contributions llvm/lib/Target/ARM/LICENSE.TXT
|
||||
md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -5,7 +5,7 @@ set exe_name=odin.exe
|
||||
|
||||
:: Debug = 0, Release = 1
|
||||
set release_mode=0
|
||||
set compiler_flags= -nologo -Oi -TC -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||
set compiler_flags= -nologo -Oi -TP -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_flags=%compiler_flags% -Od -MDd -Z7
|
||||
@@ -17,10 +17,13 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_warnings= ^
|
||||
-W4 -WX ^
|
||||
-wd4100 -wd4101 -wd4127 -wd4189 ^
|
||||
-wd4201 -wd4204 -wd4244 ^
|
||||
-wd4306 ^
|
||||
-wd4456 -wd4457 -wd4480 ^
|
||||
-wd4505 -wd4512 -wd4550
|
||||
-wd4201 ^
|
||||
-wd4512
|
||||
rem -wd4100 -wd4101 -wd4127 -wd4189 ^
|
||||
rem -wd4201 -wd4204 -wd4244 ^
|
||||
rem -wd4306 ^
|
||||
rem -wd4456 -wd4457 -wd4480 ^
|
||||
rem -wd4505 -wd4512 -wd4550
|
||||
|
||||
set compiler_includes=
|
||||
set libs= ^
|
||||
@@ -38,21 +41,15 @@ if %release_mode% EQU 0 ( rem Debug
|
||||
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
|
||||
set linker_settings=%libs% %linker_flags%
|
||||
|
||||
|
||||
del *.pdb > NUL 2> NUL
|
||||
del *.ilk > NUL 2> NUL
|
||||
|
||||
cl %compiler_settings% "src\main.c" ^
|
||||
cl %compiler_settings% "src\main.cpp" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
&& odin run code/demo.odin
|
||||
rem && odin build_dll code/example.odin ^
|
||||
rem odin run code/punity.odin
|
||||
&& odin run code/demo.odin -opt=0
|
||||
rem && odin docs core/fmt.odin
|
||||
|
||||
rem pushd src\asm
|
||||
rem nasm hellope.asm -fwin64 -o hellope.obj ^
|
||||
rem && cl /nologo hellope.obj /link kernel32.lib /entry:main ^
|
||||
rem && hellope.exe
|
||||
rem popd
|
||||
del *.obj > NUL 2> NUL
|
||||
|
||||
:end_of_build
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
|
||||
release_mode=0
|
||||
|
||||
warnings_to_disable="-std=c++11 -g -Wno-switch -Wno-pointer-sign -Wno-tautological-constant-out-of-range-compare -Wno-tautological-compare -Wno-macro-redefined -Wno-writable-strings"
|
||||
libraries="-pthread -ldl -lm -lstdc++"
|
||||
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.cpp ${warnings_to_disable} ${libraries} ${other_args} -o odin
|
||||
|
||||
./odin run code/demo.odin
|
||||
+571
-253
@@ -1,283 +1,601 @@
|
||||
#import "fmt.odin";
|
||||
#import "utf8.odin";
|
||||
// #import "atomic.odin";
|
||||
// #import "hash.odin";
|
||||
// #import "math.odin";
|
||||
// #import "mem.odin";
|
||||
// #import "opengl.odin";
|
||||
// #import "os.odin";
|
||||
// #import "sync.odin";
|
||||
// #import win32 "sys/windows.odin";
|
||||
|
||||
main :: proc() {
|
||||
syntax();
|
||||
}
|
||||
import (
|
||||
"fmt.odin";
|
||||
"strconv.odin";
|
||||
"mem.odin";
|
||||
"thread.odin" when ODIN_OS == "windows";
|
||||
win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
|
||||
syntax :: proc() {
|
||||
// Cyclic type checking
|
||||
// Uncomment to see the error
|
||||
// A :: struct {b: B};
|
||||
// B :: struct {a: A};
|
||||
/*
|
||||
"atomics.odin";
|
||||
"bits.odin";
|
||||
"hash.odin";
|
||||
"math.odin";
|
||||
"opengl.odin";
|
||||
"os.odin";
|
||||
"raw.odin";
|
||||
"sort.odin";
|
||||
"strings.odin";
|
||||
"sync.odin";
|
||||
"types.odin";
|
||||
"utf8.odin";
|
||||
"utf16.odin";
|
||||
*/
|
||||
)
|
||||
|
||||
x: int;
|
||||
y := cast(f32)x;
|
||||
z := transmute(u32)y;
|
||||
// down_cast, union_cast as similar too
|
||||
general_stuff :: proc() {
|
||||
{ // `do` for inline statmes rather than block
|
||||
foo :: proc() do fmt.println("Foo!");
|
||||
if false do foo();
|
||||
for false do foo();
|
||||
when false do foo();
|
||||
|
||||
|
||||
|
||||
// Basic directives
|
||||
fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure);
|
||||
// NOTE: new and improved `printf`
|
||||
// TODO: It does need accurate float printing
|
||||
|
||||
|
||||
|
||||
// record fields use the same syntax a procedure signatures
|
||||
Thing1 :: struct {
|
||||
x: f32,
|
||||
y: int,
|
||||
z: ^[]int,
|
||||
};
|
||||
Thing2 :: struct {x: f32, y: int, z: ^[]int};
|
||||
|
||||
// Slice interals are now just a `ptr+count`
|
||||
slice: []int; compile_assert(size_of_val(slice) == 2*size_of(int));
|
||||
|
||||
// Helper type - Help the reader understand what it is quicker
|
||||
My_Int :: type int;
|
||||
My_Proc :: type proc(int) -> f32;
|
||||
|
||||
|
||||
// All declarations with : are either variable or constant
|
||||
// To make these declarations syntactically consistent
|
||||
v_variable := 123;
|
||||
c_constant :: 123;
|
||||
c_type1 :: int;
|
||||
c_type2 :: []int;
|
||||
c_proc :: proc() { /* code here */ };
|
||||
|
||||
|
||||
x += 1;
|
||||
x -= 1;
|
||||
// ++ and -- have been removed
|
||||
// x++;
|
||||
// x--;
|
||||
// Question: Should they be added again?
|
||||
// They were removed as they are redundant and statements, not expressions
|
||||
// like in C/C++
|
||||
|
||||
|
||||
// You can now build files as a `.dll`
|
||||
// `odin build_dll demo.odin`
|
||||
|
||||
|
||||
// New vector syntax
|
||||
v: [vector 3]f32;
|
||||
v[0] = 123;
|
||||
v.x = 123; // valid for all vectors with count 1 to 4
|
||||
|
||||
|
||||
// Next part
|
||||
prefixes();
|
||||
}
|
||||
|
||||
|
||||
Prefix_Type :: struct {x: int, y: f32, z: rawptr};
|
||||
|
||||
thread_local my_tls: Prefix_Type;
|
||||
|
||||
prefixes :: proc() {
|
||||
using var: Prefix_Type;
|
||||
immutable const := Prefix_Type{1, 2, nil};
|
||||
var.x = 123;
|
||||
x = 123;
|
||||
// const.x = 123; // const is immutable
|
||||
|
||||
|
||||
|
||||
foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
|
||||
// int_ptr = nil; // Not valid
|
||||
int_ptr^ = 123; // Is valid
|
||||
if false do foo();
|
||||
else do foo();
|
||||
}
|
||||
|
||||
{ // Removal of `++` and `--` (again)
|
||||
x: int;
|
||||
x += 1;
|
||||
x -= 1;
|
||||
}
|
||||
{ // Casting syntaxes
|
||||
i := i32(137);
|
||||
ptr := &i;
|
||||
|
||||
fp1 := (^f32)(ptr);
|
||||
// ^f32(ptr) == ^(f32(ptr))
|
||||
fp2 := cast(^f32)ptr;
|
||||
|
||||
// Same as C99's `restrict`
|
||||
bar :: proc(no_alias a, b: ^int) {
|
||||
// Assumes a never equals b so it can perform optimizations with that fact
|
||||
f1 := (^f32)(ptr)^;
|
||||
f2 := (cast(^f32)ptr)^;
|
||||
|
||||
// Questions: Should there be two ways to do it?
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove *_val_of built-in procedures
|
||||
* size_of, align_of, offset_of
|
||||
* type_of, type_info_of
|
||||
*/
|
||||
|
||||
when_statements();
|
||||
{ // `expand_to_tuple` built-in procedure
|
||||
Foo :: struct {
|
||||
x: int;
|
||||
b: bool;
|
||||
}
|
||||
f := Foo{137, true};
|
||||
x, b := expand_to_tuple(f);
|
||||
fmt.println(f);
|
||||
fmt.println(x, b);
|
||||
fmt.println(expand_to_tuple(f));
|
||||
}
|
||||
|
||||
{
|
||||
// .. half-closed range
|
||||
// ... open range
|
||||
|
||||
for in 0..2 {} // 0, 1
|
||||
for in 0...2 {} // 0, 1, 2
|
||||
}
|
||||
}
|
||||
|
||||
nested_struct_declarations :: proc() {
|
||||
{
|
||||
FooInteger :: int;
|
||||
Foo :: struct {
|
||||
i: FooInteger;
|
||||
};
|
||||
f := Foo{FooInteger(137)};
|
||||
}
|
||||
{
|
||||
Foo :: struct {
|
||||
Integer :: int;
|
||||
|
||||
i: Integer;
|
||||
}
|
||||
f := Foo{Foo.Integer(137)};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
default_struct_values :: proc() {
|
||||
{
|
||||
Vector3 :: struct {
|
||||
x: f32;
|
||||
y: f32;
|
||||
z: f32;
|
||||
}
|
||||
v: Vector3;
|
||||
fmt.println(v);
|
||||
}
|
||||
{
|
||||
// Default values must be constants
|
||||
Vector3 :: struct {
|
||||
x: f32 = 1;
|
||||
y: f32 = 4;
|
||||
z: f32 = 9;
|
||||
}
|
||||
v: Vector3;
|
||||
fmt.println(v);
|
||||
|
||||
v = Vector3{};
|
||||
fmt.println(v);
|
||||
|
||||
// Uses the same semantics as a default values in a procedure
|
||||
v = Vector3{137};
|
||||
fmt.println(v);
|
||||
|
||||
v = Vector3{z = 137};
|
||||
fmt.println(v);
|
||||
}
|
||||
|
||||
{
|
||||
Vector3 :: struct {
|
||||
x := 1.0;
|
||||
y := 4.0;
|
||||
z := 9.0;
|
||||
}
|
||||
stack_default: Vector3;
|
||||
stack_literal := Vector3{};
|
||||
heap_one := new(Vector3); defer free(heap_one);
|
||||
heap_two := new_clone(Vector3{}); defer free(heap_two);
|
||||
|
||||
fmt.println("stack_default - ", stack_default);
|
||||
fmt.println("stack_literal - ", stack_literal);
|
||||
fmt.println("heap_one - ", heap_one^);
|
||||
fmt.println("heap_two - ", heap_two^);
|
||||
|
||||
|
||||
N :: 4;
|
||||
stack_array: [N]Vector3;
|
||||
heap_array := new([N]Vector3); defer free(heap_array);
|
||||
heap_slice := make([]Vector3, N); defer free(heap_slice);
|
||||
fmt.println("stack_array[1] - ", stack_array[1]);
|
||||
fmt.println("heap_array[1] - ", heap_array[1]);
|
||||
fmt.println("heap_slice[1] - ", heap_slice[1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
union_type :: proc() {
|
||||
{
|
||||
val: union{int, bool};
|
||||
val = 137;
|
||||
if i, ok := val.(int); ok {
|
||||
fmt.println(i);
|
||||
}
|
||||
val = true;
|
||||
fmt.println(val);
|
||||
|
||||
when_statements :: proc() {
|
||||
X :: 123 + 12;
|
||||
Y :: X/5;
|
||||
COND :: Y > 0;
|
||||
val = nil;
|
||||
|
||||
when COND {
|
||||
fmt.println("Y > 0");
|
||||
} else {
|
||||
fmt.println("Y <= 0");
|
||||
match v in val {
|
||||
case int: fmt.println("int", v);
|
||||
case bool: fmt.println("bool", v);
|
||||
case: fmt.println("nil");
|
||||
}
|
||||
}
|
||||
{
|
||||
// There is a duality between `any` and `union`
|
||||
// An `any` has a pointer to the data and allows for any type (open)
|
||||
// A `union` has as binary blob to store the data and allows only certain types (closed)
|
||||
// The following code is with `any` but has the same syntax
|
||||
val: any;
|
||||
val = 137;
|
||||
if i, ok := val.(int); ok {
|
||||
fmt.println(i);
|
||||
}
|
||||
val = true;
|
||||
fmt.println(val);
|
||||
|
||||
val = nil;
|
||||
|
||||
when false {
|
||||
this_code_does_not_exist(123, 321);
|
||||
but_its_syntax_is_valid();
|
||||
x :: ^^^^int;
|
||||
}
|
||||
|
||||
foreign_procedures();
|
||||
}
|
||||
|
||||
#foreign_system_library win32_user "user32.lib" when ODIN_OS == "windows";
|
||||
// NOTE: This is done on purpose for two reasons:
|
||||
// * Makes it clear where the platform specific stuff is
|
||||
// * Removes the need to solve the travelling salesman problem when importing files :P
|
||||
|
||||
foreign_procedures :: proc() {
|
||||
ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user;
|
||||
show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user "ShowWindow";
|
||||
// NOTE: If that library doesn't get used, it doesn't get linked with
|
||||
// NOTE: There is not link checking yet to see if that procedure does come from that library
|
||||
|
||||
// See sys/windows.odin for more examples
|
||||
|
||||
special_expressions();
|
||||
}
|
||||
|
||||
special_expressions :: proc() {
|
||||
// Block expression
|
||||
x := {
|
||||
a: f32 = 123;
|
||||
b := a-123;
|
||||
c := b/a;
|
||||
give c;
|
||||
}; // semicolon is required as it's an expression
|
||||
|
||||
y := if x < 50 {
|
||||
give x;
|
||||
} else {
|
||||
// TODO: Type cohesion is not yet finished
|
||||
give 123;
|
||||
}; // semicolon is required as it's an expression
|
||||
|
||||
|
||||
// This is allows for inline blocks of code and will be a useful feature to have when
|
||||
// macros will be implemented into the language
|
||||
|
||||
loops();
|
||||
}
|
||||
|
||||
loops :: proc() {
|
||||
// The C-style for loop
|
||||
for i := 0; i < 123; i += 1 {
|
||||
break;
|
||||
}
|
||||
for i := 0; i < 123; {
|
||||
break;
|
||||
}
|
||||
for false {
|
||||
break;
|
||||
}
|
||||
for {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
for i in 0..<123 { // 123 exclusive
|
||||
}
|
||||
|
||||
for i in 0...122 { // 122 inclusive
|
||||
}
|
||||
|
||||
for val, idx in 12..<16 {
|
||||
fmt.println(val, idx);
|
||||
}
|
||||
|
||||
primes := [...]int{2, 3, 5, 7, 11, 13, 17, 19};
|
||||
|
||||
for p in primes {
|
||||
fmt.println(p);
|
||||
}
|
||||
|
||||
// Pointers to arrays, slices, or strings are allowed
|
||||
for _ in ^primes {
|
||||
// ignore the value and just iterate across it
|
||||
}
|
||||
|
||||
|
||||
|
||||
name := "你好,世界";
|
||||
fmt.println(name);
|
||||
for r in name {
|
||||
compile_assert(type_of_val(r) == rune);
|
||||
fmt.printf("%r\n", r);
|
||||
}
|
||||
|
||||
when false {
|
||||
for i, size := 0; i < name.count; i += size {
|
||||
r: rune;
|
||||
r, size = utf8.decode_rune(name[i:]);
|
||||
fmt.printf("%r\n", r);
|
||||
match v in val {
|
||||
case int: fmt.println("int", v);
|
||||
case bool: fmt.println("bool", v);
|
||||
case: fmt.println("nil");
|
||||
}
|
||||
}
|
||||
|
||||
procedure_overloading();
|
||||
Vector3 :: struct {
|
||||
x, y, z: f32;
|
||||
};
|
||||
Quaternion :: struct {
|
||||
x, y, z: f32;
|
||||
w: f32 = 1;
|
||||
};
|
||||
|
||||
// More realistic examples
|
||||
{
|
||||
// NOTE(bill): For the above basic examples, you may not have any
|
||||
// particular use for it. However, my main use for them is not for these
|
||||
// simple cases. My main use is for hierarchical types. Many prefer
|
||||
// subtyping, embedding the base data into the derived types. Below is
|
||||
// an example of this for a basic game Entity.
|
||||
|
||||
Entity :: struct {
|
||||
id: u64;
|
||||
name: string;
|
||||
position: Vector3;
|
||||
orientation: Quaternion;
|
||||
|
||||
derived: any;
|
||||
}
|
||||
|
||||
Frog :: struct {
|
||||
using entity: Entity;
|
||||
jump_height: f32;
|
||||
}
|
||||
|
||||
Monster :: struct {
|
||||
using entity: Entity;
|
||||
is_robot: bool;
|
||||
is_zombie: bool;
|
||||
}
|
||||
|
||||
// See `parametric_polymorphism` procedure for details
|
||||
new_entity :: proc(T: type) -> ^Entity {
|
||||
t := new(T);
|
||||
t.derived = t^;
|
||||
return t;
|
||||
}
|
||||
|
||||
entity := new_entity(Monster);
|
||||
|
||||
match e in entity.derived {
|
||||
case Frog:
|
||||
fmt.println("Ribbit");
|
||||
case Monster:
|
||||
if e.is_robot do fmt.println("Robotic");
|
||||
if e.is_zombie do fmt.println("Grrrr!");
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// NOTE(bill): A union can be used to achieve something similar. Instead
|
||||
// of embedding the base data into the derived types, the derived data
|
||||
// in embedded into the base type. Below is the same example of the
|
||||
// basic game Entity but using an union.
|
||||
|
||||
Entity :: struct {
|
||||
id: u64;
|
||||
name: string;
|
||||
position: Vector3;
|
||||
orientation: Quaternion;
|
||||
|
||||
derived: union {Frog, Monster};
|
||||
}
|
||||
|
||||
Frog :: struct {
|
||||
using entity: ^Entity;
|
||||
jump_height: f32;
|
||||
}
|
||||
|
||||
Monster :: struct {
|
||||
using entity: ^Entity;
|
||||
is_robot: bool;
|
||||
is_zombie: bool;
|
||||
}
|
||||
|
||||
// See `parametric_polymorphism` procedure for details
|
||||
new_entity :: proc(T: type) -> ^Entity {
|
||||
t := new(Entity);
|
||||
t.derived = T{entity = t};
|
||||
return t;
|
||||
}
|
||||
|
||||
entity := new_entity(Monster);
|
||||
|
||||
match e in entity.derived {
|
||||
case Frog:
|
||||
fmt.println("Ribbit");
|
||||
case Monster:
|
||||
if e.is_robot do fmt.println("Robotic");
|
||||
if e.is_zombie do fmt.println("Grrrr!");
|
||||
}
|
||||
|
||||
// NOTE(bill): As you can see, the usage code has not changed, only its
|
||||
// memory layout. Both approaches have their own advantages but they can
|
||||
// be used together to achieve different results. The subtyping approach
|
||||
// can allow for a greater control of the memory layout and memory
|
||||
// allocation, e.g. storing the derivatives together. However, this is
|
||||
// also its disadvantage. You must either preallocate arrays for each
|
||||
// derivative separation (which can be easily missed) or preallocate a
|
||||
// bunch of "raw" memory; determining the maximum size of the derived
|
||||
// types would require the aid of metaprogramming. Unions solve this
|
||||
// particular problem as the data is stored with the base data.
|
||||
// Therefore, it is possible to preallocate, e.g. [100]Entity.
|
||||
|
||||
// It should be noted that the union approach can have the same memory
|
||||
// layout as the any and with the same type restrictions by using a
|
||||
// pointer type for the derivatives.
|
||||
|
||||
/*
|
||||
Entity :: struct {
|
||||
...
|
||||
derived: union{^Frog, ^Monster};
|
||||
}
|
||||
|
||||
Frog :: struct {
|
||||
using entity: Entity;
|
||||
...
|
||||
}
|
||||
Monster :: struct {
|
||||
using entity: Entity;
|
||||
...
|
||||
|
||||
}
|
||||
new_entity :: proc(T: type) -> ^Entity {
|
||||
t := new(T);
|
||||
t.derived = t;
|
||||
return t;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
parametric_polymorphism :: proc() {
|
||||
print_value :: proc(value: $T) {
|
||||
fmt.printf("print_value: %T %v\n", value, value);
|
||||
}
|
||||
|
||||
v1: int = 1;
|
||||
v2: f32 = 2.1;
|
||||
v3: f64 = 3.14;
|
||||
v4: string = "message";
|
||||
|
||||
print_value(v1);
|
||||
print_value(v2);
|
||||
print_value(v3);
|
||||
print_value(v4);
|
||||
|
||||
fmt.println();
|
||||
|
||||
add :: proc(p, q: $T) -> T {
|
||||
x: T = p + q;
|
||||
return x;
|
||||
}
|
||||
|
||||
a := add(3, 4);
|
||||
fmt.printf("a: %T = %v\n", a, a);
|
||||
|
||||
b := add(3.2, 4.3);
|
||||
fmt.printf("b: %T = %v\n", b, b);
|
||||
|
||||
// This is how `new` is implemented
|
||||
alloc_type :: proc(T: type) -> ^T {
|
||||
t := cast(^T)alloc(size_of(T), align_of(T));
|
||||
t^ = T{}; // Use default initialization value
|
||||
return t;
|
||||
}
|
||||
|
||||
copy_slice :: proc(dst, src: []$T) -> int {
|
||||
n := min(len(dst), len(src));
|
||||
if n > 0 {
|
||||
mem.copy(&dst[0], &src[0], n*size_of(T));
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
double_params :: proc(a: $A, b: $B) -> A {
|
||||
return a + A(b);
|
||||
}
|
||||
|
||||
fmt.println(double_params(12, 1.345));
|
||||
|
||||
|
||||
|
||||
{ // Polymorphic Types and Type Specialization
|
||||
Table :: struct(Key, Value: type) {
|
||||
Slot :: struct {
|
||||
occupied: bool;
|
||||
hash: u32;
|
||||
key: Key;
|
||||
value: Value;
|
||||
}
|
||||
SIZE_MIN :: 32;
|
||||
|
||||
count: int;
|
||||
allocator: Allocator;
|
||||
slots: []Slot;
|
||||
}
|
||||
|
||||
// Only allow types that are specializations of a (polymorphic) slice
|
||||
make_slice :: proc(T: type/[]$E, len: int) -> T {
|
||||
return make(T, len);
|
||||
}
|
||||
|
||||
|
||||
// Only allow types that are specializations of `Table`
|
||||
allocate :: proc(table: ^$T/Table, capacity: int) {
|
||||
c := context;
|
||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
||||
|
||||
push_context c {
|
||||
table.slots = make_slice([]T.Slot, max(capacity, T.SIZE_MIN));
|
||||
}
|
||||
}
|
||||
|
||||
expand :: proc(table: ^$T/Table) {
|
||||
c := context;
|
||||
if table.allocator.procedure != nil do c.allocator = table.allocator;
|
||||
|
||||
push_context c {
|
||||
old_slots := table.slots;
|
||||
|
||||
cap := max(2*cap(table.slots), T.SIZE_MIN);
|
||||
allocate(table, cap);
|
||||
|
||||
for s in old_slots do if s.occupied {
|
||||
put(table, s.key, s.value);
|
||||
}
|
||||
|
||||
free(old_slots);
|
||||
}
|
||||
}
|
||||
|
||||
// Polymorphic determination of a polymorphic struct
|
||||
// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
|
||||
put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
|
||||
hash := get_hash(key); // Ad-hoc method which would fail in a different scope
|
||||
index := find_index(table, key, hash);
|
||||
if index < 0 {
|
||||
if f64(table.count) >= 0.75*f64(cap(table.slots)) {
|
||||
expand(table);
|
||||
}
|
||||
assert(table.count <= cap(table.slots));
|
||||
|
||||
hash := get_hash(key);
|
||||
index = int(hash % u32(cap(table.slots)));
|
||||
|
||||
for table.slots[index].occupied {
|
||||
if index += 1; index >= cap(table.slots) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
table.count += 1;
|
||||
}
|
||||
|
||||
slot := &table.slots[index];
|
||||
slot.occupied = true;
|
||||
slot.hash = hash;
|
||||
slot.key = key;
|
||||
slot.value = value;
|
||||
}
|
||||
|
||||
|
||||
// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
|
||||
find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
|
||||
hash := get_hash(key);
|
||||
index := find_index(table, key, hash);
|
||||
if index < 0 {
|
||||
return Value{}, false;
|
||||
}
|
||||
return table.slots[index].value, true;
|
||||
}
|
||||
|
||||
find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
|
||||
if cap(table.slots) <= 0 do return -1;
|
||||
|
||||
index := int(hash % u32(cap(table.slots)));
|
||||
for table.slots[index].occupied {
|
||||
if table.slots[index].hash == hash {
|
||||
if table.slots[index].key == key {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
|
||||
if index += 1; index >= cap(table.slots) {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
get_hash :: proc(s: string) -> u32 { // fnv32a
|
||||
h: u32 = 0x811c9dc5;
|
||||
for i in 0..len(s) {
|
||||
h = (h ~ u32(s[i])) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
|
||||
table: Table(string, int);
|
||||
|
||||
for i in 0..36 do put(&table, "Hellope", i);
|
||||
for i in 0..42 do put(&table, "World!", i);
|
||||
|
||||
found, _ := find(&table, "Hellope");
|
||||
fmt.printf("`found` is %v\n", found);
|
||||
|
||||
found, _ = find(&table, "World!");
|
||||
fmt.printf("`found` is %v\n", found);
|
||||
|
||||
// I would not personally design a hash table like this in production
|
||||
// but this is a nice basic example
|
||||
// A better approach would either use a `u64` or equivalent for the key
|
||||
// and let the user specify the hashing function or make the user store
|
||||
// the hashing procedure with the table
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
procedure_overloading :: proc() {
|
||||
THINGF :: 14451.1;
|
||||
THINGI :: 14451;
|
||||
|
||||
foo :: proc() {
|
||||
fmt.printf("Zero args\n");
|
||||
|
||||
prefix_table := [...]string{
|
||||
"White",
|
||||
"Red",
|
||||
"Green",
|
||||
"Blue",
|
||||
"Octarine",
|
||||
"Black",
|
||||
};
|
||||
|
||||
threading_example :: proc() {
|
||||
when ODIN_OS == "windows" {
|
||||
unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
|
||||
__bounds_check_error_loc(loc, index, len(array));
|
||||
array[index] = array[len(array)-1];
|
||||
pop(array);
|
||||
}
|
||||
ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
|
||||
__bounds_check_error_loc(loc, index, len(array));
|
||||
copy(array[index..], array[index+1..]);
|
||||
pop(array);
|
||||
}
|
||||
|
||||
worker_proc :: proc(t: ^thread.Thread) -> int {
|
||||
for iteration in 1...5 {
|
||||
fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
|
||||
fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
|
||||
// win32.sleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
threads := make([]^thread.Thread, 0, len(prefix_table));
|
||||
defer free(threads);
|
||||
|
||||
for i in 0..len(prefix_table) {
|
||||
if t := thread.create(worker_proc); t != nil {
|
||||
t.init_context = context;
|
||||
t.use_init_context = true;
|
||||
t.user_index = len(threads);
|
||||
append(&threads, t);
|
||||
thread.start(t);
|
||||
}
|
||||
}
|
||||
|
||||
for len(threads) > 0 {
|
||||
for i := 0; i < len(threads); {
|
||||
if t := threads[i]; thread.is_done(t) {
|
||||
fmt.printf("Thread %d is done\n", t.user_index);
|
||||
thread.destroy(t);
|
||||
|
||||
ordered_remove(&threads, i);
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
foo :: proc(i: int) {
|
||||
fmt.printf("int arg, i=%d\n", i);
|
||||
}
|
||||
foo :: proc(f: f64) {
|
||||
i := cast(int)f;
|
||||
fmt.printf("f64 arg, f=%d\n", i);
|
||||
}
|
||||
|
||||
foo();
|
||||
foo(THINGF);
|
||||
// foo(THINGI); // 14451 is just a number so it could go to either procedures
|
||||
foo(cast(int)THINGI);
|
||||
|
||||
|
||||
|
||||
|
||||
foo :: proc(x: ^i32) -> (int, int) {
|
||||
fmt.println("^int");
|
||||
return 123, cast(int)(x^);
|
||||
}
|
||||
foo :: proc(x: rawptr) {
|
||||
fmt.println("rawptr");
|
||||
}
|
||||
|
||||
|
||||
a: i32 = 123;
|
||||
b: f32;
|
||||
c: rawptr;
|
||||
fmt.println(foo(^a));
|
||||
foo(^b);
|
||||
foo(c);
|
||||
// foo(nil); // nil could go to numerous types thus the ambiguity
|
||||
|
||||
f: proc();
|
||||
f = foo; // The correct `foo` to chosen
|
||||
f();
|
||||
|
||||
|
||||
// See math.odin and atomic.odin for more examples
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
when true {
|
||||
fmt.println("\n# general_stuff"); general_stuff();
|
||||
fmt.println("\n# nested_struct_declarations"); nested_struct_declarations();
|
||||
fmt.println("\n# default_struct_values"); default_struct_values();
|
||||
fmt.println("\n# union_type"); union_type();
|
||||
fmt.println("\n# parametric_polymorphism"); parametric_polymorphism();
|
||||
fmt.println("\n# threading_example"); threading_example();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,430 @@
|
||||
import (
|
||||
"fmt.odin";
|
||||
"atomics.odin";
|
||||
"bits.odin";
|
||||
"decimal.odin";
|
||||
"hash.odin";
|
||||
"math.odin";
|
||||
"mem.odin";
|
||||
"opengl.odin";
|
||||
"os.odin";
|
||||
"raw.odin";
|
||||
"strconv.odin";
|
||||
"strings.odin";
|
||||
"sync.odin";
|
||||
"sort.odin";
|
||||
"types.odin";
|
||||
"utf8.odin";
|
||||
"utf16.odin";
|
||||
/*
|
||||
*/
|
||||
)
|
||||
|
||||
|
||||
general_stuff :: proc() {
|
||||
// Complex numbers
|
||||
a := 3 + 4i;
|
||||
b: complex64 = 3 + 4i;
|
||||
c: complex128 = 3 + 4i;
|
||||
d := complex(2, 3);
|
||||
|
||||
e := a / conj(a);
|
||||
fmt.println("(3+4i)/(3-4i) =", e);
|
||||
fmt.println(real(e), "+", imag(e), "i");
|
||||
|
||||
|
||||
// C-style variadic procedures
|
||||
foreign __llvm_core {
|
||||
// The variadic part allows for extra type checking too which C does not provide
|
||||
c_printf :: proc(fmt: ^u8, #c_vararg args: ...any) -> i32 #link_name "printf" ---;
|
||||
}
|
||||
str := "%d\n\x00";
|
||||
// c_printf(&str[0], i32(789456123));
|
||||
|
||||
|
||||
Foo :: struct {
|
||||
x: int;
|
||||
y: f32;
|
||||
z: string;
|
||||
}
|
||||
foo := Foo{123, 0.513, "A string"};
|
||||
x, y, z := expand_to_tuple(foo);
|
||||
fmt.println(x, y, z);
|
||||
compile_assert(type_of(x) == int);
|
||||
compile_assert(type_of(y) == f32);
|
||||
compile_assert(type_of(z) == string);
|
||||
|
||||
|
||||
// By default, all variables are zeroed
|
||||
// This can be overridden with the "uninitialized value"
|
||||
// This is similar to `nil` but applied to everything
|
||||
undef_int: int = ---;
|
||||
|
||||
|
||||
// Context system is now implemented using Implicit Parameter Passing (IPP)
|
||||
// The previous implementation was Thread Local Storage (TLS)
|
||||
// IPP has the advantage that it works on systems without TLS and that you can
|
||||
// link the context to the stack frame and thus look at previous contexts
|
||||
//
|
||||
// It does mean that a pointer is implicitly passed procedures with the default
|
||||
// Odin calling convention (#cc_odin)
|
||||
// This can be overridden with something like #cc_contextless or #cc_c if performance
|
||||
// is worried about
|
||||
|
||||
}
|
||||
|
||||
foreign_blocks :: proc() {
|
||||
// See sys/windows.odin
|
||||
}
|
||||
|
||||
default_arguments :: proc() {
|
||||
hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b);
|
||||
fmt.println("\nTesting default arguments:");
|
||||
hello(1, 2);
|
||||
hello(1);
|
||||
hello();
|
||||
}
|
||||
|
||||
named_arguments :: proc() {
|
||||
Colour :: enum {
|
||||
Red,
|
||||
Orange,
|
||||
Yellow,
|
||||
Green,
|
||||
Blue,
|
||||
Octarine,
|
||||
};
|
||||
using Colour;
|
||||
|
||||
make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
|
||||
fmt.println();
|
||||
fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase);
|
||||
}
|
||||
|
||||
make_character("Frank", "¡Ay, caramba!", Blue, Green);
|
||||
|
||||
|
||||
// As the procedures have more and more parameters, it is very easy
|
||||
// to get many of the arguments in the wrong order especialy if the
|
||||
// types are the same
|
||||
make_character("¡Ay, caramba!", "Frank", Green, Blue);
|
||||
|
||||
// Named arguments help to disambiguate this problem
|
||||
make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
|
||||
least_favourite_colour = Green, favourite_colour = Blue);
|
||||
|
||||
|
||||
// The named arguments can be specifed in any order.
|
||||
make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
|
||||
least_favourite_colour = Green, name = "Dennis");
|
||||
|
||||
|
||||
// NOTE: You cannot mix named arguments with normal values
|
||||
/*
|
||||
make_character("Dennis",
|
||||
favourite_colour = Octarine, catch_phrase = "U wot m8!",
|
||||
least_favourite_colour = Green);
|
||||
*/
|
||||
|
||||
|
||||
// Named arguments can also aid with default arguments
|
||||
numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14,
|
||||
d := "The Best String!", e := false, f := 10.3/3.1, g := false) {
|
||||
g_str := g ? "true" : "false";
|
||||
fmt.printf("How many?! %s: %v\n", s, g_str);
|
||||
}
|
||||
|
||||
numerous_things("First");
|
||||
numerous_things(s = "Second", g = true);
|
||||
|
||||
|
||||
// Default values can be placed anywhere, not just at the end like in other languages
|
||||
weird :: proc(pre: string, mid: int = 0, post: string) {
|
||||
fmt.println(pre, mid, post);
|
||||
}
|
||||
|
||||
weird("How many things", 42, "huh?");
|
||||
weird(pre = "Prefix", post = "Pat");
|
||||
|
||||
}
|
||||
|
||||
|
||||
default_return_values :: proc() {
|
||||
foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") {
|
||||
match x {
|
||||
case 0: return;
|
||||
case 1: return "Goodbye";
|
||||
case 2: return "Goodbye", "cruel world...";
|
||||
case 3: return second = "cruel world...", first = "Goodbye";
|
||||
}
|
||||
|
||||
return second = "my old friend.";
|
||||
}
|
||||
|
||||
fmt.printf("%s %s\n", foo(0));
|
||||
fmt.printf("%s %s\n", foo(1));
|
||||
fmt.printf("%s %s\n", foo(2));
|
||||
fmt.printf("%s %s\n", foo(3));
|
||||
fmt.printf("%s %s\n", foo(4));
|
||||
fmt.println();
|
||||
|
||||
|
||||
// A more "real" example
|
||||
Error :: enum {
|
||||
None,
|
||||
WhyTheNumberThree,
|
||||
TenIsTooBig,
|
||||
};
|
||||
|
||||
Entity :: struct {
|
||||
name: string;
|
||||
id: u32;
|
||||
}
|
||||
|
||||
some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) {
|
||||
match {
|
||||
case input == 3: return err = Error.WhyTheNumberThree;
|
||||
case input >= 10: return err = Error.TenIsTooBig;
|
||||
}
|
||||
|
||||
e := new(Entity);
|
||||
e.id = u32(input);
|
||||
|
||||
return result = e;
|
||||
}
|
||||
}
|
||||
|
||||
call_location :: proc() {
|
||||
amazing :: proc(n: int, using loc := #caller_location) {
|
||||
fmt.printf("%s(%d:%d) just asked to do something amazing.\n",
|
||||
fully_pathed_filename, line, column);
|
||||
fmt.printf("Normal -> %d\n", n);
|
||||
fmt.printf("Amazing -> %d\n", n+1);
|
||||
fmt.println();
|
||||
}
|
||||
|
||||
loc := #location(main);
|
||||
fmt.println("`main` is located at", loc);
|
||||
|
||||
fmt.println("This line is located at", #location());
|
||||
fmt.println();
|
||||
|
||||
amazing(3);
|
||||
amazing(4, #location(call_location));
|
||||
|
||||
// See _preload.odin for the implementations of `assert` and `panic`
|
||||
|
||||
}
|
||||
|
||||
|
||||
explicit_parametric_polymorphic_procedures :: proc() {
|
||||
// This is how `new` is actually implemented, see _preload.odin
|
||||
alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T));
|
||||
|
||||
int_ptr := alloc_type(int);
|
||||
defer free(int_ptr);
|
||||
int_ptr^ = 137;
|
||||
fmt.println(int_ptr, int_ptr^);
|
||||
|
||||
// Named arguments work too!
|
||||
another_ptr := alloc_type(T = f32);
|
||||
defer free(another_ptr);
|
||||
|
||||
|
||||
add :: proc(T: type, args: ...T) -> T {
|
||||
res: T;
|
||||
for arg in args do res += arg;
|
||||
return res;
|
||||
}
|
||||
|
||||
fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
|
||||
|
||||
swap :: proc(T: type, a, b: ^T) {
|
||||
tmp := a^;
|
||||
a^ = b^;
|
||||
b^ = tmp;
|
||||
}
|
||||
|
||||
a, b: int = 3, 4;
|
||||
fmt.println("Pre-swap:", a, b);
|
||||
swap(int, &a, &b);
|
||||
fmt.println("Post-swap:", a, b);
|
||||
a, b = b, a; // Or use this syntax for this silly example case
|
||||
|
||||
|
||||
Vector2 :: struct {x, y: f32;};
|
||||
{
|
||||
// A more complicated example using subtyping
|
||||
// Something like this could be used in a game
|
||||
|
||||
Entity :: struct {
|
||||
using position: Vector2;
|
||||
flags: u64;
|
||||
id: u64;
|
||||
derived: any;
|
||||
}
|
||||
|
||||
Rock :: struct {
|
||||
using entity: Entity;
|
||||
heavy: bool;
|
||||
}
|
||||
Door :: struct {
|
||||
using entity: Entity;
|
||||
open: bool;
|
||||
}
|
||||
Monster :: struct {
|
||||
using entity: Entity;
|
||||
is_robot: bool;
|
||||
is_zombie: bool;
|
||||
}
|
||||
|
||||
new_entity :: proc(T: type, x, y: f32) -> ^T {
|
||||
result := new(T);
|
||||
result.derived = result^;
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
entities: [dynamic]^Entity;
|
||||
|
||||
rock := new_entity(Rock, 3, 5);
|
||||
|
||||
// Named arguments work too!
|
||||
door := new_entity(T = Door, x = 3, y = 6);
|
||||
|
||||
// And named arguments can be any order
|
||||
monster := new_entity(
|
||||
y = 1,
|
||||
x = 2,
|
||||
T = Monster,
|
||||
);
|
||||
|
||||
append(&entities, rock, door, monster);
|
||||
|
||||
fmt.println("Subtyping");
|
||||
for entity in entities {
|
||||
match e in entity.derived {
|
||||
case Rock: fmt.println("Rock", e.x, e.y);
|
||||
case Door: fmt.println("Door", e.x, e.y);
|
||||
case Monster: fmt.println("Monster", e.x, e.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
Entity :: struct {
|
||||
using position: Vector2;
|
||||
flags: u64;
|
||||
id: u64;
|
||||
variant: union { Rock, Door, Monster };
|
||||
}
|
||||
|
||||
Rock :: struct {
|
||||
using entity: ^Entity;
|
||||
heavy: bool;
|
||||
}
|
||||
Door :: struct {
|
||||
using entity: ^Entity;
|
||||
open: bool;
|
||||
}
|
||||
Monster :: struct {
|
||||
using entity: ^Entity;
|
||||
is_robot: bool;
|
||||
is_zombie: bool;
|
||||
}
|
||||
|
||||
new_entity :: proc(T: type, x, y: f32) -> ^T {
|
||||
result := new(Entity);
|
||||
result.variant = T{entity = result};
|
||||
result.x = x;
|
||||
result.y = y;
|
||||
|
||||
return cast(^T)&result.variant;
|
||||
}
|
||||
|
||||
entities: [dynamic]^Entity;
|
||||
|
||||
rock := new_entity(Rock, 3, 5);
|
||||
|
||||
// Named arguments work too!
|
||||
door := new_entity(T = Door, x = 3, y = 6);
|
||||
|
||||
// And named arguments can be any order
|
||||
monster := new_entity(
|
||||
y = 1,
|
||||
x = 2,
|
||||
T = Monster,
|
||||
);
|
||||
|
||||
append(&entities, rock, door, monster);
|
||||
|
||||
fmt.println("Union");
|
||||
for entity in entities {
|
||||
match e in entity.variant {
|
||||
case Rock: fmt.println("Rock", e.x, e.y);
|
||||
case Door: fmt.println("Door", e.x, e.y);
|
||||
case Monster: fmt.println("Monster", e.x, e.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
implicit_polymorphic_assignment :: proc() {
|
||||
yep :: proc(p: proc(x: int)) {
|
||||
p(123);
|
||||
}
|
||||
|
||||
frank :: proc(x: $T) do fmt.println("frank ->", x);
|
||||
tim :: proc(x, y: $T) do fmt.println("tim ->", x, y);
|
||||
yep(frank);
|
||||
// yep(tim);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
main :: proc() {
|
||||
/*
|
||||
foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y);
|
||||
foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y);
|
||||
foo :: proc(x: type) do fmt.println("#3", type_info(x));
|
||||
|
||||
f :: foo;
|
||||
|
||||
f(y = 3785.1546, x = 123);
|
||||
f(x = int, y = 897.513);
|
||||
f(x = f32);
|
||||
|
||||
general_stuff();
|
||||
foreign_blocks();
|
||||
default_arguments();
|
||||
named_arguments();
|
||||
default_return_values();
|
||||
call_location();
|
||||
explicit_parametric_polymorphic_procedures();
|
||||
implicit_polymorphic_assignment();
|
||||
|
||||
|
||||
// Command line argument(s)!
|
||||
// -opt=0,1,2,3
|
||||
*/
|
||||
/*
|
||||
program := "+ + * - /";
|
||||
accumulator := 0;
|
||||
|
||||
for token in program {
|
||||
match token {
|
||||
case '+': accumulator += 1;
|
||||
case '-': accumulator -= 1;
|
||||
case '*': accumulator *= 2;
|
||||
case '/': accumulator /= 2;
|
||||
case: // Ignore everything else
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n",
|
||||
program, accumulator);
|
||||
*/
|
||||
}
|
||||
+97
-90
@@ -1,91 +1,91 @@
|
||||
#import "win32.odin" when ODIN_OS == "windows";
|
||||
#import "fmt.odin";
|
||||
#import "math.odin";
|
||||
#import "os.odin";
|
||||
#import gl "opengl.odin";
|
||||
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";
|
||||
import gl "opengl.odin";
|
||||
|
||||
TWO_HEARTS :: '💕';
|
||||
const TWO_HEARTS = '💕';
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
|
||||
time_now :: proc() -> f64 {
|
||||
var win32_perf_count_freq = win32.get_query_performance_frequency();
|
||||
proc time_now() -> f64 {
|
||||
assert(win32_perf_count_freq != 0);
|
||||
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := counter as f64 / win32_perf_count_freq as f64;
|
||||
return result;
|
||||
var counter: i64;
|
||||
win32.query_performance_counter(&counter);
|
||||
return f64(counter) / f64(win32_perf_count_freq);
|
||||
}
|
||||
win32_print_last_error :: proc() {
|
||||
err_code := win32.GetLastError() as int;
|
||||
proc win32_print_last_error() {
|
||||
var err_code = win32.get_last_error();
|
||||
if err_code != 0 {
|
||||
fmt.println("GetLastError: %", err_code);
|
||||
fmt.println("get_last_error: ", err_code);
|
||||
}
|
||||
}
|
||||
|
||||
// Yuk!
|
||||
to_c_string :: proc(s: string) -> []u8 {
|
||||
c_str := new_slice(u8, s.count+1);
|
||||
copy(c_str, s as []byte);
|
||||
c_str[s.count] = 0;
|
||||
proc to_c_string(s: string) -> []u8 {
|
||||
var c_str = make([]u8, len(s)+1);
|
||||
copy(c_str, []u8(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;
|
||||
type Window struct {
|
||||
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) {
|
||||
proc make_window(title: string, msg, height: int, window_proc: win32.WndProc) -> (Window, bool) {
|
||||
using win32;
|
||||
|
||||
w: Window;
|
||||
var 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 {
|
||||
var class_name = "Win32-Odin-Window\x00";
|
||||
var 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 = []u8(title);
|
||||
}
|
||||
|
||||
instance := GetModuleHandleA(nil);
|
||||
var instance = get_module_handle_a(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 = Hinstance(instance),
|
||||
class_name = c_class_name,
|
||||
wnd_proc = window_proc,
|
||||
};
|
||||
|
||||
if RegisterClassExA(^w.wc) == 0 {
|
||||
if register_class_ex_a(&w.wc) == 0 {
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.hwnd = CreateWindowExA(0,
|
||||
c_class_name, w.c_title.data,
|
||||
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
w.width as i32, w.height as i32,
|
||||
nil, nil, instance, nil);
|
||||
w.hwnd = create_window_ex_a(0,
|
||||
c_class_name, &w.c_title[0],
|
||||
WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
i32(w.width), i32(w.height),
|
||||
nil, nil, instance, nil);
|
||||
|
||||
if w.hwnd == nil {
|
||||
win32_print_last_error();
|
||||
return w, false;
|
||||
}
|
||||
|
||||
w.dc = GetDC(w.hwnd);
|
||||
w.dc = get_dc(w.hwnd);
|
||||
|
||||
{
|
||||
pfd := PIXELFORMATDESCRIPTOR{
|
||||
size = size_of(PIXELFORMATDESCRIPTOR) as u32,
|
||||
var pfd = PixelFormatDescriptor{
|
||||
size = size_of(PixelFormatDescriptor),
|
||||
version = 1,
|
||||
flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
|
||||
pixel_type = PFD_TYPE_RGBA,
|
||||
@@ -96,89 +96,91 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
|
||||
layer_type = PFD_MAIN_PLANE,
|
||||
};
|
||||
|
||||
SetPixelFormat(w.dc, ChoosePixelFormat(w.dc, ^pfd), nil);
|
||||
w.opengl_context = wglCreateContext(w.dc);
|
||||
wglMakeCurrent(w.dc, w.opengl_context);
|
||||
set_pixel_format(w.dc, choose_pixel_format(w.dc, &pfd), nil);
|
||||
w.opengl_context = wgl.create_context(w.dc);
|
||||
wgl.make_current(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,
|
||||
var attribs = [8]i32{
|
||||
wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
|
||||
wgl.CONTEXT_MINOR_VERSION_ARB, 1,
|
||||
wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
|
||||
0, // NOTE(bill): tells the proc that this is the end of attribs
|
||||
};
|
||||
|
||||
wglCreateContextAttribsARB := wglGetProcAddress(("wglCreateContextAttribsARB\x00" as string).data) as wglCreateContextAttribsARBType;
|
||||
w.rc = wglCreateContextAttribsARB(w.dc, 0, ^attribs[0]);
|
||||
wglMakeCurrent(w.dc, w.rc);
|
||||
SwapBuffers(w.dc);
|
||||
var wgl_str = "wglCreateContextAttribsARB\x00";
|
||||
var wglCreateContextAttribsARB = wgl.CreateContextAttribsARBType(wgl.get_proc_address(&wgl_str[0]));
|
||||
w.rc = wglCreateContextAttribsARB(w.dc, nil, &attribs[0]);
|
||||
wgl.make_current(w.dc, w.rc);
|
||||
swap_buffers(w.dc);
|
||||
}
|
||||
|
||||
return w, true;
|
||||
}
|
||||
|
||||
destroy_window :: proc(w: ^Window) {
|
||||
free(w.c_title.data);
|
||||
proc destroy_window(w: ^Window) {
|
||||
free(w.c_title);
|
||||
}
|
||||
|
||||
display_window :: proc(w: ^Window) {
|
||||
win32.SwapBuffers(w.dc);
|
||||
proc display_window(w: ^Window) {
|
||||
win32.swap_buffers(w.dc);
|
||||
}
|
||||
|
||||
|
||||
run :: proc() {
|
||||
using win32;
|
||||
proc run() {
|
||||
using math;
|
||||
|
||||
win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline {
|
||||
proc win32_proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
|
||||
using win32;
|
||||
if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
|
||||
os.exit(0);
|
||||
return 0;
|
||||
}
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam);
|
||||
return def_window_proc_a(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc);
|
||||
var window, window_success = make_window("Odin Language Demo", 854, 480, win32.WndProc(win32_proc));
|
||||
if !window_success {
|
||||
return;
|
||||
}
|
||||
defer destroy_window(^window);
|
||||
defer destroy_window(&window);
|
||||
|
||||
gl.init();
|
||||
|
||||
using win32;
|
||||
|
||||
prev_time := time_now();
|
||||
running := true;
|
||||
var prev_time = time_now();
|
||||
var running = true;
|
||||
|
||||
pos := Vec2{100, 100};
|
||||
var pos = Vec2{100, 100};
|
||||
|
||||
for running {
|
||||
curr_time := time_now();
|
||||
dt := (curr_time - prev_time) as f32;
|
||||
var curr_time = time_now();
|
||||
var dt = f32(curr_time - prev_time);
|
||||
prev_time = curr_time;
|
||||
|
||||
msg: MSG;
|
||||
for PeekMessageA(^msg, nil, 0, 0, PM_REMOVE) > 0 {
|
||||
var msg: Msg;
|
||||
for peek_message_a(&msg, nil, 0, 0, PM_REMOVE) > 0 {
|
||||
if msg.message == WM_QUIT {
|
||||
running = false;
|
||||
}
|
||||
TranslateMessage(^msg);
|
||||
DispatchMessageA(^msg);
|
||||
translate_message(&msg);
|
||||
dispatch_message_a(&msg);
|
||||
}
|
||||
|
||||
if is_key_down(Key_Code.ESCAPE) {
|
||||
if is_key_down(KeyCode.Escape) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
{
|
||||
SPEED :: 500;
|
||||
v: Vec2;
|
||||
const SPEED = 500;
|
||||
var v: Vec2;
|
||||
|
||||
if is_key_down(Key_Code.RIGHT) { v[0] += 1; }
|
||||
if is_key_down(Key_Code.LEFT) { v[0] -= 1; }
|
||||
if is_key_down(Key_Code.UP) { v[1] += 1; }
|
||||
if is_key_down(Key_Code.DOWN) { v[1] -= 1; }
|
||||
if is_key_down(KeyCode.Right) { v[0] += 1; }
|
||||
if is_key_down(KeyCode.Left) { v[0] -= 1; }
|
||||
if is_key_down(KeyCode.Up) { v[1] += 1; }
|
||||
if is_key_down(KeyCode.Down) { v[1] -= 1; }
|
||||
|
||||
v = vec2_norm0(v);
|
||||
v = norm(v);
|
||||
|
||||
pos += v * Vec2{SPEED * dt};
|
||||
}
|
||||
@@ -188,10 +190,10 @@ 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, f64(window.width),
|
||||
0, f64(window.height), 0, 1);
|
||||
|
||||
draw_rect :: proc(x, y, w, h: f32) {
|
||||
proc draw_rect(x, y, w, h: f32) {
|
||||
gl.Begin(gl.TRIANGLES);
|
||||
defer gl.End();
|
||||
|
||||
@@ -206,10 +208,15 @@ run :: proc() {
|
||||
|
||||
draw_rect(pos.x, pos.y, 50, 50);
|
||||
|
||||
display_window(^window);
|
||||
ms_to_sleep := (16 - 1000*dt) as i32;
|
||||
display_window(&window);
|
||||
var ms_to_sleep = i32(16 - 1000*dt);
|
||||
if ms_to_sleep > 0 {
|
||||
win32.Sleep(ms_to_sleep);
|
||||
win32.sleep(ms_to_sleep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
proc main() {
|
||||
run();
|
||||
}
|
||||
|
||||
+103
-99
@@ -1,12 +1,12 @@
|
||||
#import "fmt.odin"
|
||||
import "fmt.odin";
|
||||
|
||||
#foreign_system_library "Ws2_32" when ODIN_OS == "windows"
|
||||
foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
|
||||
|
||||
|
||||
SOCKET :: type uint
|
||||
INVALID_SOCKET :: ~(0 as SOCKET)
|
||||
type SOCKET uint;
|
||||
const INVALID_SOCKET = ~SOCKET(0);
|
||||
|
||||
AF :: enum i32 {
|
||||
type AF enum i32 {
|
||||
UNSPEC = 0, // unspecified
|
||||
UNIX = 1, // local to host (pipes, portals)
|
||||
INET = 2, // internetwork: UDP, TCP, etc.
|
||||
@@ -35,113 +35,117 @@ AF :: enum i32 {
|
||||
SIP = 24, // Simple Internet Protocol
|
||||
PIP = 25, // Help Identify PIP packets
|
||||
MAX = 26,
|
||||
}
|
||||
};
|
||||
|
||||
SOCK_STREAM :: 1
|
||||
SOCKET_ERROR :: -1
|
||||
IPPROTO_TCP :: 6
|
||||
AI_PASSIVE :: 0x0020
|
||||
SOMAXCONN :: 128
|
||||
const (
|
||||
SOCK_STREAM = 1;
|
||||
SOCKET_ERROR = -1;
|
||||
IPPROTO_TCP = 6;
|
||||
AI_PASSIVE = 0x0020;
|
||||
SOMAXCONN = 128;
|
||||
)
|
||||
const (
|
||||
SD_RECEIVE = 0;
|
||||
SD_SEND = 1;
|
||||
SD_BOTH = 2;
|
||||
)
|
||||
|
||||
SD_RECEIVE :: 0
|
||||
SD_SEND :: 1
|
||||
SD_BOTH :: 2
|
||||
|
||||
WSADESCRIPTION_LEN :: 256
|
||||
WSASYS_STATUS_LEN :: 128
|
||||
WSADATA :: struct #ordered {
|
||||
version: i16
|
||||
high_version: i16
|
||||
const WSADESCRIPTION_LEN = 256;
|
||||
const WSASYS_STATUS_LEN = 128;
|
||||
type WSADATA struct #ordered {
|
||||
version: i16,
|
||||
high_version: i16,
|
||||
|
||||
|
||||
// NOTE(bill): This is x64 ordering
|
||||
max_sockets: u16
|
||||
max_udp_dg: u16
|
||||
vendor_info: ^byte
|
||||
description: [WSADESCRIPTION_LEN+1]byte
|
||||
system_status: [WSASYS_STATUS_LEN+1]byte
|
||||
max_sockets: u16,
|
||||
max_udp_dg: u16,
|
||||
vendor_info: ^u8,
|
||||
description: [WSADESCRIPTION_LEN+1]u8,
|
||||
system_status: [WSASYS_STATUS_LEN+1]u8,
|
||||
}
|
||||
|
||||
addrinfo :: struct #ordered {
|
||||
flags: i32
|
||||
family: i32
|
||||
socktype: i32
|
||||
protocol: i32
|
||||
addrlen: uint
|
||||
canonname: ^u8
|
||||
addr: ^sockaddr
|
||||
next: ^addrinfo
|
||||
type addrinfo struct #ordered {
|
||||
flags: i32,
|
||||
family: i32,
|
||||
socktype: i32,
|
||||
protocol: i32,
|
||||
addrlen: uint,
|
||||
canonname: ^u8,
|
||||
addr: ^sockaddr,
|
||||
next: ^addrinfo,
|
||||
}
|
||||
|
||||
sockaddr :: struct #ordered {
|
||||
family: u16
|
||||
data: [14]byte
|
||||
type sockaddr struct #ordered {
|
||||
family: u16,
|
||||
data: [14]u8,
|
||||
}
|
||||
|
||||
|
||||
WSAStartup :: proc(version_requested: i16, data: ^WSADATA) -> i32 #foreign #dll_import
|
||||
WSACleanup :: proc() -> i32 #foreign #dll_import
|
||||
getaddrinfo :: proc(node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32 #foreign #dll_import
|
||||
freeaddrinfo :: proc(ai: ^addrinfo) #foreign #dll_import
|
||||
socket :: proc(af, type_, protocol: i32) -> SOCKET #foreign #dll_import
|
||||
closesocket :: proc(s: SOCKET) -> i32 #foreign #dll_import
|
||||
bind :: proc(s: SOCKET, name: ^sockaddr, name_len: i32) -> i32 #foreign #dll_import
|
||||
listen :: proc(s: SOCKET, back_log: i32) -> i32 #foreign #dll_import
|
||||
accept :: proc(s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET #foreign #dll_import
|
||||
recv :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign #dll_import
|
||||
send :: proc(s: SOCKET, buf: ^byte, len: i32, flags: i32) -> i32 #foreign #dll_import
|
||||
shutdown :: proc(s: SOCKET, how: i32) -> i32 #foreign #dll_import
|
||||
WSAGetLastError :: proc() -> i32 #foreign #dll_import
|
||||
|
||||
to_c_string :: proc(s: string) -> ^byte {
|
||||
c_str := new_slice(byte, s.count+1)
|
||||
assert(c_str.data != nil)
|
||||
copy(c_str, s as []byte)
|
||||
c_str[s.count] = 0
|
||||
return c_str.data
|
||||
foreign ws2 {
|
||||
proc WSAStartup (version_requested: i16, data: ^WSADATA) -> i32;
|
||||
proc WSACleanup () -> i32;
|
||||
proc getaddrinfo (node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32;
|
||||
proc freeaddrinfo (ai: ^addrinfo);
|
||||
proc socket (af, type_, protocol: i32) -> SOCKET;
|
||||
proc closesocket (s: SOCKET) -> i32;
|
||||
proc bind (s: SOCKET, name: ^sockaddr, name_len: i32) -> i32;
|
||||
proc listen (s: SOCKET, back_log: i32) -> i32;
|
||||
proc accept (s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET;
|
||||
proc recv (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
|
||||
proc send (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
|
||||
proc shutdown (s: SOCKET, how: i32) -> i32;
|
||||
proc WSAGetLastError() -> i32;
|
||||
}
|
||||
proc to_c_string(s: string) -> ^u8 {
|
||||
var c_str = make([]u8, len(s)+1);
|
||||
copy(c_str, []u8(s));
|
||||
c_str[len(s)] = 0;
|
||||
return &c_str[0];
|
||||
}
|
||||
|
||||
run :: proc() {
|
||||
wsa: WSADATA
|
||||
res: ^addrinfo = nil
|
||||
hints: addrinfo
|
||||
s, client: SOCKET
|
||||
proc run() {
|
||||
var (
|
||||
wsa: WSADATA;
|
||||
res: ^addrinfo = nil;
|
||||
hints: addrinfo;
|
||||
s, client: SOCKET;
|
||||
)
|
||||
|
||||
if WSAStartup(2 | (2 << 8), ^wsa) != 0 {
|
||||
fmt.println("WSAStartup failed: ", WSAGetLastError())
|
||||
return
|
||||
if WSAStartup(2 | (2 << 8), &wsa) != 0 {
|
||||
fmt.println("WSAStartup failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer WSACleanup()
|
||||
defer WSACleanup();
|
||||
|
||||
hints.family = AF.INET as i32
|
||||
hints.socktype = SOCK_STREAM
|
||||
hints.protocol = IPPROTO_TCP
|
||||
hints.flags = AI_PASSIVE
|
||||
hints.family = i32(AF.INET);
|
||||
hints.socktype = SOCK_STREAM;
|
||||
hints.protocol = IPPROTO_TCP;
|
||||
hints.flags = AI_PASSIVE;
|
||||
|
||||
if getaddrinfo(nil, to_c_string("8080"), ^hints, ^res) != 0 {
|
||||
fmt.println("getaddrinfo failed: ", WSAGetLastError())
|
||||
return
|
||||
if getaddrinfo(nil, to_c_string("8080"), &hints, &res) != 0 {
|
||||
fmt.println("getaddrinfo failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer freeaddrinfo(res)
|
||||
defer freeaddrinfo(res);
|
||||
|
||||
s = socket(res.family, res.socktype, res.protocol)
|
||||
s = socket(res.family, res.socktype, res.protocol);
|
||||
if s == INVALID_SOCKET {
|
||||
fmt.println("socket failed: ", WSAGetLastError())
|
||||
return
|
||||
fmt.println("socket failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer closesocket(s)
|
||||
defer closesocket(s);
|
||||
|
||||
bind(s, res.addr, res.addrlen as i32)
|
||||
listen(s, SOMAXCONN)
|
||||
bind(s, res.addr, i32(res.addrlen));
|
||||
listen(s, SOMAXCONN);
|
||||
|
||||
client = accept(s, nil, 0)
|
||||
client = accept(s, nil, 0);
|
||||
if client == INVALID_SOCKET {
|
||||
fmt.println("socket failed: ", WSAGetLastError())
|
||||
return
|
||||
fmt.println("socket failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
defer closesocket(client)
|
||||
defer closesocket(client);
|
||||
|
||||
html :=
|
||||
var html =
|
||||
`HTTP/1.1 200 OK
|
||||
Connection: close
|
||||
Content-type: text/html
|
||||
@@ -154,27 +158,27 @@ Content-type: text/html
|
||||
<h1 style="color: orange;">Odin Server Demo</h1>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
`;
|
||||
|
||||
buf: [1024]byte
|
||||
var buf: [1024]u8;
|
||||
for {
|
||||
bytes := recv(client, ^buf[0], buf.count as i32, 0)
|
||||
var bytes = recv(client, &buf[0], i32(len(buf)), 0);
|
||||
if bytes > 0 {
|
||||
// fmt.println(buf[:bytes] as string)
|
||||
bytes_sent := send(client, html.data, (html.count-1) as i32, 0)
|
||||
// fmt.println(string(buf[0..<bytes]))
|
||||
var bytes_sent = send(client, &html[0], i32(len(html)-1), 0);
|
||||
if bytes_sent == SOCKET_ERROR {
|
||||
fmt.println("send failed: ", WSAGetLastError())
|
||||
return
|
||||
fmt.println("send failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
break
|
||||
break;
|
||||
} else if bytes == 0 {
|
||||
fmt.println("Connection closing...")
|
||||
break
|
||||
fmt.println("Connection closing...");
|
||||
break;
|
||||
} else {
|
||||
fmt.println("recv failed: ", WSAGetLastError())
|
||||
return
|
||||
fmt.println("recv failed: ", WSAGetLastError());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(client, SD_SEND)
|
||||
shutdown(client, SD_SEND);
|
||||
}
|
||||
|
||||
+25
-22
@@ -6,21 +6,24 @@
|
||||
// #import "punity.odin" as pn;
|
||||
|
||||
main :: proc() {
|
||||
// struct_padding()
|
||||
// bounds_checking()
|
||||
// type_introspection()
|
||||
// any_type()
|
||||
// crazy_introspection()
|
||||
// namespaces_and_files()
|
||||
// miscellany()
|
||||
// ht.run()
|
||||
// game.run()
|
||||
// {
|
||||
// init :: proc(c: ^pn.Core) {}
|
||||
// step :: proc(c: ^pn.Core) {}
|
||||
struct_padding();
|
||||
bounds_checking();
|
||||
type_introspection();
|
||||
any_type();
|
||||
crazy_introspection();
|
||||
namespaces_and_files();
|
||||
miscellany();
|
||||
|
||||
// pn.run(init, step)
|
||||
// }
|
||||
/*
|
||||
ht.run();
|
||||
game.run();
|
||||
{
|
||||
init :: proc(c: ^pn.Core) {}
|
||||
step :: proc(c: ^pn.Core) {}
|
||||
|
||||
pn.run(init, step);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
struct_padding :: proc() {
|
||||
@@ -133,7 +136,7 @@ bounds_checking :: proc() {
|
||||
|
||||
{
|
||||
base: [10]int;
|
||||
s := base[2:6];
|
||||
s := base[2..6];
|
||||
a, b := -1, 6;
|
||||
|
||||
#no_bounds_check {
|
||||
@@ -161,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:
|
||||
@@ -171,7 +174,7 @@ type_introspection :: proc() {
|
||||
}
|
||||
|
||||
// Unsafe cast
|
||||
integer_info := cast(^Type_Info.Integer)info;
|
||||
integer_info := cast(^Type_Info.Integer)cast(rawptr)info;
|
||||
}
|
||||
|
||||
{
|
||||
@@ -260,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");
|
||||
|
||||
|
||||
+361
-369
File diff suppressed because it is too large
Load Diff
+17
-17
@@ -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^);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,284 @@
|
||||
#import "fmt.odin";
|
||||
#import "utf8.odin";
|
||||
// #import "atomic.odin";
|
||||
// #import "hash.odin";
|
||||
// #import "math.odin";
|
||||
// #import "mem.odin";
|
||||
// #import "opengl.odin";
|
||||
// #import "os.odin";
|
||||
// #import "sync.odin";
|
||||
// #import win32 "sys/windows.odin";
|
||||
|
||||
main :: proc() {
|
||||
// syntax();
|
||||
procedure_overloading();
|
||||
}
|
||||
|
||||
syntax :: proc() {
|
||||
// Cyclic type checking
|
||||
// Uncomment to see the error
|
||||
// A :: struct {b: B};
|
||||
// B :: struct {a: A};
|
||||
|
||||
x: int;
|
||||
y := cast(f32)x;
|
||||
z := transmute(u32)y;
|
||||
// down_cast, union_cast are similar too
|
||||
|
||||
|
||||
|
||||
// Basic directives
|
||||
fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure);
|
||||
// NOTE: new and improved `printf`
|
||||
// TODO: It does need accurate float printing
|
||||
|
||||
|
||||
|
||||
// record fields use the same syntax a procedure signatures
|
||||
Thing1 :: struct {
|
||||
x: f32,
|
||||
y: int,
|
||||
z: ^[]int,
|
||||
};
|
||||
Thing2 :: struct {x: f32, y: int, z: ^[]int};
|
||||
|
||||
// Slice interals are now just a `ptr+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;
|
||||
|
||||
|
||||
// All declarations with : are either variable or constant
|
||||
// To make these declarations syntactically consistent
|
||||
v_variable := 123;
|
||||
c_constant :: 123;
|
||||
c_type1 :: int;
|
||||
c_type2 :: []int;
|
||||
c_proc :: proc() { /* code here */ };
|
||||
|
||||
|
||||
/*
|
||||
x += 1;
|
||||
x -= 1;
|
||||
// ++ and -- have been removed
|
||||
// x++;
|
||||
// x--;
|
||||
// Question: Should they be added again?
|
||||
// They were removed as they are redundant and statements, not expressions
|
||||
// like in C/C++
|
||||
*/
|
||||
|
||||
// You can now build files as a `.dll`
|
||||
// `odin build_dll demo.odin`
|
||||
|
||||
|
||||
// New vector syntax
|
||||
u, v: [vector 3]f32;
|
||||
v[0] = 123;
|
||||
v.x = 123; // valid for all vectors with count 1 to 4
|
||||
|
||||
// Next part
|
||||
prefixes();
|
||||
}
|
||||
|
||||
|
||||
Prefix_Type :: struct {x: int, y: f32, z: rawptr};
|
||||
|
||||
#thread_local my_tls: Prefix_Type;
|
||||
|
||||
prefixes :: proc() {
|
||||
using var: Prefix_Type;
|
||||
immutable const := Prefix_Type{1, 2, nil};
|
||||
var.x = 123;
|
||||
x = 123;
|
||||
// const.x = 123; // const is immutable
|
||||
|
||||
|
||||
|
||||
foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
|
||||
// int_ptr = nil; // Not valid
|
||||
// int_ptr^ = 123; // Not valid
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Same as C99's `restrict`
|
||||
bar :: proc(no_alias a, b: ^int) {
|
||||
// Assumes a never equals b so it can perform optimizations with that fact
|
||||
}
|
||||
|
||||
|
||||
when_statements();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
when_statements :: proc() {
|
||||
X :: 123 + 12;
|
||||
Y :: X/5;
|
||||
COND :: Y > 0;
|
||||
|
||||
when COND {
|
||||
fmt.println("Y > 0");
|
||||
} else {
|
||||
fmt.println("Y <= 0");
|
||||
}
|
||||
|
||||
|
||||
when false {
|
||||
this_code_does_not_exist(123, 321);
|
||||
but_its_syntax_is_valid();
|
||||
x :: ^^^^int;
|
||||
}
|
||||
|
||||
foreign_procedures();
|
||||
}
|
||||
|
||||
#foreign_system_library win32_user "user32.lib" when ODIN_OS == "windows";
|
||||
// NOTE: This is done on purpose for two reasons:
|
||||
// * Makes it clear where the platform specific stuff is
|
||||
// * Removes the need to solve the travelling salesman problem when importing files :P
|
||||
|
||||
foreign_procedures :: proc() {
|
||||
ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user;
|
||||
show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user "ShowWindow";
|
||||
// NOTE: If that library doesn't get used, it doesn't get linked with
|
||||
// NOTE: There is not link checking yet to see if that procedure does come from that library
|
||||
|
||||
// See sys/windows.odin for more examples
|
||||
|
||||
special_expressions();
|
||||
}
|
||||
|
||||
special_expressions :: proc() {
|
||||
/*
|
||||
// Block expression
|
||||
x := {
|
||||
a: f32 = 123;
|
||||
b := a-123;
|
||||
c := b/a;
|
||||
give c;
|
||||
}; // semicolon is required as it's an expression
|
||||
|
||||
y := if x < 50 {
|
||||
give x;
|
||||
} else {
|
||||
// TODO: Type cohesion is not yet finished
|
||||
give 123;
|
||||
}; // semicolon is required as it's an expression
|
||||
*/
|
||||
|
||||
// This is allows for inline blocks of code and will be a useful feature to have when
|
||||
// macros will be implemented into the language
|
||||
|
||||
loops();
|
||||
}
|
||||
|
||||
loops :: proc() {
|
||||
// The C-style for loop
|
||||
for i := 0; i < 123; i += 1 {
|
||||
break;
|
||||
}
|
||||
for i := 0; i < 123; {
|
||||
break;
|
||||
}
|
||||
for false {
|
||||
break;
|
||||
}
|
||||
for {
|
||||
break;
|
||||
}
|
||||
|
||||
for i in 0..123 { // 123 exclusive
|
||||
}
|
||||
|
||||
for i in 0..123-1 { // 122 inclusive
|
||||
}
|
||||
|
||||
for val, idx in 12..16 {
|
||||
fmt.println(val, idx);
|
||||
}
|
||||
|
||||
primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19};
|
||||
|
||||
for p in primes {
|
||||
fmt.println(p);
|
||||
}
|
||||
|
||||
// Pointers to arrays, slices, or strings are allowed
|
||||
for _ in ^primes {
|
||||
// ignore the value and just iterate across it
|
||||
}
|
||||
|
||||
|
||||
|
||||
name := "你好,世界";
|
||||
fmt.println(name);
|
||||
for r in name {
|
||||
compile_assert(type_of_val(r) == rune);
|
||||
fmt.printf("%r\n", r);
|
||||
}
|
||||
|
||||
when false {
|
||||
for i, size := 0; i < name.count; i += size {
|
||||
r: rune;
|
||||
r, size = utf8.decode_rune(name[i..]);
|
||||
fmt.printf("%r\n", r);
|
||||
}
|
||||
}
|
||||
|
||||
procedure_overloading();
|
||||
}
|
||||
|
||||
|
||||
procedure_overloading :: proc() {
|
||||
THINGF :: 14451.1;
|
||||
THINGI :: 14451;
|
||||
|
||||
foo :: proc() {
|
||||
fmt.printf("Zero args\n");
|
||||
}
|
||||
foo :: proc(i: int) {
|
||||
fmt.printf("int arg, i=%d\n", i);
|
||||
}
|
||||
foo :: proc(f: f64) {
|
||||
i := cast(int)f;
|
||||
fmt.printf("f64 arg, f=%d\n", i);
|
||||
}
|
||||
|
||||
foo();
|
||||
foo(THINGF);
|
||||
// foo(THINGI); // 14451 is just a number so it could go to either procedures
|
||||
foo(cast(int)THINGI);
|
||||
|
||||
|
||||
|
||||
|
||||
foo :: proc(x: ^i32) -> (int, int) {
|
||||
fmt.println("^int");
|
||||
return 123, cast(int)(x^);
|
||||
}
|
||||
foo :: proc(x: rawptr) {
|
||||
fmt.println("rawptr");
|
||||
}
|
||||
|
||||
|
||||
a: i32 = 123;
|
||||
b: f32;
|
||||
c: rawptr;
|
||||
fmt.println(foo(^a));
|
||||
foo(^b);
|
||||
foo(c);
|
||||
// foo(nil); // nil could go to numerous types thus the ambiguity
|
||||
|
||||
f: proc();
|
||||
f = foo; // The correct `foo` to chosen
|
||||
f();
|
||||
|
||||
|
||||
// See math.odin and atomic.odin for more examples
|
||||
}
|
||||
+233
-224
@@ -1,35 +1,42 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
#import "mem.odin";
|
||||
import (
|
||||
win32 "sys/windows.odin";
|
||||
"fmt.odin";
|
||||
"os.odin";
|
||||
"mem.odin";
|
||||
)
|
||||
|
||||
CANVAS_WIDTH :: 128;
|
||||
CANVAS_HEIGHT :: 128;
|
||||
CANVAS_SCALE :: 3;
|
||||
FRAME_TIME :: 1.0/30.0;
|
||||
WINDOW_TITLE :: "Punity\x00";
|
||||
const (
|
||||
CANVAS_WIDTH = 128;
|
||||
CANVAS_HEIGHT = 128;
|
||||
CANVAS_SCALE = 3;
|
||||
FRAME_TIME = 1.0/30.0;
|
||||
WINDOW_TITLE = "Punity\x00";
|
||||
)
|
||||
|
||||
_ := compile_assert(CANVAS_WIDTH % 16 == 0);
|
||||
const _ = compile_assert(CANVAS_WIDTH % 16 == 0);
|
||||
|
||||
WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE;
|
||||
WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE;
|
||||
const (
|
||||
WINDOW_WIDTH = CANVAS_WIDTH * CANVAS_SCALE;
|
||||
WINDOW_HEIGHT = CANVAS_HEIGHT * CANVAS_SCALE;
|
||||
)
|
||||
|
||||
const (
|
||||
STACK_CAPACITY = 1<<20;
|
||||
STORAGE_CAPACITY = 1<<20;
|
||||
|
||||
STACK_CAPACITY :: 1<<20;
|
||||
STORAGE_CAPACITY :: 1<<20;
|
||||
DRAW_LIST_RESERVE = 128;
|
||||
|
||||
DRAW_LIST_RESERVE :: 128;
|
||||
MAX_KEYS = 256;
|
||||
)
|
||||
|
||||
MAX_KEYS :: 256;
|
||||
|
||||
Core :: struct {
|
||||
type Core struct {
|
||||
stack: ^Bank,
|
||||
storage: ^Bank,
|
||||
|
||||
running: bool,
|
||||
key_modifiers: u32,
|
||||
key_states: [MAX_KEYS]byte,
|
||||
key_deltas: [MAX_KEYS]byte,
|
||||
key_states: [MAX_KEYS]u8,
|
||||
key_deltas: [MAX_KEYS]u8,
|
||||
|
||||
perf_frame,
|
||||
perf_frame_inner,
|
||||
@@ -45,52 +52,52 @@ Core :: struct {
|
||||
draw_list: ^Draw_List,
|
||||
}
|
||||
|
||||
Perf_Span :: struct {
|
||||
type Perf_Span struct {
|
||||
stamp: f64,
|
||||
delta: f32,
|
||||
}
|
||||
|
||||
Bank :: struct {
|
||||
memory: []byte,
|
||||
type Bank struct {
|
||||
memory: []u8,
|
||||
cursor: int,
|
||||
}
|
||||
|
||||
Bank_State :: struct {
|
||||
type Bank_State struct {
|
||||
state: Bank,
|
||||
bank: ^Bank,
|
||||
}
|
||||
|
||||
|
||||
Color :: raw_union {
|
||||
using channels: struct{a, b, g, r: byte},
|
||||
type Color raw_union {
|
||||
using channels: struct{a, b, g, r: u8},
|
||||
rgba: u32,
|
||||
}
|
||||
|
||||
Palette :: struct {
|
||||
type Palette struct {
|
||||
colors: [256]Color,
|
||||
colors_count: byte,
|
||||
colors_count: u8,
|
||||
}
|
||||
|
||||
|
||||
Rect :: raw_union {
|
||||
type Rect raw_union {
|
||||
using minmax: struct {min_x, min_y, max_x, max_y: int},
|
||||
using pos: struct {left, top, right, bottom: int},
|
||||
e: [4]int,
|
||||
}
|
||||
|
||||
Bitmap :: struct {
|
||||
pixels: []byte,
|
||||
type Bitmap struct {
|
||||
pixels: []u8,
|
||||
width: int,
|
||||
height: int,
|
||||
}
|
||||
|
||||
Font :: struct {
|
||||
type Font struct {
|
||||
using bitmap: Bitmap,
|
||||
char_width: int,
|
||||
char_height: int,
|
||||
}
|
||||
|
||||
Canvas :: struct {
|
||||
type Canvas struct {
|
||||
using bitmap: ^Bitmap,
|
||||
palette: Palette,
|
||||
translate_x: int,
|
||||
@@ -99,89 +106,92 @@ Canvas :: struct {
|
||||
font: ^Font,
|
||||
}
|
||||
|
||||
DrawFlag :: enum {
|
||||
type DrawFlag enum {
|
||||
NONE = 0,
|
||||
FLIP_H = 1<<0,
|
||||
FLIP_V = 1<<1,
|
||||
MASK = 1<<2,
|
||||
}
|
||||
|
||||
Draw_Item :: struct {}
|
||||
Draw_List :: struct {
|
||||
type Draw_Item struct {}
|
||||
type Draw_List struct {
|
||||
items: []Draw_Item,
|
||||
}
|
||||
|
||||
Key :: enum {
|
||||
MOD_SHIFT = 0x0001,
|
||||
MOD_CONTROL = 0x0002,
|
||||
MOD_ALT = 0x0004,
|
||||
MOD_SUPER = 0x0008,
|
||||
type Key enum {
|
||||
ModShift = 0x0001,
|
||||
ModControl = 0x0002,
|
||||
ModAlt = 0x0004,
|
||||
ModSuper = 0x0008,
|
||||
|
||||
UNKNOWN =-1,
|
||||
INVALID =-2,
|
||||
|
||||
LBUTTON = 1,
|
||||
RBUTTON = 2,
|
||||
CANCEL = 3,
|
||||
MBUTTON = 4,
|
||||
Unknown =-1,
|
||||
Invalid =-2,
|
||||
|
||||
BACK = 8,
|
||||
TAB = 9,
|
||||
CLEAR = 12,
|
||||
RETURN = 13,
|
||||
SHIFT = 16,
|
||||
CONTROL = 17,
|
||||
MENU = 18,
|
||||
PAUSE = 19,
|
||||
CAPITAL = 20,
|
||||
KANA = 0x15,
|
||||
HANGEUL = 0x15,
|
||||
HANGUL = 0x15,
|
||||
JUNJA = 0x17,
|
||||
FINAL = 0x18,
|
||||
HANJA = 0x19,
|
||||
KANJI = 0x19,
|
||||
ESCAPE = 0x1B,
|
||||
CONVERT = 0x1C,
|
||||
NONCONVERT = 0x1D,
|
||||
ACCEPT = 0x1E,
|
||||
MODECHANGE = 0x1F,
|
||||
SPACE = 32,
|
||||
PRIOR = 33,
|
||||
NEXT = 34,
|
||||
END = 35,
|
||||
HOME = 36,
|
||||
LEFT = 37,
|
||||
UP = 38,
|
||||
RIGHT = 39,
|
||||
DOWN = 40,
|
||||
SELECT = 41,
|
||||
PRINT = 42,
|
||||
EXEC = 43,
|
||||
SNAPSHOT = 44,
|
||||
INSERT = 45,
|
||||
DELETE = 46,
|
||||
HELP = 47,
|
||||
LWIN = 0x5B,
|
||||
RWIN = 0x5C,
|
||||
APPS = 0x5D,
|
||||
SLEEP = 0x5F,
|
||||
NUMPAD0 = 0x60,
|
||||
NUMPAD1 = 0x61,
|
||||
NUMPAD2 = 0x62,
|
||||
NUMPAD3 = 0x63,
|
||||
NUMPAD4 = 0x64,
|
||||
NUMPAD5 = 0x65,
|
||||
NUMPAD6 = 0x66,
|
||||
NUMPAD7 = 0x67,
|
||||
NUMPAD8 = 0x68,
|
||||
NUMPAD9 = 0x69,
|
||||
MULTIPLY = 0x6A,
|
||||
ADD = 0x6B,
|
||||
SEPARATOR = 0x6C,
|
||||
SUBTRACT = 0x6D,
|
||||
DECIMAL = 0x6E,
|
||||
DIVIDE = 0x6F,
|
||||
|
||||
Lbutton = 1,
|
||||
Rbutton = 2,
|
||||
Cancel = 3,
|
||||
Mbutton = 4,
|
||||
|
||||
|
||||
Back = 8,
|
||||
Tab = 9,
|
||||
Clear = 12,
|
||||
Return = 13,
|
||||
Shift = 16,
|
||||
Control = 17,
|
||||
Menu = 18,
|
||||
Pause = 19,
|
||||
Capital = 20,
|
||||
Kana = 0x15,
|
||||
Hangeul = 0x15,
|
||||
Hangul = 0x15,
|
||||
Junja = 0x17,
|
||||
Final = 0x18,
|
||||
Hanja = 0x19,
|
||||
Kanji = 0x19,
|
||||
Escape = 0x1B,
|
||||
Convert = 0x1C,
|
||||
NonConvert = 0x1D,
|
||||
Accept = 0x1E,
|
||||
ModeChange = 0x1F,
|
||||
Space = 32,
|
||||
Prior = 33,
|
||||
Next = 34,
|
||||
End = 35,
|
||||
Home = 36,
|
||||
Left = 37,
|
||||
Up = 38,
|
||||
Right = 39,
|
||||
Down = 40,
|
||||
Select = 41,
|
||||
Print = 42,
|
||||
Exec = 43,
|
||||
Snapshot = 44,
|
||||
Insert = 45,
|
||||
Delete = 46,
|
||||
Help = 47,
|
||||
Lwin = 0x5B,
|
||||
Rwin = 0x5C,
|
||||
Apps = 0x5D,
|
||||
Sleep = 0x5F,
|
||||
Numpad0 = 0x60,
|
||||
Numpad1 = 0x61,
|
||||
Numpad2 = 0x62,
|
||||
Numpad3 = 0x63,
|
||||
Numpad4 = 0x64,
|
||||
Numpad5 = 0x65,
|
||||
Numpad6 = 0x66,
|
||||
Numpad7 = 0x67,
|
||||
Numpad8 = 0x68,
|
||||
Numpad9 = 0x69,
|
||||
Multiply = 0x6A,
|
||||
Add = 0x6B,
|
||||
Separator = 0x6C,
|
||||
Subtract = 0x6D,
|
||||
Decimal = 0x6E,
|
||||
Divide = 0x6F,
|
||||
F1 = 0x70,
|
||||
F2 = 0x71,
|
||||
F3 = 0x72,
|
||||
@@ -206,32 +216,33 @@ Key :: enum {
|
||||
F22 = 0x85,
|
||||
F23 = 0x86,
|
||||
F24 = 0x87,
|
||||
NUMLOCK = 0x90,
|
||||
SCROLL = 0x91,
|
||||
LSHIFT = 0xA0,
|
||||
RSHIFT = 0xA1,
|
||||
LCONTROL = 0xA2,
|
||||
RCONTROL = 0xA3,
|
||||
LMENU = 0xA4,
|
||||
RMENU = 0xA5,
|
||||
Numlock = 0x90,
|
||||
Scroll = 0x91,
|
||||
Lshift = 0xA0,
|
||||
Rshift = 0xA1,
|
||||
Lcontrol = 0xA2,
|
||||
Rcontrol = 0xA3,
|
||||
Lmenu = 0xA4,
|
||||
Rmenu = 0xA5,
|
||||
|
||||
APOSTROPHE = 39, /* ' */
|
||||
COMMA = 44, /* , */
|
||||
MINUS = 45, /* - */
|
||||
PERIOD = 46, /* . */
|
||||
SLASH = 47, /* / */
|
||||
NUM0 = 48,
|
||||
NUM1 = 49,
|
||||
NUM2 = 50,
|
||||
NUM3 = 51,
|
||||
NUM4 = 52,
|
||||
NUM5 = 53,
|
||||
NUM6 = 54,
|
||||
NUM7 = 55,
|
||||
NUM8 = 56,
|
||||
NUM9 = 57,
|
||||
SEMICOLON = 59, /* ; */
|
||||
EQUAL = 61, /* = */
|
||||
|
||||
Apostrophe = 39, /* ' */
|
||||
Comma = 44, /* , */
|
||||
Minus = 45, /* - */
|
||||
Period = 46, /* . */
|
||||
Slash = 47, /* / */
|
||||
Num0 = 48,
|
||||
Num1 = 49,
|
||||
Num2 = 50,
|
||||
Num3 = 51,
|
||||
Num4 = 52,
|
||||
Num5 = 53,
|
||||
Num6 = 54,
|
||||
Num7 = 55,
|
||||
Num8 = 56,
|
||||
Num9 = 57,
|
||||
Semicolon = 59, /* ; */
|
||||
Equal = 61, /* = */
|
||||
A = 65,
|
||||
B = 66,
|
||||
C = 67,
|
||||
@@ -258,56 +269,55 @@ Key :: enum {
|
||||
X = 88,
|
||||
Y = 89,
|
||||
Z = 90,
|
||||
LEFT_BRACKET = 91, /* [ */
|
||||
BACKSLASH = 92, /* \ */
|
||||
RIGHT_BRACKET = 93, /* ] */
|
||||
GRAVE_ACCENT = 96, /* ` */
|
||||
LeftBracket = 91, /* [ */
|
||||
Backslash = 92, /* \ */
|
||||
RightBracket = 93, /* ] */
|
||||
GraveAccent = 96, /* ` */
|
||||
};
|
||||
|
||||
|
||||
key_down :: proc(k: Key) -> bool {
|
||||
proc key_down(k: Key) -> bool {
|
||||
return _core.key_states[k] != 0;
|
||||
}
|
||||
|
||||
key_pressed :: proc(k: Key) -> bool {
|
||||
proc key_pressed(k: Key) -> bool {
|
||||
return (_core.key_deltas[k] != 0) && key_down(k);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
win32_perf_count_freq := win32.GetQueryPerformanceFrequency();
|
||||
time_now :: proc() -> f64 {
|
||||
let win32_perf_count_freq = win32.get_query_performance_frequency();
|
||||
proc time_now() -> f64 {
|
||||
assert(win32_perf_count_freq != 0);
|
||||
|
||||
counter: i64;
|
||||
win32.QueryPerformanceCounter(^counter);
|
||||
result := cast(f64)counter / cast(f64)win32_perf_count_freq;
|
||||
return result;
|
||||
var counter: i64;
|
||||
win32.query_performance_counter(&counter);
|
||||
return f64(counter) / f64(win32_perf_count_freq);
|
||||
}
|
||||
|
||||
_core: Core;
|
||||
var _core: Core;
|
||||
|
||||
run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
proc run(user_init, user_step: proc(c: ^Core)) {
|
||||
using win32;
|
||||
|
||||
_core.running = true;
|
||||
|
||||
win32_proc :: proc(hwnd: win32.HWND, msg: u32, wparam: win32.WPARAM, lparam: win32.LPARAM) -> win32.LRESULT #no_inline #cc_c {
|
||||
win32_app_key_mods :: proc() -> u32 {
|
||||
mods: u32 = 0;
|
||||
proc win32_proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline #cc_c {
|
||||
proc win32_app_key_mods() -> u32 {
|
||||
var mods: u32 = 0;
|
||||
|
||||
if is_key_down(Key_Code.SHIFT) {
|
||||
mods |= cast(u32)Key.MOD_SHIFT;
|
||||
if is_key_down(KeyCode.Shift) {
|
||||
mods |= u32(Key.ModShift);
|
||||
}
|
||||
if is_key_down(Key_Code.CONTROL) {
|
||||
mods |= cast(u32)Key.MOD_CONTROL;
|
||||
if is_key_down(KeyCode.Control) {
|
||||
mods |= u32(Key.ModControl);
|
||||
}
|
||||
if is_key_down(Key_Code.MENU) {
|
||||
mods |= cast(u32)Key.MOD_ALT;
|
||||
if is_key_down(KeyCode.Menu) {
|
||||
mods |= u32(Key.ModAlt);
|
||||
}
|
||||
if is_key_down(Key_Code.LWIN) || is_key_down(Key_Code.RWIN) {
|
||||
mods |= cast(u32)Key.MOD_SUPER;
|
||||
if is_key_down(KeyCode.Lwin) || is_key_down(KeyCode.Rwin) {
|
||||
mods |= u32(Key.ModSuper);
|
||||
}
|
||||
|
||||
return mods;
|
||||
@@ -331,61 +341,62 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
return 0;
|
||||
|
||||
case WM_CLOSE:
|
||||
PostQuitMessage(0);
|
||||
post_quit_message(0);
|
||||
_core.running = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProcA(hwnd, msg, wparam, lparam);
|
||||
return def_window_proc_a(hwnd, msg, wparam, lparam);
|
||||
}
|
||||
|
||||
|
||||
window_class := WNDCLASSEXA{
|
||||
class_name = (cast(string)"Punity\x00").data, // C-style string
|
||||
size = size_of(WNDCLASSEXA),
|
||||
var class_name = "Punity\x00";
|
||||
var window_class = WndClassExA{
|
||||
class_name = &class_name[0],
|
||||
size = size_of(WndClassExA),
|
||||
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
|
||||
instance = cast(HINSTANCE)GetModuleHandleA(nil),
|
||||
instance = Hinstance(get_module_handle_a(nil)),
|
||||
wnd_proc = win32_proc,
|
||||
// wnd_proc = DefWindowProcA,
|
||||
background = cast(HBRUSH)GetStockObject(BLACK_BRUSH),
|
||||
background = Hbrush(get_stock_object(BLACK_BRUSH)),
|
||||
};
|
||||
|
||||
if RegisterClassExA(^window_class) == 0 {
|
||||
fmt.fprintln(os.stderr, "RegisterClassExA failed");
|
||||
if register_class_ex_a(&window_class) == 0 {
|
||||
fmt.fprintln(os.stderr, "register_class_ex_a failed");
|
||||
return;
|
||||
}
|
||||
|
||||
screen_width := GetSystemMetrics(SM_CXSCREEN);
|
||||
screen_height := GetSystemMetrics(SM_CYSCREEN);
|
||||
var screen_width = get_system_metrics(SM_CXSCREEN);
|
||||
var screen_height = get_system_metrics(SM_CYSCREEN);
|
||||
|
||||
rc: RECT;
|
||||
var rc: Rect;
|
||||
rc.left = (screen_width - WINDOW_WIDTH) / 2;
|
||||
rc.top = (screen_height - WINDOW_HEIGHT) / 2;
|
||||
rc.right = rc.left + WINDOW_WIDTH;
|
||||
rc.bottom = rc.top + WINDOW_HEIGHT;
|
||||
|
||||
style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||
assert(AdjustWindowRect(^rc, style, 0) != 0);
|
||||
var style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
|
||||
assert(adjust_window_rect(&rc, style, 0) != 0);
|
||||
|
||||
wt := WINDOW_TITLE;
|
||||
var wt = WINDOW_TITLE;
|
||||
|
||||
win32_window := CreateWindowExA(0,
|
||||
window_class.class_name,
|
||||
wt.data,
|
||||
style,
|
||||
rc.left, rc.top,
|
||||
rc.right-rc.left, rc.bottom-rc.top,
|
||||
nil, nil, window_class.instance,
|
||||
nil);
|
||||
var win32_window = create_window_ex_a(0,
|
||||
window_class.class_name,
|
||||
&wt[0],
|
||||
style,
|
||||
rc.left, rc.top,
|
||||
rc.right-rc.left, rc.bottom-rc.top,
|
||||
nil, nil, window_class.instance,
|
||||
nil);
|
||||
|
||||
if win32_window == nil {
|
||||
fmt.fprintln(os.stderr, "CreateWindowExA failed");
|
||||
fmt.fprintln(os.stderr, "create_window_ex_a failed");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
window_bmi: BITMAPINFO;
|
||||
window_bmi.size = size_of(BITMAPINFOHEADER);
|
||||
var window_bmi: BitmapInfo;
|
||||
window_bmi.size = size_of(BitmapInfoHeader);
|
||||
window_bmi.width = CANVAS_WIDTH;
|
||||
window_bmi.height = CANVAS_HEIGHT;
|
||||
window_bmi.planes = 1;
|
||||
@@ -393,27 +404,27 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
window_bmi.compression = BI_RGB;
|
||||
|
||||
|
||||
user_init(^_core);
|
||||
user_init(&_core);
|
||||
|
||||
ShowWindow(win32_window, SW_SHOW);
|
||||
show_window(win32_window, SW_SHOW);
|
||||
|
||||
window_buffer := new_slice(u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
||||
var window_buffer = make([]u32, CANVAS_WIDTH * CANVAS_HEIGHT);
|
||||
defer free(window_buffer);
|
||||
|
||||
|
||||
for i := 0; i < window_buffer.count; i += 1 {
|
||||
for _, i in window_buffer {
|
||||
window_buffer[i] = 0xff00ff;
|
||||
}
|
||||
|
||||
var (
|
||||
dt: f64;
|
||||
prev_time = time_now();
|
||||
curr_time = time_now();
|
||||
total_time : f64 = 0;
|
||||
offset_x = 0;
|
||||
offset_y = 0;
|
||||
)
|
||||
|
||||
dt: f64;
|
||||
prev_time := time_now();
|
||||
curr_time := time_now();
|
||||
total_time : f64 = 0;
|
||||
offset_x := 0;
|
||||
offset_y := 0;
|
||||
|
||||
message: MSG;
|
||||
var message: Msg;
|
||||
for _core.running {
|
||||
curr_time = time_now();
|
||||
dt = curr_time - prev_time;
|
||||
@@ -424,64 +435,62 @@ run :: proc(user_init, user_step: proc(c: ^Core)) {
|
||||
offset_y += 2;
|
||||
|
||||
{
|
||||
data: [128]byte;
|
||||
buf: fmt.Buffer;
|
||||
buf.data = data[:];
|
||||
fmt.bprintf(^buf, "Punity: %.4f ms\x00", dt*1000);
|
||||
win32.SetWindowTextA(win32_window, ^buf[0]);
|
||||
var buf: [128]u8;
|
||||
var s = fmt.bprintf(buf[..], "Punity: %.4f ms\x00", dt*1000);
|
||||
win32.set_window_text_a(win32_window, &s[0]);
|
||||
}
|
||||
|
||||
|
||||
for y := 0; y < CANVAS_HEIGHT; y += 1 {
|
||||
for x := 0; x < CANVAS_WIDTH; x += 1 {
|
||||
g := (x % 32) * 8;
|
||||
b := (y % 32) * 8;
|
||||
window_buffer[x + y*CANVAS_WIDTH] = cast(u32)(g << 8 | b);
|
||||
for var y = 0; y < CANVAS_HEIGHT; y++ {
|
||||
for var x = 0; x < CANVAS_WIDTH; x++ {
|
||||
var g = (x % 32) * 8;
|
||||
var b = (y % 32) * 8;
|
||||
window_buffer[x + y*CANVAS_WIDTH] = u32(g << 8 | b);
|
||||
}
|
||||
}
|
||||
|
||||
mem.zero(^_core.key_deltas[0], size_of_val(_core.key_deltas));
|
||||
mem.zero(&_core.key_deltas[0], size_of(_core.key_deltas));
|
||||
|
||||
for PeekMessageA(^message, nil, 0, 0, PM_REMOVE) != 0 {
|
||||
for peek_message_a(&message, nil, 0, 0, PM_REMOVE) != 0 {
|
||||
if message.message == WM_QUIT {
|
||||
_core.running = false;
|
||||
}
|
||||
TranslateMessage(^message);
|
||||
DispatchMessageA(^message);
|
||||
translate_message(&message);
|
||||
dispatch_message_a(&message);
|
||||
}
|
||||
|
||||
user_step(^_core);
|
||||
user_step(&_core);
|
||||
|
||||
dc := GetDC(win32_window);
|
||||
StretchDIBits(dc,
|
||||
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
||||
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||
window_buffer.data,
|
||||
^window_bmi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY);
|
||||
ReleaseDC(win32_window, dc);
|
||||
var dc = get_dc(win32_window);
|
||||
stretch_dibits(dc,
|
||||
0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
|
||||
0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
||||
&window_buffer[0],
|
||||
&window_bmi,
|
||||
DIB_RGB_COLORS,
|
||||
SRCCOPY);
|
||||
release_dc(win32_window, dc);
|
||||
|
||||
|
||||
{
|
||||
delta := time_now() - prev_time;
|
||||
ms := cast(i32)((FRAME_TIME - delta) * 1000);
|
||||
var delta = time_now() - prev_time;
|
||||
var ms = i32((FRAME_TIME - delta) * 1000);
|
||||
if ms > 0 {
|
||||
win32.Sleep(ms);
|
||||
win32.sleep(ms);
|
||||
}
|
||||
}
|
||||
|
||||
_core.frame += 1;
|
||||
_core.frame++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
user_init :: proc(c: ^Core) {
|
||||
proc main() {
|
||||
proc user_init(c: ^Core) {
|
||||
|
||||
}
|
||||
|
||||
user_step :: proc(c: ^Core) {
|
||||
proc user_step(c: ^Core) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#import "fmt.odin" as fmt
|
||||
|
||||
thing :: proc() {
|
||||
fmt.println("Sub Hello!")
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/*#import "fmt.odin"
|
||||
|
||||
thing :: proc() {
|
||||
fmt.println("Hello1!")
|
||||
}*/
|
||||
|
||||
|
||||
#import "fmt.odin" as format
|
||||
|
||||
thing :: proc() {
|
||||
format.println("Hello2!")
|
||||
}
|
||||
|
||||
|
||||
/*#import "fmt.odin" as .
|
||||
|
||||
thing :: proc() {
|
||||
println("Hello3!")
|
||||
}
|
||||
|
||||
|
||||
*/
|
||||
/*#import "fmt.odin" as _
|
||||
|
||||
thing :: proc() {
|
||||
// println("Hello4!")
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#include "fmt.odin"
|
||||
|
||||
thing :: proc() {
|
||||
println("Hello5!")
|
||||
}*/
|
||||
+942
-237
File diff suppressed because it is too large
Load Diff
+210
-142
@@ -1,158 +1,226 @@
|
||||
#shared_global_scope;
|
||||
|
||||
|
||||
// import "fmt.odin";
|
||||
|
||||
// proc __u128_mod(a, b: u128) -> u128 #link_name "__umodti3" {
|
||||
// var _, r := __u128_quo_mod(a, b)
|
||||
// return r
|
||||
// }
|
||||
|
||||
// proc __u128_quo(a, b: u128) -> u128 #link_name "__udivti3" {
|
||||
// var n, _ := __u128_quo_mod(a, b)
|
||||
// return n
|
||||
// }
|
||||
|
||||
// proc __i128_mod(a, b: i128) -> i128 #link_name "__modti3" {
|
||||
// var _, r := __i128_quo_mod(a, b)
|
||||
// return r
|
||||
// }
|
||||
|
||||
// proc __i128_quo(a, b: i128) -> i128 #link_name "__divti3" {
|
||||
// var n, _ := __i128_quo_mod(a, b)
|
||||
// return n
|
||||
// }
|
||||
|
||||
// proc __i128_quo_mod(a, b: i128) -> (i128, i128) #link_name "__divmodti4" {
|
||||
// var s := b >> 127
|
||||
// b = (b ~ s) - s
|
||||
// s = a >> 127
|
||||
// a = (a ~ s) - s
|
||||
|
||||
// var n, r := __u128_quo_mod(a as u128, b as u128)
|
||||
// return (n as i128 ~ s) - s, (r as i128 ~ s) - s
|
||||
// }
|
||||
__multi3 :: proc(a, b: u128) -> u128 #cc_c #link_name "__multi3" {
|
||||
bits_in_dword_2 :: size_of(i64) * 4;
|
||||
lower_mask :: u128(~u64(0) >> bits_in_dword_2);
|
||||
|
||||
|
||||
// proc __u128_quo_mod(a, b: u128) -> (u128, u128) #link_name "__udivmodti4" {
|
||||
// proc clz(x: u64) -> u64 {
|
||||
// proc clz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.ctlz.i64"
|
||||
// return clz_u64(x, false)
|
||||
// }
|
||||
// proc ctz(x: u64) -> u64 {
|
||||
// proc ctz_u64(x: u64, is_zero_undef: bool) -> u64 #foreign "llvm.cttz.i64"
|
||||
// return ctz_u64(x, false)
|
||||
// }
|
||||
TWords :: struct #raw_union {
|
||||
all: u128;
|
||||
using _: struct {
|
||||
when ODIN_ENDIAN == "big" {
|
||||
lo, hi: u64;
|
||||
} else {
|
||||
hi, lo: u64;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
r: TWords;
|
||||
t: u64;
|
||||
|
||||
r.lo = u64(a & lower_mask) * u64(b & lower_mask);
|
||||
t = r.lo >> bits_in_dword_2;
|
||||
r.lo &= u64(lower_mask);
|
||||
t += u64(a >> bits_in_dword_2) * u64(b & lower_mask);
|
||||
r.lo += u64(t & u64(lower_mask)) << bits_in_dword_2;
|
||||
r.hi = t >> bits_in_dword_2;
|
||||
t = r.lo >> bits_in_dword_2;
|
||||
r.lo &= u64(lower_mask);
|
||||
t += u64(b >> bits_in_dword_2) * u64(a & lower_mask);
|
||||
r.lo += u64(t & u64(lower_mask)) << bits_in_dword_2;
|
||||
r.hi += t >> bits_in_dword_2;
|
||||
r.hi += u64(a >> bits_in_dword_2) * u64(b >> bits_in_dword_2);
|
||||
return r.all;
|
||||
}
|
||||
|
||||
__u128_mod :: proc(a, b: u128) -> u128 #cc_c #link_name "__umodti3" {
|
||||
r: u128;
|
||||
__u128_quo_mod(a, b, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__u128_quo :: proc(a, b: u128) -> u128 #cc_c #link_name "__udivti3" {
|
||||
return __u128_quo_mod(a, b, nil);
|
||||
}
|
||||
|
||||
__i128_mod :: proc(a, b: i128) -> i128 #cc_c #link_name "__modti3" {
|
||||
r: i128;
|
||||
__i128_quo_mod(a, b, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
__i128_quo :: proc(a, b: i128) -> i128 #cc_c #link_name "__divti3" {
|
||||
return __i128_quo_mod(a, b, nil);
|
||||
}
|
||||
|
||||
__i128_quo_mod :: proc(a, b: i128, rem: ^i128) -> (quo: i128) #cc_c #link_name "__divmodti4" {
|
||||
s: i128;
|
||||
s = b >> 127;
|
||||
b = (b~s) - s;
|
||||
s = a >> 127;
|
||||
b = (a~s) - s;
|
||||
|
||||
uquo: u128;
|
||||
urem := __u128_quo_mod(transmute(u128)a, transmute(u128)b, &uquo);
|
||||
iquo := transmute(i128)uquo;
|
||||
irem := transmute(i128)urem;
|
||||
|
||||
iquo = (iquo~s) - s;
|
||||
irem = (irem~s) - s;
|
||||
if rem != nil do rem^ = irem;
|
||||
return iquo;
|
||||
}
|
||||
|
||||
|
||||
// u128_lo_hi :: raw_union {
|
||||
// all: u128
|
||||
// using _lohi: struct {lo, hi: u64;}
|
||||
// }
|
||||
__u128_quo_mod :: proc(a, b: u128, rem: ^u128) -> (quo: u128) #cc_c #link_name "__udivmodti4" {
|
||||
alo, ahi := u64(a), u64(a>>64);
|
||||
blo, bhi := u64(b), u64(b>>64);
|
||||
if b == 0 {
|
||||
if rem != nil do rem^ = 0;
|
||||
return u128(alo/blo);
|
||||
}
|
||||
|
||||
// n, d, q, r: u128_lo_hi
|
||||
// sr: u64
|
||||
r, d, x, q: u128 = a, b, 1, 0;
|
||||
|
||||
// n.all = a
|
||||
// d.all = b
|
||||
for r >= d && (d>>127)&1 == 0 {
|
||||
x <<= 1;
|
||||
d <<= 1;
|
||||
}
|
||||
|
||||
// if n.hi == 0 {
|
||||
// if d.hi == 0 {
|
||||
// return (n.lo / d.lo) as u128, (n.lo % d.lo) as u128
|
||||
// }
|
||||
// return 0, n.lo as u128
|
||||
// }
|
||||
// if d.lo == 0 {
|
||||
// if d.hi == 0 {
|
||||
// return (n.hi / d.lo) as u128, (n.hi % d.lo) as u128
|
||||
// }
|
||||
// if n.lo == 0 {
|
||||
// r.hi = n.hi % d.hi
|
||||
// r.lo = 0
|
||||
// return (n.hi / d.hi) as u128, r.all
|
||||
// }
|
||||
// if (d.hi & (d.hi-1)) == 0 {
|
||||
// r.lo = n.lo
|
||||
// r.hi = n.hi & (d.hi-1)
|
||||
// return (n.hi >> ctz(d.hi)) as u128, r.all
|
||||
// }
|
||||
for x != 0 {
|
||||
if r >= d {
|
||||
r -= d;
|
||||
q |= x;
|
||||
}
|
||||
x >>= 1;
|
||||
d >>= 1;
|
||||
}
|
||||
|
||||
// sr = clz(d.hi) - clz(n.hi)
|
||||
// if sr > 64 - 2 {
|
||||
// return 0, n.all
|
||||
// }
|
||||
// sr++
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// if d.hi == 0 {
|
||||
// if (d.lo & (d.lo - 1)) == 0 {
|
||||
// var rem := (n.lo % (d.lo - 1)) as u128
|
||||
// if d.lo == 1 {
|
||||
// return n.all, rem
|
||||
// }
|
||||
// sr = ctz(d.lo)
|
||||
// q.hi = n.hi >> sr
|
||||
// q.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// return q.all, rem
|
||||
// }
|
||||
if rem != nil do rem^ = r;
|
||||
return q;
|
||||
}
|
||||
|
||||
// sr = 1 + 64 + clz(d.lo) - clz(n.hi)
|
||||
/*
|
||||
__f16_to_f32 :: proc(f: f16) -> f32 #cc_c #no_inline #link_name "__gnu_h2f_ieee" {
|
||||
when true {
|
||||
// Source: https://fgiesen.wordpress.com/2012/03/28/half-to-float-done-quic/
|
||||
FP32 :: struct #raw_union {u: u32, f: f32};
|
||||
|
||||
// q.all = n.all << (128-sr)
|
||||
// r.all = n.all >> sr
|
||||
// if sr == 64 {
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo
|
||||
// r.hi = 0
|
||||
// r.lo = n.hi
|
||||
// } else if sr < 64 {
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// q.lo = n.lo << (128-sr)
|
||||
// q.hi = (n.hi << (128-sr)) | (n.lo >> (sr-64))
|
||||
// r.hi = 0
|
||||
// r.lo = n.hi >> (sr-64)
|
||||
// }
|
||||
// } else {
|
||||
// sr = clz(d.hi) - clz(n.hi)
|
||||
// if sr > 64-1 {
|
||||
// return 0, n.all
|
||||
// }
|
||||
// sr++
|
||||
// q.lo = 0
|
||||
// q.hi = n.lo << (64-sr)
|
||||
// r.all = n.all >> sr
|
||||
// if sr < 64 {
|
||||
// r.hi = n.hi >> sr
|
||||
// r.lo = (n.hi << (64-sr)) | (n.lo >> sr)
|
||||
// } else {
|
||||
// r.hi = 0
|
||||
// r.lo = n.hi
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
magic, was_infnan: FP32;
|
||||
magic.u = (254-15) << 23;
|
||||
was_infnan.u = (127+16) << 23;
|
||||
|
||||
// carry: u64
|
||||
// for ; sr > 0; sr-- {
|
||||
// r.hi = (r.hi << 1) | (r.lo >> (64-1))
|
||||
// r.lo = (r.lo << 1) | (r.hi >> (64-1))
|
||||
// q.hi = (q.hi << 1) | (q.lo >> (64-1))
|
||||
// q.lo = (q.lo << 1) | carry
|
||||
hu := transmute(u16, f);
|
||||
|
||||
// carry = 0
|
||||
// if r.all >= d.all {
|
||||
// r.all -= d.all
|
||||
// carry = 1
|
||||
// }
|
||||
// }
|
||||
o := FP32{};
|
||||
|
||||
// q.all = (q.all << 1) | (carry as u128)
|
||||
// return q.all, r.all
|
||||
// }
|
||||
o.u = u32(hu & 0x7fff) << 13);
|
||||
o.f *= magic.f;
|
||||
if o.f >= was_infnan.f {
|
||||
o.u |= 255 << 23;
|
||||
}
|
||||
o.u |= u32(hu & 0x8000) << 16;
|
||||
return o.f;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
__f32_to_f16 :: proc(f_: f32) -> f16 #cc_c #no_inline #link_name "__gnu_f2h_ieee" {
|
||||
when false {
|
||||
// Source: https://gist.github.com/rygorous/2156668
|
||||
FP16 :: struct #raw_union {u: u16, f: f16};
|
||||
FP32 :: struct #raw_union {u: u32, f: f32};
|
||||
|
||||
f32infty, f16infty, magic: FP32;
|
||||
f32infty.u = 255<<23;
|
||||
f16infty.u = 31<<23;
|
||||
magic.u = 15<<23;
|
||||
|
||||
sign_mask :: u32(0x80000000);
|
||||
round_mask :: ~u32(0x0fff);
|
||||
|
||||
f := transmute(FP32, f_);
|
||||
|
||||
o: FP16;
|
||||
sign := f.u & sign_mask;
|
||||
f.u ~= sign;
|
||||
|
||||
// NOTE all the integer compares in this function can be safely
|
||||
// compiled into signed compares since all operands are below
|
||||
// 0x80000000. Important if you want fast straight SSE2 code
|
||||
// (since there's no unsigned PCMPGTD).
|
||||
|
||||
if f.u >= f32infty.u { // Inf or NaN (all exponent bits set)
|
||||
o.u = f.u > f32infty.u ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf
|
||||
} else { // (De)normalized number or zero
|
||||
f.u &= round_mask;
|
||||
f.f *= magic.f;
|
||||
f.u -= round_mask;
|
||||
if f.u > f16infty.u {
|
||||
f.u = f16infty.u; // Clamp to signed infinity if overflowed
|
||||
}
|
||||
|
||||
o.u = u16(f.u >> 13); // Take the bits!
|
||||
}
|
||||
|
||||
o.u |= u16(sign >> 16);
|
||||
return o.f;
|
||||
} else {
|
||||
f := transmute(u32, f_);
|
||||
h: u16;
|
||||
hs, he, hf: u16;
|
||||
|
||||
fs := (f >> 31) & 1;
|
||||
fe := (f >> 23) & 0b1111_1111;
|
||||
ff := (f >> 0) & 0b0111_1111_1111_1111_1111_1111;
|
||||
|
||||
add_one := false;
|
||||
|
||||
if (fe == 0) {
|
||||
he = 0;
|
||||
} else if (fe == 255) {
|
||||
he = 31;
|
||||
hf = ff != 0 ? 0x200 : 0;
|
||||
} else {
|
||||
ne := fe - 127 + 15;
|
||||
if ne >= 31 {
|
||||
he = 31;
|
||||
} else if ne <= 0 {
|
||||
if (14-ne) <= 24 {
|
||||
mant := ff | 0x800000;
|
||||
hf = u16(mant >> (14-ne));
|
||||
|
||||
if (mant >> (13-ne)) & 1 != 0 {
|
||||
add_one = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
he = u16(ne);
|
||||
hf = u16(ff >> 13);
|
||||
if ff&0x1000 != 0 {
|
||||
add_one = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hs = u16(hs);
|
||||
h |= (he&0b0001_1111)<<10;
|
||||
h |= (hf&0b0011_1111_1111);
|
||||
if add_one {
|
||||
h++;
|
||||
}
|
||||
h |= (hs&1) << 15;
|
||||
return transmute(f16, h);
|
||||
}
|
||||
}
|
||||
|
||||
__f64_to_f16 :: proc(f: f64) -> f16 #cc_c #no_inline #link_name "__truncdfhf2" {
|
||||
return __f32_to_f16(f32(f));
|
||||
}
|
||||
|
||||
__f16_to_f64 :: proc(f: f16) -> f64 #cc_c #no_inline {
|
||||
return f64(__f16_to_f32(f));
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// TODO(bill): Use assembly instead here to implement atomics
|
||||
// Inline vs external file?
|
||||
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
_ := compile_assert(ODIN_ARCH == "amd64"); // TODO(bill): x86 version
|
||||
|
||||
|
||||
yield_thread :: proc() { win32._mm_pause(); }
|
||||
mfence :: proc() { win32.ReadWriteBarrier(); }
|
||||
sfence :: proc() { win32.WriteBarrier(); }
|
||||
lfence :: proc() { win32.ReadBarrier(); }
|
||||
yield_thread :: proc() { win32.mm_pause(); }
|
||||
mfence :: proc() { win32.read_write_barrier(); }
|
||||
sfence :: proc() { win32.write_barrier(); }
|
||||
lfence :: proc() { win32.read_barrier(); }
|
||||
|
||||
|
||||
load :: proc(a: ^i32) -> i32 {
|
||||
@@ -18,20 +18,20 @@ store :: proc(a: ^i32, value: i32) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange :: proc(a: ^i32, expected, desired: i32) -> i32 {
|
||||
return win32.InterlockedCompareExchange(a, desired, expected);
|
||||
return win32.interlocked_compare_exchange(a, desired, expected);
|
||||
}
|
||||
exchanged :: proc(a: ^i32, desired: i32) -> i32 {
|
||||
return win32.InterlockedExchange(a, desired);
|
||||
return win32.interlocked_exchange(a, desired);
|
||||
}
|
||||
fetch_add :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedExchangeAdd(a, operand);
|
||||
return win32.interlocked_exchange_add(a, operand);
|
||||
|
||||
}
|
||||
fetch_and :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedAnd(a, operand);
|
||||
return win32.interlocked_and(a, operand);
|
||||
}
|
||||
fetch_or :: proc(a: ^i32, operand: i32) -> i32 {
|
||||
return win32.InterlockedOr(a, operand);
|
||||
return win32.interlocked_or(a, operand);
|
||||
}
|
||||
spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
@@ -63,19 +63,19 @@ store :: proc(a: ^i64, value: i64) {
|
||||
a^ = value;
|
||||
}
|
||||
compare_exchange :: proc(a: ^i64, expected, desired: i64) -> i64 {
|
||||
return win32.InterlockedCompareExchange64(a, desired, expected);
|
||||
return win32.interlocked_compare_exchange64(a, desired, expected);
|
||||
}
|
||||
exchanged :: proc(a: ^i64, desired: i64) -> i64 {
|
||||
return win32.InterlockedExchange64(a, desired);
|
||||
return win32.interlocked_exchange64(a, desired);
|
||||
}
|
||||
fetch_add :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedExchangeAdd64(a, operand);
|
||||
return win32.interlocked_exchange_add64(a, operand);
|
||||
}
|
||||
fetch_and :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedAnd64(a, operand);
|
||||
return win32.interlocked_and64(a, operand);
|
||||
}
|
||||
fetch_or :: proc(a: ^i64, operand: i64) -> i64 {
|
||||
return win32.InterlockedOr64(a, operand);
|
||||
return win32.interlocked_or64(a, operand);
|
||||
}
|
||||
spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 as default
|
||||
old_value := compare_exchange(a, 1, 0);
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
U8_MIN :: u8(0);
|
||||
U16_MIN :: u16(0);
|
||||
U32_MIN :: u32(0);
|
||||
U64_MIN :: u64(0);
|
||||
U128_MIN :: u128(0);
|
||||
|
||||
I8_MIN :: i8(-0x80);
|
||||
I16_MIN :: i16(-0x8000);
|
||||
I32_MIN :: i32(-0x8000_0000);
|
||||
I64_MIN :: i64(-0x8000_0000_0000_0000);
|
||||
I128_MIN :: i128(-0x8000_0000_0000_0000_0000_0000_0000_0000);
|
||||
|
||||
U8_MAX :: ~u8(0);
|
||||
U16_MAX :: ~u16(0);
|
||||
U32_MAX :: ~u32(0);
|
||||
U64_MAX :: ~u64(0);
|
||||
U128_MAX :: ~u128(0);
|
||||
|
||||
I8_MAX :: i8(0x7f);
|
||||
I16_MAX :: i16(0x7fff);
|
||||
I32_MAX :: i32(0x7fff_ffff);
|
||||
I64_MAX :: i64(0x7fff_ffff_ffff_ffff);
|
||||
I128_MAX :: i128(0x7fff_ffff_ffff_ffff_ffff_ffff_ffff_ffff);
|
||||
|
||||
count_ones :: proc(i: u8) -> u8 { foreign __llvm_core __llvm_ctpop :: proc(u8) -> u8 #link_name "llvm.ctpop.i8" ---; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_ctpop :: proc(i8) -> i8 #link_name "llvm.ctpop.i8" ---; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_ctpop :: proc(u16) -> u16 #link_name "llvm.ctpop.i16" ---; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_ctpop :: proc(i16) -> i16 #link_name "llvm.ctpop.i16" ---; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_ctpop :: proc(u32) -> u32 #link_name "llvm.ctpop.i32" ---; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_ctpop :: proc(i32) -> i32 #link_name "llvm.ctpop.i32" ---; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_ctpop :: proc(u64) -> u64 #link_name "llvm.ctpop.i64" ---; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_ctpop :: proc(i64) -> i64 #link_name "llvm.ctpop.i64" ---; return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_ctpop :: proc(u128) -> u128 #link_name "llvm.ctpop.i128" ---;return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_ctpop :: proc(i128) -> i128 #link_name "llvm.ctpop.i128" ---;return __llvm_ctpop(i); }
|
||||
count_ones :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(count_ones(u32(i))); } else { return uint(count_ones(u64(i))); } }
|
||||
count_ones :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(count_ones(i32(i))); } else { return int(count_ones(i64(i))); } }
|
||||
|
||||
count_zeros :: proc(i: u8) -> u8 { return 8 - count_ones(i); }
|
||||
count_zeros :: proc(i: i8) -> i8 { return 8 - count_ones(i); }
|
||||
count_zeros :: proc(i: u16) -> u16 { return 16 - count_ones(i); }
|
||||
count_zeros :: proc(i: i16) -> i16 { return 16 - count_ones(i); }
|
||||
count_zeros :: proc(i: u32) -> u32 { return 32 - count_ones(i); }
|
||||
count_zeros :: proc(i: i32) -> i32 { return 32 - count_ones(i); }
|
||||
count_zeros :: proc(i: u64) -> u64 { return 64 - count_ones(i); }
|
||||
count_zeros :: proc(i: i64) -> i64 { return 64 - count_ones(i); }
|
||||
count_zeros :: proc(i: u128) -> u128 { return 128 - count_ones(i); }
|
||||
count_zeros :: proc(i: i128) -> i128 { return 128 - count_ones(i); }
|
||||
count_zeros :: proc(i: uint) -> uint { return 8*size_of(uint) - count_ones(i); }
|
||||
count_zeros :: proc(i: int) -> int { return 8*size_of(int) - count_ones(i); }
|
||||
|
||||
|
||||
rotate_left :: proc(i: u8, s: uint) -> u8 { return (i << s)|(i >> (8*size_of(u8) - s)); }
|
||||
rotate_left :: proc(i: i8, s: uint) -> i8 { return (i << s)|(i >> (8*size_of(i8) - s)); }
|
||||
rotate_left :: proc(i: u16, s: uint) -> u16 { return (i << s)|(i >> (8*size_of(u16) - s)); }
|
||||
rotate_left :: proc(i: i16, s: uint) -> i16 { return (i << s)|(i >> (8*size_of(i16) - s)); }
|
||||
rotate_left :: proc(i: u32, s: uint) -> u32 { return (i << s)|(i >> (8*size_of(u32) - s)); }
|
||||
rotate_left :: proc(i: i32, s: uint) -> i32 { return (i << s)|(i >> (8*size_of(i32) - s)); }
|
||||
rotate_left :: proc(i: u64, s: uint) -> u64 { return (i << s)|(i >> (8*size_of(u64) - s)); }
|
||||
rotate_left :: proc(i: i64, s: uint) -> i64 { return (i << s)|(i >> (8*size_of(i64) - s)); }
|
||||
rotate_left :: proc(i: u128, s: uint) -> u128 { return (i << s)|(i >> (8*size_of(u128) - s)); }
|
||||
rotate_left :: proc(i: i128, s: uint) -> i128 { return (i << s)|(i >> (8*size_of(i128) - s)); }
|
||||
rotate_left :: proc(i: uint, s: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(rotate_left(u32(i), s)); } else { return uint(rotate_left(u64(i), s)); } }
|
||||
rotate_left :: proc(i: int, s: uint) -> int { when size_of(int) == size_of(i32) { return int(rotate_left(i32(i), s)); } else { return int(rotate_left(i64(i), s)); } }
|
||||
|
||||
|
||||
rotate_right :: proc(i: u8, s: uint) -> u8 { return (i >> s)|(i << (8*size_of(u8) - s)); }
|
||||
rotate_right :: proc(i: i8, s: uint) -> i8 { return (i >> s)|(i << (8*size_of(i8) - s)); }
|
||||
rotate_right :: proc(i: u16, s: uint) -> u16 { return (i >> s)|(i << (8*size_of(u16) - s)); }
|
||||
rotate_right :: proc(i: i16, s: uint) -> i16 { return (i >> s)|(i << (8*size_of(i16) - s)); }
|
||||
rotate_right :: proc(i: u32, s: uint) -> u32 { return (i >> s)|(i << (8*size_of(u32) - s)); }
|
||||
rotate_right :: proc(i: i32, s: uint) -> i32 { return (i >> s)|(i << (8*size_of(i32) - s)); }
|
||||
rotate_right :: proc(i: u64, s: uint) -> u64 { return (i >> s)|(i << (8*size_of(u64) - s)); }
|
||||
rotate_right :: proc(i: i64, s: uint) -> i64 { return (i >> s)|(i << (8*size_of(i64) - s)); }
|
||||
rotate_right :: proc(i: u128, s: uint) -> u128 { return (i >> s)|(i << (8*size_of(u128) - s)); }
|
||||
rotate_right :: proc(i: i128, s: uint) -> i128 { return (i >> s)|(i << (8*size_of(i128) - s)); }
|
||||
rotate_right :: proc(i: uint, s: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(rotate_right(u32(i), s)); } else { return uint(rotate_right(u64(i), s)); } }
|
||||
rotate_right :: proc(i: int, s: uint) -> int { when size_of(int) == size_of(i32) { return int(rotate_right(i32(i), s)); } else { return int(rotate_right(i64(i), s)); } }
|
||||
|
||||
|
||||
leading_zeros :: proc(i: u8) -> u8 { foreign __llvm_core __llvm_ctlz :: proc(u8, bool) -> u8 #link_name "llvm.ctlz.i8" ---; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_ctlz :: proc(i8, bool) -> i8 #link_name "llvm.ctlz.i8" ---; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_ctlz :: proc(u16, bool) -> u16 #link_name "llvm.ctlz.i16" ---; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_ctlz :: proc(i16, bool) -> i16 #link_name "llvm.ctlz.i16" ---; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_ctlz :: proc(u32, bool) -> u32 #link_name "llvm.ctlz.i32" ---; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_ctlz :: proc(i32, bool) -> i32 #link_name "llvm.ctlz.i32" ---; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_ctlz :: proc(u64, bool) -> u64 #link_name "llvm.ctlz.i64" ---; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_ctlz :: proc(i64, bool) -> i64 #link_name "llvm.ctlz.i64" ---; return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_ctlz :: proc(u128, bool) -> u128 #link_name "llvm.ctlz.i128" ---;return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_ctlz :: proc(i128, bool) -> i128 #link_name "llvm.ctlz.i128" ---;return __llvm_ctlz(i, false); }
|
||||
leading_zeros :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(leading_zeros(u32(i))); } else { return uint(leading_zeros(u64(i))); } }
|
||||
leading_zeros :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(leading_zeros(i32(i))); } else { return int(leading_zeros(i64(i))); } }
|
||||
|
||||
trailing_zeros :: proc(i: u8) -> u8 { foreign __llvm_core __llvm_cttz :: proc(u8, bool) -> u8 #link_name "llvm.cttz.i8" ---; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_cttz :: proc(i8, bool) -> i8 #link_name "llvm.cttz.i8" ---; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_cttz :: proc(u16, bool) -> u16 #link_name "llvm.cttz.i16" ---; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_cttz :: proc(i16, bool) -> i16 #link_name "llvm.cttz.i16" ---; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_cttz :: proc(u32, bool) -> u32 #link_name "llvm.cttz.i32" ---; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_cttz :: proc(i32, bool) -> i32 #link_name "llvm.cttz.i32" ---; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_cttz :: proc(u64, bool) -> u64 #link_name "llvm.cttz.i64" ---; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_cttz :: proc(i64, bool) -> i64 #link_name "llvm.cttz.i64" ---; return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_cttz :: proc(u128, bool) -> u128 #link_name "llvm.cttz.i128" ---;return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_cttz :: proc(i128, bool) -> i128 #link_name "llvm.cttz.i128" ---;return __llvm_cttz(i, false); }
|
||||
trailing_zeros :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(trailing_zeros(u32(i))); } else { return uint(trailing_zeros(u64(i))); } }
|
||||
trailing_zeros :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(trailing_zeros(i32(i))); } else { return int(trailing_zeros(i64(i))); } }
|
||||
|
||||
|
||||
reverse_bits :: proc(i: u8) -> u8 { foreign __llvm_core __llvm_bitreverse :: proc(u8) -> u8 #link_name "llvm.bitreverse.i8" ---; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i8) -> i8 { foreign __llvm_core __llvm_bitreverse :: proc(i8) -> i8 #link_name "llvm.bitreverse.i8" ---; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: u16) -> u16 { foreign __llvm_core __llvm_bitreverse :: proc(u16) -> u16 #link_name "llvm.bitreverse.i16" ---; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i16) -> i16 { foreign __llvm_core __llvm_bitreverse :: proc(i16) -> i16 #link_name "llvm.bitreverse.i16" ---; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: u32) -> u32 { foreign __llvm_core __llvm_bitreverse :: proc(u32) -> u32 #link_name "llvm.bitreverse.i32" ---; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i32) -> i32 { foreign __llvm_core __llvm_bitreverse :: proc(i32) -> i32 #link_name "llvm.bitreverse.i32" ---; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: u64) -> u64 { foreign __llvm_core __llvm_bitreverse :: proc(u64) -> u64 #link_name "llvm.bitreverse.i64" ---; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i64) -> i64 { foreign __llvm_core __llvm_bitreverse :: proc(i64) -> i64 #link_name "llvm.bitreverse.i64" ---; return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: u128) -> u128 { foreign __llvm_core __llvm_bitreverse :: proc(u128) -> u128 #link_name "llvm.bitreverse.i128" ---;return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: i128) -> i128 { foreign __llvm_core __llvm_bitreverse :: proc(i128) -> i128 #link_name "llvm.bitreverse.i128" ---;return __llvm_bitreverse(i); }
|
||||
reverse_bits :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(reverse_bits(u32(i))); } else { return uint(reverse_bits(u64(i))); } }
|
||||
reverse_bits :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(reverse_bits(i32(i))); } else { return int(reverse_bits(i64(i))); } }
|
||||
|
||||
foreign __llvm_core {
|
||||
byte_swap :: proc(u16) -> u16 #link_name "llvm.bswap.i16" ---;
|
||||
byte_swap :: proc(i16) -> i16 #link_name "llvm.bswap.i16" ---;
|
||||
byte_swap :: proc(u32) -> u32 #link_name "llvm.bswap.i32" ---;
|
||||
byte_swap :: proc(i32) -> i32 #link_name "llvm.bswap.i32" ---;
|
||||
byte_swap :: proc(u64) -> u64 #link_name "llvm.bswap.i64" ---;
|
||||
byte_swap :: proc(i64) -> i64 #link_name "llvm.bswap.i64" ---;
|
||||
byte_swap :: proc(u128) -> u128 #link_name "llvm.bswap.i128" ---;
|
||||
byte_swap :: proc(i128) -> i128 #link_name "llvm.bswap.i128" ---;
|
||||
}
|
||||
byte_swap :: proc(i: uint) -> uint { when size_of(uint) == size_of(u32) { return uint(byte_swap(u32(i))); } else { return uint(byte_swap(u64(i))); } }
|
||||
byte_swap :: proc(i: int) -> int { when size_of(int) == size_of(i32) { return int(byte_swap(i32(i))); } else { return int(byte_swap(i64(i))); } }
|
||||
|
||||
from_be :: proc(i: u8) -> u8 { return i; }
|
||||
from_be :: proc(i: i8) -> i8 { return i; }
|
||||
from_be :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
from_be :: proc(i: int) -> int { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
from_le :: proc(i: u8) -> u8 { return i; }
|
||||
from_le :: proc(i: i8) -> i8 { return i; }
|
||||
from_le :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
from_le :: proc(i: int) -> int { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
to_be :: proc(i: u8) -> u8 { return i; }
|
||||
to_be :: proc(i: i8) -> i8 { return i; }
|
||||
to_be :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: uint) -> uint { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
to_be :: proc(i: int) -> int { when ODIN_ENDIAN == "big" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
to_le :: proc(i: u8) -> u8 { return i; }
|
||||
to_le :: proc(i: i8) -> i8 { return i; }
|
||||
to_le :: proc(i: u16) -> u16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: i16) -> i16 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: u32) -> u32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: i32) -> i32 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: u64) -> u64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: i64) -> i64 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: u128) -> u128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: i128) -> i128 { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: uint) -> uint { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
to_le :: proc(i: int) -> int { when ODIN_ENDIAN == "little" { return i; } else { return byte_swap(i); } }
|
||||
|
||||
|
||||
overflowing_add :: proc(lhs, rhs: u8) -> (u8, bool) { foreign __llvm_core op :: proc(u8, u8) -> (u8, bool) #link_name "llvm.uadd.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core op :: proc(i8, i8) -> (i8, bool) #link_name "llvm.sadd.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core op :: proc(u16, u16) -> (u16, bool) #link_name "llvm.uadd.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core op :: proc(i16, i16) -> (i16, bool) #link_name "llvm.sadd.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core op :: proc(u32, u32) -> (u32, bool) #link_name "llvm.uadd.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core op :: proc(i32, i32) -> (i32, bool) #link_name "llvm.sadd.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core op :: proc(u64, u64) -> (u64, bool) #link_name "llvm.uadd.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core op :: proc(i64, i64) -> (i64, bool) #link_name "llvm.sadd.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: u128) -> (u128, bool) { foreign __llvm_core op :: proc(u128, u128) -> (u128, bool) #link_name "llvm.uadd.with.overflow.i128" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: i128) -> (i128, bool) { foreign __llvm_core op :: proc(i128, i128) -> (i128, bool) #link_name "llvm.sadd.with.overflow.i128" ---; return op(lhs, rhs); }
|
||||
overflowing_add :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_add(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_add :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_add(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_add(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_sub :: proc(lhs, rhs: u8) -> (u8, bool) { foreign __llvm_core op :: proc(u8, u8) -> (u8, bool) #link_name "llvm.usub.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core op :: proc(i8, i8) -> (i8, bool) #link_name "llvm.ssub.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core op :: proc(u16, u16) -> (u16, bool) #link_name "llvm.usub.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core op :: proc(i16, i16) -> (i16, bool) #link_name "llvm.ssub.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core op :: proc(u32, u32) -> (u32, bool) #link_name "llvm.usub.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core op :: proc(i32, i32) -> (i32, bool) #link_name "llvm.ssub.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core op :: proc(u64, u64) -> (u64, bool) #link_name "llvm.usub.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core op :: proc(i64, i64) -> (i64, bool) #link_name "llvm.ssub.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: u128) -> (u128, bool) { foreign __llvm_core op :: proc(u128, u128) -> (u128, bool) #link_name "llvm.usub.with.overflow.i128" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: i128) -> (i128, bool) { foreign __llvm_core op :: proc(i128, i128) -> (i128, bool) #link_name "llvm.ssub.with.overflow.i128" ---; return op(lhs, rhs); }
|
||||
overflowing_sub :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_sub(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_sub :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_sub(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_sub(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
overflowing_mul :: proc(lhs, rhs: u8) -> (u8, bool) { foreign __llvm_core op :: proc(u8, u8) -> (u8, bool) #link_name "llvm.umul.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i8) -> (i8, bool) { foreign __llvm_core op :: proc(i8, i8) -> (i8, bool) #link_name "llvm.smul.with.overflow.i8" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: u16) -> (u16, bool) { foreign __llvm_core op :: proc(u16, u16) -> (u16, bool) #link_name "llvm.umul.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i16) -> (i16, bool) { foreign __llvm_core op :: proc(i16, i16) -> (i16, bool) #link_name "llvm.smul.with.overflow.i16" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: u32) -> (u32, bool) { foreign __llvm_core op :: proc(u32, u32) -> (u32, bool) #link_name "llvm.umul.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i32) -> (i32, bool) { foreign __llvm_core op :: proc(i32, i32) -> (i32, bool) #link_name "llvm.smul.with.overflow.i32" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: u64) -> (u64, bool) { foreign __llvm_core op :: proc(u64, u64) -> (u64, bool) #link_name "llvm.umul.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i64) -> (i64, bool) { foreign __llvm_core op :: proc(i64, i64) -> (i64, bool) #link_name "llvm.smul.with.overflow.i64" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: u128) -> (u128, bool) { foreign __llvm_core op :: proc(u128, u128) -> (u128, bool) #link_name "llvm.umul.with.overflow.i128" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: i128) -> (i128, bool) { foreign __llvm_core op :: proc(i128, i128) -> (i128, bool) #link_name "llvm.smul.with.overflow.i128" ---; return op(lhs, rhs); }
|
||||
overflowing_mul :: proc(lhs, rhs: uint) -> (uint, bool) {
|
||||
when size_of(uint) == size_of(u32) {
|
||||
x, ok := overflowing_mul(u32(lhs), u32(rhs));
|
||||
return uint(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul(u64(lhs), u64(rhs));
|
||||
return uint(x), ok;
|
||||
}
|
||||
}
|
||||
overflowing_mul :: proc(lhs, rhs: int) -> (int, bool) {
|
||||
when size_of(int) == size_of(i32) {
|
||||
x, ok := overflowing_mul(i32(lhs), i32(rhs));
|
||||
return int(x), ok;
|
||||
} else {
|
||||
x, ok := overflowing_mul(i64(lhs), i64(rhs));
|
||||
return int(x), ok;
|
||||
}
|
||||
}
|
||||
|
||||
is_power_of_two :: proc(i: u8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i8) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: u16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i16) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: u32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i32) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: u64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i64) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: u128) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: i128) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: uint) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
is_power_of_two :: proc(i: int) -> bool { return i > 0 && (i & (i-1)) == 0; }
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
CHAR_BIT :: 8;
|
||||
|
||||
c_bool :: bool;
|
||||
|
||||
c_char :: u8;
|
||||
|
||||
c_schar :: i8;
|
||||
c_uchar :: i8;
|
||||
|
||||
c_short :: i16;
|
||||
c_ushort :: i16;
|
||||
|
||||
c_int :: i32;
|
||||
c_uint :: u32;
|
||||
|
||||
c_long :: ODIN_OS == "windows" ?
|
||||
i32 :
|
||||
(size_of(int) == 4) ?
|
||||
i32 :
|
||||
i64;
|
||||
|
||||
c_ulong :: ODIN_OS == "windows" ?
|
||||
u32 :
|
||||
(size_of(int) == 4) ?
|
||||
u32 :
|
||||
u64;
|
||||
|
||||
c_longlong :: i64;
|
||||
c_ulonglong :: u64;
|
||||
|
||||
c_float :: f32;
|
||||
c_double :: f64;
|
||||
|
||||
c_complex_float :: complex64;
|
||||
c_complex_double :: complex128;
|
||||
|
||||
c_size_t :: uint;
|
||||
c_ssize_t :: int;
|
||||
c_ptrdiff_t :: int;
|
||||
c_uintptr_t :: uint;
|
||||
c_intptr_t :: int;
|
||||
@@ -0,0 +1,255 @@
|
||||
// #import "fmt.odin";
|
||||
// Multiple precision decimal numbers
|
||||
// NOTE: This is only for floating point printing and nothing else
|
||||
|
||||
Decimal :: struct {
|
||||
digits: [384]u8; // big-endian digits
|
||||
count: int;
|
||||
decimal_point: int;
|
||||
neg, trunc: bool;
|
||||
}
|
||||
|
||||
decimal_to_string :: proc(buf: []u8, a: ^Decimal) -> string {
|
||||
digit_zero :: proc(buf: []u8) -> int {
|
||||
for _, i in buf do buf[i] = '0';
|
||||
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(len(buf) >= n);
|
||||
buf = buf[0..n];
|
||||
|
||||
if a.count == 0 {
|
||||
buf[0] = '0';
|
||||
return string(buf[0..1]);
|
||||
}
|
||||
|
||||
w := 0;
|
||||
if a.decimal_point <= 0 {
|
||||
buf[w] = '0'; w+=1;
|
||||
buf[w] = '.'; w+=1;
|
||||
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]);
|
||||
buf[w] = '.'; w+=1;
|
||||
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]);
|
||||
}
|
||||
|
||||
return string(buf[0..w]);
|
||||
}
|
||||
|
||||
// trim trailing zeros
|
||||
trim :: proc(a: ^Decimal) {
|
||||
for a.count > 0 && a.digits[a.count-1] == '0' {
|
||||
a.count -= 1;
|
||||
}
|
||||
if a.count == 0 {
|
||||
a.decimal_point = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
assign :: proc(a: ^Decimal, i: u64) {
|
||||
buf: [32]u8;
|
||||
n := 0;
|
||||
for i > 0 {
|
||||
j := i/10;
|
||||
i -= 10*j;
|
||||
buf[n] = u8('0'+i);
|
||||
n+=1;
|
||||
i = j;
|
||||
}
|
||||
|
||||
a.count = 0;
|
||||
for n -= 1; n >= 0; n -= 1 {
|
||||
a.digits[a.count] = buf[n];
|
||||
a.count+=1;
|
||||
}
|
||||
a.decimal_point = a.count;
|
||||
trim(a);
|
||||
}
|
||||
|
||||
|
||||
|
||||
shift_right :: proc(a: ^Decimal, k: uint) {
|
||||
r := 0; // read index
|
||||
w := 0; // write index
|
||||
|
||||
n: uint;
|
||||
for ; n>>k == 0; r+=1 {
|
||||
if r >= a.count {
|
||||
if n == 0 {
|
||||
// Just in case
|
||||
a.count = 0;
|
||||
return;
|
||||
}
|
||||
for n>>k == 0 {
|
||||
n = n * 10;
|
||||
r+=1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
c := uint(a.digits[r]);
|
||||
n = n*10 + c - '0';
|
||||
}
|
||||
a.decimal_point -= r-1;
|
||||
|
||||
mask: uint = (1<<k) - 1;
|
||||
|
||||
for ; r < a.count; r+=1 {
|
||||
c := uint(a.digits[r]);
|
||||
dig := n>>k;
|
||||
n &= mask;
|
||||
a.digits[w] = u8('0' + dig);
|
||||
w+=1;
|
||||
n = n*10 + c - '0';
|
||||
}
|
||||
|
||||
for n > 0 {
|
||||
dig := n>>k;
|
||||
n &= mask;
|
||||
if w < len(a.digits) {
|
||||
a.digits[w] = u8('0' + dig);
|
||||
w+=1;
|
||||
} else if dig > 0 {
|
||||
a.trunc = true;
|
||||
}
|
||||
n *= 10;
|
||||
}
|
||||
|
||||
|
||||
a.count = w;
|
||||
trim(a);
|
||||
}
|
||||
|
||||
shift_left :: proc(a: ^Decimal, k: uint) {
|
||||
delta := int(k/4);
|
||||
|
||||
r := a.count; // read index
|
||||
w := a.count+delta; // write index
|
||||
|
||||
n: uint;
|
||||
for r -= 1; r >= 0; r -= 1 {
|
||||
n += (uint(a.digits[r]) - '0') << k;
|
||||
quo := n/10;
|
||||
rem := n - 10*quo;
|
||||
w -= 1;
|
||||
if w < len(a.digits) {
|
||||
a.digits[w] = u8('0' + rem);
|
||||
} else if rem != 0 {
|
||||
a.trunc = true;
|
||||
}
|
||||
n = quo;
|
||||
}
|
||||
|
||||
for n > 0 {
|
||||
quo := n/10;
|
||||
rem := n - 10*quo;
|
||||
w -= 1;
|
||||
if 0 <= w && w < len(a.digits) {
|
||||
a.digits[w] = u8('0' + rem);
|
||||
} else if rem != 0 {
|
||||
a.trunc = true;
|
||||
}
|
||||
n = quo;
|
||||
}
|
||||
|
||||
a.count += delta;
|
||||
a.count = min(a.count, len(a.digits));
|
||||
a.decimal_point += delta;
|
||||
trim(a);
|
||||
}
|
||||
|
||||
shift :: proc(a: ^Decimal, k: int) {
|
||||
uint_size :: 8*size_of(uint);
|
||||
max_shift :: uint_size-4;
|
||||
|
||||
match {
|
||||
case a.count == 0:
|
||||
// no need to update
|
||||
case k > 0:
|
||||
for k > max_shift {
|
||||
shift_left(a, max_shift);
|
||||
k -= max_shift;
|
||||
}
|
||||
shift_left(a, uint(k));
|
||||
|
||||
|
||||
case k < 0:
|
||||
for k < -max_shift {
|
||||
shift_right(a, max_shift);
|
||||
k += max_shift;
|
||||
}
|
||||
shift_right(a, uint(-k));
|
||||
}
|
||||
}
|
||||
|
||||
can_round_up :: proc(a: ^Decimal, nd: int) -> bool {
|
||||
if nd < 0 || nd >= a.count { return false ; }
|
||||
if a.digits[nd] == '5' && nd+1 == a.count {
|
||||
if a.trunc do return true;
|
||||
return nd > 0 && (a.digits[nd-1]-'0')%2 != 0;
|
||||
}
|
||||
|
||||
return a.digits[nd] >= '5';
|
||||
}
|
||||
|
||||
round :: proc(a: ^Decimal, nd: int) {
|
||||
if nd < 0 || nd >= a.count { return; }
|
||||
if can_round_up(a, nd) {
|
||||
round_up(a, nd);
|
||||
} else {
|
||||
round_down(a, nd);
|
||||
}
|
||||
}
|
||||
|
||||
round_up :: proc(a: ^Decimal, nd: int) {
|
||||
if nd < 0 || nd >= a.count { return; }
|
||||
|
||||
for i := nd-1; i >= 0; i -= 1 {
|
||||
if c := a.digits[i]; c < '9' {
|
||||
a.digits[i]+=1;
|
||||
a.count = i+1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Number is just 9s
|
||||
a.digits[0] = '1';
|
||||
a.count = 1;
|
||||
a.decimal_point+=1;
|
||||
}
|
||||
|
||||
round_down :: proc(a: ^Decimal, nd: int) {
|
||||
if nd < 0 || nd >= a.count { return; }
|
||||
a.count = nd;
|
||||
trim(a);
|
||||
}
|
||||
|
||||
|
||||
// Extract integer part, rounded appropriately. There are no guarantees about overflow.
|
||||
rounded_integer :: proc(a: ^Decimal) -> u64 {
|
||||
if a.decimal_point > 20 {
|
||||
return 0xffff_ffff_ffff_ffff;
|
||||
}
|
||||
i: int = 0;
|
||||
n: u64 = 0;
|
||||
m := min(a.decimal_point, a.count);
|
||||
for ; i < m; i += 1 {
|
||||
n = n*10 + u64(a.digits[i]-'0');
|
||||
}
|
||||
for ; i < a.decimal_point; i += 1 {
|
||||
n *= 10;
|
||||
}
|
||||
if can_round_up(a, a.decimal_point) {
|
||||
n+=1;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
+809
-654
File diff suppressed because it is too large
Load Diff
+104
-66
@@ -1,77 +1,113 @@
|
||||
crc32 :: proc(data: rawptr, len: int) -> u32 {
|
||||
result := ~cast(u32)0;
|
||||
s := slice_ptr(cast(^u8)data, len);
|
||||
for i in 0..<len {
|
||||
b := cast(u32)s[i];
|
||||
result = result>>8 ~ __CRC32_TABLE[(result ~ b) & 0xff];
|
||||
import "mem.odin";
|
||||
|
||||
crc32 :: proc(data: []u8) -> u32 {
|
||||
result := ~u32(0);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc32_table[(result ~ u32(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
crc64 :: proc(data: rawptr, len: int) -> u64 {
|
||||
result := ~cast(u64)0;
|
||||
s := slice_ptr(cast(^u8)data, len);
|
||||
for i in 0..<len {
|
||||
b := cast(u64)s[i];
|
||||
result = result>>8 ~ __CRC64_TABLE[(result ~ b) & 0xff];
|
||||
crc64 :: proc(data: []u8) -> u64 {
|
||||
result := ~u64(0);
|
||||
for b in data {
|
||||
result = result>>8 ~ _crc64_table[(result ~ u64(b)) & 0xff];
|
||||
}
|
||||
return ~result;
|
||||
}
|
||||
|
||||
fnv32 :: proc(data: rawptr, len: int) -> u32 {
|
||||
s := slice_ptr(cast(^u8)data, len);
|
||||
|
||||
fnv32 :: proc(data: []u8) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for i in 0..<len {
|
||||
h = (h * 0x01000193) ~ cast(u32)s[i];
|
||||
for b in data {
|
||||
h = (h * 0x01000193) ~ u32(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64 :: proc(data: rawptr, len: int) -> u64 {
|
||||
s := slice_ptr(cast(^u8)data, len);
|
||||
|
||||
fnv64 :: proc(data: []u8) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for i in 0..<len {
|
||||
h = (h * 0x100000001b3) ~ cast(u64)s[i];
|
||||
for b in data {
|
||||
h = (h * 0x100000001b3) ~ u64(b);
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv32a :: proc(data: rawptr, len: int) -> u32 {
|
||||
s := slice_ptr(cast(^u8)data, len);
|
||||
|
||||
fnv32a :: proc(data: []u8) -> u32 {
|
||||
h: u32 = 0x811c9dc5;
|
||||
for i in 0..<len {
|
||||
h = (h ~ cast(u32)s[i]) * 0x01000193;
|
||||
for b in data {
|
||||
h = (h ~ u32(b)) * 0x01000193;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
fnv64a :: proc(data: rawptr, len: int) -> u64 {
|
||||
s := slice_ptr(cast(^u8)data, len);
|
||||
|
||||
h :u64 = 0xcbf29ce484222325;
|
||||
for i in 0..<len {
|
||||
h = (h ~ cast(u64)s[i]) * 0x100000001b3;
|
||||
fnv64a :: proc(data: []u8) -> u64 {
|
||||
h: u64 = 0xcbf29ce484222325;
|
||||
for b in data {
|
||||
h = (h ~ u64(b)) * 0x100000001b3;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
murmur32 :: proc(data: []u8) -> u32 {
|
||||
c1_32: u32 : 0xcc9e2d51;
|
||||
c2_32: u32 : 0x1b873593;
|
||||
|
||||
murmur64 :: proc(data_: rawptr, len: int) -> u64 {
|
||||
h1: u32 = 0;
|
||||
nblocks := len(data)/4;
|
||||
p := &data[0];
|
||||
p1 := p + 4*nblocks;
|
||||
|
||||
for ; p < p1; p += 4 {
|
||||
k1 := (cast(^u32)p)^;
|
||||
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17);
|
||||
k1 *= c2_32;
|
||||
|
||||
h1 ~= k1;
|
||||
h1 = (h1 << 13) | (h1 >> 19);
|
||||
h1 = h1*5 + 0xe6546b64;
|
||||
}
|
||||
|
||||
tail := data[nblocks*4 ..];
|
||||
k1: u32;
|
||||
match len(tail)&3 {
|
||||
case 3:
|
||||
k1 ~= u32(tail[2]) << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
k1 ~= u32(tail[2]) << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
k1 ~= u32(tail[0]);
|
||||
k1 *= c1_32;
|
||||
k1 = (k1 << 15) | (k1 >> 17) ;
|
||||
k1 *= c2_32;
|
||||
h1 ~= k1;
|
||||
}
|
||||
|
||||
h1 ~= u32(len(data));
|
||||
|
||||
h1 ~= h1 >> 16;
|
||||
h1 *= 0x85ebca6b;
|
||||
h1 ~= h1 >> 13;
|
||||
h1 *= 0xc2b2ae35;
|
||||
h1 ~= h1 >> 16;
|
||||
|
||||
return h1;
|
||||
}
|
||||
|
||||
murmur64 :: proc(data: []u8) -> u64 {
|
||||
SEED :: 0x9747b28c;
|
||||
|
||||
when size_of(int) == 8 {
|
||||
m :: 0xc6a4a7935bd1e995;
|
||||
r :: 47;
|
||||
|
||||
h: u64 = SEED ~ (cast(u64)len * m);
|
||||
h: u64 = SEED ~ (u64(len(data)) * m);
|
||||
data64 := mem.slice_ptr(cast(^u64)&data[0], len(data)/size_of(u64));
|
||||
|
||||
data := slice_ptr(cast(^u64)data_, len/size_of(u64));
|
||||
data2 := slice_ptr(cast(^u8)data_, len);
|
||||
|
||||
for i in 0 ..< data.count {
|
||||
k := data[i];
|
||||
for _, i in data64 {
|
||||
k := data64[i];
|
||||
|
||||
k *= m;
|
||||
k ~= k>>r;
|
||||
@@ -81,15 +117,15 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
|
||||
h *= m;
|
||||
}
|
||||
|
||||
match len & 7 {
|
||||
case 7: h ~= cast(u64)data2[6] << 48; fallthrough;
|
||||
case 6: h ~= cast(u64)data2[5] << 40; fallthrough;
|
||||
case 5: h ~= cast(u64)data2[4] << 32; fallthrough;
|
||||
case 4: h ~= cast(u64)data2[3] << 24; fallthrough;
|
||||
case 3: h ~= cast(u64)data2[2] << 16; fallthrough;
|
||||
case 2: h ~= cast(u64)data2[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)data2[0];
|
||||
h ~= u64(data[0]);
|
||||
h *= m;
|
||||
}
|
||||
|
||||
@@ -102,15 +138,15 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
|
||||
m :: 0x5bd1e995;
|
||||
r :: 24;
|
||||
|
||||
h1: u32 = cast(u32)SEED ~ cast(u32)len;
|
||||
h2: u32 = SEED >> 32;
|
||||
|
||||
data := slice_ptr(cast(^u32)data_, len/size_of(u32));
|
||||
|
||||
h1 := u32(SEED) ~ u32(len(data));
|
||||
h2 := u32(SEED) >> 32;
|
||||
data32 := mem.slice_ptr(cast(^u32)&data[0], len(data)/size_of(u32));
|
||||
len := len(data);
|
||||
i := 0;
|
||||
|
||||
for len >= 8 {
|
||||
k1, k2: u32;
|
||||
k1 = data[i]; i += 1;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
@@ -118,7 +154,7 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
|
||||
h1 ~= k1;
|
||||
len -= 4;
|
||||
|
||||
k2 = data[i]; i += 1;
|
||||
k2 = data32[i]; i += 1;
|
||||
k2 *= m;
|
||||
k2 ~= k2>>r;
|
||||
k2 *= m;
|
||||
@@ -129,7 +165,7 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
|
||||
|
||||
if len >= 4 {
|
||||
k1: u32;
|
||||
k1 = data[i]; i += 1;
|
||||
k1 = data32[i]; i += 1;
|
||||
k1 *= m;
|
||||
k1 ~= k1>>r;
|
||||
k1 *= m;
|
||||
@@ -138,13 +174,17 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
data8 := slice_ptr(cast(^u8)(data.data+i), 3); // NOTE(bill): This is unsafe
|
||||
|
||||
// TODO(bill): Fix this
|
||||
#no_bounds_check data8 := slice_to_bytes(data32[i..])[..3];
|
||||
match len {
|
||||
case 3: h2 ~= cast(u32)data8[2] << 16; fallthrough;
|
||||
case 2: h2 ~= cast(u32)data8[1] << 8; fallthrough;
|
||||
case 3:
|
||||
h2 ~= u32(data8[2]) << 16;
|
||||
fallthrough;
|
||||
case 2:
|
||||
h2 ~= u32(data8[1]) << 8;
|
||||
fallthrough;
|
||||
case 1:
|
||||
h2 ~= cast(u32)data8[0];
|
||||
h2 ~= u32(data8[0]);
|
||||
h2 *= m;
|
||||
}
|
||||
|
||||
@@ -157,14 +197,12 @@ murmur64 :: proc(data_: rawptr, len: int) -> u64 {
|
||||
h2 ~= h1>>19;
|
||||
h2 *= m;
|
||||
|
||||
h := cast(u64)(h1)<<32 | cast(u64)(h2);
|
||||
return h;
|
||||
return u64(h1)<<32 | u64(h2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
__CRC32_TABLE := [256]u32{
|
||||
_crc32_table := [256]u32{
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
|
||||
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
|
||||
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
@@ -230,7 +268,7 @@ __CRC32_TABLE := [256]u32{
|
||||
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
|
||||
};
|
||||
__CRC64_TABLE := [256]u64{
|
||||
_crc64_table := [256]u64{
|
||||
0x0000000000000000, 0x42f0e1eba9ea3693, 0x85e1c3d753d46d26, 0xc711223cfa3e5bb5,
|
||||
0x493366450e42ecdf, 0x0bc387aea7a8da4c, 0xccd2a5925d9681f9, 0x8e224479f47cb76a,
|
||||
0x9266cc8a1c85d9be, 0xd0962d61b56fef2d, 0x17870f5d4f51b498, 0x5577eeb6e6bb820b,
|
||||
|
||||
+96
-93
@@ -20,73 +20,86 @@ 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;
|
||||
|
||||
sqrt :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sqrt.f32";
|
||||
sqrt :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sqrt.f64";
|
||||
Complex :: complex64;
|
||||
|
||||
sin :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.sin.f32";
|
||||
sin :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.sin.f64";
|
||||
foreign __llvm_core {
|
||||
sqrt :: proc(x: f32) -> f32 #link_name "llvm.sqrt.f32" ---;
|
||||
sqrt :: proc(x: f64) -> f64 #link_name "llvm.sqrt.f64" ---;
|
||||
|
||||
cos :: proc(x: f32) -> f32 #foreign __llvm_core "llvm.cos.f32";
|
||||
cos :: proc(x: f64) -> f64 #foreign __llvm_core "llvm.cos.f64";
|
||||
sin :: proc(θ: f32) -> f32 #link_name "llvm.sin.f32" ---;
|
||||
sin :: proc(θ: f64) -> f64 #link_name "llvm.sin.f64" ---;
|
||||
|
||||
tan :: proc(x: f32) -> f32 #inline { return sin(x)/cos(x); }
|
||||
tan :: proc(x: f64) -> f64 #inline { return sin(x)/cos(x); }
|
||||
cos :: proc(θ: f32) -> f32 #link_name "llvm.cos.f32" ---;
|
||||
cos :: proc(θ: f64) -> f64 #link_name "llvm.cos.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; }
|
||||
pow :: proc(x, power: f32) -> f32 #link_name "llvm.pow.f32" ---;
|
||||
pow :: proc(x, power: f64) -> f64 #link_name "llvm.pow.f64" ---;
|
||||
|
||||
sign :: proc(x: f32) -> f32 { if x >= 0 { return +1; } return -1; }
|
||||
sign :: proc(x: f64) -> f64 { if x >= 0 { return +1; } return -1; }
|
||||
fmuladd :: proc(a, b, c: f32) -> f32 #link_name "llvm.fmuladd.f32" ---;
|
||||
fmuladd :: proc(a, b, c: f64) -> f64 #link_name "llvm.fmuladd.f64" ---;
|
||||
}
|
||||
|
||||
bit_reverse :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bitreverse.i16";
|
||||
bit_reverse :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bitreverse.i32";
|
||||
bit_reverse :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bitreverse.i64";
|
||||
tan :: proc(θ: f32) -> f32 #inline do return sin(θ)/cos(θ);
|
||||
tan :: proc(θ: f64) -> f64 #inline do return sin(θ)/cos(θ);
|
||||
|
||||
byte_swap :: proc(b: u16) -> u16 #foreign __llvm_core "llvm.bswap.i16";
|
||||
byte_swap :: proc(b: u32) -> u32 #foreign __llvm_core "llvm.bswap.i32";
|
||||
byte_swap :: proc(b: u64) -> u64 #foreign __llvm_core "llvm.bswap.i64";
|
||||
|
||||
fmuladd :: proc(a, b, c: f32) -> f32 #foreign __llvm_core "llvm.fmuladd.f32";
|
||||
fmuladd :: proc(a, b, c: f64) -> f64 #foreign __llvm_core "llvm.fmuladd.f64";
|
||||
lerp :: proc(a, b, t: f32) -> (x: f32) do return a*(1-t) + b*t;
|
||||
lerp :: proc(a, b, t: f64) -> (x: f64) do return a*(1-t) + b*t;
|
||||
unlerp :: proc(a, b, x: f32) -> (t: f32) do return (x-a)/(b-a);
|
||||
unlerp :: proc(a, b, x: f64) -> (t: f64) do return (x-a)/(b-a);
|
||||
|
||||
|
||||
sign :: proc(x: f32) -> f32 { if x >= 0 do return +1; return -1; }
|
||||
sign :: proc(x: f64) -> f64 { if x >= 0 do return +1; return -1; }
|
||||
|
||||
|
||||
|
||||
copy_sign :: proc(x, y: f32) -> f32 {
|
||||
ix := transmute(u32)x;
|
||||
iy := transmute(u32)y;
|
||||
ix &= 0x7fffffff;
|
||||
ix |= iy & 0x80000000;
|
||||
ix &= 0x7fff_ffff;
|
||||
ix |= iy & 0x8000_0000;
|
||||
return transmute(f32)ix;
|
||||
}
|
||||
round :: proc(x: f32) -> f32 {
|
||||
if x >= 0 {
|
||||
return floor(x + 0.5);
|
||||
}
|
||||
return ceil(x - 0.5);
|
||||
}
|
||||
floor :: proc(x: f32) -> f32 {
|
||||
if x >= 0 {
|
||||
return cast(f32)cast(int)x;
|
||||
}
|
||||
return cast(f32)cast(int)(x-0.5);
|
||||
}
|
||||
ceil :: proc(x: f32) -> f32 {
|
||||
if x < 0 {
|
||||
return cast(f32)cast(int)x;
|
||||
}
|
||||
return cast(f32)cast(int)(x+1);
|
||||
|
||||
copy_sign :: proc(x, y: f64) -> f64 {
|
||||
ix := transmute(u64)x;
|
||||
iy := transmute(u64)y;
|
||||
ix &= 0x7fff_ffff_ffff_ff;
|
||||
ix |= iy & 0x8000_0000_0000_0000;
|
||||
return transmute(f64)ix;
|
||||
}
|
||||
|
||||
remainder32 :: proc(x, y: f32) -> f32 {
|
||||
return x - round(x/y) * y;
|
||||
}
|
||||
round :: proc(x: f32) -> f32 { if x >= 0 do return floor(x + 0.5); return ceil(x - 0.5); }
|
||||
round :: proc(x: f64) -> f64 { if x >= 0 do return floor(x + 0.5); return ceil(x - 0.5); }
|
||||
|
||||
fmod32 :: proc(x, y: f32) -> f32 {
|
||||
floor :: proc(x: f32) -> f32 { if x >= 0 do return f32(i64(x)); return f32(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
floor :: proc(x: f64) -> f64 { if x >= 0 do return f64(i64(x)); return f64(i64(x-0.5)); } // TODO: Get accurate versions
|
||||
|
||||
ceil :: proc(x: f32) -> f32 { if x < 0 do return f32(i64(x)); return f32(i64(x+1)); }// TODO: Get accurate versions
|
||||
ceil :: proc(x: f64) -> f64 { if x < 0 do return f64(i64(x)); return f64(i64(x+1)); }// TODO: Get accurate versions
|
||||
|
||||
remainder :: proc(x, y: f32) -> f32 do return x - round(x/y) * y;
|
||||
remainder :: proc(x, y: f64) -> f64 do return x - round(x/y) * y;
|
||||
|
||||
mod :: proc(x, y: f32) -> f32 {
|
||||
result: f32;
|
||||
y = abs(y);
|
||||
result := remainder32(abs(x), y);
|
||||
result = remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
return copy_sign(result, x);
|
||||
}
|
||||
mod :: proc(x, y: f64) -> f64 {
|
||||
result: f64;
|
||||
y = abs(y);
|
||||
result = remainder(abs(x), y);
|
||||
if sign(result) < 0 {
|
||||
result += y;
|
||||
}
|
||||
@@ -94,53 +107,46 @@ fmod32 :: proc(x, y: f32) -> f32 {
|
||||
}
|
||||
|
||||
|
||||
to_radians :: proc(degrees: f32) -> f32 { return degrees * TAU / 360; }
|
||||
to_degrees :: proc(radians: f32) -> f32 { return radians * 360 / TAU; }
|
||||
to_radians :: proc(degrees: f32) -> f32 do return degrees * TAU / 360;
|
||||
to_degrees :: proc(radians: f32) -> f32 do return radians * 360 / TAU;
|
||||
|
||||
|
||||
|
||||
dot :: proc(a, b: $T/[vector 2]$E) -> E { c := a*b; return c.x + c.y; }
|
||||
dot :: proc(a, b: $T/[vector 3]$E) -> E { c := a*b; return c.x + c.y + c.z; }
|
||||
dot :: proc(a, b: $T/[vector 4]$E) -> E { c := a*b; return c.x + c.y + c.z + c.w; }
|
||||
|
||||
dot :: proc(a, b: Vec2) -> f32 { c := a*b; return c.x + c.y; }
|
||||
dot :: proc(a, b: Vec3) -> f32 { c := a*b; return c.x + c.y + c.z; }
|
||||
dot :: proc(a, b: Vec4) -> f32 { c := a*b; return c.x + c.y + c.z + c.w; }
|
||||
|
||||
cross :: proc(x, y: Vec3) -> Vec3 {
|
||||
cross :: proc(x, y: $T/[vector 3]$E) -> T {
|
||||
a := swizzle(x, 1, 2, 0) * swizzle(y, 2, 0, 1);
|
||||
b := swizzle(x, 2, 0, 1) * swizzle(y, 1, 2, 0);
|
||||
return a - b;
|
||||
return T(a - b);
|
||||
}
|
||||
|
||||
|
||||
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)); }
|
||||
mag :: proc(v: $T/[vector 2]$E) -> E do return sqrt(dot(v, v));
|
||||
mag :: proc(v: $T/[vector 3]$E) -> E do return sqrt(dot(v, v));
|
||||
mag :: proc(v: $T/[vector 4]$E) -> E do 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: $T/[vector 2]$E) -> T do return v / mag(v);
|
||||
norm :: proc(v: $T/[vector 3]$E) -> T do return v / mag(v);
|
||||
norm :: proc(v: $T/[vector 4]$E) -> T do return v / mag(v);
|
||||
|
||||
norm0 :: proc(v: Vec2) -> Vec2 {
|
||||
norm0 :: proc(v: $T/[vector 2]$E) -> T {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return Vec2{0};
|
||||
}
|
||||
return v / Vec2{m};
|
||||
if m == 0 do return 0;
|
||||
return v/m;
|
||||
}
|
||||
|
||||
norm0 :: proc(v: Vec3) -> Vec3 {
|
||||
norm0 :: proc(v: $T/[vector 3]$E) -> T {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return Vec3{0};
|
||||
}
|
||||
return v / Vec3{m};
|
||||
if m == 0 do return 0;
|
||||
return v/m;
|
||||
}
|
||||
|
||||
norm0 :: proc(v: Vec4) -> Vec4 {
|
||||
norm0 :: proc(v: $T/[vector 4]$E) -> T {
|
||||
m := mag(v);
|
||||
if m == 0 {
|
||||
return Vec4{0};
|
||||
}
|
||||
return v / Vec4{m};
|
||||
if m == 0 do return 0;
|
||||
return v/m;
|
||||
}
|
||||
|
||||
|
||||
@@ -155,8 +161,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];
|
||||
}
|
||||
}
|
||||
@@ -165,8 +171,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] +
|
||||
@@ -208,6 +214,7 @@ inverse :: proc(m: Mat4) -> Mat4 {
|
||||
sf17 := m[1][0] * m[2][2] - m[2][0] * m[1][2];
|
||||
sf18 := m[1][0] * m[2][1] - m[2][0] * m[1][1];
|
||||
|
||||
|
||||
o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02);
|
||||
o[0][1] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04);
|
||||
o[0][2] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05);
|
||||
@@ -268,7 +275,7 @@ mat4_rotate :: proc(v: Vec3, angle_radians: f32) -> Mat4 {
|
||||
s := sin(angle_radians);
|
||||
|
||||
a := norm(v);
|
||||
t := a * Vec3{1-c};
|
||||
t := a * (1-c);
|
||||
|
||||
rot := mat4_identity();
|
||||
|
||||
@@ -310,18 +317,18 @@ look_at :: proc(eye, centre, up: Vec3) -> Mat4 {
|
||||
s := norm(cross(f, up));
|
||||
u := cross(s, f);
|
||||
|
||||
m: Mat4;
|
||||
|
||||
m[0] = Vec4{+s.x, +s.y, +s.z, 0};
|
||||
m[1] = Vec4{+u.x, +u.y, +u.z, 0};
|
||||
m[2] = Vec4{-f.x, -f.y, -f.z, 0};
|
||||
m[3] = Vec4{dot(s, eye), dot(u, eye), dot(f, eye), 1};
|
||||
|
||||
return m;
|
||||
return Mat4{
|
||||
{+s.x, +u.x, -f.x, 0},
|
||||
{+s.y, +u.y, -f.y, 0},
|
||||
{+s.z, +u.z, -f.z, 0},
|
||||
{-dot(s, eye), -dot(u, eye), dot(f, eye), 1},
|
||||
};
|
||||
}
|
||||
|
||||
perspective :: proc(fovy, aspect, near, far: f32) -> Mat4 {
|
||||
m: Mat4;
|
||||
tan_half_fovy := tan(0.5 * fovy);
|
||||
|
||||
m[0][0] = 1.0 / (aspect*tan_half_fovy);
|
||||
m[1][1] = 1.0 / (tan_half_fovy);
|
||||
m[2][2] = -(far + near) / (far - near);
|
||||
@@ -345,7 +352,6 @@ ortho3d :: proc(left, right, bottom, top, near, far: f32) -> Mat4 {
|
||||
|
||||
|
||||
|
||||
|
||||
F32_DIG :: 6;
|
||||
F32_EPSILON :: 1.192092896e-07;
|
||||
F32_GUARD :: 0;
|
||||
@@ -371,6 +377,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
|
||||
|
||||
|
||||
|
||||
|
||||
+134
-144
@@ -1,90 +1,90 @@
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
|
||||
set :: proc(data: rawptr, value: i32, len: int) -> rawptr #link_name "__mem_set" {
|
||||
llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memset.p0i8.i64";
|
||||
llvm_memset_64bit(data, cast(byte)value, len, 1, false);
|
||||
return data;
|
||||
import (
|
||||
"fmt.odin";
|
||||
"os.odin";
|
||||
"raw.odin";
|
||||
)
|
||||
foreign __llvm_core {
|
||||
swap :: proc(b: u16) -> u16 #link_name "llvm.bswap.i16" ---;
|
||||
swap :: proc(b: u32) -> u32 #link_name "llvm.bswap.i32" ---;
|
||||
swap :: proc(b: u64) -> u64 #link_name "llvm.bswap.i64" ---;
|
||||
}
|
||||
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr #link_name "__mem_zero" {
|
||||
return set(data, 0, len);
|
||||
set :: proc(data: rawptr, value: i32, len: int) -> rawptr #cc_contextless {
|
||||
return __mem_set(data, value, len);
|
||||
}
|
||||
|
||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy" {
|
||||
// NOTE(bill): This _must_ implemented like C's memmove
|
||||
llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memmove.p0i8.p0i8.i64";
|
||||
llvm_memmove_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
zero :: proc(data: rawptr, len: int) -> rawptr #cc_contextless {
|
||||
return __mem_zero(data, len);
|
||||
}
|
||||
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #link_name "__mem_copy_non_overlapping" {
|
||||
// NOTE(bill): This _must_ implemented like C's memcpy
|
||||
llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign __llvm_core "llvm.memcpy.p0i8.p0i8.i64";
|
||||
llvm_memcpy_64bit(dst, src, len, 1, false);
|
||||
return dst;
|
||||
copy :: proc(dst, src: rawptr, len: int) -> rawptr #cc_contextless {
|
||||
return __mem_copy(dst, src, len);
|
||||
}
|
||||
|
||||
compare :: proc(dst, src: rawptr, n: int) -> int #link_name "__mem_compare" {
|
||||
a := slice_ptr(cast(^byte)dst, n);
|
||||
b := slice_ptr(cast(^byte)src, n);
|
||||
for i in 0..<n {
|
||||
match {
|
||||
case a[i] < b[i]:
|
||||
return -1;
|
||||
case a[i] > b[i]:
|
||||
return +1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
copy_non_overlapping :: proc(dst, src: rawptr, len: int) -> rawptr #cc_contextless {
|
||||
return __mem_copy_non_overlapping(dst, src, len);
|
||||
}
|
||||
compare :: proc(a, b: []u8) -> int #cc_contextless {
|
||||
return __mem_compare(&a[0], &b[0], min(len(a), len(b)));
|
||||
}
|
||||
|
||||
|
||||
slice_ptr :: proc(ptr: ^$T, len: int) -> []T #cc_contextless {
|
||||
assert(len >= 0);
|
||||
slice := raw.Slice{data = ptr, len = len, cap = len};
|
||||
return (cast(^[]T)&slice)^;
|
||||
}
|
||||
slice_ptr :: proc(ptr: ^$T, len, cap: int) -> []T #cc_contextless {
|
||||
assert(0 <= len && len <= cap);
|
||||
slice := raw.Slice{data = ptr, len = len, cap = cap};
|
||||
return (cast(^[]T)&slice)^;
|
||||
}
|
||||
|
||||
kilobytes :: proc(x: int) -> int #inline { return (x) * 1024; }
|
||||
megabytes :: proc(x: int) -> int #inline { return kilobytes(x) * 1024; }
|
||||
gigabytes :: proc(x: int) -> int #inline { return gigabytes(x) * 1024; }
|
||||
terabytes :: proc(x: int) -> int #inline { return terabytes(x) * 1024; }
|
||||
slice_to_bytes :: proc(slice: []$T) -> []u8 #cc_contextless {
|
||||
s := cast(^raw.Slice)&slice;
|
||||
s.len *= size_of(T);
|
||||
s.cap *= size_of(T);
|
||||
return (cast(^[]u8)s)^;
|
||||
}
|
||||
|
||||
|
||||
kilobytes :: proc(x: int) -> int #inline #cc_contextless { return (x) * 1024; }
|
||||
megabytes :: proc(x: int) -> int #inline #cc_contextless { return kilobytes(x) * 1024; }
|
||||
gigabytes :: proc(x: int) -> int #inline #cc_contextless { return megabytes(x) * 1024; }
|
||||
terabytes :: proc(x: int) -> int #inline #cc_contextless { return gigabytes(x) * 1024; }
|
||||
|
||||
is_power_of_two :: proc(x: int) -> bool {
|
||||
if x <= 0 {
|
||||
return false;
|
||||
}
|
||||
if x <= 0 do return false;
|
||||
return (x & (x-1)) == 0;
|
||||
}
|
||||
|
||||
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;
|
||||
if modulo != 0 do p += a - modulo;
|
||||
return rawptr(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Allocation_Header :: struct {
|
||||
size: int,
|
||||
AllocationHeader :: struct {
|
||||
size: int;
|
||||
}
|
||||
|
||||
allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: int) {
|
||||
allocation_header_fill :: proc(header: ^AllocationHeader, data: rawptr, size: int) {
|
||||
header.size = size;
|
||||
ptr := cast(^int)(header+1);
|
||||
ptr := cast(^uint)(header+1);
|
||||
n := cast(^uint)data - ptr;
|
||||
|
||||
for i := 0; cast(rawptr)ptr < data; i += 1 {
|
||||
(ptr+i)^ = -1;
|
||||
for i in 0..n {
|
||||
(ptr+i)^ = ~uint(0);
|
||||
}
|
||||
}
|
||||
allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
|
||||
p := cast(^int)data;
|
||||
for (p-1)^ == -1 {
|
||||
p = (p-1);
|
||||
}
|
||||
return cast(^Allocation_Header)p-1;
|
||||
allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
|
||||
if data == nil do return nil;
|
||||
p := cast(^uint)data;
|
||||
for (p-1)^ == ~uint(0) do p = (p-1);
|
||||
return cast(^AllocationHeader)(p-1);
|
||||
}
|
||||
|
||||
|
||||
@@ -92,35 +92,36 @@ allocation_header :: proc(data: rawptr) -> ^Allocation_Header {
|
||||
|
||||
|
||||
// Custom allocators
|
||||
|
||||
Arena :: struct {
|
||||
backing: Allocator,
|
||||
offset: int,
|
||||
memory: []byte,
|
||||
temp_count: int,
|
||||
backing: Allocator;
|
||||
offset: int;
|
||||
memory: []u8;
|
||||
temp_count: int;
|
||||
}
|
||||
|
||||
Arena_Temp_Memory :: struct {
|
||||
arena: ^Arena,
|
||||
original_count: int,
|
||||
ArenaTempMemory :: struct {
|
||||
arena: ^Arena;
|
||||
original_count: int;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
|
||||
init_arena_from_memory :: proc(using a: ^Arena, data: []u8) {
|
||||
backing = Allocator{};
|
||||
memory = data[:0];
|
||||
memory = data[..0];
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
init_arena_from_context :: proc(using a: ^Arena, size: int) {
|
||||
backing = context.allocator;
|
||||
memory = new_slice(byte, size);
|
||||
memory = make([]u8, size);
|
||||
temp_count = 0;
|
||||
}
|
||||
|
||||
free_arena :: proc(using a: ^Arena) {
|
||||
destroy_arena :: proc(using a: ^Arena) {
|
||||
if backing.procedure != nil {
|
||||
push_allocator backing {
|
||||
free(memory);
|
||||
@@ -137,53 +138,53 @@ arena_allocator :: proc(arena: ^Arena) -> Allocator {
|
||||
};
|
||||
}
|
||||
|
||||
arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
|
||||
using Allocator_Mode;
|
||||
arena_allocator_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;
|
||||
|
||||
match mode {
|
||||
case ALLOC:
|
||||
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;
|
||||
return zero(ptr, size);
|
||||
|
||||
case FREE:
|
||||
case Free:
|
||||
// NOTE(bill): Free all at once
|
||||
// Use Arena_Temp_Memory if you want to free a block
|
||||
// Use ArenaTempMemory if you want to free a block
|
||||
|
||||
case FREE_ALL:
|
||||
case FreeAll:
|
||||
arena.offset = 0;
|
||||
|
||||
case RESIZE:
|
||||
case Resize:
|
||||
return default_resize_align(old_memory, old_size, size, alignment);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
tmp: Arena_Temp_Memory;
|
||||
begin_arena_temp_memory :: proc(a: ^Arena) -> ArenaTempMemory {
|
||||
tmp: ArenaTempMemory;
|
||||
tmp.arena = a;
|
||||
tmp.original_count = a.memory.count;
|
||||
tmp.original_count = len(a.memory);
|
||||
a.temp_count += 1;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
assert(arena.memory.count >= original_count);
|
||||
end_arena_temp_memory :: proc(using tmp: ArenaTempMemory) {
|
||||
assert(len(arena.memory) >= original_count);
|
||||
assert(arena.temp_count > 0);
|
||||
arena.memory.count = original_count;
|
||||
arena.memory = arena.memory[..original_count];
|
||||
arena.temp_count -= 1;
|
||||
}
|
||||
|
||||
@@ -193,11 +194,9 @@ end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
|
||||
|
||||
|
||||
align_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||
align_of_type_info :: proc(type_info: ^TypeInfo) -> int {
|
||||
prev_pow2 :: proc(n: i64) -> i64 {
|
||||
if n <= 0 {
|
||||
return 0;
|
||||
}
|
||||
if n <= 0 do return 0;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
@@ -209,40 +208,47 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||
|
||||
WORD_SIZE :: size_of(int);
|
||||
MAX_ALIGN :: size_of([vector 64]f64); // TODO(bill): Should these constants be builtin constants?
|
||||
using Type_Info;
|
||||
|
||||
match type info in type_info {
|
||||
using TypeInfo;
|
||||
match info in type_info.variant {
|
||||
case Named:
|
||||
return align_of_type_info(info.base);
|
||||
case Integer:
|
||||
return info.size;
|
||||
return type_info.align;
|
||||
case Rune:
|
||||
return type_info.align;
|
||||
case Float:
|
||||
return info.size;
|
||||
return type_info.align;
|
||||
case String:
|
||||
return WORD_SIZE;
|
||||
case Boolean:
|
||||
return 1;
|
||||
case Any:
|
||||
return WORD_SIZE;
|
||||
case Pointer:
|
||||
return WORD_SIZE;
|
||||
case Maybe:
|
||||
return max(align_of_type_info(info.elem), 1);
|
||||
case Procedure:
|
||||
return WORD_SIZE;
|
||||
case Array:
|
||||
return align_of_type_info(info.elem);
|
||||
case DynamicArray:
|
||||
return WORD_SIZE;
|
||||
case Slice:
|
||||
return WORD_SIZE;
|
||||
case Vector:
|
||||
size := size_of_type_info(info.elem);
|
||||
count := cast(int)max(prev_pow2(cast(i64)info.count), 1);
|
||||
size := size_of_type_info(info.elem);
|
||||
count := int(max(prev_pow2(i64(info.count)), 1));
|
||||
total := size * count;
|
||||
return clamp(total, 1, MAX_ALIGN);
|
||||
case Tuple:
|
||||
return type_info.align;
|
||||
case Struct:
|
||||
return info.align;
|
||||
return type_info.align;
|
||||
case Union:
|
||||
return info.align;
|
||||
case Raw_Union:
|
||||
return info.align;
|
||||
return type_info.align;
|
||||
case Enum:
|
||||
return align_of_type_info(info.base);
|
||||
case Map:
|
||||
return align_of_type_info(info.generated_struct);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -253,70 +259,54 @@ align_formula :: proc(size, align: int) -> int {
|
||||
return result - result%align;
|
||||
}
|
||||
|
||||
size_of_type_info :: proc(type_info: ^Type_Info) -> int {
|
||||
size_of_type_info :: proc(type_info: ^TypeInfo) -> int {
|
||||
WORD_SIZE :: size_of(int);
|
||||
using Type_Info;
|
||||
match type info in type_info {
|
||||
using TypeInfo;
|
||||
match info in type_info.variant {
|
||||
case Named:
|
||||
return size_of_type_info(info.base);
|
||||
case Integer:
|
||||
return info.size;
|
||||
return type_info.size;
|
||||
case Rune:
|
||||
return type_info.size;
|
||||
case Float:
|
||||
return info.size;
|
||||
case Any:
|
||||
return 2*WORD_SIZE;
|
||||
return type_info.size;
|
||||
case String:
|
||||
return 2*WORD_SIZE;
|
||||
case Boolean:
|
||||
return 1;
|
||||
case Any:
|
||||
return 2*WORD_SIZE;
|
||||
case Pointer:
|
||||
return WORD_SIZE;
|
||||
case Maybe:
|
||||
return size_of_type_info(info.elem) + 1;
|
||||
case Procedure:
|
||||
return WORD_SIZE;
|
||||
case Array:
|
||||
count := info.count;
|
||||
if count == 0 {
|
||||
return 0;
|
||||
}
|
||||
if count == 0 do return 0;
|
||||
size := size_of_type_info(info.elem);
|
||||
align := align_of_type_info(info.elem);
|
||||
alignment := align_formula(size, align);
|
||||
return alignment*(count-1) + size;
|
||||
case DynamicArray:
|
||||
return size_of(rawptr) + 2*size_of(int) + size_of(Allocator);
|
||||
case Slice:
|
||||
return 3*WORD_SIZE;
|
||||
return 2*WORD_SIZE;
|
||||
case Vector:
|
||||
is_bool :: proc(type_info: ^Type_Info) -> bool {
|
||||
match type info in type_info {
|
||||
case Named:
|
||||
return is_bool(info.base);
|
||||
case Boolean:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
count := info.count;
|
||||
if count == 0 {
|
||||
return 0;
|
||||
}
|
||||
bit_size := 8*size_of_type_info(info.elem);
|
||||
if is_bool(info.elem) {
|
||||
// NOTE(bill): LLVM can store booleans as 1 bit because a boolean _is_ an `i1`
|
||||
// Silly LLVM spec
|
||||
bit_size = 1;
|
||||
}
|
||||
total_size_in_bits := bit_size * count;
|
||||
total_size := (total_size_in_bits+7)/8;
|
||||
return total_size;
|
||||
|
||||
if count == 0 do return 0;
|
||||
size := size_of_type_info(info.elem);
|
||||
align := align_of_type_info(info.elem);
|
||||
alignment := align_formula(size, align);
|
||||
return alignment*(count-1) + size;
|
||||
case Struct:
|
||||
return info.size;
|
||||
return type_info.size;
|
||||
case Union:
|
||||
return info.size;
|
||||
case Raw_Union:
|
||||
return info.size;
|
||||
return type_info.size;
|
||||
case Enum:
|
||||
return size_of_type_info(info.base);
|
||||
case Map:
|
||||
return size_of_type_info(info.generated_struct);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
+149
-126
@@ -1,155 +1,178 @@
|
||||
#foreign_system_library lib "opengl32.lib" when ODIN_OS == "windows";
|
||||
#import win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
#include "opengl_constants.odin";
|
||||
foreign_system_library (
|
||||
lib "opengl32.lib" when ODIN_OS == "windows";
|
||||
lib "gl" when ODIN_OS == "linux";
|
||||
)
|
||||
import (
|
||||
win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
"sys/wgl.odin" when ODIN_OS == "windows";
|
||||
)
|
||||
import_load "opengl_constants.odin";
|
||||
|
||||
Clear :: proc(mask: u32) #foreign lib "glClear";
|
||||
ClearColor :: proc(r, g, b, a: f32) #foreign lib "glClearColor";
|
||||
Begin :: proc(mode: i32) #foreign lib "glBegin";
|
||||
End :: proc() #foreign lib "glEnd";
|
||||
Finish :: proc() #foreign lib "glFinish";
|
||||
BlendFunc :: proc(sfactor, dfactor: i32) #foreign lib "glBlendFunc";
|
||||
Enable :: proc(cap: i32) #foreign lib "glEnable";
|
||||
Disable :: proc(cap: i32) #foreign lib "glDisable";
|
||||
GenTextures :: proc(count: i32, result: ^u32) #foreign lib "glGenTextures";
|
||||
DeleteTextures:: proc(count: i32, result: ^u32) #foreign lib "glDeleteTextures";
|
||||
TexParameteri :: proc(target, pname, param: i32) #foreign lib "glTexParameteri";
|
||||
TexParameterf :: proc(target: i32, pname: i32, param: f32) #foreign lib "glTexParameterf";
|
||||
BindTexture :: proc(target: i32, texture: u32) #foreign lib "glBindTexture";
|
||||
LoadIdentity :: proc() #foreign lib "glLoadIdentity";
|
||||
Viewport :: proc(x, y, width, height: i32) #foreign lib "glViewport";
|
||||
Ortho :: proc(left, right, bottom, top, near, far: f64) #foreign lib "glOrtho";
|
||||
Color3f :: proc(r, g, b: f32) #foreign lib "glColor3f";
|
||||
Vertex3f :: proc(x, y, z: f32) #foreign lib "glVertex3f";
|
||||
TexImage2D :: proc(target, level, internal_format,
|
||||
width, height, border,
|
||||
format, _type: i32, pixels: rawptr) #foreign lib "glTexImage2D";
|
||||
_ := compile_assert(ODIN_OS != "osx");
|
||||
|
||||
GetError :: proc() -> i32 #foreign lib "glGetError";
|
||||
GetString :: proc(name: i32) -> ^byte #foreign lib "glGetString";
|
||||
GetIntegerv :: proc(name: i32, v: ^i32) #foreign lib "glGetIntegerv";
|
||||
|
||||
|
||||
string_data :: proc(s: string) -> ^u8 #inline { return ^s[0]; }
|
||||
|
||||
_libgl := win32.LoadLibraryA(string_data("opengl32.dll\x00"));
|
||||
|
||||
GetProcAddress :: proc(name: string) -> proc() #cc_c {
|
||||
assert(name[name.count-1] == 0);
|
||||
res := win32.wglGetProcAddress(name.data);
|
||||
if res == nil {
|
||||
res = win32.GetProcAddress(_libgl, name.data);
|
||||
}
|
||||
return res;
|
||||
foreign lib {
|
||||
Clear :: proc(mask: u32) #link_name "glClear" ---;
|
||||
ClearColor :: proc(r, g, b, a: f32) #link_name "glClearColor" ---;
|
||||
Begin :: proc(mode: i32) #link_name "glBegin" ---;
|
||||
End :: proc() #link_name "glEnd" ---;
|
||||
Finish :: proc() #link_name "glFinish" ---;
|
||||
BlendFunc :: proc(sfactor, dfactor: i32) #link_name "glBlendFunc" ---;
|
||||
Enable :: proc(cap: i32) #link_name "glEnable" ---;
|
||||
Disable :: proc(cap: i32) #link_name "glDisable" ---;
|
||||
GenTextures :: proc(count: i32, result: ^u32) #link_name "glGenTextures" ---;
|
||||
DeleteTextures :: proc(count: i32, result: ^u32) #link_name "glDeleteTextures"---;
|
||||
TexParameteri :: proc(target, pname, param: i32) #link_name "glTexParameteri" ---;
|
||||
TexParameterf :: proc(target: i32, pname: i32, param: f32) #link_name "glTexParameterf" ---;
|
||||
BindTexture :: proc(target: i32, texture: u32) #link_name "glBindTexture" ---;
|
||||
LoadIdentity :: proc() #link_name "glLoadIdentity" ---;
|
||||
Viewport :: proc(x, y, width, height: i32) #link_name "glViewport" ---;
|
||||
Ortho :: proc(left, right, bottom, top, near, far: f64) #link_name "glOrtho" ---;
|
||||
Color3f :: proc(r, g, b: f32) #link_name "glColor3f" ---;
|
||||
Vertex3f :: proc(x, y, z: f32) #link_name "glVertex3f" ---;
|
||||
GetError :: proc() -> i32 #link_name "glGetError" ---;
|
||||
GetString :: proc(name: i32) -> ^u8 #link_name "glGetString" ---;
|
||||
GetIntegerv :: proc(name: i32, v: ^i32) #link_name "glGetIntegerv" ---;
|
||||
TexCoord2f :: proc(x, y: f32) #link_name "glTexCoord2f" ---;
|
||||
TexImage2D :: proc(target, level, internal_format,
|
||||
width, height, border,
|
||||
format, type_: i32, pixels: rawptr) #link_name "glTexImage2D" ---;
|
||||
}
|
||||
|
||||
GenBuffers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenVertexArrays: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
GenSamplers: proc(count: i32, buffers: ^u32) #cc_c;
|
||||
BindBuffer: proc(target: i32, buffer: u32) #cc_c;
|
||||
BindVertexArray: proc(buffer: u32) #cc_c;
|
||||
BindSampler: proc(position: i32, sampler: u32) #cc_c;
|
||||
BufferData: proc(target: i32, size: int, data: rawptr, usage: i32) #cc_c;
|
||||
BufferSubData: proc(target: i32, offset, size: int, data: rawptr) #cc_c;
|
||||
|
||||
DrawArrays: proc(mode, first: i32, count: u32) #cc_c;
|
||||
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c;
|
||||
_string_data :: proc(s: string) -> ^u8 #inline { return &s[0]; }
|
||||
|
||||
MapBuffer: proc(target, access: i32) -> rawptr #cc_c;
|
||||
UnmapBuffer: proc(target: i32) #cc_c;
|
||||
_libgl := win32.load_library_a(_string_data("opengl32.dll\x00"));
|
||||
|
||||
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c;
|
||||
EnableVertexAttribArray: proc(index: u32) #cc_c;
|
||||
get_proc_address :: proc(name: string) -> rawptr {
|
||||
if name[len(name)-1] == 0 {
|
||||
name = name[..len(name)-1];
|
||||
}
|
||||
// NOTE(bill): null terminated
|
||||
assert((&name[0] + len(name))^ == 0);
|
||||
res := wgl.get_proc_address(&name[0]);
|
||||
if res == nil {
|
||||
res = win32.get_proc_address(_libgl, &name[0]);
|
||||
}
|
||||
return rawptr(res);
|
||||
}
|
||||
|
||||
CreateShader: proc(shader_type: i32) -> u32 #cc_c;
|
||||
ShaderSource: proc(shader: u32, count: u32, str: ^^byte, length: ^i32) #cc_c;
|
||||
CompileShader: proc(shader: u32) #cc_c;
|
||||
CreateProgram: proc() -> u32 #cc_c;
|
||||
AttachShader: proc(program, shader: u32) #cc_c;
|
||||
DetachShader: proc(program, shader: u32) #cc_c;
|
||||
DeleteShader: proc(shader: u32) #cc_c;
|
||||
LinkProgram: proc(program: u32) #cc_c;
|
||||
UseProgram: proc(program: u32) #cc_c;
|
||||
DeleteProgram: proc(program: u32) #cc_c;
|
||||
// Procedures
|
||||
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;
|
||||
DeleteVertexArrays: proc(count: i32, arrays: ^u32) #cc_c;
|
||||
BindSampler: proc(position: i32, sampler: u32) #cc_c;
|
||||
BufferData: proc(target: i32, size: int, data: rawptr, usage: i32) #cc_c;
|
||||
BufferSubData: proc(target: i32, offset, size: int, data: rawptr) #cc_c;
|
||||
|
||||
DrawArrays: proc(mode, first: i32, count: u32) #cc_c;
|
||||
DrawElements: proc(mode: i32, count: u32, type_: i32, indices: rawptr) #cc_c;
|
||||
|
||||
MapBuffer: proc(target, access: i32) -> rawptr #cc_c;
|
||||
UnmapBuffer: proc(target: i32) #cc_c;
|
||||
|
||||
VertexAttribPointer: proc(index: u32, size, type_: i32, normalized: i32, stride: u32, pointer: rawptr) #cc_c;
|
||||
EnableVertexAttribArray: proc(index: u32) #cc_c;
|
||||
|
||||
CreateShader: proc(shader_type: i32) -> u32 #cc_c;
|
||||
ShaderSource: proc(shader: u32, count: u32, str: ^^u8, length: ^i32) #cc_c;
|
||||
CompileShader: proc(shader: u32) #cc_c;
|
||||
CreateProgram: proc() -> u32 #cc_c;
|
||||
AttachShader: proc(program, shader: u32) #cc_c;
|
||||
DetachShader: proc(program, shader: u32) #cc_c;
|
||||
DeleteShader: proc(shader: u32) #cc_c;
|
||||
LinkProgram: proc(program: u32) #cc_c;
|
||||
UseProgram: proc(program: u32) #cc_c;
|
||||
DeleteProgram: proc(program: u32) #cc_c;
|
||||
|
||||
|
||||
GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c;
|
||||
GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^byte) #cc_c;
|
||||
GetShaderiv: proc(shader: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetProgramiv: proc(program: u32, pname: i32, params: ^i32) #cc_c;
|
||||
GetShaderInfoLog: proc(shader: u32, max_length: u32, length: ^u32, info_long: ^u8) #cc_c;
|
||||
GetProgramInfoLog: proc(program: u32, max_length: u32, length: ^u32, info_long: ^u8) #cc_c;
|
||||
|
||||
ActiveTexture: proc(texture: i32) #cc_c;
|
||||
GenerateMipmap: proc(target: i32) #cc_c;
|
||||
ActiveTexture: proc(texture: i32) #cc_c;
|
||||
GenerateMipmap: proc(target: i32) #cc_c;
|
||||
|
||||
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c;
|
||||
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c;
|
||||
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c;
|
||||
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c;
|
||||
SamplerParameteri: proc(sampler: u32, pname: i32, param: i32) #cc_c;
|
||||
SamplerParameterf: proc(sampler: u32, pname: i32, param: f32) #cc_c;
|
||||
SamplerParameteriv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterfv: proc(sampler: u32, pname: i32, params: ^f32) #cc_c;
|
||||
SamplerParameterIiv: proc(sampler: u32, pname: i32, params: ^i32) #cc_c;
|
||||
SamplerParameterIuiv: proc(sampler: u32, pname: i32, params: ^u32) #cc_c;
|
||||
|
||||
|
||||
Uniform1i: proc(loc: i32, v0: i32) #cc_c;
|
||||
Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c;
|
||||
Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c;
|
||||
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c;
|
||||
Uniform1f: proc(loc: i32, v0: f32) #cc_c;
|
||||
Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c;
|
||||
Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c;
|
||||
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c;
|
||||
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c;
|
||||
Uniform1i: proc(loc: i32, v0: i32) #cc_c;
|
||||
Uniform2i: proc(loc: i32, v0, v1: i32) #cc_c;
|
||||
Uniform3i: proc(loc: i32, v0, v1, v2: i32) #cc_c;
|
||||
Uniform4i: proc(loc: i32, v0, v1, v2, v3: i32) #cc_c;
|
||||
Uniform1f: proc(loc: i32, v0: f32) #cc_c;
|
||||
Uniform2f: proc(loc: i32, v0, v1: f32) #cc_c;
|
||||
Uniform3f: proc(loc: i32, v0, v1, v2: f32) #cc_c;
|
||||
Uniform4f: proc(loc: i32, v0, v1, v2, v3: f32) #cc_c;
|
||||
UniformMatrix4fv: proc(loc: i32, count: u32, transpose: i32, value: ^f32) #cc_c;
|
||||
|
||||
GetUniformLocation: proc(program: u32, name: ^u8) -> i32 #cc_c;
|
||||
|
||||
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 := cast(^rawptr)p;
|
||||
x^ = get_proc_address(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(&DeleteVertexArrays, "glDeleteVertexArrays\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,4 +1,3 @@
|
||||
|
||||
FALSE :: 0;
|
||||
TRUE :: 1;
|
||||
|
||||
@@ -1382,4 +1381,3 @@ DEBUG_LOGGED_MESSAGES_ARB :: 0x9145;
|
||||
DEBUG_SEVERITY_HIGH_ARB :: 0x9146;
|
||||
DEBUG_SEVERITY_MEDIUM_ARB :: 0x9147;
|
||||
DEBUG_SEVERITY_LOW_ARB :: 0x9148;
|
||||
|
||||
|
||||
+48
-1
@@ -1,2 +1,49 @@
|
||||
#include "os_windows.odin" when ODIN_OS == "windows"
|
||||
import_load (
|
||||
"os_windows.odin" when ODIN_OS == "windows";
|
||||
"os_x.odin" when ODIN_OS == "osx";
|
||||
"os_linux.odin" when ODIN_OS == "linux";
|
||||
)
|
||||
|
||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||
return write(fd, cast([]u8)str);
|
||||
}
|
||||
|
||||
read_entire_file :: proc(name: string) -> (data: []u8, success: bool) {
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != 0 {
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
length: i64;
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return nil, true;
|
||||
}
|
||||
|
||||
data := make([]u8, int(length));
|
||||
if data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
bytes_read, read_err := read(fd, data);
|
||||
if read_err != 0 {
|
||||
free(data);
|
||||
return nil, false;
|
||||
}
|
||||
return data[0..bytes_read], true;
|
||||
}
|
||||
|
||||
write_entire_file :: proc(name: string, data: []u8) -> (sucess: bool) {
|
||||
fd, err := open(name, O_WRONLY, 0);
|
||||
if err != 0 {
|
||||
return false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
bytes_written, write_err := write(fd, data);
|
||||
return write_err != 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
foreign_system_library (
|
||||
dl "dl";
|
||||
libc "c";
|
||||
)
|
||||
import "strings.odin";
|
||||
|
||||
Handle :: i32;
|
||||
FileTime :: u64;
|
||||
Errno :: i32;
|
||||
|
||||
|
||||
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
|
||||
args := _alloc_command_line_arguments();
|
||||
|
||||
_FileTime :: 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: _FileTime; // Time of last access
|
||||
modified: _FileTime; // Time of last modification
|
||||
status_change: _FileTime; // 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;}
|
||||
|
||||
F_OK :: 0; // Test for file existance
|
||||
X_OK :: 1; // Test for execute permission
|
||||
W_OK :: 2; // Test for write permission
|
||||
R_OK :: 4; // Test for read permission
|
||||
|
||||
foreign libc {
|
||||
_unix_open :: proc(path: ^u8, mode: int) -> Handle #link_name "open" ---;
|
||||
_unix_close :: proc(fd: Handle) -> i32 #link_name "close" ---;
|
||||
_unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int #link_name "read" ---;
|
||||
_unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int #link_name "write" ---;
|
||||
_unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 #link_name "lseek64" ---;
|
||||
_unix_gettid :: proc() -> u64 #link_name "gettid" ---;
|
||||
_unix_stat :: proc(path: ^u8, stat: ^Stat) -> i32 #link_name "stat" ---;
|
||||
_unix_access :: proc(path: ^u8, mask: int) -> i32 #link_name "access" ---;
|
||||
|
||||
_unix_malloc :: proc(size: int) -> rawptr #link_name "malloc" ---;
|
||||
_unix_free :: proc(ptr: rawptr) #link_name "free" ---;
|
||||
_unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #link_name "realloc" ---;
|
||||
_unix_getenv :: proc(^u8) -> ^u8 #link_name "getenv" ---;
|
||||
|
||||
_unix_exit :: proc(status: int) #link_name "exit" ---;
|
||||
}
|
||||
foreign dl {
|
||||
_unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #link_name "dlopen" ---;
|
||||
_unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #link_name "dlsym" ---;
|
||||
_unix_dlclose :: proc(handle: rawptr) -> int #link_name "dlclose" ---;
|
||||
_unix_dlerror :: proc() -> ^u8 #link_name "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 = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
_unix_close(fd);
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
sz := _unix_read(fd, &data[0], len(data));
|
||||
return sz, 0;
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (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;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
prev, _ := seek(fd, 0, SEEK_CUR);
|
||||
size, err := seek(fd, 0, SEEK_END);
|
||||
seek(fd, prev, SEEK_SET);
|
||||
return size, err;
|
||||
}
|
||||
|
||||
|
||||
// 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) -> FileTime {}
|
||||
last_write_time_by_name :: proc(name: string) -> FileTime {}
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
+202
-160
@@ -1,14 +1,14 @@
|
||||
#import win32 "sys/windows.odin";
|
||||
#import "fmt.odin";
|
||||
import win32 "sys/windows.odin";
|
||||
import "mem.odin";
|
||||
|
||||
Handle :: int;
|
||||
FileTime :: u64;
|
||||
|
||||
Handle :: int;
|
||||
File_Time :: u64;
|
||||
Errno :: int;
|
||||
|
||||
INVALID_HANDLE: Handle : -1;
|
||||
|
||||
|
||||
|
||||
O_RDONLY :: 0x00000;
|
||||
O_WRONLY :: 0x00001;
|
||||
O_RDWR :: 0x00002;
|
||||
@@ -22,135 +22,168 @@ O_SYNC :: 0x01000;
|
||||
O_ASYNC :: 0x02000;
|
||||
O_CLOEXEC :: 0x80000;
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
ERROR_FILE_NOT_FOUND: Errno : 2;
|
||||
ERROR_PATH_NOT_FOUND: Errno : 3;
|
||||
ERROR_ACCESS_DENIED: Errno : 5;
|
||||
ERROR_NO_MORE_FILES: Errno : 18;
|
||||
ERROR_HANDLE_EOF: Errno : 38;
|
||||
ERROR_NETNAME_DELETED: Errno : 64;
|
||||
ERROR_FILE_EXISTS: Errno : 80;
|
||||
ERROR_BROKEN_PIPE: Errno : 109;
|
||||
ERROR_BUFFER_OVERFLOW: Errno : 111;
|
||||
ERROR_INSUFFICIENT_BUFFER: Errno : 122;
|
||||
ERROR_MOD_NOT_FOUND: Errno : 126;
|
||||
ERROR_PROC_NOT_FOUND: Errno : 127;
|
||||
ERROR_DIR_NOT_EMPTY: Errno : 145;
|
||||
ERROR_ALREADY_EXISTS: Errno : 183;
|
||||
ERROR_ENVVAR_NOT_FOUND: Errno : 203;
|
||||
ERROR_MORE_DATA: Errno : 234;
|
||||
ERROR_OPERATION_ABORTED: Errno : 995;
|
||||
ERROR_IO_PENDING: Errno : 997;
|
||||
ERROR_NOT_FOUND: Errno : 1168;
|
||||
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
|
||||
WSAEACCES: Errno : 10013;
|
||||
WSAECONNRESET: Errno : 10054;
|
||||
Errno :: int;
|
||||
|
||||
ERROR_NONE: Errno : 0;
|
||||
ERROR_FILE_NOT_FOUND: Errno : 2;
|
||||
ERROR_PATH_NOT_FOUND: Errno : 3;
|
||||
ERROR_ACCESS_DENIED: Errno : 5;
|
||||
ERROR_NO_MORE_FILES: Errno : 18;
|
||||
ERROR_HANDLE_EOF: Errno : 38;
|
||||
ERROR_NETNAME_DELETED: Errno : 64;
|
||||
ERROR_FILE_EXISTS: Errno : 80;
|
||||
ERROR_BROKEN_PIPE: Errno : 109;
|
||||
ERROR_BUFFER_OVERFLOW: Errno : 111;
|
||||
ERROR_INSUFFICIENT_BUFFER: Errno : 122;
|
||||
ERROR_MOD_NOT_FOUND: Errno : 126;
|
||||
ERROR_PROC_NOT_FOUND: Errno : 127;
|
||||
ERROR_DIR_NOT_EMPTY: Errno : 145;
|
||||
ERROR_ALREADY_EXISTS: Errno : 183;
|
||||
ERROR_ENVVAR_NOT_FOUND: Errno : 203;
|
||||
ERROR_MORE_DATA: Errno : 234;
|
||||
ERROR_OPERATION_ABORTED: Errno : 995;
|
||||
ERROR_IO_PENDING: Errno : 997;
|
||||
ERROR_NOT_FOUND: Errno : 1168;
|
||||
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314;
|
||||
WSAEACCES: Errno : 10013;
|
||||
WSAECONNRESET: Errno : 10054;
|
||||
|
||||
// Windows reserves errors >= 1<<29 for application use
|
||||
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
|
||||
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0;
|
||||
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
args := _alloc_command_line_arguments();
|
||||
|
||||
|
||||
open :: proc(path: string, mode: int, perm: u32) -> (Handle, Errno) {
|
||||
using win32;
|
||||
if path.count == 0 {
|
||||
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
open :: proc(path: string, mode: int = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
|
||||
if len(path) == 0 do return INVALID_HANDLE, ERROR_FILE_NOT_FOUND;
|
||||
|
||||
access: u32;
|
||||
match mode & (O_RDONLY|O_WRONLY|O_RDWR) {
|
||||
case O_RDONLY: access = FILE_GENERIC_READ;
|
||||
case O_WRONLY: access = FILE_GENERIC_WRITE;
|
||||
case O_RDWR: access = FILE_GENERIC_READ | FILE_GENERIC_WRITE;
|
||||
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 |= FILE_GENERIC_WRITE;
|
||||
access |= win32.FILE_GENERIC_WRITE;
|
||||
}
|
||||
if mode&O_APPEND != 0 {
|
||||
access &~= FILE_GENERIC_WRITE;
|
||||
access |= FILE_APPEND_DATA;
|
||||
access &~= win32.FILE_GENERIC_WRITE;
|
||||
access |= win32.FILE_APPEND_DATA;
|
||||
}
|
||||
|
||||
share_mode := cast(u32)(FILE_SHARE_READ|FILE_SHARE_WRITE);
|
||||
sa: ^SECURITY_ATTRIBUTES = nil;
|
||||
sa_inherit := SECURITY_ATTRIBUTES{length = size_of(SECURITY_ATTRIBUTES), inherit_handle = 1};
|
||||
share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE);
|
||||
sa: ^win32.SecurityAttributes = nil;
|
||||
sa_inherit := win32.SecurityAttributes{length = size_of(win32.SecurityAttributes), 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 = CREATE_NEW;
|
||||
create_mode = win32.CREATE_NEW;
|
||||
case mode&(O_CREAT|O_TRUNC) == (O_CREAT | O_TRUNC):
|
||||
create_mode = CREATE_ALWAYS;
|
||||
create_mode = win32.CREATE_ALWAYS;
|
||||
case mode&O_CREAT == O_CREAT:
|
||||
create_mode = OPEN_ALWAYS;
|
||||
create_mode = win32.OPEN_ALWAYS;
|
||||
case mode&O_TRUNC == O_TRUNC:
|
||||
create_mode = TRUNCATE_EXISTING;
|
||||
default:
|
||||
create_mode = OPEN_EXISTING;
|
||||
create_mode = win32.TRUNCATE_EXISTING;
|
||||
case:
|
||||
create_mode = win32.OPEN_EXISTING;
|
||||
}
|
||||
|
||||
buf: [300]byte;
|
||||
copy(buf[:], cast([]byte)path);
|
||||
buf: [300]u8;
|
||||
copy(buf[..], cast([]u8)path);
|
||||
|
||||
handle := cast(Handle)CreateFileA(^buf[0], access, share_mode, sa, create_mode, FILE_ATTRIBUTE_NORMAL, nil);
|
||||
if handle != INVALID_HANDLE {
|
||||
return handle, ERROR_NONE;
|
||||
}
|
||||
err := GetLastError();
|
||||
return INVALID_HANDLE, cast(Errno)err;
|
||||
handle := Handle(win32.create_file_a(&buf[0], access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL, nil));
|
||||
if handle != INVALID_HANDLE do return handle, ERROR_NONE;
|
||||
|
||||
err := Errno(win32.get_last_error());
|
||||
return INVALID_HANDLE, err;
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
win32.CloseHandle(cast(win32.HANDLE)fd);
|
||||
win32.close_handle(win32.Handle(fd));
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
bytes_written: i32;
|
||||
e := win32.WriteFile(cast(win32.HANDLE)fd, data.data, cast(i32)data.count, ^bytes_written, nil);
|
||||
if e == win32.FALSE {
|
||||
err := win32.GetLastError();
|
||||
return 0, cast(Errno)err;
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
if len(data) == 0 do return 0, ERROR_NONE;
|
||||
|
||||
single_write_length: i32;
|
||||
total_write: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_write < length {
|
||||
remaining := length - total_write;
|
||||
MAX :: 1<<31-1;
|
||||
to_write: i32 = min(i32(remaining), MAX);
|
||||
|
||||
e := win32.write_file(win32.Handle(fd), &data[total_write], to_write, &single_write_length, nil);
|
||||
if single_write_length <= 0 || e == win32.FALSE {
|
||||
err := Errno(win32.get_last_error());
|
||||
return int(total_write), err;
|
||||
}
|
||||
total_write += i64(single_write_length);
|
||||
}
|
||||
return cast(int)bytes_written, ERROR_NONE;
|
||||
return int(total_write), ERROR_NONE;
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
bytes_read: i32;
|
||||
e := win32.ReadFile(cast(win32.HANDLE)fd, data.data, cast(u32)data.count, ^bytes_read, nil);
|
||||
if e == win32.FALSE {
|
||||
err := win32.GetLastError();
|
||||
return 0, cast(Errno)err;
|
||||
read :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
if len(data) == 0 do return 0, ERROR_NONE;
|
||||
|
||||
single_read_length: i32;
|
||||
total_read: i64;
|
||||
length := i64(len(data));
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read;
|
||||
MAX :: 1<<32-1;
|
||||
to_read: u32 = min(u32(remaining), MAX);
|
||||
|
||||
e := win32.read_file(win32.Handle(fd), &data[total_read], to_read, &single_read_length, nil);
|
||||
if single_read_length <= 0 || e == win32.FALSE {
|
||||
err := Errno(win32.get_last_error());
|
||||
return int(total_read), err;
|
||||
}
|
||||
total_read += i64(single_read_length);
|
||||
}
|
||||
return cast(int)bytes_read, ERROR_NONE;
|
||||
return int(total_read), ERROR_NONE;
|
||||
}
|
||||
|
||||
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;
|
||||
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 {
|
||||
return 0, ERROR_FILE_IS_PIPE;
|
||||
hi := i32(offset>>32);
|
||||
lo := i32(offset);
|
||||
ft := win32.get_file_type(win32.Handle(fd));
|
||||
if ft == win32.FILE_TYPE_PIPE do return 0, ERROR_FILE_IS_PIPE;
|
||||
|
||||
dw_ptr := win32.set_file_pointer(win32.Handle(fd), lo, &hi, w);
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := Errno(win32.get_last_error());
|
||||
return 0, err;
|
||||
}
|
||||
dw_ptr := SetFilePointer(cast(HANDLE)fd, lo, ^hi, w);
|
||||
if dw_ptr == INVALID_SET_FILE_POINTER {
|
||||
err := GetLastError();
|
||||
return 0, cast(Errno)err;
|
||||
}
|
||||
return cast(i64)hi<<32 + cast(i64)dw_ptr, ERROR_NONE;
|
||||
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
length: i64;
|
||||
err: Errno;
|
||||
if win32.get_file_size_ex(win32.Handle(fd), &length) == 0 {
|
||||
err = Errno(win32.get_last_error());
|
||||
}
|
||||
return length, err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin := get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
@@ -159,9 +192,9 @@ stderr := get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
|
||||
get_std_handle :: proc(h: int) -> Handle {
|
||||
fd := win32.GetStdHandle(cast(i32)h);
|
||||
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
return cast(Handle)fd;
|
||||
fd := win32.get_std_handle(i32(h));
|
||||
win32.set_handle_information(fd, win32.HANDLE_FLAG_INHERIT, 0);
|
||||
return Handle(fd);
|
||||
}
|
||||
|
||||
|
||||
@@ -169,104 +202,113 @@ get_std_handle :: proc(h: int) -> Handle {
|
||||
|
||||
|
||||
|
||||
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;
|
||||
last_write_time :: proc(fd: Handle) -> FileTime {
|
||||
file_info: win32.ByHandleFileInformation;
|
||||
win32.get_file_information_by_handle(win32.Handle(fd), &file_info);
|
||||
lo := FileTime(file_info.last_write_time.lo);
|
||||
hi := FileTime(file_info.last_write_time.hi);
|
||||
return lo | hi << 32;
|
||||
}
|
||||
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {
|
||||
last_write_time: win32.FILETIME;
|
||||
data: win32.FILE_ATTRIBUTE_DATA;
|
||||
buf: [1024]byte;
|
||||
last_write_time_by_name :: proc(name: string) -> FileTime {
|
||||
last_write_time: win32.Filetime;
|
||||
data: win32.FileAttributeData;
|
||||
buf: [1024]u8;
|
||||
|
||||
assert(buf.count > name.count);
|
||||
assert(len(buf) > len(name));
|
||||
|
||||
copy(buf[:], cast([]byte)name);
|
||||
copy(buf[..], cast([]u8)name);
|
||||
|
||||
if win32.GetFileAttributesExA(^buf[0], win32.GetFileExInfoStandard, ^data) != 0 {
|
||||
if win32.get_file_attributes_ex_a(&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 := FileTime(last_write_time.lo);
|
||||
h := FileTime(last_write_time.hi);
|
||||
return l | h << 32;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string) -> ([]byte, bool) {
|
||||
buf: [300]byte;
|
||||
copy(buf[:], cast([]byte)name);
|
||||
|
||||
fd, err := open(name, O_RDONLY, 0);
|
||||
if err != ERROR_NONE {
|
||||
return nil, false;
|
||||
}
|
||||
defer close(fd);
|
||||
|
||||
length: i64;
|
||||
file_size_ok := win32.GetFileSizeEx(cast(win32.HANDLE)fd, ^length) != 0;
|
||||
if !file_size_ok {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
data := new_slice(u8, length);
|
||||
if data.data == nil {
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
single_read_length: i32;
|
||||
total_read: i64;
|
||||
|
||||
for total_read < length {
|
||||
remaining := length - total_read;
|
||||
to_read: u32;
|
||||
MAX :: 1<<32-1;
|
||||
if remaining <= MAX {
|
||||
to_read = cast(u32)remaining;
|
||||
} else {
|
||||
to_read = MAX;
|
||||
}
|
||||
|
||||
win32.ReadFile(cast(win32.HANDLE)fd, ^data[total_read], to_read, ^single_read_length, nil);
|
||||
if single_read_length <= 0 {
|
||||
free(data);
|
||||
return nil, false;
|
||||
}
|
||||
|
||||
total_read += cast(i64)single_read_length;
|
||||
}
|
||||
|
||||
return data, true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr {
|
||||
return win32.HeapAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, size);
|
||||
return win32.heap_alloc(win32.get_process_heap(), win32.HEAP_ZERO_MEMORY, size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
return win32.HeapReAlloc(win32.GetProcessHeap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
|
||||
if new_size == 0 {
|
||||
heap_free(ptr);
|
||||
return nil;
|
||||
}
|
||||
if ptr == nil do return heap_alloc(new_size);
|
||||
|
||||
return win32.heap_realloc(win32.get_process_heap(), win32.HEAP_ZERO_MEMORY, ptr, new_size);
|
||||
}
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
win32.HeapFree(win32.GetProcessHeap(), 0, ptr);
|
||||
if ptr == nil do return;
|
||||
win32.heap_free(win32.get_process_heap(), 0, ptr);
|
||||
}
|
||||
|
||||
|
||||
exit :: proc(code: int) {
|
||||
win32.ExitProcess(cast(u32)code);
|
||||
win32.exit_process(u32(code));
|
||||
}
|
||||
|
||||
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
return cast(int)win32.GetCurrentThreadId();
|
||||
return int(win32.get_current_thread_id());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string {
|
||||
wstr_len := 0;
|
||||
for (wstr+wstr_len)^ != 0 do wstr_len += 1;
|
||||
|
||||
len := 2*wstr_len-1;
|
||||
buf := make([]u8, len+1);
|
||||
str := mem.slice_ptr(wstr, wstr_len+1);
|
||||
|
||||
i, j := 0, 0;
|
||||
for str[j] != 0 {
|
||||
match {
|
||||
case str[j] < 0x80:
|
||||
if i+1 > len do return "";
|
||||
buf[i] = u8(str[j]); i += 1;
|
||||
j += 1;
|
||||
case str[j] < 0x800:
|
||||
if i+2 > len do return "";
|
||||
buf[i] = u8(0xc0 + (str[j]>>6)); i += 1;
|
||||
buf[i] = u8(0x80 + (str[j]&0x3f)); i += 1;
|
||||
j += 1;
|
||||
case 0xd800 <= str[j] && str[j] < 0xdc00:
|
||||
if i+4 > len do return "";
|
||||
c := rune((str[j] - 0xd800) << 10) + rune((str[j+1]) - 0xdc00) + 0x10000;
|
||||
buf[i] = u8(0xf0 + (c >> 18)); i += 1;
|
||||
buf[i] = u8(0x80 + ((c >> 12) & 0x3f)); i += 1;
|
||||
buf[i] = u8(0x80 + ((c >> 6) & 0x3f)); i += 1;
|
||||
buf[i] = u8(0x80 + ((c ) & 0x3f)); i += 1;
|
||||
j += 2;
|
||||
case 0xdc00 <= str[j] && str[j] < 0xe000:
|
||||
return "";
|
||||
case:
|
||||
if i+3 > len do return "";
|
||||
buf[i] = 0xe0 + u8 (str[j] >> 12); i += 1;
|
||||
buf[i] = 0x80 + u8((str[j] >> 6) & 0x3f); i += 1;
|
||||
buf[i] = 0x80 + u8((str[j] ) & 0x3f); i += 1;
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return string(buf[..i]);
|
||||
}
|
||||
|
||||
arg_count: i32;
|
||||
arg_list_ptr := win32.command_line_to_argv_w(win32.get_command_line_w(), &arg_count);
|
||||
arg_list := make([]string, int(arg_count));
|
||||
for _, i in arg_list do arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^);
|
||||
return arg_list;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
foreign_system_library (
|
||||
dl "dl";
|
||||
libc "c";
|
||||
)
|
||||
|
||||
import "strings.odin";
|
||||
|
||||
Handle :: i32;
|
||||
FileTime :: u64;
|
||||
Errno :: int;
|
||||
|
||||
|
||||
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 :: 0x1;
|
||||
RTLD_NOW :: 0x2;
|
||||
RTLD_LOCAL :: 0x4;
|
||||
RTLD_GLOBAL :: 0x8;
|
||||
RTLD_NODELETE :: 0x80;
|
||||
RTLD_NOLOAD :: 0x10;
|
||||
RTLD_FIRST :: 0x100;
|
||||
|
||||
|
||||
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 do return (m & S_IFMT) == S_IFLNK;
|
||||
S_ISREG :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFREG;
|
||||
S_ISDIR :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFDIR;
|
||||
S_ISCHR :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFCHR;
|
||||
S_ISBLK :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFBLK;
|
||||
S_ISFIFO :: proc(m: u32) -> bool #inline do return (m & S_IFMT) == S_IFIFO;
|
||||
S_ISSOCK :: proc(m: u32) -> bool #inline do 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 libc {
|
||||
unix_open :: proc(path: ^u8, mode: int) -> Handle #link_name "open" ---;
|
||||
unix_close :: proc(handle: Handle) #link_name "close" ---;
|
||||
unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int #link_name "read" ---;
|
||||
unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int #link_name "write" ---;
|
||||
unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int #link_name "lseek" ---;
|
||||
unix_gettid :: proc() -> u64 #link_name "gettid" ---;
|
||||
unix_stat :: proc(path: ^u8, stat: ^Stat) -> int #link_name "stat" ---;
|
||||
unix_access :: proc(path: ^u8, mask: int) -> int #link_name "access" ---;
|
||||
|
||||
unix_malloc :: proc(size: int) -> rawptr #link_name "malloc" ---;
|
||||
unix_free :: proc(ptr: rawptr) #link_name "free" ---;
|
||||
unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr #link_name "realloc" ---;
|
||||
unix_getenv :: proc(^u8) -> ^u8 #link_name "getenv" ---;
|
||||
|
||||
unix_exit :: proc(status: int) #link_name "exit" ---;
|
||||
}
|
||||
|
||||
foreign dl {
|
||||
unix_dlopen :: proc(filename: ^u8, flags: int) -> rawptr #link_name "dlopen" ---;
|
||||
unix_dlsym :: proc(handle: rawptr, symbol: ^u8) -> (proc() #cc_c) #link_name "dlsym" ---;
|
||||
unix_dlclose :: proc(handle: rawptr) -> int #link_name "dlclose" ---;
|
||||
unix_dlerror :: proc() -> ^u8 #link_name "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 = O_RDONLY, perm: u32 = 0) -> (Handle, Errno) {
|
||||
return open_simple(path, mode);
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
unix_close(fd);
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, 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: []u8) -> (int, 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) {
|
||||
assert(fd != -1);
|
||||
|
||||
final_offset := i64(unix_lseek(fd, offset, whence));
|
||||
if(final_offset == -1) {
|
||||
return 0, 1;
|
||||
}
|
||||
return final_offset, 0;
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
prev, _ := seek(fd, 0, SEEK_CUR);
|
||||
size, err := seek(fd, 0, SEEK_END);
|
||||
seek(fd, prev, SEEK_SET);
|
||||
return i64(size), err;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
stdin: Handle = 0; // get_std_handle(win32.STD_INPUT_HANDLE);
|
||||
stdout: Handle = 1; // get_std_handle(win32.STD_OUTPUT_HANDLE);
|
||||
stderr: Handle = 2; // get_std_handle(win32.STD_ERROR_HANDLE);
|
||||
|
||||
/* TODO(zangent): Implement these!
|
||||
last_write_time :: proc(fd: Handle) -> FileTime {}
|
||||
last_write_time_by_name :: proc(name: string) -> FileTime {}
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> bool #inline {
|
||||
cstr := strings.new_c_string(path);
|
||||
defer free(cstr);
|
||||
return unix_access(cstr, mask) == 0;
|
||||
}
|
||||
|
||||
heap_alloc :: proc(size: int) -> rawptr #inline {
|
||||
assert(size > 0);
|
||||
return unix_malloc(size);
|
||||
}
|
||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr #inline {
|
||||
return unix_realloc(ptr, new_size);
|
||||
}
|
||||
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) #inline {
|
||||
unix_exit(code);
|
||||
}
|
||||
|
||||
|
||||
current_thread_id :: proc() -> int {
|
||||
// return cast(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());
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
Any :: struct #ordered {
|
||||
data: rawptr;
|
||||
type_info: ^TypeInfo;
|
||||
};
|
||||
|
||||
String :: struct #ordered {
|
||||
data: ^u8;
|
||||
len: int;
|
||||
};
|
||||
|
||||
Slice :: struct #ordered {
|
||||
data: rawptr;
|
||||
len: int;
|
||||
cap: int;
|
||||
};
|
||||
|
||||
DynamicArray :: struct #ordered {
|
||||
data: rawptr;
|
||||
len: int;
|
||||
cap: int;
|
||||
allocator: Allocator;
|
||||
};
|
||||
|
||||
DynamicMap :: struct #ordered {
|
||||
hashes: [dynamic]int;
|
||||
entries: DynamicArray;
|
||||
};
|
||||
|
||||
+213
@@ -0,0 +1,213 @@
|
||||
bubble_sort :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
assert(f != nil);
|
||||
count := len(array);
|
||||
|
||||
init_j, last_j := 0, count-1;
|
||||
|
||||
for {
|
||||
init_swap, prev_swap := -1, -1;
|
||||
|
||||
for j in init_j..last_j {
|
||||
if f(array[j], array[j+1]) > 0 {
|
||||
array[j], array[j+1] = array[j+1], array[j];
|
||||
prev_swap = j;
|
||||
if init_swap == -1 do init_swap = j;
|
||||
}
|
||||
}
|
||||
|
||||
if prev_swap == -1 do return;
|
||||
|
||||
init_j = max(init_swap-1, 0);
|
||||
last_j = prev_swap;
|
||||
}
|
||||
}
|
||||
|
||||
bubble_sort :: proc(array: $A/[]$T) {
|
||||
count := len(array);
|
||||
|
||||
init_j, last_j := 0, count-1;
|
||||
|
||||
for {
|
||||
init_swap, prev_swap := -1, -1;
|
||||
|
||||
for j in init_j..last_j {
|
||||
if array[j] > array[j+1] {
|
||||
array[j], array[j+1] = array[j+1], array[j];
|
||||
prev_swap = j;
|
||||
if init_swap == -1 do init_swap = j;
|
||||
}
|
||||
}
|
||||
|
||||
if prev_swap == -1 do return;
|
||||
|
||||
init_j = max(init_swap-1, 0);
|
||||
last_j = prev_swap;
|
||||
}
|
||||
}
|
||||
|
||||
quick_sort :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
assert(f != nil);
|
||||
a := array;
|
||||
n := len(a);
|
||||
if n < 2 do return;
|
||||
|
||||
p := a[n/2];
|
||||
i, j := 0, n-1;
|
||||
|
||||
loop: for {
|
||||
for f(a[i], p) < 0 do i += 1;
|
||||
for f(p, a[j]) < 0 do j -= 1;
|
||||
|
||||
if i >= j do break loop;
|
||||
|
||||
a[i], a[j] = a[j], a[i];
|
||||
i += 1;
|
||||
j -= 1;
|
||||
}
|
||||
|
||||
quick_sort(a[0..i], f);
|
||||
quick_sort(a[i..n], f);
|
||||
}
|
||||
|
||||
quick_sort :: proc(array: $A/[]$T) {
|
||||
a := array;
|
||||
n := len(a);
|
||||
if n < 2 do return;
|
||||
|
||||
p := a[n/2];
|
||||
i, j := 0, n-1;
|
||||
|
||||
loop: for {
|
||||
for a[i] < p do i += 1;
|
||||
for p < a[j] do j -= 1;
|
||||
|
||||
if i >= j do break loop;
|
||||
|
||||
a[i], a[j] = a[j], a[i];
|
||||
i += 1;
|
||||
j -= 1;
|
||||
}
|
||||
|
||||
quick_sort(a[0..i]);
|
||||
quick_sort(a[i..n]);
|
||||
}
|
||||
|
||||
_log2 :: proc(n: int) -> int {
|
||||
res := 0;
|
||||
for ; n != 0; n >>= 1 do res += 1;
|
||||
return res;
|
||||
}
|
||||
|
||||
merge_sort :: proc(array: $A/[]$T, f: proc(T, T) -> int) {
|
||||
merge_slices :: proc(arr1, arr2, out: A, f: proc(T, T) -> int) {
|
||||
N1, N2 := len(arr1), len(arr2);
|
||||
i, j := 0, 0;
|
||||
for k in 0..N1+N2 {
|
||||
if j == N2 || i < N1 && j < N2 && f(arr1[i], arr2[j]) < 0 {
|
||||
out[k] = arr1[i];
|
||||
i += 1;
|
||||
} else {
|
||||
out[k] = arr2[j];
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(f != nil);
|
||||
|
||||
arr1 := array;
|
||||
N := len(arr1);
|
||||
arr2 := make([]T, N);
|
||||
defer free(arr2);
|
||||
|
||||
a, b, m, M := N/2, N, 1, _log2(N);
|
||||
|
||||
for i in 0..M+1 {
|
||||
for j in 0..a {
|
||||
k := 2*j*m;
|
||||
merge_slices(arr1[k..k+m], arr1[k+m..k+m+m], arr2[k..], f);
|
||||
}
|
||||
if N-b > m {
|
||||
k := 2*a*m;
|
||||
merge_slices(arr1[k..k+m], arr1[k+m..k+m+(N-b)&(m-1)], arr2[k..], f);
|
||||
} else {
|
||||
copy(arr2[b..N], arr1[b..N]);
|
||||
}
|
||||
arr1, arr2 = arr2, arr1;
|
||||
m <<= 1;
|
||||
a >>= 1;
|
||||
b = a << uint(i) << 2;
|
||||
}
|
||||
|
||||
if M & 1 == 0 do copy(arr2, arr1);
|
||||
}
|
||||
|
||||
merge_sort :: proc(array: $A/[]$T) {
|
||||
merge_slices :: proc(arr1, arr2, out: A) {
|
||||
N1, N2 := len(arr1), len(arr2);
|
||||
i, j := 0, 0;
|
||||
for k in 0..N1+N2 {
|
||||
if j == N2 || i < N1 && j < N2 && arr1[i] < arr2[j] {
|
||||
out[k] = arr1[i];
|
||||
i += 1;
|
||||
} else {
|
||||
out[k] = arr2[j];
|
||||
j += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
arr1 := array;
|
||||
N := len(arr1);
|
||||
arr2 := make([]T, N);
|
||||
defer free(arr2);
|
||||
|
||||
a, b, m, M := N/2, N, 1, _log2(N);
|
||||
|
||||
for i in 0..M+1 {
|
||||
for j in 0..a {
|
||||
k := 2*j*m;
|
||||
merge_slices(arr1[k..k+m], arr1[k+m..k+m+m], arr2[k..]);
|
||||
}
|
||||
if N-b > m {
|
||||
k := 2*a*m;
|
||||
merge_slices(arr1[k..k+m], arr1[k+m..k+m+(N-b)&(m-1)], arr2[k..]);
|
||||
} else {
|
||||
copy(arr2[b..N], arr1[b..N]);
|
||||
}
|
||||
arr1, arr2 = arr2, arr1;
|
||||
m <<= 1;
|
||||
a >>= 1;
|
||||
b = a << uint(i) << 2;
|
||||
}
|
||||
|
||||
if M & 1 == 0 do copy(arr2, arr1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
compare_ints :: proc(a, b: int) -> int {
|
||||
match delta := a - b; {
|
||||
case delta < 0: return -1;
|
||||
case delta > 0: return +1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
compare_f32s :: proc(a, b: f32) -> int {
|
||||
match delta := a - b; {
|
||||
case delta < 0: return -1;
|
||||
case delta > 0: return +1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
compare_f64s :: proc(a, b: f64) -> int {
|
||||
match delta := a - b; {
|
||||
case delta < 0: return -1;
|
||||
case delta > 0: return +1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
compare_strings :: proc(a, b: string) -> int {
|
||||
return __string_cmp(a, b);
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
import . "decimal.odin";
|
||||
|
||||
IntFlag :: enum {
|
||||
Prefix = 1<<0,
|
||||
Plus = 1<<1,
|
||||
Space = 1<<2,
|
||||
}
|
||||
|
||||
|
||||
parse_bool :: proc(s: string) -> (result: bool, ok: bool) {
|
||||
match s {
|
||||
case "1", "t", "T", "true", "TRUE", "True":
|
||||
return true, true;
|
||||
case "0", "f", "F", "false", "FALSE", "False":
|
||||
return false, true;
|
||||
}
|
||||
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_i128 :: proc(s: string) -> i128 {
|
||||
neg := false;
|
||||
if len(s) > 1 {
|
||||
match s[0] {
|
||||
case '-':
|
||||
neg = true;
|
||||
s = s[1..];
|
||||
case '+':
|
||||
s = s[1..];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
base: i128 = 10;
|
||||
if len(s) > 2 && s[0] == '0' {
|
||||
match s[1] {
|
||||
case 'b': base = 2; s = s[2..];
|
||||
case 'o': base = 8; s = s[2..];
|
||||
case 'd': base = 10; s = s[2..];
|
||||
case 'z': base = 12; s = s[2..];
|
||||
case 'x': base = 16; s = s[2..];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
value: i128;
|
||||
for r in s {
|
||||
if r == '_' {
|
||||
continue;
|
||||
}
|
||||
|
||||
v := i128(_digit_value(r));
|
||||
if v >= base {
|
||||
break;
|
||||
}
|
||||
value *= base;
|
||||
value += v;
|
||||
}
|
||||
|
||||
if neg do return -value;
|
||||
return value;
|
||||
}
|
||||
|
||||
parse_u128 :: proc(s: string) -> u128 {
|
||||
neg := false;
|
||||
if len(s) > 1 && s[0] == '+' {
|
||||
s = s[1..];
|
||||
}
|
||||
|
||||
|
||||
base := u128(10);
|
||||
if len(s) > 2 && s[0] == '0' {
|
||||
match s[1] {
|
||||
case 'b': base = 2; s = s[2..];
|
||||
case 'o': base = 8; s = s[2..];
|
||||
case 'd': base = 10; s = s[2..];
|
||||
case 'z': base = 12; s = s[2..];
|
||||
case 'x': base = 16; s = s[2..];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
value: u128;
|
||||
for r in s {
|
||||
if r == '_' do continue;
|
||||
v := u128(_digit_value(r));
|
||||
if v >= base do break;
|
||||
value *= base;
|
||||
value += u128(v);
|
||||
}
|
||||
|
||||
if neg do return -value;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
parse_int :: proc(s: string) -> int {
|
||||
return int(parse_i128(s));
|
||||
}
|
||||
parse_uint :: proc(s: string, base: int) -> uint {
|
||||
return uint(parse_u128(s));
|
||||
}
|
||||
|
||||
parse_f64 :: proc(s: string) -> f64 {
|
||||
i := 0;
|
||||
|
||||
sign: f64 = 1;
|
||||
match s[i] {
|
||||
case '-': i += 1; sign = -1;
|
||||
case '+': i += 1;
|
||||
}
|
||||
|
||||
value: f64 = 0;
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i]);
|
||||
if r == '_' do continue;
|
||||
|
||||
v := _digit_value(r);
|
||||
if v >= 10 do break;
|
||||
value *= 10;
|
||||
value += f64(v);
|
||||
}
|
||||
|
||||
if s[i] == '.' {
|
||||
pow10: f64 = 10;
|
||||
i += 1;
|
||||
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i]);
|
||||
if r == '_' do continue;
|
||||
|
||||
v := _digit_value(r);
|
||||
if v >= 10 do break;
|
||||
value += f64(v)/pow10;
|
||||
pow10 *= 10;
|
||||
}
|
||||
}
|
||||
|
||||
frac := false;
|
||||
scale: f64 = 1;
|
||||
|
||||
if s[i] == 'e' || s[i] == 'E' {
|
||||
i += 1;
|
||||
|
||||
match s[i] {
|
||||
case '-': i += 1; frac = true;
|
||||
case '+': i += 1;
|
||||
}
|
||||
|
||||
exp: u32 = 0;
|
||||
for ; i < len(s); i += 1 {
|
||||
r := rune(s[i]);
|
||||
if r == '_' do continue;
|
||||
|
||||
d := u32(_digit_value(r));
|
||||
if d >= 10 do break;
|
||||
exp = exp * 10 + d;
|
||||
}
|
||||
if exp > 308 { exp = 308; }
|
||||
|
||||
for exp >= 50 { scale *= 1e50; exp -= 50; }
|
||||
for exp >= 8 { scale *= 1e8; exp -= 8; }
|
||||
for exp > 0 { scale *= 10; exp -= 1; }
|
||||
}
|
||||
|
||||
if frac do return sign * (value/scale);
|
||||
return sign * (value*scale);
|
||||
}
|
||||
|
||||
|
||||
append_bool :: proc(buf: []u8, b: bool) -> string {
|
||||
if b {
|
||||
append(&buf, "true");
|
||||
} else {
|
||||
append(&buf, "false");
|
||||
}
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
append_uint :: proc(buf: []u8, u: u64, base: int) -> string {
|
||||
return append_bits(buf, u128(u), base, false, 8*size_of(uint), digits, 0);
|
||||
}
|
||||
append_int :: proc(buf: []u8, i: i64, base: int) -> string {
|
||||
return append_bits(buf, u128(i), base, true, 8*size_of(int), digits, 0);
|
||||
}
|
||||
itoa :: proc(buf: []u8, i: int) -> string { return append_int(buf, i64(i), 10); }
|
||||
|
||||
append_float :: proc(buf: []u8, f: f64, fmt: u8, prec, bit_size: int) -> string {
|
||||
return string(generic_ftoa(buf, f, fmt, prec, bit_size));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
DecimalSlice :: struct {
|
||||
digits: []u8;
|
||||
count: int;
|
||||
decimal_point: int;
|
||||
neg: bool;
|
||||
}
|
||||
|
||||
FloatInfo :: struct {
|
||||
mantbits: uint;
|
||||
expbits: uint;
|
||||
bias: int;
|
||||
}
|
||||
|
||||
|
||||
_f16_info := FloatInfo{10, 5, -15};
|
||||
_f32_info := FloatInfo{23, 8, -127};
|
||||
_f64_info := FloatInfo{52, 11, -1023};
|
||||
|
||||
|
||||
generic_ftoa :: proc(buf: []u8, val: f64, fmt: u8, prec, bit_size: int) -> []u8 {
|
||||
bits: u64;
|
||||
flt: ^FloatInfo;
|
||||
match bit_size {
|
||||
case 32:
|
||||
bits = u64(transmute(u32)f32(val));
|
||||
flt = &_f32_info;
|
||||
case 64:
|
||||
bits = transmute(u64)val;
|
||||
flt = &_f64_info;
|
||||
case:
|
||||
panic("strconv: invalid bit_size");
|
||||
}
|
||||
|
||||
neg := bits>>(flt.expbits+flt.mantbits) != 0;
|
||||
exp := int(bits>>flt.mantbits) & (1<<flt.expbits - 1);
|
||||
mant := bits & (u64(1) << flt.mantbits - 1);
|
||||
|
||||
match exp {
|
||||
case 1<<flt.expbits - 1:
|
||||
s: string;
|
||||
if mant != 0 {
|
||||
s = "NaN";
|
||||
} else if neg {
|
||||
s = "-Inf";
|
||||
} else {
|
||||
s = "+Inf";
|
||||
}
|
||||
append(&buf, ...cast([]u8)s);
|
||||
return buf;
|
||||
|
||||
case 0: // denormalized
|
||||
exp += 1;
|
||||
|
||||
case:
|
||||
mant |= u64(1) << flt.mantbits;
|
||||
}
|
||||
|
||||
exp += flt.bias;
|
||||
|
||||
d_: Decimal;
|
||||
d := &d_;
|
||||
assign(d, mant);
|
||||
shift(d, exp - int(flt.mantbits));
|
||||
digs: DecimalSlice;
|
||||
shortest := prec < 0;
|
||||
if shortest {
|
||||
round_shortest(d, mant, exp, flt);
|
||||
digs = DecimalSlice{digits = d.digits[..], count = d.count, decimal_point = d.decimal_point};
|
||||
match fmt {
|
||||
case 'e', 'E': prec = digs.count-1;
|
||||
case 'f', 'F': prec = max(digs.count-digs.decimal_point, 0);
|
||||
case 'g', 'G': prec = digs.count;
|
||||
}
|
||||
} else {
|
||||
match fmt {
|
||||
case 'e', 'E': round(d, prec+1);
|
||||
case 'f', 'F': round(d, d.decimal_point+prec);
|
||||
case 'g', 'G':
|
||||
if prec == 0 {
|
||||
prec = 1;
|
||||
}
|
||||
round(d, prec);
|
||||
}
|
||||
|
||||
digs = DecimalSlice{digits = d.digits[..], count = d.count, decimal_point = d.decimal_point};
|
||||
}
|
||||
return format_digits(buf, shortest, neg, digs, prec, fmt);
|
||||
}
|
||||
|
||||
|
||||
|
||||
format_digits :: proc(buf: []u8, shortest: bool, neg: bool, digs: DecimalSlice, prec: int, fmt: u8) -> []u8 {
|
||||
match fmt {
|
||||
case 'f', 'F':
|
||||
append(&buf, neg ? '-' : '+');
|
||||
|
||||
// integer, padded with zeros when needed
|
||||
if digs.decimal_point > 0 {
|
||||
m := min(digs.count, digs.decimal_point);
|
||||
append(&buf, ...digs.digits[..m]);
|
||||
for ; m < digs.decimal_point; m += 1 {
|
||||
append(&buf, '0');
|
||||
}
|
||||
} else {
|
||||
append(&buf, '0');
|
||||
}
|
||||
|
||||
|
||||
// fractional part
|
||||
if prec > 0 {
|
||||
append(&buf, '.');
|
||||
for i in 0..prec {
|
||||
c: u8 = '0';
|
||||
if j := digs.decimal_point + i; 0 <= j && j < digs.count {
|
||||
c = digs.digits[j];
|
||||
}
|
||||
append(&buf, c);
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
|
||||
case 'e', 'E':
|
||||
panic("strconv: e/E float printing is not yet supported");
|
||||
return buf; // TODO
|
||||
|
||||
case 'g', 'G':
|
||||
panic("strconv: g/G float printing is not yet supported");
|
||||
return buf; // TODO
|
||||
}
|
||||
|
||||
c := [2]u8{'%', fmt};
|
||||
append(&buf, ...c[..]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^FloatInfo) {
|
||||
if mant == 0 { // If mantissa is zero, the number is zero
|
||||
d.count = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
10^(dp-nd) > 2^(exp-mantbits)
|
||||
log2(10) * (dp-nd) > exp-mantbits
|
||||
log(2) >~ 0.332
|
||||
332*(dp-nd) >= 100*(exp-mantbits)
|
||||
*/
|
||||
minexp := flt.bias+1;
|
||||
if exp > minexp && 332*(d.decimal_point-d.count) >= 100*(exp - int(flt.mantbits)) {
|
||||
// Number is already its shortest
|
||||
return;
|
||||
}
|
||||
|
||||
upper_: Decimal; upper := &upper_;
|
||||
assign(upper, 2*mant - 1);
|
||||
shift(upper, exp - int(flt.mantbits) - 1);
|
||||
|
||||
mantlo: u64;
|
||||
explo: int;
|
||||
if mant > 1<<flt.mantbits || exp == minexp {
|
||||
mantlo = mant-1;
|
||||
explo = exp;
|
||||
} else {
|
||||
mantlo = 2*mant - 1;
|
||||
explo = exp-1;
|
||||
}
|
||||
lower_: Decimal; lower := &lower_;
|
||||
assign(lower, 2*mantlo + 1);
|
||||
shift(lower, explo - int(flt.mantbits) - 1);
|
||||
|
||||
inclusive := mant%2 == 0;
|
||||
|
||||
for i in 0..d.count {
|
||||
l: u8 = '0'; // lower digit
|
||||
if i < lower.count {
|
||||
l = lower.digits[i];
|
||||
}
|
||||
m := d.digits[i]; // middle digit
|
||||
u: u8 = '0'; // upper digit
|
||||
if i < upper.count {
|
||||
u = upper.digits[i];
|
||||
}
|
||||
|
||||
ok_round_down := l != m || inclusive && i+1 == lower.count;
|
||||
ok_round_up := m != u && (inclusive || m+1 < u || i+1 < upper.count);
|
||||
|
||||
if (ok_round_down && ok_round_up) {
|
||||
round(d, i+1);
|
||||
return;
|
||||
}
|
||||
if (ok_round_down) {
|
||||
round_down(d, i+1);
|
||||
return;
|
||||
}
|
||||
if (ok_round_up) {
|
||||
round_up(d, i+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MAX_BASE :: 32;
|
||||
digits := "0123456789abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
|
||||
is_integer_negative :: proc(u: u128, is_signed: bool, bit_size: int) -> (unsigned: u128, neg: bool) {
|
||||
neg := false;
|
||||
if is_signed {
|
||||
match bit_size {
|
||||
case 8:
|
||||
i := i8(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case 16:
|
||||
i := i16(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case 32:
|
||||
i := i32(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case 64:
|
||||
i := i64(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case 128:
|
||||
i := i128(u);
|
||||
neg = i < 0;
|
||||
if neg { i = -i; }
|
||||
u = u128(i);
|
||||
case:
|
||||
panic("is_integer_negative: Unknown integer size");
|
||||
}
|
||||
}
|
||||
return u, neg;
|
||||
}
|
||||
|
||||
append_bits :: proc(buf: []u8, u: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: IntFlag) -> string {
|
||||
if base < 2 || base > MAX_BASE {
|
||||
panic("strconv: illegal base passed to append_bits");
|
||||
}
|
||||
|
||||
neg: bool;
|
||||
a: [129]u8;
|
||||
i := len(a);
|
||||
u, neg = is_integer_negative(u, is_signed, bit_size);
|
||||
b := u128(base);
|
||||
for u >= b {
|
||||
i-=1; a[i] = digits[uint(u % b)];
|
||||
u /= b;
|
||||
}
|
||||
i-=1; a[i] = digits[uint(u % b)];
|
||||
|
||||
if flags&IntFlag.Prefix != 0 {
|
||||
ok := true;
|
||||
match base {
|
||||
case 2: i-=1; a[i] = 'b';
|
||||
case 8: i-=1; a[i] = 'o';
|
||||
case 10: i-=1; a[i] = 'd';
|
||||
case 12: i-=1; a[i] = 'z';
|
||||
case 16: i-=1; a[i] = 'x';
|
||||
case: ok = false;
|
||||
}
|
||||
if ok {
|
||||
i-=1; a[i] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
if neg {
|
||||
i-=1; a[i] = '-';
|
||||
} else if flags&IntFlag.Plus != 0 {
|
||||
i-=1; a[i] = '+';
|
||||
} else if flags&IntFlag.Space != 0 {
|
||||
i-=1; a[i] = ' ';
|
||||
}
|
||||
|
||||
append(&buf, ...a[i..]);
|
||||
return string(buf);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import "mem.odin";
|
||||
|
||||
new_string :: proc(s: string) -> string {
|
||||
c := make([]u8, len(s)+1);
|
||||
copy(c, cast([]u8)s);
|
||||
c[len(s)] = 0;
|
||||
return string(c[..len(s)]);
|
||||
}
|
||||
|
||||
new_c_string :: proc(s: string) -> ^u8 {
|
||||
c := make([]u8, len(s)+1);
|
||||
copy(c, cast([]u8)s);
|
||||
c[len(s)] = 0;
|
||||
return &c[0];
|
||||
}
|
||||
|
||||
to_odin_string :: proc(c: ^u8) -> string {
|
||||
if c == nil do return "";
|
||||
len := 0;
|
||||
for (c+len)^ != 0 do len+=1;
|
||||
return string(mem.slice_ptr(c, len));
|
||||
}
|
||||
+4
-91
@@ -1,91 +1,4 @@
|
||||
#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 += 1;
|
||||
}
|
||||
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 += 1;
|
||||
return true;
|
||||
}
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
recursion: i32;
|
||||
thread_id := current_thread_id();
|
||||
assert(thread_id == atomic.load(^m.owner));
|
||||
|
||||
m.recursion -= 1;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import_load (
|
||||
"sync_windows.odin" when ODIN_OS == "windows";
|
||||
"sync_linux.odin" when ODIN_OS == "linux";
|
||||
)
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
import (
|
||||
"atomics.odin";
|
||||
"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 += 1;
|
||||
}
|
||||
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 += 1;
|
||||
return true;
|
||||
}
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
recursion: i32;
|
||||
thread_id := current_thread_id();
|
||||
assert(thread_id == atomics.load(&m._owner));
|
||||
|
||||
m._recursion -= 1;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import (
|
||||
win32 "sys/windows.odin" when ODIN_OS == "windows";
|
||||
"atomics.odin";
|
||||
)
|
||||
|
||||
Semaphore :: struct {
|
||||
_handle: win32.Handle;
|
||||
}
|
||||
|
||||
/*
|
||||
Mutex :: struct {
|
||||
_semaphore: Semaphore;
|
||||
_counter: i32;
|
||||
_owner: i32;
|
||||
_recursion: i32;
|
||||
}
|
||||
*/
|
||||
|
||||
Mutex :: struct {
|
||||
_critical_section: win32.CriticalSection;
|
||||
}
|
||||
|
||||
current_thread_id :: proc() -> i32 {
|
||||
return i32(win32.get_current_thread_id());
|
||||
}
|
||||
|
||||
semaphore_init :: proc(s: ^Semaphore) {
|
||||
s._handle = win32.create_semaphore_a(nil, 0, 1<<31-1, nil);
|
||||
}
|
||||
|
||||
semaphore_destroy :: proc(s: ^Semaphore) {
|
||||
win32.close_handle(s._handle);
|
||||
}
|
||||
|
||||
semaphore_post :: proc(s: ^Semaphore, count: int) {
|
||||
win32.release_semaphore(s._handle, i32(count), nil);
|
||||
}
|
||||
|
||||
semaphore_release :: proc(s: ^Semaphore) #inline { semaphore_post(s, 1); }
|
||||
|
||||
semaphore_wait :: proc(s: ^Semaphore) {
|
||||
win32.wait_for_single_object(s._handle, win32.INFINITE);
|
||||
}
|
||||
|
||||
|
||||
mutex_init :: proc(m: ^Mutex, spin_count := 0) {
|
||||
win32.initialize_critical_section_and_spin_count(&m._critical_section, u32(spin_count));
|
||||
}
|
||||
|
||||
mutex_destroy :: proc(m: ^Mutex) {
|
||||
win32.delete_critical_section(&m._critical_section);
|
||||
}
|
||||
|
||||
mutex_lock :: proc(m: ^Mutex) {
|
||||
win32.enter_critical_section(&m._critical_section);
|
||||
}
|
||||
|
||||
mutex_try_lock :: proc(m: ^Mutex) -> bool {
|
||||
return win32.try_enter_critical_section(&m._critical_section) != 0;
|
||||
}
|
||||
|
||||
mutex_unlock :: proc(m: ^Mutex) {
|
||||
win32.leave_critical_section(&m._critical_section);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,83 @@
|
||||
foreign_system_library "opengl32.lib" when ODIN_OS == "windows";
|
||||
import . "windows.odin";
|
||||
|
||||
|
||||
CONTEXT_MAJOR_VERSION_ARB :: 0x2091;
|
||||
CONTEXT_MINOR_VERSION_ARB :: 0x2092;
|
||||
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;
|
||||
ColorRef :: u32;
|
||||
|
||||
LayerPlaneDescriptor :: struct {
|
||||
size: u16;
|
||||
version: u16;
|
||||
flags: u32;
|
||||
pixel_type: u8;
|
||||
color_bits: u8;
|
||||
red_bits: u8;
|
||||
red_shift: u8;
|
||||
green_bits: u8;
|
||||
green_shift: u8;
|
||||
blue_bits: u8;
|
||||
blue_shift: u8;
|
||||
alpha_bits: u8;
|
||||
alpha_shift: u8;
|
||||
accum_bits: u8;
|
||||
accum_red_bits: u8;
|
||||
accum_green_bits: u8;
|
||||
accum_blue_bits: u8;
|
||||
accum_alpha_bits: u8;
|
||||
depth_bits: u8;
|
||||
stencil_bits: u8;
|
||||
aux_buffers: u8;
|
||||
layer_type: u8;
|
||||
reserved: u8;
|
||||
transparent: ColorRef;
|
||||
}
|
||||
|
||||
PointFloat :: struct {x, y: f32};
|
||||
|
||||
Glyph_MetricsFloat :: struct {
|
||||
black_box_x: f32;
|
||||
black_box_y: f32;
|
||||
glyph_origin: PointFloat;
|
||||
cell_inc_x: f32;
|
||||
cell_inc_y: f32;
|
||||
}
|
||||
|
||||
CreateContextAttribsARBType :: proc(hdc: Hdc, h_share_context: rawptr, attribList: ^i32) -> Hglrc;
|
||||
ChoosePixelFormatARBType :: proc(hdc: Hdc, attrib_i_list: ^i32, attrib_f_list: ^f32, max_formats: u32, formats: ^i32, num_formats : ^u32) -> Bool #cc_c;
|
||||
SwapIntervalEXTType :: proc(interval: i32) -> bool #cc_c;
|
||||
GetExtensionsStringARBType :: proc(Hdc) -> ^u8 #cc_c;
|
||||
|
||||
// Procedures
|
||||
create_context_attribs_arb: CreateContextAttribsARBType;
|
||||
choose_pixel_format_arb: ChoosePixelFormatARBType;
|
||||
swap_interval_ext: SwapIntervalEXTType;
|
||||
get_extensions_string_arb: GetExtensionsStringARBType;
|
||||
|
||||
|
||||
|
||||
foreign opengl32 {
|
||||
create_context :: proc(hdc: Hdc) -> Hglrc #link_name "wglCreateContext" ---;
|
||||
make_current :: proc(hdc: Hdc, hglrc: Hglrc) -> Bool #link_name "wglMakeCurrent" ---;
|
||||
get_proc_address :: proc(c_str: ^u8) -> rawptr #link_name "wglGetProcAddress" ---;
|
||||
delete_context :: proc(hglrc: Hglrc) -> Bool #link_name "wglDeleteContext" ---;
|
||||
copy_context :: proc(src, dst: Hglrc, mask: u32) -> Bool #link_name "wglCopyContext" ---;
|
||||
create_layer_context :: proc(hdc: Hdc, layer_plane: i32) -> Hglrc #link_name "wglCreateLayerContext" ---;
|
||||
describe_layer_plane :: proc(hdc: Hdc, pixel_format, layer_plane: i32, bytes: u32, pd: ^LayerPlaneDescriptor) -> Bool #link_name "wglDescribeLayerPlane" ---;
|
||||
get_current_context :: proc() -> Hglrc #link_name "wglGetCurrentContext" ---;
|
||||
get_current_dc :: proc() -> Hdc #link_name "wglGetCurrentDC" ---;
|
||||
get_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^ColorRef) -> i32 #link_name "wglGetLayerPaletteEntries" ---;
|
||||
realize_layer_palette :: proc(hdc: Hdc, layer_plane: i32, realize: Bool) -> Bool #link_name "wglRealizeLayerPalette" ---;
|
||||
set_layer_palette_entries :: proc(hdc: Hdc, layer_plane, start, entries: i32, cr: ^ColorRef) -> i32 #link_name "wglSetLayerPaletteEntries" ---;
|
||||
share_lists :: proc(hglrc1, hglrc2: Hglrc) -> Bool #link_name "wglShareLists" ---;
|
||||
swap_layer_buffers :: proc(hdc: Hdc, planes: u32) -> Bool #link_name "wglSwapLayerBuffers" ---;
|
||||
use_font_bitmaps :: proc(hdc: Hdc, first, count, list_base: u32) -> Bool #link_name "wglUseFontBitmaps" ---;
|
||||
use_font_outlines :: proc(hdc: Hdc, first, count, list_base: u32, deviation, extrusion: f32, format: i32, gmf: ^Glyph_MetricsFloat) -> Bool #link_name "wglUseFontOutlines" ---;
|
||||
}
|
||||
+559
-396
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,76 @@
|
||||
_ :: compile_assert(ODIN_OS == "windows");
|
||||
|
||||
import win32 "sys/windows.odin";
|
||||
|
||||
Thread :: struct {
|
||||
using specific: OsSpecific;
|
||||
procedure: Proc;
|
||||
data: any;
|
||||
user_index: int;
|
||||
|
||||
init_context: Context;
|
||||
use_init_context: bool;
|
||||
|
||||
Proc :: #type proc(^Thread) -> int;
|
||||
OsSpecific :: struct {
|
||||
win32_thread: win32.Handle;
|
||||
win32_thread_id: u32;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
create :: proc(procedure: Thread.Proc) -> ^Thread {
|
||||
win32_thread_id: u32;
|
||||
|
||||
__windows_thread_entry_proc :: proc(data: rawptr) -> i32 #cc_c {
|
||||
if data == nil do return 0;
|
||||
|
||||
t := cast(^Thread)data;
|
||||
|
||||
c := context;
|
||||
if t.use_init_context {
|
||||
c = t.init_context;
|
||||
}
|
||||
|
||||
exit := 0;
|
||||
push_context c {
|
||||
exit = t.procedure(t);
|
||||
}
|
||||
|
||||
return cast(i32)exit;
|
||||
}
|
||||
|
||||
|
||||
win32_thread_proc := cast(rawptr)__windows_thread_entry_proc;
|
||||
thread := new(Thread);
|
||||
|
||||
win32_thread := win32.create_thread(nil, 0, win32_thread_proc, thread, win32.CREATE_SUSPENDED, &win32_thread_id);
|
||||
if win32_thread == nil {
|
||||
free(thread);
|
||||
return nil;
|
||||
}
|
||||
thread.procedure = procedure;
|
||||
thread.win32_thread = win32_thread;
|
||||
thread.win32_thread_id = win32_thread_id;
|
||||
|
||||
return thread;
|
||||
}
|
||||
|
||||
start :: proc(using thread: ^Thread) {
|
||||
win32.resume_thread(win32_thread);
|
||||
}
|
||||
|
||||
is_done :: proc(using thread: ^Thread) -> bool {
|
||||
res := win32.wait_for_single_object(win32_thread, 0);
|
||||
return res != win32.WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
join :: proc(using thread: ^Thread) {
|
||||
win32.wait_for_single_object(win32_thread, win32.INFINITE);
|
||||
win32.close_handle(win32_thread);
|
||||
win32_thread = win32.INVALID_HANDLE;
|
||||
}
|
||||
destroy :: proc(thread: ^Thread) {
|
||||
join(thread);
|
||||
free(thread);
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
is_signed :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
match i in type_info_base(info).variant {
|
||||
case TypeInfo.Integer: return i.signed;
|
||||
case TypeInfo.Float: return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
is_integer :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Integer);
|
||||
return ok;
|
||||
}
|
||||
is_rune :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Rune);
|
||||
return ok;
|
||||
}
|
||||
is_float :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Float);
|
||||
return ok;
|
||||
}
|
||||
is_complex :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Complex);
|
||||
return ok;
|
||||
}
|
||||
is_any :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Any);
|
||||
return ok;
|
||||
}
|
||||
is_string :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.String);
|
||||
return ok;
|
||||
}
|
||||
is_boolean :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Boolean);
|
||||
return ok;
|
||||
}
|
||||
is_pointer :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Pointer);
|
||||
return ok;
|
||||
}
|
||||
is_procedure :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Procedure);
|
||||
return ok;
|
||||
}
|
||||
is_array :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Array);
|
||||
return ok;
|
||||
}
|
||||
is_dynamic_array :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.DynamicArray);
|
||||
return ok;
|
||||
}
|
||||
is_dynamic_map :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Map);
|
||||
return ok;
|
||||
}
|
||||
is_slice :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Slice);
|
||||
return ok;
|
||||
}
|
||||
is_vector :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Vector);
|
||||
return ok;
|
||||
}
|
||||
is_tuple :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Tuple);
|
||||
return ok;
|
||||
}
|
||||
is_struct :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
s, ok := type_info_base(info).variant.(TypeInfo.Struct);
|
||||
return ok && !s.is_raw_union;
|
||||
}
|
||||
is_raw_union :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
s, ok := type_info_base(info).variant.(TypeInfo.Struct);
|
||||
return ok && s.is_raw_union;
|
||||
}
|
||||
is_union :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Union);
|
||||
return ok;
|
||||
}
|
||||
is_enum :: proc(info: ^TypeInfo) -> bool {
|
||||
if info == nil do return false;
|
||||
_, ok := type_info_base(info).variant.(TypeInfo.Enum);
|
||||
return ok;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
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 do if r >= _surr_self do n += 1;
|
||||
|
||||
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 += 1;
|
||||
|
||||
case _surr_self..MAX_RUNE:
|
||||
r1, r2 := encode_surrogate_pair(r);
|
||||
d[n] = u16(r1);
|
||||
d[n+1] = u16(r2);
|
||||
n += 2;
|
||||
|
||||
case:
|
||||
d[n] = u16(REPLACEMENT_CHAR);
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
+72
-32
@@ -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;
|
||||
|
||||
@@ -24,13 +24,13 @@ RUNE1_MAX :: 1<<7 - 1;
|
||||
RUNE2_MAX :: 1<<11 - 1;
|
||||
RUNE3_MAX :: 1<<16 - 1;
|
||||
|
||||
// The default lowest and highest continuation byte.
|
||||
// The default lowest and highest continuation byte.
|
||||
LOCB :: 0b1000_0000;
|
||||
HICB :: 0b1011_1111;
|
||||
|
||||
Accept_Range :: struct { lo, hi: u8 }
|
||||
AcceptRange :: struct { lo, hi: u8 }
|
||||
|
||||
immutable accept_ranges := [5]Accept_Range{
|
||||
accept_ranges := [5]AcceptRange{
|
||||
{0x80, 0xbf},
|
||||
{0xa0, 0xbf},
|
||||
{0x80, 0x9f},
|
||||
@@ -38,7 +38,7 @@ immutable accept_ranges := [5]Accept_Range{
|
||||
{0x80, 0x8f},
|
||||
};
|
||||
|
||||
immutable accept_sizes := [256]byte{
|
||||
accept_sizes := [256]u8{
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f
|
||||
0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f
|
||||
@@ -58,17 +58,17 @@ immutable accept_sizes := [256]byte{
|
||||
0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xf0-0xff
|
||||
};
|
||||
|
||||
encode_rune :: proc(r: rune) -> ([4]byte, int) {
|
||||
buf: [4]byte;
|
||||
i := cast(u32)r;
|
||||
mask: byte : 0x3f;
|
||||
encode_rune :: proc(r: rune) -> ([4]u8, int) {
|
||||
buf: [4]u8;
|
||||
i := u32(r);
|
||||
mask: u8 : 0x3f;
|
||||
if i <= 1<<7-1 {
|
||||
buf[0] = cast(byte)r;
|
||||
buf[0] = u8(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 | u8(r>>6);
|
||||
buf[1] = 0x80 | u8(r) & mask;
|
||||
return buf, 2;
|
||||
}
|
||||
|
||||
@@ -79,33 +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 | u8(r>>12);
|
||||
buf[1] = 0x80 | u8(r>>6) & mask;
|
||||
buf[2] = 0x80 | u8(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 | u8(r>>18);
|
||||
buf[1] = 0x80 | u8(r>>12) & mask;
|
||||
buf[2] = 0x80 | u8(r>>6) & mask;
|
||||
buf[3] = 0x80 | u8(r) & mask;
|
||||
return buf, 4;
|
||||
}
|
||||
|
||||
decode_rune :: proc(s: string) -> (rune, int) {
|
||||
n := s.count;
|
||||
decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune(cast([]u8)s); }
|
||||
decode_rune :: proc(s: []u8) -> (rune, int) {
|
||||
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];
|
||||
@@ -113,23 +114,59 @@ decode_rune :: proc(s: string) -> (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([]u8)s); }
|
||||
decode_last_rune :: proc(s: []u8) -> (rune, int) {
|
||||
r: rune;
|
||||
size: int;
|
||||
start, end, limit: int;
|
||||
|
||||
end = len(s);
|
||||
if end == 0 {
|
||||
return RUNE_ERROR, 0;
|
||||
}
|
||||
start = end-1;
|
||||
r = rune(s[start]);
|
||||
if r < RUNE_SELF {
|
||||
return r, 1;
|
||||
}
|
||||
|
||||
|
||||
limit = max(end - UTF_MAX, 0);
|
||||
|
||||
for start-=1; start >= limit; start-=1 {
|
||||
if rune_start(s[start]) do break;
|
||||
}
|
||||
|
||||
start = max(start, 0);
|
||||
r, size = decode_rune(s[start..end]);
|
||||
if start+size != end {
|
||||
return RUNE_ERROR, 1;
|
||||
}
|
||||
return r, size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
valid_rune :: proc(r: rune) -> bool {
|
||||
if r < 0 {
|
||||
return false;
|
||||
@@ -142,7 +179,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
|
||||
@@ -153,7 +190,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;
|
||||
}
|
||||
@@ -174,9 +211,12 @@ valid_string :: proc(s: string) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
rune_count :: proc(s: string) -> int {
|
||||
rune_start :: proc(b: u8) -> bool #inline { return b&0xc0 != 0x80; }
|
||||
|
||||
rune_count :: proc(s: string) -> int #inline { return rune_count(cast([]u8)s); }
|
||||
rune_count :: proc(s: []u8) -> int {
|
||||
count := 0;
|
||||
n := s.count;
|
||||
n := len(s);
|
||||
|
||||
for i := 0; i < n; {
|
||||
defer count += 1;
|
||||
@@ -190,7 +230,7 @@ rune_count :: proc(s: string) -> int {
|
||||
i += 1;
|
||||
continue;
|
||||
}
|
||||
size := cast(int)(x & 7);
|
||||
size := int(x & 7);
|
||||
if i+size > n {
|
||||
i += 1;
|
||||
continue;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
@echo off
|
||||
setlocal EnableDelayedExpansion
|
||||
|
||||
set file_input=%1
|
||||
set name=%1
|
||||
FOR %%f IN (name) do (
|
||||
FOR %%g in (!%%f!) do set "%%f=%%~ng"
|
||||
)
|
||||
|
||||
call clang -O2 -c %file_input% -o %name%.o ^
|
||||
&& call ar %name%.o -rcs %name%.lib
|
||||
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 246 KiB |
+4
-2
@@ -1,8 +1,10 @@
|
||||
@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
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL
|
||||
rem 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\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL
|
||||
rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL
|
||||
set _NO_DEBUG_HEAP=1
|
||||
|
||||
set path=w:\Odin\misc;%path%
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
@echo off
|
||||
|
||||
|
||||
rem call clang -c -emit-llvm -DGB_IMPLEMENTATION -DGB_DEF=GB_DLL_EXPORT ..\src\gb\gb.h
|
||||
+102
-105
@@ -1,104 +1,7 @@
|
||||
#define ARRAY_GROW_FORMULA(x) (2*(x) + 8)
|
||||
GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
|
||||
|
||||
#define Array(Type_) struct { \
|
||||
gbAllocator allocator; \
|
||||
Type_ * e; \
|
||||
isize count; \
|
||||
isize capacity; \
|
||||
}
|
||||
|
||||
typedef Array(void) ArrayVoid;
|
||||
|
||||
#define array_init_reserve(x_, allocator_, init_capacity_) do { \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != NULL); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = 0; \
|
||||
(x_)->capacity = (init_capacity_); \
|
||||
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \
|
||||
} while (0)
|
||||
|
||||
#define array_init_count(x_, allocator_, init_count_) do { \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != NULL); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = (init_count_); \
|
||||
(x_)->capacity = (init_count_); \
|
||||
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \
|
||||
} while (0)
|
||||
|
||||
#define array_init(x_, allocator_) do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0)
|
||||
#define array_free(x_) do { gb_free((x_)->allocator, (x_)->e); } while (0)
|
||||
#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0)
|
||||
|
||||
#define array_grow(x_, min_capacity_) do { \
|
||||
isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \
|
||||
if (new_capacity < (min_capacity_)) { \
|
||||
new_capacity = (min_capacity_); \
|
||||
} \
|
||||
array_set_capacity(x_, new_capacity); \
|
||||
} while (0)
|
||||
|
||||
#define array_add(x_, item_) do { \
|
||||
if ((x_)->capacity < (x_)->count+1) { \
|
||||
array_grow(x_, 0); \
|
||||
} \
|
||||
(x_)->e[(x_)->count++] = item_; \
|
||||
} while (0)
|
||||
|
||||
#define array_pop(x_) do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0)
|
||||
#define array_clear(x_) do { (x_)->count = 0; } while (0)
|
||||
|
||||
#define array_resize(x_, new_count_) do { \
|
||||
if ((x_)->capacity < (new_count_)) { \
|
||||
array_grow((x_), (new_count_)); \
|
||||
} \
|
||||
(x_)->count = (new_count_); \
|
||||
} while (0)
|
||||
|
||||
#define array_reserve(x_, new_capacity_) do { \
|
||||
if ((x_)->capacity < (new_capacity_)) { \
|
||||
array_set_capacity((x_), (new_capacity_)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
|
||||
void array__set_capacity(void *ptr, isize capacity, isize element_size) {
|
||||
ArrayVoid *x = cast(ArrayVoid *)ptr;
|
||||
GB_ASSERT(ptr != NULL);
|
||||
|
||||
GB_ASSERT(element_size > 0);
|
||||
|
||||
if (capacity == x->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (capacity < x->count) {
|
||||
if (x->capacity < capacity) {
|
||||
isize new_capacity = ARRAY_GROW_FORMULA(x->capacity);
|
||||
if (new_capacity < capacity) {
|
||||
new_capacity = capacity;
|
||||
}
|
||||
array__set_capacity(ptr, new_capacity, element_size);
|
||||
}
|
||||
x->count = capacity;
|
||||
}
|
||||
|
||||
{
|
||||
// TODO(bill): Resize rather than copy and delete
|
||||
void *new_data = gb_alloc(x->allocator, element_size*capacity);
|
||||
gb_memmove(new_data, x->e, element_size*x->count);
|
||||
gb_free(x->allocator, x->e);
|
||||
x->capacity = capacity;
|
||||
x->e = new_data;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
#if 1
|
||||
template <typename T>
|
||||
struct Array {
|
||||
gbAllocator allocator;
|
||||
@@ -107,12 +10,16 @@ struct Array {
|
||||
isize capacity;
|
||||
|
||||
T &operator[](isize index) {
|
||||
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
|
||||
#if !defined(NO_ARRAY_BOUNDS_CHECK)
|
||||
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
|
||||
#endif
|
||||
return data[index];
|
||||
}
|
||||
|
||||
T const &operator[](isize index) const {
|
||||
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
|
||||
#if !defined(NO_ARRAY_BOUNDS_CHECK)
|
||||
GB_ASSERT_MSG(0 <= index && index < count, "Index %td is out of bounds ranges 0..<%td", index, count);
|
||||
#endif
|
||||
return data[index];
|
||||
}
|
||||
};
|
||||
@@ -128,7 +35,6 @@ template <typename T> void array_reserve (Array<T> *array, isize capacit
|
||||
template <typename T> void array_resize (Array<T> *array, isize count);
|
||||
template <typename T> void array_set_capacity(Array<T> *array, isize capacity);
|
||||
|
||||
|
||||
template <typename T>
|
||||
void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
|
||||
array->allocator = a;
|
||||
@@ -158,7 +64,7 @@ Array<T> array_make(T *data, isize count, isize capacity) {
|
||||
|
||||
template <typename T>
|
||||
void array_free(Array<T> *array) {
|
||||
if (array->allocator.proc != NULL) {
|
||||
if (array->allocator.proc != nullptr) {
|
||||
gb_free(array->allocator, array->data);
|
||||
}
|
||||
array->count = 0;
|
||||
@@ -220,7 +126,7 @@ void array_set_capacity(Array<T> *array, isize capacity) {
|
||||
array_resize(array, capacity);
|
||||
}
|
||||
|
||||
T *new_data = NULL;
|
||||
T *new_data = nullptr;
|
||||
if (capacity > 0) {
|
||||
new_data = gb_alloc_array(array->allocator, T, capacity);
|
||||
gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity);
|
||||
@@ -230,6 +136,97 @@ void array_set_capacity(Array<T> *array, isize capacity) {
|
||||
array->capacity = capacity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
#define Array(Type_) struct { \
|
||||
gbAllocator allocator; \
|
||||
Type_ * e; \
|
||||
isize count; \
|
||||
isize capacity; \
|
||||
}
|
||||
|
||||
typedef Array(void) ArrayVoid;
|
||||
|
||||
#define array_init_reserve(x_, allocator_, init_capacity_) do { \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != nullptr); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = 0; \
|
||||
(x_)->capacity = (init_capacity_); \
|
||||
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \
|
||||
} while (0)
|
||||
|
||||
#define array_init_count(x_, allocator_, init_count_) do { \
|
||||
void **e = cast(void **)&((x_)->e); \
|
||||
GB_ASSERT((x_) != nullptr); \
|
||||
(x_)->allocator = (allocator_); \
|
||||
(x_)->count = (init_count_); \
|
||||
(x_)->capacity = (init_count_); \
|
||||
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \
|
||||
} while (0)
|
||||
|
||||
#define array_init(x_, allocator_) do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0)
|
||||
#define array_free(x_) do { gb_free((x_)->allocator, (x_)->e); } while (0)
|
||||
#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0)
|
||||
|
||||
#define array_grow(x_, min_capacity_) do { \
|
||||
isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \
|
||||
if (new_capacity < (min_capacity_)) { \
|
||||
new_capacity = (min_capacity_); \
|
||||
} \
|
||||
array_set_capacity(x_, new_capacity); \
|
||||
} while (0)
|
||||
|
||||
#define array_add(x_, item_) do { \
|
||||
if ((x_)->capacity < (x_)->count+1) { \
|
||||
array_grow(x_, 0); \
|
||||
} \
|
||||
(x_)->e[(x_)->count++] = item_; \
|
||||
} while (0)
|
||||
|
||||
#define array_pop(x_) do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0)
|
||||
#define array_clear(x_) do { (x_)->count = 0; } while (0)
|
||||
|
||||
#define array_resize(x_, new_count_) do { \
|
||||
if ((x_)->capacity < (new_count_)) { \
|
||||
array_grow((x_), (new_count_)); \
|
||||
} \
|
||||
(x_)->count = (new_count_); \
|
||||
} while (0)
|
||||
|
||||
#define array_reserve(x_, new_capacity_) do { \
|
||||
if ((x_)->capacity < (new_capacity_)) { \
|
||||
array_set_capacity((x_), (new_capacity_)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
|
||||
void array__set_capacity(void *ptr, isize capacity, isize element_size) {
|
||||
ArrayVoid *x = cast(ArrayVoid *)ptr;
|
||||
GB_ASSERT(ptr != nullptr);
|
||||
|
||||
GB_ASSERT(element_size > 0);
|
||||
|
||||
if (capacity == x->capacity) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (capacity < x->count) {
|
||||
if (x->capacity < capacity) {
|
||||
isize new_capacity = ARRAY_GROW_FORMULA(x->capacity);
|
||||
if (new_capacity < capacity) {
|
||||
new_capacity = capacity;
|
||||
}
|
||||
array__set_capacity(ptr, new_capacity, element_size);
|
||||
}
|
||||
x->count = capacity;
|
||||
}
|
||||
|
||||
x->e = gb_resize(x->allocator, x->e, element_size*x->capacity, element_size*capacity);
|
||||
x->capacity = capacity;
|
||||
}
|
||||
#endif
|
||||
|
||||
-172
@@ -1,172 +0,0 @@
|
||||
typedef struct BuildContext {
|
||||
String ODIN_OS; // target operating system
|
||||
String ODIN_ARCH; // target architecture
|
||||
String ODIN_VENDOR; // compiler vendor
|
||||
String ODIN_VERSION; // compiler version
|
||||
String ODIN_ROOT; // Odin ROOT
|
||||
|
||||
i64 word_size;
|
||||
i64 max_align;
|
||||
String llc_flags;
|
||||
String link_flags;
|
||||
bool is_dll;
|
||||
} BuildContext;
|
||||
|
||||
// TODO(bill): OS dependent versions for the BuildContext
|
||||
// join_path
|
||||
// is_dir
|
||||
// is_file
|
||||
// is_abs_path
|
||||
// has_subdir
|
||||
|
||||
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
|
||||
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
|
||||
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
Array(wchar_t) path_buf;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
wchar_t *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
array_init_count(&path_buf, heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
len = GetModuleFileNameW(NULL, &path_buf.e[0], path_buf.count);
|
||||
if (len == 0) {
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
if (len < path_buf.count) {
|
||||
break;
|
||||
}
|
||||
array_resize(&path_buf, 2*path_buf.count + 300);
|
||||
}
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
|
||||
text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
|
||||
GetModuleFileNameW(NULL, text, len);
|
||||
path = string16_to_string(heap_allocator(), make_string16(text, len));
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path.text[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
|
||||
array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
String16 string16 = string_to_string16(string_buffer_allocator, s);
|
||||
String result = {0};
|
||||
|
||||
DWORD len = GetFullPathNameW(string16.text, 0, NULL, NULL);
|
||||
if (len != 0) {
|
||||
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
GetFullPathNameW(string16.text, len, text, NULL);
|
||||
text[len] = 0;
|
||||
result = string16_to_string(a, make_string16(text, len));
|
||||
}
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
|
||||
String res = {0};
|
||||
isize str_len = base_dir.len+path.len;
|
||||
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len;
|
||||
gb_memmove(str+i, path.text, path.len);
|
||||
str[str_len] = '\0';
|
||||
res = path_to_fullpath(a, make_string(str, str_len));
|
||||
gb_free(heap_allocator(), str);
|
||||
return res;
|
||||
}
|
||||
|
||||
String get_fullpath_core(gbAllocator a, String path) {
|
||||
String module_dir = odin_root_dir();
|
||||
String res = {0};
|
||||
|
||||
char core[] = "core/";
|
||||
isize core_len = gb_size_of(core)-1;
|
||||
|
||||
isize str_len = module_dir.len + core_len + path.len;
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
|
||||
|
||||
gb_memmove(str, module_dir.text, module_dir.len);
|
||||
gb_memmove(str+module_dir.len, core, core_len);
|
||||
gb_memmove(str+module_dir.len+core_len, path.text, path.len);
|
||||
str[str_len] = '\0';
|
||||
|
||||
res = path_to_fullpath(a, make_string(str, str_len));
|
||||
gb_free(heap_allocator(), str);
|
||||
return res;
|
||||
}
|
||||
|
||||
String get_filepath_extension(String path) {
|
||||
isize dot = 0;
|
||||
bool seen_slash = false;
|
||||
for (isize i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path.text[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
seen_slash = true;
|
||||
}
|
||||
|
||||
if (c == '.') {
|
||||
if (seen_slash) {
|
||||
return str_lit("");
|
||||
}
|
||||
|
||||
dot = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return make_string(path.text, dot);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void init_build_context(BuildContext *bc) {
|
||||
bc->ODIN_VENDOR = str_lit("odin");
|
||||
bc->ODIN_VERSION = str_lit("0.0.5e");
|
||||
bc->ODIN_ROOT = odin_root_dir();
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
bc->ODIN_OS = str_lit("windows");
|
||||
bc->ODIN_ARCH = str_lit("amd64");
|
||||
#else
|
||||
#error Implement system
|
||||
#endif
|
||||
|
||||
if (str_eq(bc->ODIN_ARCH, str_lit("amd64"))) {
|
||||
bc->word_size = 8;
|
||||
bc->max_align = 16;
|
||||
bc->llc_flags = str_lit("-march=x86-64 ");
|
||||
bc->link_flags = str_lit("/machine:x64 ");
|
||||
} else if (str_eq(bc->ODIN_ARCH, str_lit("x86"))) {
|
||||
bc->word_size = 4;
|
||||
bc->max_align = 8;
|
||||
bc->llc_flags = str_lit("-march=x86 ");
|
||||
bc->link_flags = str_lit("/machine:x86 ");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
// This stores the information for the specify architecture of this build
|
||||
struct BuildContext {
|
||||
// Constants
|
||||
String ODIN_OS; // target operating system
|
||||
String ODIN_ARCH; // target architecture
|
||||
String ODIN_ENDIAN; // target endian
|
||||
String ODIN_VENDOR; // compiler vendor
|
||||
String ODIN_VERSION; // compiler version
|
||||
String ODIN_ROOT; // Odin ROOT
|
||||
|
||||
// In bytes
|
||||
i64 word_size; // Size of a pointer, must be >= 4
|
||||
i64 max_align; // max alignment, must be >= 1 (and typically >= word_size)
|
||||
|
||||
String opt_flags;
|
||||
String llc_flags;
|
||||
String link_flags;
|
||||
bool is_dll;
|
||||
bool generate_docs;
|
||||
i32 optimization_level;
|
||||
bool show_timings;
|
||||
bool keep_temp_files;
|
||||
|
||||
gbAffinity affinity;
|
||||
isize thread_count;
|
||||
};
|
||||
|
||||
|
||||
gb_global BuildContext build_context = {0};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO(bill): OS dependent versions for the BuildContext
|
||||
// join_path
|
||||
// is_dir
|
||||
// is_file
|
||||
// is_abs_path
|
||||
// has_subdir
|
||||
|
||||
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
|
||||
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
Array<wchar_t> path_buf;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
wchar_t *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
array_init_count(&path_buf, heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
len = GetModuleFileNameW(nullptr, &path_buf[0], cast(int)path_buf.count);
|
||||
if (len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
if (len < path_buf.count) {
|
||||
break;
|
||||
}
|
||||
array_resize(&path_buf, 2*path_buf.count + 300);
|
||||
}
|
||||
len += 1; // NOTE(bill): It needs an extra 1 for some reason
|
||||
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
|
||||
GetModuleFileNameW(nullptr, text, cast(int)len);
|
||||
path = string16_to_string(heap_allocator(), make_string16(text, len));
|
||||
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
|
||||
array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
|
||||
String odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
Array<char> path_buf;
|
||||
isize len, i;
|
||||
gbTempArenaMemory tmp;
|
||||
u8 *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
array_init_count(&path_buf, heap_allocator(), 300);
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
u32 sz = path_buf.count;
|
||||
int res = _NSGetExecutablePath(&path_buf[0], &sz);
|
||||
if(res == 0) {
|
||||
len = sz;
|
||||
break;
|
||||
} else {
|
||||
array_resize(&path_buf, sz + 1);
|
||||
}
|
||||
}
|
||||
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
|
||||
gb_memmove(text, &path_buf[0], len);
|
||||
|
||||
path = make_string(text, len);
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
|
||||
// array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
#else
|
||||
|
||||
// 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);
|
||||
defer (array_free(&path_buf));
|
||||
|
||||
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[0], path_buf.count);
|
||||
if(len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
if (len < path_buf.count) {
|
||||
break;
|
||||
}
|
||||
array_resize(&path_buf, 2*path_buf.count + 300);
|
||||
}
|
||||
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
text = gb_alloc_array(string_buffer_allocator, u8, len + 1);
|
||||
|
||||
gb_memmove(text, &path_buf[0], len);
|
||||
|
||||
path = make_string(text, len);
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
return path;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
String result = {};
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
defer (gb_mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
String16 string16 = string_to_string16(string_buffer_allocator, s);
|
||||
|
||||
DWORD len = GetFullPathNameW(&string16[0], 0, nullptr, nullptr);
|
||||
if (len != 0) {
|
||||
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
|
||||
GetFullPathNameW(&string16[0], len, text, nullptr);
|
||||
text[len] = 0;
|
||||
result = string16_to_string(a, make_string16(text, len));
|
||||
}
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
return result;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||
String path_to_fullpath(gbAllocator a, String s) {
|
||||
char *p;
|
||||
gb_mutex_lock(&string_buffer_mutex);
|
||||
p = realpath(cast(char *)s.text, 0);
|
||||
gb_mutex_unlock(&string_buffer_mutex);
|
||||
if(p == nullptr) return make_string_c("");
|
||||
return make_string_c(p);
|
||||
}
|
||||
#else
|
||||
#error Implement system
|
||||
#endif
|
||||
|
||||
|
||||
String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
|
||||
String res = {0};
|
||||
isize str_len = base_dir.len+path.len;
|
||||
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, &base_dir[0], base_dir.len); i += base_dir.len;
|
||||
gb_memmove(str+i, &path[0], path.len);
|
||||
str[str_len] = '\0';
|
||||
res = path_to_fullpath(a, make_string(str, str_len));
|
||||
gb_free(heap_allocator(), str);
|
||||
return res;
|
||||
}
|
||||
|
||||
String get_fullpath_core(gbAllocator a, String path) {
|
||||
String module_dir = odin_root_dir();
|
||||
String res = {0};
|
||||
|
||||
char core[] = "core/";
|
||||
isize core_len = gb_size_of(core)-1;
|
||||
|
||||
isize str_len = module_dir.len + core_len + path.len;
|
||||
u8 *str = gb_alloc_array(heap_allocator(), u8, str_len+1);
|
||||
|
||||
gb_memmove(str, &module_dir[0], module_dir.len);
|
||||
gb_memmove(str+module_dir.len, core, core_len);
|
||||
gb_memmove(str+module_dir.len+core_len, &path[0], path.len);
|
||||
str[str_len] = '\0';
|
||||
|
||||
res = path_to_fullpath(a, make_string(str, str_len));
|
||||
gb_free(heap_allocator(), str);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
String const ODIN_VERSION = str_lit("0.6.1a");
|
||||
|
||||
void init_build_context(void) {
|
||||
BuildContext *bc = &build_context;
|
||||
|
||||
gb_affinity_init(&bc->affinity);
|
||||
if (bc->thread_count == 0) {
|
||||
bc->thread_count = gb_max(bc->affinity.thread_count, 1);
|
||||
}
|
||||
|
||||
bc->ODIN_VENDOR = str_lit("odin");
|
||||
bc->ODIN_VERSION = ODIN_VERSION;
|
||||
bc->ODIN_ROOT = odin_root_dir();
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
bc->ODIN_OS = str_lit("windows");
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
bc->ODIN_OS = str_lit("osx");
|
||||
#else
|
||||
bc->ODIN_OS = str_lit("linux");
|
||||
#endif
|
||||
|
||||
#if defined(GB_ARCH_64_BIT)
|
||||
bc->ODIN_ARCH = str_lit("amd64");
|
||||
#else
|
||||
bc->ODIN_ARCH = str_lit("x86");
|
||||
#endif
|
||||
|
||||
{
|
||||
u16 x = 1;
|
||||
bool big = !(*cast(u8 *)&x);
|
||||
bc->ODIN_ENDIAN = big ? str_lit("big") : str_lit("little");
|
||||
}
|
||||
|
||||
|
||||
// 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(bc->ODIN_ARCH == "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 (bc->ODIN_ARCH == "amd64") {
|
||||
bc->word_size = 8;
|
||||
bc->max_align = 16;
|
||||
|
||||
bc->llc_flags = str_lit("-march=x86-64 ");
|
||||
bc->link_flags = str_lit(LINK_FLAG_X64 " ");
|
||||
} else if (bc->ODIN_ARCH == "x86") {
|
||||
bc->word_size = 4;
|
||||
bc->max_align = 8;
|
||||
bc->llc_flags = str_lit("-march=x86 ");
|
||||
bc->link_flags = str_lit(LINK_FLAG_X86 " ");
|
||||
} else {
|
||||
gb_printf_err("This current architecture is not supported");
|
||||
gb_exit(1);
|
||||
}
|
||||
|
||||
|
||||
isize opt_max = 1023;
|
||||
char *opt_flags_string = gb_alloc_array(heap_allocator(), char, opt_max+1);
|
||||
isize opt_len = 0;
|
||||
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
|
||||
if (bc->optimization_level != 0) {
|
||||
opt_len = gb_snprintf(opt_flags_string, opt_max, "-O%d", bc->optimization_level);
|
||||
} else {
|
||||
opt_len = gb_snprintf(opt_flags_string, opt_max, "");
|
||||
}
|
||||
if (opt_len > 0) {
|
||||
opt_len--;
|
||||
}
|
||||
bc->opt_flags = make_string(cast(u8 *)opt_flags_string, opt_len);
|
||||
|
||||
|
||||
#undef LINK_FLAG_X64
|
||||
#undef LINK_FLAG_X86
|
||||
}
|
||||
@@ -1,553 +0,0 @@
|
||||
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) {
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
gbString expr_str = expr_to_string(operand->expr);
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
error_node(operand->expr,
|
||||
"Cannot assign builtin procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
|
||||
gb_string_free(expr_str);
|
||||
}
|
||||
|
||||
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (e->type == NULL) {
|
||||
// NOTE(bill): Use the type of the operand
|
||||
Type *t = operand->type;
|
||||
if (is_type_untyped(t)) {
|
||||
if (t == t_invalid || is_type_untyped_nil(t)) {
|
||||
error(e->token, "Use of untyped nil in %.*s", LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
return NULL;
|
||||
}
|
||||
t = default_type(t);
|
||||
}
|
||||
GB_ASSERT(is_type_typed(t));
|
||||
e->type = t;
|
||||
}
|
||||
|
||||
check_assignment(c, operand, e->type, context_name);
|
||||
if (operand->mode == Addressing_Invalid) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return e->type;
|
||||
}
|
||||
|
||||
void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArray inits, String context_name) {
|
||||
if ((lhs == NULL || lhs_count == 0) && inits.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
|
||||
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
||||
// an extra allocation
|
||||
Array(Operand) operands;
|
||||
array_init_reserve(&operands, c->tmp_allocator, 2*lhs_count);
|
||||
|
||||
// TODO(bill): Allow for type hints from the entities
|
||||
for_array(i, inits) {
|
||||
AstNode *rhs = inits.e[i];
|
||||
Operand o = {0};
|
||||
check_multi_expr(c, &o, rhs);
|
||||
if (o.type->kind != Type_Tuple) {
|
||||
array_add(&operands, o);
|
||||
} else {
|
||||
TypeTuple *tuple = &o.type->Tuple;
|
||||
for (isize j = 0; j < tuple->variable_count; j++) {
|
||||
o.type = tuple->variables[j]->type;
|
||||
array_add(&operands, o);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isize rhs_count = operands.count;
|
||||
for_array(i, operands) {
|
||||
if (operands.e[i].mode == Addressing_Invalid) {
|
||||
rhs_count--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
isize max = gb_min(lhs_count, rhs_count);
|
||||
for (isize i = 0; i < max; i++) {
|
||||
check_init_variable(c, lhs[i], &operands.e[i], context_name);
|
||||
}
|
||||
|
||||
if (rhs_count > 0 && lhs_count != rhs_count) {
|
||||
error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (lhs[0]->kind == Entity_Variable &&
|
||||
lhs[0]->Variable.is_let) {
|
||||
if (lhs_count != rhs_count) {
|
||||
error(lhs[0]->token, "`let` variables must be initialized, `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
}
|
||||
|
||||
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
operand->type == t_invalid ||
|
||||
e->type == t_invalid) {
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
// TODO(bill): better error
|
||||
gbString str = expr_to_string(operand->expr);
|
||||
error_node(operand->expr, "`%s` is not a constant", str);
|
||||
gb_string_free(str);
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!is_type_constant_type(operand->type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
error_node(operand->expr, "Invalid constant type: `%s`", type_str);
|
||||
gb_string_free(type_str);
|
||||
if (e->type == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->type == NULL) { // NOTE(bill): type inference
|
||||
e->type = operand->type;
|
||||
}
|
||||
|
||||
check_assignment(c, operand, e->type, str_lit("constant declaration"));
|
||||
if (operand->mode == Addressing_Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
e->Constant.value = operand->value;
|
||||
}
|
||||
|
||||
void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
Type *named = make_type_named(c->allocator, e->token.string, NULL, e);
|
||||
named->Named.type_name = e;
|
||||
if (def != NULL && def->kind == Type_Named) {
|
||||
def->Named.base = named;
|
||||
}
|
||||
e->type = named;
|
||||
|
||||
// gb_printf_err("%.*s %p\n", LIT(e->token.string), e);
|
||||
|
||||
Type *bt = check_type_extra(c, type_expr, named);
|
||||
named->Named.base = base_type(bt);
|
||||
if (named->Named.base == t_invalid) {
|
||||
// gb_printf("check_type_decl: %s\n", type_to_string(named));
|
||||
}
|
||||
}
|
||||
|
||||
void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init, Type *named_type) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
GB_ASSERT(e->kind == Entity_Constant);
|
||||
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (type_expr) {
|
||||
Type *t = check_type(c, type_expr);
|
||||
if (!is_type_constant_type(t)) {
|
||||
gbString str = type_to_string(t);
|
||||
error_node(type_expr, "Invalid constant type `%s`", str);
|
||||
gb_string_free(str);
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->type = t;
|
||||
}
|
||||
|
||||
Operand operand = {0};
|
||||
if (init != NULL) {
|
||||
check_expr_or_type(c, &operand, init);
|
||||
}
|
||||
if (operand.mode == Addressing_Type) {
|
||||
e->kind = Entity_TypeName;
|
||||
|
||||
DeclInfo *d = c->context.decl;
|
||||
d->type_expr = d->init_expr;
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
return;
|
||||
}
|
||||
|
||||
check_init_constant(c, e, &operand);
|
||||
|
||||
if (operand.mode == Addressing_Invalid ||
|
||||
base_type(operand.type) == t_invalid) {
|
||||
error(e->token, "Invalid declaration type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool are_signatures_similar_enough(Type *a_, Type *b_) {
|
||||
GB_ASSERT(a_->kind == Type_Proc);
|
||||
GB_ASSERT(b_->kind == Type_Proc);
|
||||
TypeProc *a = &a_->Proc;
|
||||
TypeProc *b = &b_->Proc;
|
||||
|
||||
if (a->param_count != b->param_count) {
|
||||
return false;
|
||||
}
|
||||
if (a->result_count != b->result_count) {
|
||||
return false;
|
||||
}
|
||||
for (isize i = 0; i < a->param_count; i++) {
|
||||
Type *x = base_type(a->params->Tuple.variables[i]->type);
|
||||
Type *y = base_type(b->params->Tuple.variables[i]->type);
|
||||
if (is_type_pointer(x) && is_type_pointer(y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!are_types_identical(x, y)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (isize i = 0; i < a->result_count; i++) {
|
||||
Type *x = base_type(a->results->Tuple.variables[i]->type);
|
||||
Type *y = base_type(b->results->Tuple.variables[i]->type);
|
||||
if (is_type_pointer(x) && is_type_pointer(y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!are_types_identical(x, y)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_proc_lit(Checker *c, Entity *e, DeclInfo *d) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
if (d->proc_lit->kind != AstNode_ProcLit) {
|
||||
// TOOD(bill): Better error message
|
||||
error_node(d->proc_lit, "Expected a procedure to check");
|
||||
return;
|
||||
}
|
||||
|
||||
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false, ProcCC_Odin);
|
||||
e->type = proc_type;
|
||||
ast_node(pd, ProcLit, d->proc_lit);
|
||||
|
||||
check_open_scope(c, pd->type);
|
||||
check_procedure_type(c, proc_type, pd->type);
|
||||
|
||||
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
|
||||
bool is_link_name = (pd->tags & ProcTag_link_name) != 0;
|
||||
bool is_export = (pd->tags & ProcTag_export) != 0;
|
||||
bool is_inline = (pd->tags & ProcTag_inline) != 0;
|
||||
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
|
||||
|
||||
if ((d->scope->is_file || d->scope->is_global) &&
|
||||
str_eq(e->token.string, str_lit("main"))) {
|
||||
if (proc_type != NULL) {
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
if (pt->param_count != 0 ||
|
||||
pt->result_count != 0) {
|
||||
gbString str = type_to_string(proc_type);
|
||||
error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_inline && is_no_inline) {
|
||||
error_node(pd->type, "You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
}
|
||||
|
||||
if (is_foreign && is_link_name) {
|
||||
error_node(pd->type, "You cannot apply both `foreign` and `link_name` to a procedure");
|
||||
} else if (is_foreign && is_export) {
|
||||
error_node(pd->type, "You cannot apply both `foreign` and `export` to a procedure");
|
||||
}
|
||||
|
||||
|
||||
if (pd->body != NULL) {
|
||||
if (is_foreign) {
|
||||
error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
|
||||
}
|
||||
|
||||
// TODO(bill): Is this the best option? What about passing to external shit?!
|
||||
// if (proc_type->Proc.calling_convention != ProcCC_Odin) {
|
||||
// error_node(d->proc_lit, "An internal procedure may only have the Odin calling convention");
|
||||
// proc_type->Proc.calling_convention = ProcCC_Odin;
|
||||
// }
|
||||
|
||||
d->scope = c->context.scope;
|
||||
|
||||
GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
|
||||
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
|
||||
}
|
||||
|
||||
if (is_foreign) {
|
||||
MapEntity *fp = &c->info.foreign_procs;
|
||||
String name = e->token.string;
|
||||
if (pd->foreign_name.len > 0) {
|
||||
name = pd->foreign_name;
|
||||
}
|
||||
|
||||
AstNode *foreign_library = d->proc_lit->ProcLit.foreign_library;
|
||||
if (foreign_library == NULL) {
|
||||
error(e->token, "#foreign procedures must declare which library they are from");
|
||||
} else if (foreign_library->kind != AstNode_Ident) {
|
||||
error_node(foreign_library, "#foreign library names must be an identifier");
|
||||
} else {
|
||||
String name = foreign_library->Ident.string;
|
||||
Entity *found = scope_lookup_entity(c->context.scope, name);
|
||||
if (found == NULL) {
|
||||
if (str_eq(name, str_lit("_"))) {
|
||||
error_node(foreign_library, "`_` cannot be used as a value type");
|
||||
} else {
|
||||
error_node(foreign_library, "Undeclared name: %.*s", LIT(name));
|
||||
}
|
||||
} else if (found->kind != Entity_LibraryName) {
|
||||
error_node(foreign_library, "`_` cannot be used as a library name");
|
||||
} else {
|
||||
// TODO(bill): Extra stuff to do with library names?
|
||||
e->Procedure.foreign_library = found;
|
||||
add_entity_use(c, foreign_library, found);
|
||||
}
|
||||
}
|
||||
|
||||
e->Procedure.is_foreign = true;
|
||||
e->Procedure.foreign_name = name;
|
||||
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
Type *this_type = base_type(e->type);
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
map_entity_set(fp, key, e);
|
||||
}
|
||||
} else {
|
||||
String name = e->token.string;
|
||||
if (is_link_name) {
|
||||
name = pd->link_name;
|
||||
}
|
||||
|
||||
if (is_link_name || is_export) {
|
||||
MapEntity *fp = &c->info.foreign_procs;
|
||||
|
||||
e->Procedure.link_name = name;
|
||||
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_entity_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
// TODO(bill): Better error message?
|
||||
error_node(d->proc_lit,
|
||||
"Non unique linking name for procedure `%.*s`\n"
|
||||
"\tother at %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
map_entity_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_close_scope(c);
|
||||
}
|
||||
|
||||
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
|
||||
GB_ASSERT(e->type == NULL);
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (type_expr != NULL) {
|
||||
e->type = check_type_extra(c, type_expr, NULL);
|
||||
}
|
||||
|
||||
if (init_expr == NULL) {
|
||||
if (type_expr == NULL) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entities == NULL || entity_count == 1) {
|
||||
GB_ASSERT(entities == NULL || entities[0] == e);
|
||||
Operand operand = {0};
|
||||
check_expr(c, &operand, init_expr);
|
||||
check_init_variable(c, e, &operand, str_lit("variable declaration"));
|
||||
}
|
||||
|
||||
if (type_expr != NULL) {
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
entities[i]->type = e->type;
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeArray inits;
|
||||
array_init_reserve(&inits, c->allocator, 1);
|
||||
array_add(&inits, init_expr);
|
||||
check_init_variables(c, entities, entity_count, inits, str_lit("variable declaration"));
|
||||
}
|
||||
|
||||
|
||||
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
if (e->type != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d == NULL) {
|
||||
DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e));
|
||||
if (found) {
|
||||
d = *found;
|
||||
} else {
|
||||
// TODO(bill): Err here?
|
||||
e->type = t_invalid;
|
||||
set_base_type(named_type, t_invalid);
|
||||
return;
|
||||
// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
|
||||
}
|
||||
}
|
||||
|
||||
CheckerContext prev = c->context;
|
||||
c->context.scope = d->scope;
|
||||
c->context.decl = d;
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_Variable:
|
||||
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
|
||||
break;
|
||||
case Entity_Constant:
|
||||
check_const_decl(c, e, d->type_expr, d->init_expr, named_type);
|
||||
break;
|
||||
case Entity_TypeName:
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
break;
|
||||
case Entity_Procedure:
|
||||
check_proc_lit(c, e, d);
|
||||
break;
|
||||
}
|
||||
|
||||
c->context = prev;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
|
||||
GB_ASSERT(body->kind == AstNode_BlockStmt);
|
||||
|
||||
String proc_name = {0};
|
||||
if (token.kind == Token_Ident) {
|
||||
proc_name = token.string;
|
||||
} else {
|
||||
// TODO(bill): Better name
|
||||
proc_name = str_lit("(anonymous-procedure)");
|
||||
}
|
||||
|
||||
CheckerContext old_context = c->context;
|
||||
c->context.scope = decl->scope;
|
||||
c->context.decl = decl;
|
||||
c->context.proc_name = proc_name;
|
||||
|
||||
|
||||
GB_ASSERT(type->kind == Type_Proc);
|
||||
if (type->Proc.param_count > 0) {
|
||||
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)) {
|
||||
continue;
|
||||
}
|
||||
bool is_immutable = e->Variable.is_immutable;
|
||||
String name = e->token.string;
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
if (is_type_struct(t) || is_type_raw_union(t)) {
|
||||
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
|
||||
GB_ASSERT(found != NULL);
|
||||
for_array(i, (*found)->elements.entries) {
|
||||
Entity *f = (*found)->elements.entries.e[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
uvar->Variable.is_immutable = is_immutable;
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != NULL) {
|
||||
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(e->token, "`using` can only be applied to variables of type struct or raw_union");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
push_procedure(c, type);
|
||||
{
|
||||
ast_node(bs, BlockStmt, body);
|
||||
check_stmt_list(c, bs->stmts, 0);
|
||||
if (type->Proc.result_count > 0) {
|
||||
if (!check_is_terminating(body)) {
|
||||
if (token.kind == Token_Ident) {
|
||||
error(bs->close, "Missing return statement at the end of the procedure `%.*s`", LIT(token.string));
|
||||
} else {
|
||||
error(bs->close, "Missing return statement at the end of the procedure");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pop_procedure(c);
|
||||
|
||||
|
||||
check_scope_usage(c, c->context.scope);
|
||||
|
||||
c->context = old_context;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,768 @@
|
||||
bool check_is_terminating(AstNode *node);
|
||||
void check_stmt (Checker *c, AstNode *node, 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) {
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
gbString expr_str = expr_to_string(operand->expr);
|
||||
|
||||
// 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(operand->expr,
|
||||
"Cannot assign built-in procedure `%s` in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
|
||||
gb_string_free(expr_str);
|
||||
}
|
||||
|
||||
|
||||
if (operand->mode == Addressing_Overload) {
|
||||
if (e->type == nullptr) {
|
||||
error(operand->expr, "Cannot determine type from overloaded procedure `%.*s`", LIT(operand->overload_entities[0]->token.string));
|
||||
} else {
|
||||
check_assignment(c, operand, e->type, str_lit("variable assignment"));
|
||||
if (operand->mode != Addressing_Type) {
|
||||
return operand->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (e->type == nullptr) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (e->type == nullptr) {
|
||||
|
||||
// NOTE(bill): Use the type of the operand
|
||||
Type *t = operand->type;
|
||||
if (is_type_untyped(t)) {
|
||||
if (t == t_invalid || is_type_untyped_nil(t)) {
|
||||
error(e->token, "Invalid use of untyped nil in %.*s", LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
return nullptr;
|
||||
}
|
||||
if (t == t_invalid || is_type_untyped_undef(t)) {
|
||||
error(e->token, "Invalid use of --- in %.*s", LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
return nullptr;
|
||||
}
|
||||
t = default_type(t);
|
||||
}
|
||||
if (is_type_polymorphic(t)) {
|
||||
gbString str = type_to_string(t);
|
||||
defer (gb_string_free(str));
|
||||
error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
return nullptr;
|
||||
} else if (is_type_empty_union(t)) {
|
||||
gbString str = type_to_string(t);
|
||||
defer (gb_string_free(str));
|
||||
error(e->token, "An empty union `%s` cannot be instantiated in %.*s", str, LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
return nullptr;
|
||||
}
|
||||
if (is_type_bit_field_value(t)) {
|
||||
t = default_bit_field_value_type(t);
|
||||
}
|
||||
GB_ASSERT(is_type_typed(t));
|
||||
e->type = t;
|
||||
}
|
||||
|
||||
e->parent_proc_decl = c->context.curr_proc_decl;
|
||||
|
||||
check_assignment(c, operand, e->type, context_name);
|
||||
if (operand->mode == Addressing_Invalid) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return e->type;
|
||||
}
|
||||
|
||||
void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, Array<AstNode *> inits, String context_name) {
|
||||
if ((lhs == nullptr || lhs_count == 0) && inits.count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
|
||||
defer (gb_temp_arena_memory_end(tmp));
|
||||
|
||||
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
|
||||
// an extra allocation
|
||||
Array<Operand> operands = {};
|
||||
array_init(&operands, c->tmp_allocator, 2*lhs_count);
|
||||
check_unpack_arguments(c, lhs_count, &operands, inits, true);
|
||||
|
||||
isize rhs_count = operands.count;
|
||||
for_array(i, operands) {
|
||||
if (operands[i].mode == Addressing_Invalid) {
|
||||
rhs_count--;
|
||||
}
|
||||
}
|
||||
|
||||
isize max = gb_min(lhs_count, rhs_count);
|
||||
for (isize i = 0; i < max; i++) {
|
||||
check_init_variable(c, lhs[i], &operands[i], context_name);
|
||||
}
|
||||
if (rhs_count > 0 && lhs_count != rhs_count) {
|
||||
error(lhs[0]->token, "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
|
||||
}
|
||||
}
|
||||
|
||||
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
operand->type == t_invalid ||
|
||||
e->type == t_invalid) {
|
||||
if (e->type == nullptr) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
// TODO(bill): better error
|
||||
gbString str = expr_to_string(operand->expr);
|
||||
error(operand->expr, "`%s` is not a constant", str);
|
||||
gb_string_free(str);
|
||||
if (e->type == nullptr) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!is_type_constant_type(operand->type)) {
|
||||
gbString type_str = type_to_string(operand->type);
|
||||
error(operand->expr, "Invalid constant type: `%s`", type_str);
|
||||
gb_string_free(type_str);
|
||||
if (e->type == nullptr) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (e->type == nullptr) { // NOTE(bill): type inference
|
||||
e->type = operand->type;
|
||||
}
|
||||
|
||||
check_assignment(c, operand, e->type, str_lit("constant declaration"));
|
||||
if (operand->mode == Addressing_Invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
e->parent_proc_decl = c->context.curr_proc_decl;
|
||||
|
||||
e->Constant.value = operand->value;
|
||||
}
|
||||
|
||||
void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def) {
|
||||
GB_ASSERT(e->type == nullptr);
|
||||
String name = e->token.string;
|
||||
Type *named = make_type_named(c->allocator, name, nullptr, e);
|
||||
named->Named.type_name = e;
|
||||
if (def != nullptr && def->kind == Type_Named) {
|
||||
def->Named.base = named;
|
||||
}
|
||||
e->type = named;
|
||||
|
||||
// gb_printf_err("%.*s %p\n", LIT(e->token.string), e);
|
||||
|
||||
Type *bt = check_type(c, type_expr, named);
|
||||
named->Named.base = base_type(bt);
|
||||
if (named->Named.base == t_invalid) {
|
||||
// gb_printf("check_type_decl: %s\n", type_to_string(named));
|
||||
}
|
||||
}
|
||||
|
||||
void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init, Type *named_type) {
|
||||
GB_ASSERT(e->type == nullptr);
|
||||
GB_ASSERT(e->kind == Entity_Constant);
|
||||
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
if (type_expr) {
|
||||
Type *t = check_type(c, type_expr);
|
||||
if (!is_type_constant_type(t)) {
|
||||
gbString str = type_to_string(t);
|
||||
error(type_expr, "Invalid constant type `%s`", str);
|
||||
gb_string_free(str);
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->type = t;
|
||||
}
|
||||
|
||||
Operand operand = {};
|
||||
|
||||
if (init != nullptr) {
|
||||
Entity *entity = nullptr;
|
||||
if (init->kind == AstNode_Ident) {
|
||||
entity = check_ident(c, &operand, init, nullptr, e->type, true);
|
||||
} else if (init->kind == AstNode_SelectorExpr) {
|
||||
entity = check_selector(c, &operand, init, e->type);
|
||||
} else {
|
||||
check_expr_or_type(c, &operand, init, e->type);
|
||||
}
|
||||
|
||||
switch (operand.mode) {
|
||||
case Addressing_Type: {
|
||||
e->kind = Entity_TypeName;
|
||||
|
||||
DeclInfo *d = c->context.decl;
|
||||
if (d->type_expr != nullptr) {
|
||||
error(e->token, "A type declaration cannot have an type parameter");
|
||||
}
|
||||
d->type_expr = d->init_expr;
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
return;
|
||||
} break;
|
||||
|
||||
// NOTE(bill): Check to see if the expression it to be aliases
|
||||
#if 1
|
||||
case Addressing_Builtin:
|
||||
if (e->type != nullptr) {
|
||||
error(type_expr, "A constant alias of a built-in procedure may not have a type initializer");
|
||||
}
|
||||
e->kind = Entity_Builtin;
|
||||
e->Builtin.id = operand.builtin_id;
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
|
||||
case Addressing_Overload:
|
||||
e->kind = Entity_Alias;
|
||||
e->Alias.base = operand.overload_entities[0];
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
#if 1
|
||||
if (entity != nullptr) {
|
||||
switch (entity->kind) {
|
||||
case Entity_Alias:
|
||||
e->kind = Entity_Alias;
|
||||
e->type = entity->type;
|
||||
e->Alias.base = entity->Alias.base;
|
||||
return;
|
||||
case Entity_Procedure:
|
||||
e->kind = Entity_Alias;
|
||||
e->type = entity->type;
|
||||
e->Alias.base = entity;
|
||||
return;
|
||||
case Entity_ImportName:
|
||||
e->kind = Entity_ImportName;
|
||||
e->type = entity->type;
|
||||
e->ImportName.path = entity->ImportName.path;
|
||||
e->ImportName.name = entity->ImportName.path;
|
||||
e->ImportName.scope = entity->ImportName.scope;
|
||||
e->ImportName.used = false;
|
||||
return;
|
||||
case Entity_LibraryName:
|
||||
e->kind = Entity_LibraryName;
|
||||
e->type = entity->type;
|
||||
e->LibraryName.path = entity->LibraryName.path;
|
||||
e->LibraryName.name = entity->LibraryName.path;
|
||||
e->LibraryName.used = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (init != nullptr) {
|
||||
check_expr_or_type(c, &operand, init, e->type);
|
||||
}
|
||||
|
||||
check_init_constant(c, e, &operand);
|
||||
|
||||
if (operand.mode == Addressing_Invalid ||
|
||||
base_type(operand.type) == t_invalid) {
|
||||
gbString str = expr_to_string(init);
|
||||
error(e->token, "Invalid declaration type `%s`", str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool are_signatures_similar_enough(Type *a_, Type *b_) {
|
||||
GB_ASSERT(a_->kind == Type_Proc);
|
||||
GB_ASSERT(b_->kind == Type_Proc);
|
||||
TypeProc *a = &a_->Proc;
|
||||
TypeProc *b = &b_->Proc;
|
||||
|
||||
if (a->param_count != b->param_count) {
|
||||
return false;
|
||||
}
|
||||
if (a->result_count != b->result_count) {
|
||||
return false;
|
||||
}
|
||||
for (isize i = 0; i < a->param_count; i++) {
|
||||
Type *x = core_type(a->params->Tuple.variables[i]->type);
|
||||
Type *y = core_type(b->params->Tuple.variables[i]->type);
|
||||
if (is_type_pointer(x) && is_type_pointer(y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_type_integer(x) && is_type_integer(y)) {
|
||||
GB_ASSERT(x->kind == Type_Basic);
|
||||
GB_ASSERT(y->kind == Type_Basic);
|
||||
i64 sx = type_size_of(heap_allocator(), x);
|
||||
i64 sy = type_size_of(heap_allocator(), y);
|
||||
if (sx == sy) continue;
|
||||
}
|
||||
|
||||
if (!are_types_identical(x, y)) return false;
|
||||
}
|
||||
for (isize i = 0; i < a->result_count; i++) {
|
||||
Type *x = base_type(a->results->Tuple.variables[i]->type);
|
||||
Type *y = base_type(b->results->Tuple.variables[i]->type);
|
||||
if (is_type_pointer(x) && is_type_pointer(y)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_type_integer(x) && is_type_integer(y)) {
|
||||
GB_ASSERT(x->kind == Type_Basic);
|
||||
GB_ASSERT(y->kind == Type_Basic);
|
||||
i64 sx = type_size_of(heap_allocator(), x);
|
||||
i64 sy = type_size_of(heap_allocator(), y);
|
||||
if (sx == sy) continue;
|
||||
}
|
||||
|
||||
if (!are_types_identical(x, y)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void init_entity_foreign_library(Checker *c, Entity *e) {
|
||||
AstNode *ident = nullptr;
|
||||
Entity **foreign_library = nullptr;
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_Procedure:
|
||||
ident = e->Procedure.foreign_library_ident;
|
||||
foreign_library = &e->Procedure.foreign_library;
|
||||
break;
|
||||
case Entity_Variable:
|
||||
ident = e->Variable.foreign_library_ident;
|
||||
foreign_library = &e->Variable.foreign_library;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (ident == nullptr) {
|
||||
error(e->token, "foreign entiies must declare which library they are from");
|
||||
} else if (ident->kind != AstNode_Ident) {
|
||||
error(ident, "foreign library names must be an identifier");
|
||||
} else {
|
||||
String name = ident->Ident.token.string;
|
||||
Entity *found = scope_lookup_entity(c->context.scope, name);
|
||||
if (found == nullptr) {
|
||||
if (is_blank_ident(name)) {
|
||||
error(ident, "`_` cannot be used as a value type");
|
||||
} else {
|
||||
error(ident, "Undeclared name: %.*s", LIT(name));
|
||||
}
|
||||
} else if (found->kind != Entity_LibraryName) {
|
||||
error(ident, "`%.*s` cannot be used as a library name", LIT(name));
|
||||
} else {
|
||||
// TODO(bill): Extra stuff to do with library names?
|
||||
*foreign_library = found;
|
||||
add_entity_use(c, ident, found);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
|
||||
GB_ASSERT(e->type == nullptr);
|
||||
if (d->proc_lit->kind != AstNode_ProcLit) {
|
||||
// TOOD(bill): Better error message
|
||||
error(d->proc_lit, "Expected a procedure to check");
|
||||
return;
|
||||
}
|
||||
|
||||
Type *proc_type = e->type;
|
||||
if (d->gen_proc_type != nullptr) {
|
||||
proc_type = d->gen_proc_type;
|
||||
} else {
|
||||
proc_type = make_type_proc(c->allocator, e->scope, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
|
||||
}
|
||||
e->type = proc_type;
|
||||
ast_node(pl, ProcLit, d->proc_lit);
|
||||
|
||||
check_open_scope(c, pl->type);
|
||||
defer (check_close_scope(c));
|
||||
|
||||
|
||||
|
||||
|
||||
auto prev_context = c->context;
|
||||
c->context.allow_polymorphic_types = true;
|
||||
check_procedure_type(c, proc_type, pl->type);
|
||||
c->context = prev_context;
|
||||
|
||||
TypeProc *pt = &proc_type->Proc;
|
||||
|
||||
bool is_foreign = (pl->tags & ProcTag_foreign) != 0;
|
||||
bool is_link_name = (pl->tags & ProcTag_link_name) != 0;
|
||||
bool is_export = (pl->tags & ProcTag_export) != 0;
|
||||
bool is_inline = (pl->tags & ProcTag_inline) != 0;
|
||||
bool is_no_inline = (pl->tags & ProcTag_no_inline) != 0;
|
||||
bool is_require_results = (pl->tags & ProcTag_require_results) != 0;
|
||||
|
||||
|
||||
|
||||
if (d->scope->is_file && e->token.string == "main") {
|
||||
if (pt->param_count != 0 ||
|
||||
pt->result_count != 0) {
|
||||
gbString str = type_to_string(proc_type);
|
||||
error(e->token, "Procedure type of `main` was expected to be `proc()`, got %s", str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
if (pt->calling_convention != ProcCC_Odin &&
|
||||
pt->calling_convention != ProcCC_Contextless) {
|
||||
error(e->token, "Procedure `main` cannot have a custom calling convention");
|
||||
}
|
||||
pt->calling_convention = ProcCC_Contextless;
|
||||
}
|
||||
|
||||
if (is_inline && is_no_inline) {
|
||||
error(pl->type, "You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
}
|
||||
|
||||
if (is_foreign && is_export) {
|
||||
error(pl->type, "A foreign procedure cannot have an `export` tag");
|
||||
}
|
||||
|
||||
if (pt->is_polymorphic) {
|
||||
if (pl->body == nullptr) {
|
||||
error(e->token, "Polymorphic procedures must have a body");
|
||||
}
|
||||
|
||||
if (is_foreign) {
|
||||
error(e->token, "A foreign procedure cannot be a polymorphic");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (pl->body != nullptr) {
|
||||
if (is_foreign) {
|
||||
error(pl->body, "A foreign procedure cannot have a body");
|
||||
}
|
||||
if (proc_type->Proc.c_vararg) {
|
||||
error(pl->body, "A procedure with a `#c_vararg` field cannot have a body and must be foreign");
|
||||
}
|
||||
|
||||
d->scope = c->context.scope;
|
||||
|
||||
GB_ASSERT(pl->body->kind == AstNode_BlockStmt);
|
||||
if (!pt->is_polymorphic) {
|
||||
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pl->body, pl->tags);
|
||||
}
|
||||
} else if (!is_foreign) {
|
||||
error(e->token, "Only a foreign procedure cannot have a body");
|
||||
}
|
||||
|
||||
if (pt->result_count == 0 && is_require_results) {
|
||||
error(pl->type, "`#require_results` is not needed on a procedure with no results");
|
||||
} else {
|
||||
pt->require_results = is_require_results;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (is_foreign) {
|
||||
String name = e->token.string;
|
||||
if (pl->link_name.len > 0) {
|
||||
name = pl->link_name;
|
||||
}
|
||||
e->Procedure.is_foreign = true;
|
||||
e->Procedure.link_name = name;
|
||||
|
||||
init_entity_foreign_library(c, e);
|
||||
|
||||
|
||||
auto *fp = &c->info.foreigns;
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
Type *this_type = base_type(e->type);
|
||||
Type *other_type = base_type(f->type);
|
||||
if (is_type_proc(this_type) && is_type_proc(other_type)) {
|
||||
if (!are_signatures_similar_enough(this_type, other_type)) {
|
||||
error(d->proc_lit,
|
||||
"Redeclaration of foreign procedure `%.*s` with different type signatures\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
}
|
||||
} else if (!are_types_identical(this_type, other_type)) {
|
||||
error(d->proc_lit,
|
||||
"Foreign entity `%.*s` previously declared elsewhere with a different type\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
}
|
||||
} else {
|
||||
map_set(fp, key, e);
|
||||
}
|
||||
} else {
|
||||
String name = e->token.string;
|
||||
if (is_link_name) {
|
||||
name = pl->link_name;
|
||||
}
|
||||
|
||||
if (is_link_name || is_export) {
|
||||
auto *fp = &c->info.foreigns;
|
||||
|
||||
e->Procedure.link_name = name;
|
||||
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
// TODO(bill): Better error message?
|
||||
error(d->proc_lit,
|
||||
"Non unique linking name for procedure `%.*s`\n"
|
||||
"\tother at %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
} else {
|
||||
map_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
|
||||
GB_ASSERT(e->type == nullptr);
|
||||
GB_ASSERT(e->kind == Entity_Variable);
|
||||
|
||||
if (e->flags & EntityFlag_Visited) {
|
||||
e->type = t_invalid;
|
||||
return;
|
||||
}
|
||||
e->flags |= EntityFlag_Visited;
|
||||
|
||||
String context_name = str_lit("variable declaration");
|
||||
|
||||
if (type_expr != nullptr) {
|
||||
e->type = check_type(c, type_expr);
|
||||
}
|
||||
if (e->type != nullptr) {
|
||||
if (is_type_polymorphic(base_type(e->type))) {
|
||||
gbString str = type_to_string(e->type);
|
||||
defer (gb_string_free(str));
|
||||
error(e->token, "Invalid use of a polymorphic type `%s` in %.*s", str, LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
} else if (is_type_empty_union(e->type)) {
|
||||
gbString str = type_to_string(e->type);
|
||||
defer (gb_string_free(str));
|
||||
error(e->token, "An empty union `%s` cannot be instantiated in %.*s", str, LIT(context_name));
|
||||
e->type = t_invalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (e->Variable.is_foreign) {
|
||||
if (init_expr != nullptr) {
|
||||
error(e->token, "A foreign variable declaration cannot have a default value");
|
||||
}
|
||||
init_entity_foreign_library(c, e);
|
||||
|
||||
String name = e->token.string;
|
||||
auto *fp = &c->info.foreigns;
|
||||
HashKey key = hash_string(name);
|
||||
Entity **found = map_get(fp, key);
|
||||
if (found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
Type *this_type = base_type(e->type);
|
||||
Type *other_type = base_type(f->type);
|
||||
if (!are_types_identical(this_type, other_type)) {
|
||||
error(e->token,
|
||||
"Foreign entity `%.*s` previously declared elsewhere with a different type\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name), LIT(pos.file), pos.line, pos.column);
|
||||
}
|
||||
} else {
|
||||
map_set(fp, key, e);
|
||||
}
|
||||
}
|
||||
|
||||
if (init_expr == nullptr) {
|
||||
if (type_expr == nullptr) {
|
||||
e->type = t_invalid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (entities == nullptr || entity_count == 1) {
|
||||
GB_ASSERT(entities == nullptr || entities[0] == e);
|
||||
Operand operand = {};
|
||||
check_expr(c, &operand, init_expr);
|
||||
check_init_variable(c, e, &operand, context_name);
|
||||
}
|
||||
|
||||
if (type_expr != nullptr) {
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
entities[i]->type = e->type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Array<AstNode *> inits;
|
||||
array_init(&inits, c->allocator, 1);
|
||||
array_add(&inits, init_expr);
|
||||
check_init_variables(c, entities, entity_count, inits, context_name);
|
||||
}
|
||||
|
||||
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) {
|
||||
if (e->type != nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (d == nullptr) {
|
||||
d = decl_info_of_entity(&c->info, e);
|
||||
if (d == nullptr) {
|
||||
// TODO(bill): Err here?
|
||||
e->type = t_invalid;
|
||||
set_base_type(named_type, t_invalid);
|
||||
return;
|
||||
// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
|
||||
}
|
||||
}
|
||||
|
||||
CheckerContext prev = c->context;
|
||||
c->context.scope = d->scope;
|
||||
c->context.decl = d;
|
||||
|
||||
e->parent_proc_decl = c->context.curr_proc_decl;
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_Variable:
|
||||
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
|
||||
break;
|
||||
case Entity_Constant:
|
||||
check_const_decl(c, e, d->type_expr, d->init_expr, named_type);
|
||||
break;
|
||||
case Entity_TypeName:
|
||||
check_type_decl(c, e, d->type_expr, named_type);
|
||||
break;
|
||||
case Entity_Procedure:
|
||||
check_proc_decl(c, e, d);
|
||||
break;
|
||||
}
|
||||
|
||||
c->context = prev;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
|
||||
if (body == nullptr) {
|
||||
return;
|
||||
}
|
||||
GB_ASSERT(body->kind == AstNode_BlockStmt);
|
||||
|
||||
String proc_name = {};
|
||||
if (token.kind == Token_Ident) {
|
||||
proc_name = token.string;
|
||||
} else {
|
||||
// TODO(bill): Better name
|
||||
proc_name = str_lit("(anonymous-procedure)");
|
||||
}
|
||||
|
||||
CheckerContext old_context = c->context;
|
||||
defer (c->context = old_context);
|
||||
|
||||
c->context.scope = decl->scope;
|
||||
c->context.decl = decl;
|
||||
c->context.proc_name = proc_name;
|
||||
c->context.curr_proc_decl = decl;
|
||||
|
||||
GB_ASSERT(type->kind == Type_Proc);
|
||||
if (type->Proc.param_count > 0) {
|
||||
TypeTuple *params = &type->Proc.params->Tuple;
|
||||
for_array(i, params->variables) {
|
||||
Entity *e = params->variables[i];
|
||||
if (e->kind != Entity_Variable) {
|
||||
continue;
|
||||
}
|
||||
if (!(e->flags & EntityFlag_Using)) {
|
||||
continue;
|
||||
}
|
||||
bool is_immutable = e->Variable.is_immutable;
|
||||
String name = e->token.string;
|
||||
Type *t = base_type(type_deref(e->type));
|
||||
if (t->kind == Type_Struct) {
|
||||
Scope *scope = t->Struct.scope;
|
||||
if (scope == nullptr) {
|
||||
scope = scope_of_node(&c->info, t->Struct.node);
|
||||
}
|
||||
GB_ASSERT(scope != nullptr);
|
||||
for_array(i, scope->elements.entries) {
|
||||
Entity *f = scope->elements.entries[i].value;
|
||||
if (f->kind == Entity_Variable) {
|
||||
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
|
||||
uvar->Variable.is_immutable = is_immutable;
|
||||
Entity *prev = scope_insert_entity(c->context.scope, uvar);
|
||||
if (prev != nullptr) {
|
||||
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(e->token, "`using` can only be applied to variables of type struct");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
push_procedure(c, type);
|
||||
{
|
||||
ast_node(bs, BlockStmt, body);
|
||||
check_stmt_list(c, bs->stmts, Stmt_CheckScopeDecls);
|
||||
if (type->Proc.result_count > 0) {
|
||||
if (!check_is_terminating(body)) {
|
||||
if (token.kind == Token_Ident) {
|
||||
error(bs->close, "Missing return statement at the end of the procedure `%.*s`", LIT(token.string));
|
||||
} else {
|
||||
error(bs->close, "Missing return statement at the end of the procedure");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pop_procedure(c);
|
||||
|
||||
check_scope_usage(c, c->context.scope);
|
||||
|
||||
if (decl->parent != nullptr) {
|
||||
// NOTE(bill): Add the dependencies from the procedure literal (lambda)
|
||||
for_array(i, decl->deps.entries) {
|
||||
HashKey key = decl->deps.entries[i].key;
|
||||
Entity *e = cast(Entity *)key.ptr;
|
||||
map_set(&decl->parent->deps, key, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
-5191
File diff suppressed because it is too large
Load Diff
+8618
File diff suppressed because it is too large
Load Diff
-1358
File diff suppressed because it is too large
Load Diff
+1833
File diff suppressed because it is too large
Load Diff
-1872
File diff suppressed because it is too large
Load Diff
+2434
File diff suppressed because it is too large
Load Diff
-147
@@ -1,147 +0,0 @@
|
||||
#define GB_NO_DEFER
|
||||
#define GB_IMPLEMENTATION
|
||||
#include "gb/gb.h"
|
||||
|
||||
gbAllocator heap_allocator(void) {
|
||||
return gb_heap_allocator();
|
||||
}
|
||||
|
||||
#include "string.c"
|
||||
#include "array.c"
|
||||
|
||||
gb_global String global_module_path = {0};
|
||||
gb_global bool global_module_path_set = false;
|
||||
|
||||
gb_global gbScratchMemory scratch_memory = {0};
|
||||
|
||||
void init_scratch_memory(isize size) {
|
||||
void *memory = gb_alloc(heap_allocator(), size);
|
||||
gb_scratch_memory_init(&scratch_memory, memory, size);
|
||||
}
|
||||
|
||||
gbAllocator scratch_allocator(void) {
|
||||
return gb_scratch_allocator(&scratch_memory);
|
||||
}
|
||||
|
||||
|
||||
i64 next_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
i64 prev_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
|
||||
i16 f32_to_f16(f32 value) {
|
||||
union { u32 i; f32 f; } v;
|
||||
i32 i, s, e, m;
|
||||
|
||||
v.f = value;
|
||||
i = (i32)v.i;
|
||||
|
||||
s = (i >> 16) & 0x00008000;
|
||||
e = ((i >> 23) & 0x000000ff) - (127 - 15);
|
||||
m = i & 0x007fffff;
|
||||
|
||||
|
||||
if (e <= 0) {
|
||||
if (e < -10) return cast(i16)s;
|
||||
m = (m | 0x00800000) >> (1 - e);
|
||||
|
||||
if (m & 0x00001000)
|
||||
m += 0x00002000;
|
||||
|
||||
return cast(i16)(s | (m >> 13));
|
||||
} else if (e == 0xff - (127 - 15)) {
|
||||
if (m == 0) {
|
||||
return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */
|
||||
} else {
|
||||
/* NOTE(bill): NAN */
|
||||
m >>= 13;
|
||||
return cast(i16)(s | 0x7c00 | m | (m == 0));
|
||||
}
|
||||
} else {
|
||||
if (m & 0x00001000) {
|
||||
m += 0x00002000;
|
||||
if (m & 0x00800000) {
|
||||
m = 0;
|
||||
e += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (e > 30) {
|
||||
float volatile f = 1e12f;
|
||||
int j;
|
||||
for (j = 0; j < 10; j++) {
|
||||
f *= f; /* NOTE(bill): Cause overflow */
|
||||
}
|
||||
|
||||
return cast(i16)(s | 0x7c00);
|
||||
}
|
||||
|
||||
return cast(i16)(s | (e << 10) | (m >> 13));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
|
||||
|
||||
|
||||
// Doubly Linked Lists
|
||||
|
||||
#define DLIST_SET(curr_element, next_element) do { \
|
||||
(curr_element)->next = (next_element); \
|
||||
(curr_element)->next->prev = (curr_element); \
|
||||
(curr_element) = (curr_element)->next; \
|
||||
} while (0)
|
||||
|
||||
#define DLIST_APPEND(root_element, curr_element, next_element) do { \
|
||||
if ((root_element) == NULL) { \
|
||||
(root_element) = (curr_element) = (next_element); \
|
||||
} else { \
|
||||
DLIST_SET(curr_element, next_element); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generic Data Structures
|
||||
//
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
#define MAP_TYPE String
|
||||
#define MAP_PROC map_string_
|
||||
#define MAP_NAME MapString
|
||||
#include "map.c"
|
||||
|
||||
#define MAP_TYPE bool
|
||||
#define MAP_PROC map_bool_
|
||||
#define MAP_NAME MapBool
|
||||
#include "map.c"
|
||||
|
||||
#define MAP_TYPE isize
|
||||
#define MAP_PROC map_isize_
|
||||
#define MAP_NAME MapIsize
|
||||
#include "map.c"
|
||||
+463
@@ -0,0 +1,463 @@
|
||||
#if defined(GB_SYSTEM_UNIX)
|
||||
// Required for intrinsics on GCC
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#define GB_IMPLEMENTATION
|
||||
#include "gb/gb.h"
|
||||
|
||||
|
||||
#include <wchar.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
GB_ALLOCATOR_PROC(heap_allocator_proc);
|
||||
|
||||
gbAllocator heap_allocator(void) {
|
||||
gbAllocator a;
|
||||
a.proc = heap_allocator_proc;
|
||||
a.data = NULL;
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
GB_ALLOCATOR_PROC(heap_allocator_proc) {
|
||||
void *ptr = NULL;
|
||||
gb_unused(allocator_data);
|
||||
gb_unused(old_size);
|
||||
// TODO(bill): Throughly test!
|
||||
switch (type) {
|
||||
#if defined(GB_COMPILER_MSVC)
|
||||
case gbAllocation_Alloc:
|
||||
ptr = _aligned_malloc(size, alignment);
|
||||
if (flags & gbAllocatorFlag_ClearToZero)
|
||||
gb_zero_size(ptr, size);
|
||||
break;
|
||||
case gbAllocation_Free:
|
||||
_aligned_free(old_memory);
|
||||
break;
|
||||
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(heap_allocator(), old_memory, old_size, size, alignment);
|
||||
} break;
|
||||
#else
|
||||
// TODO(bill): *nix version that's decent
|
||||
case gbAllocation_Alloc: {
|
||||
posix_memalign(&ptr, alignment, size);
|
||||
|
||||
if (flags & gbAllocatorFlag_ClearToZero) {
|
||||
gb_zero_size(ptr, size);
|
||||
}
|
||||
} break;
|
||||
|
||||
case gbAllocation_Free: {
|
||||
free(old_memory);
|
||||
} break;
|
||||
|
||||
case gbAllocation_Resize: {
|
||||
ptr = gb_default_resize_align(heap_allocator(), old_memory, old_size, size, alignment);
|
||||
} break;
|
||||
#endif
|
||||
|
||||
case gbAllocation_FreeAll:
|
||||
break;
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#include "unicode.cpp"
|
||||
#include "string.cpp"
|
||||
#include "array.cpp"
|
||||
#include "integer128.cpp"
|
||||
#include "murmurhash3.cpp"
|
||||
|
||||
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
|
||||
|
||||
|
||||
u128 fnv128a(void const *data, isize len) {
|
||||
u128 o = u128_lo_hi(0x13bull, 0x1000000ull);
|
||||
u128 h = u128_lo_hi(0x62b821756295c58dull, 0x6c62272e07bb0142ull);
|
||||
u8 const *bytes = cast(u8 const *)data;
|
||||
for (isize i = 0; i < len; i++) {
|
||||
h.lo ^= bytes[i];
|
||||
h = h * o;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
#include "map.cpp"
|
||||
|
||||
|
||||
|
||||
gb_global String global_module_path = {0};
|
||||
gb_global bool global_module_path_set = false;
|
||||
|
||||
gb_global gbScratchMemory scratch_memory = {0};
|
||||
|
||||
void init_scratch_memory(isize size) {
|
||||
void *memory = gb_alloc(heap_allocator(), size);
|
||||
gb_scratch_memory_init(&scratch_memory, memory, size);
|
||||
}
|
||||
|
||||
gbAllocator scratch_allocator(void) {
|
||||
return gb_scratch_allocator(&scratch_memory);
|
||||
}
|
||||
|
||||
struct Pool {
|
||||
isize memblock_size;
|
||||
isize out_of_band_size;
|
||||
isize alignment;
|
||||
|
||||
Array<u8 *> unused_memblock;
|
||||
Array<u8 *> used_memblock;
|
||||
Array<u8 *> out_of_band_allocations;
|
||||
|
||||
u8 * current_memblock;
|
||||
u8 * current_pos;
|
||||
isize bytes_left;
|
||||
|
||||
gbAllocator block_allocator;
|
||||
};
|
||||
|
||||
enum {
|
||||
POOL_BUCKET_SIZE_DEFAULT = 65536,
|
||||
POOL_OUT_OF_BAND_SIZE_DEFAULT = 6554,
|
||||
};
|
||||
|
||||
void pool_init(Pool *pool,
|
||||
isize memblock_size = POOL_BUCKET_SIZE_DEFAULT,
|
||||
isize out_of_band_size = POOL_OUT_OF_BAND_SIZE_DEFAULT,
|
||||
isize alignment = 8,
|
||||
gbAllocator block_allocator = heap_allocator(),
|
||||
gbAllocator array_allocator = heap_allocator()) {
|
||||
pool->memblock_size = memblock_size;
|
||||
pool->out_of_band_size = out_of_band_size;
|
||||
pool->alignment = alignment;
|
||||
pool->block_allocator = block_allocator;
|
||||
|
||||
array_init(&pool->unused_memblock, array_allocator);
|
||||
array_init(&pool->used_memblock, array_allocator);
|
||||
array_init(&pool->out_of_band_allocations, array_allocator);
|
||||
}
|
||||
|
||||
void pool_free_all(Pool *p) {
|
||||
if (p->current_memblock != nullptr) {
|
||||
array_add(&p->unused_memblock, p->current_memblock);
|
||||
p->current_memblock = nullptr;
|
||||
}
|
||||
|
||||
for_array(i, p->used_memblock) {
|
||||
array_add(&p->unused_memblock, p->used_memblock[i]);
|
||||
}
|
||||
array_clear(&p->unused_memblock);
|
||||
|
||||
for_array(i, p->out_of_band_allocations) {
|
||||
gb_free(p->block_allocator, p->out_of_band_allocations[i]);
|
||||
}
|
||||
array_clear(&p->out_of_band_allocations);
|
||||
}
|
||||
|
||||
void pool_destroy(Pool *p) {
|
||||
pool_free_all(p);
|
||||
|
||||
for_array(i, p->unused_memblock) {
|
||||
gb_free(p->block_allocator, p->unused_memblock[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void pool_cycle_new_block(Pool *p) {
|
||||
GB_ASSERT_MSG(p->block_allocator.proc != nullptr,
|
||||
"You must call pool_init on a Pool before using it!");
|
||||
|
||||
if (p->current_memblock != nullptr) {
|
||||
array_add(&p->used_memblock, p->current_memblock);
|
||||
}
|
||||
|
||||
u8 *new_block = nullptr;
|
||||
|
||||
if (p->unused_memblock.count > 0) {
|
||||
new_block = array_pop(&p->unused_memblock);
|
||||
} else {
|
||||
GB_ASSERT(p->block_allocator.proc != nullptr);
|
||||
new_block = cast(u8 *)gb_alloc_align(p->block_allocator, p->memblock_size, p->alignment);
|
||||
}
|
||||
|
||||
p->bytes_left = p->memblock_size;
|
||||
p->current_memblock = new_block;
|
||||
p->current_memblock = new_block;
|
||||
}
|
||||
|
||||
void *pool_get(Pool *p,
|
||||
isize size, isize alignment = 0) {
|
||||
if (alignment <= 0) alignment = p->alignment;
|
||||
|
||||
isize extra = alignment - (size & alignment);
|
||||
size += extra;
|
||||
if (size >= p->out_of_band_size) {
|
||||
GB_ASSERT(p->block_allocator.proc != nullptr);
|
||||
u8 *memory = cast(u8 *)gb_alloc_align(p->block_allocator, p->memblock_size, alignment);
|
||||
if (memory != nullptr) {
|
||||
array_add(&p->out_of_band_allocations, memory);
|
||||
}
|
||||
return memory;
|
||||
}
|
||||
|
||||
if (p->bytes_left < size) {
|
||||
pool_cycle_new_block(p);
|
||||
if (p->current_memblock != nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
u8 *res = p->current_pos;
|
||||
p->current_pos += size;
|
||||
p->bytes_left -= size;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
gbAllocator pool_allocator(Pool *pool);
|
||||
|
||||
GB_ALLOCATOR_PROC(pool_allocator_procedure) {
|
||||
Pool *p = cast(Pool *)allocator_data;
|
||||
void *ptr = nullptr;
|
||||
|
||||
switch (type) {
|
||||
case gbAllocation_Alloc:
|
||||
return pool_get(p, size, alignment);
|
||||
case gbAllocation_Free:
|
||||
// Does nothing
|
||||
break;
|
||||
case gbAllocation_FreeAll:
|
||||
pool_free_all(p);
|
||||
break;
|
||||
case gbAllocation_Resize:
|
||||
return gb_default_resize_align(pool_allocator(p), old_memory, old_size, size, alignment);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
gbAllocator pool_allocator(Pool *pool) {
|
||||
gbAllocator allocator;
|
||||
allocator.proc = pool_allocator_procedure;
|
||||
allocator.data = pool;
|
||||
return allocator;
|
||||
}
|
||||
|
||||
|
||||
|
||||
i32 next_pow2(i32 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
i64 next_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n--;
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
i32 prev_pow2(i32 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
i64 prev_pow2(i64 n) {
|
||||
if (n <= 0) {
|
||||
return 0;
|
||||
}
|
||||
n |= n >> 1;
|
||||
n |= n >> 2;
|
||||
n |= n >> 4;
|
||||
n |= n >> 8;
|
||||
n |= n >> 16;
|
||||
n |= n >> 32;
|
||||
return n - (n >> 1);
|
||||
}
|
||||
|
||||
i16 f32_to_f16(f32 value) {
|
||||
union { u32 i; f32 f; } v;
|
||||
i32 i, s, e, m;
|
||||
|
||||
v.f = value;
|
||||
i = (i32)v.i;
|
||||
|
||||
s = (i >> 16) & 0x00008000;
|
||||
e = ((i >> 23) & 0x000000ff) - (127 - 15);
|
||||
m = i & 0x007fffff;
|
||||
|
||||
|
||||
if (e <= 0) {
|
||||
if (e < -10) return cast(i16)s;
|
||||
m = (m | 0x00800000) >> (1 - e);
|
||||
|
||||
if (m & 0x00001000)
|
||||
m += 0x00002000;
|
||||
|
||||
return cast(i16)(s | (m >> 13));
|
||||
} else if (e == 0xff - (127 - 15)) {
|
||||
if (m == 0) {
|
||||
return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */
|
||||
} else {
|
||||
/* NOTE(bill): NAN */
|
||||
m >>= 13;
|
||||
return cast(i16)(s | 0x7c00 | m | (m == 0));
|
||||
}
|
||||
} else {
|
||||
if (m & 0x00001000) {
|
||||
m += 0x00002000;
|
||||
if (m & 0x00800000) {
|
||||
m = 0;
|
||||
e += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (e > 30) {
|
||||
float volatile f = 1e12f;
|
||||
int j;
|
||||
for (j = 0; j < 10; j++) {
|
||||
f *= f; /* NOTE(bill): Cause overflow */
|
||||
}
|
||||
|
||||
return cast(i16)(s | 0x7c00);
|
||||
}
|
||||
|
||||
return cast(i16)(s | (e << 10) | (m >> 13));
|
||||
}
|
||||
}
|
||||
|
||||
f64 gb_sqrt(f64 x) {
|
||||
return sqrt(x);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Doubly Linked Lists
|
||||
|
||||
#define DLIST_SET(curr_element, next_element) do { \
|
||||
(curr_element)->next = (next_element); \
|
||||
(curr_element)->next->prev = (curr_element); \
|
||||
(curr_element) = (curr_element)->next; \
|
||||
} while (0)
|
||||
|
||||
#define DLIST_APPEND(root_element, curr_element, next_element) do { \
|
||||
if ((root_element) == nullptr) { \
|
||||
(root_element) = (curr_element) = (next_element); \
|
||||
} else { \
|
||||
DLIST_SET(curr_element, next_element); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
|
||||
wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
|
||||
u32 i, j;
|
||||
|
||||
u32 len = cast(u32)string16_len(cmd_line);
|
||||
i = ((len+2)/2)*gb_size_of(void *) + gb_size_of(void *);
|
||||
|
||||
wchar_t **argv = cast(wchar_t **)GlobalAlloc(GMEM_FIXED, i + (len+2)*gb_size_of(wchar_t));
|
||||
wchar_t *_argv = cast(wchar_t *)((cast(u8 *)argv)+i);
|
||||
|
||||
u32 argc = 0;
|
||||
argv[argc] = _argv;
|
||||
bool in_quote = false;
|
||||
bool in_text = false;
|
||||
bool in_space = true;
|
||||
i = 0;
|
||||
j = 0;
|
||||
|
||||
for (;;) {
|
||||
wchar_t a = cmd_line[i];
|
||||
if (a == 0) {
|
||||
break;
|
||||
}
|
||||
if (in_quote) {
|
||||
if (a == '\"') {
|
||||
in_quote = false;
|
||||
} else {
|
||||
_argv[j++] = a;
|
||||
}
|
||||
} else {
|
||||
switch (a) {
|
||||
case '\"':
|
||||
in_quote = true;
|
||||
in_text = true;
|
||||
if (in_space) argv[argc++] = _argv+j;
|
||||
in_space = false;
|
||||
break;
|
||||
case ' ':
|
||||
case '\t':
|
||||
case '\n':
|
||||
case '\r':
|
||||
if (in_text) _argv[j++] = '\0';
|
||||
in_text = false;
|
||||
in_space = true;
|
||||
break;
|
||||
default:
|
||||
in_text = true;
|
||||
if (in_space) argv[argc++] = _argv+j;
|
||||
_argv[j++] = a;
|
||||
in_space = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
_argv[j] = '\0';
|
||||
argv[argc] = nullptr;
|
||||
|
||||
if (_argc) *_argc = argc;
|
||||
return argv;
|
||||
}
|
||||
|
||||
#endif
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
// Generates Documentation
|
||||
|
||||
gbString expr_to_string(AstNode *expression);
|
||||
|
||||
String alloc_comment_group_string(gbAllocator a, CommentGroup g) {
|
||||
isize len = 0;
|
||||
for_array(i, g.list) {
|
||||
String comment = g.list[i].string;
|
||||
len += comment.len;
|
||||
len += 1; // for \n
|
||||
}
|
||||
if (len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
u8 *text = gb_alloc_array(a, u8, len+1);
|
||||
len = 0;
|
||||
for_array(i, g.list) {
|
||||
String comment = g.list[i].string;
|
||||
if (comment[1] == '/') {
|
||||
comment.text += 2;
|
||||
comment.len -= 2;
|
||||
} else if (comment[1] == '*') {
|
||||
comment.text += 2;
|
||||
comment.len -= 4;
|
||||
}
|
||||
comment = string_trim_whitespace(comment);
|
||||
gb_memmove(text+len, comment.text, comment.len);
|
||||
len += comment.len;
|
||||
text[len++] = '\n';
|
||||
}
|
||||
return make_string(text, len);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void print_type_spec(AstNode *spec) {
|
||||
ast_node(ts, TypeSpec, spec);
|
||||
GB_ASSERT(ts->name->kind == AstNode_Ident);
|
||||
String name = ts->name->Ident.string;
|
||||
if (name.len == 0) {
|
||||
return;
|
||||
}
|
||||
if (name[0] == '_') {
|
||||
return;
|
||||
}
|
||||
gb_printf("type %.*s\n", LIT(name));
|
||||
}
|
||||
|
||||
void print_proc_decl(AstNodeProcDecl *pd) {
|
||||
GB_ASSERT(pd->name->kind == AstNode_Ident);
|
||||
String name = pd->name->Ident.string;
|
||||
if (name.len == 0) {
|
||||
return;
|
||||
}
|
||||
if (name[0] == '_') {
|
||||
return;
|
||||
}
|
||||
|
||||
String docs = alloc_comment_group_string(heap_allocator(), pd->docs);
|
||||
defer (gb_free(heap_allocator(), docs.text));
|
||||
|
||||
if (docs.len > 0) {
|
||||
gb_file_write(&gb__std_files[gbFileStandard_Output], docs.text, docs.len);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
ast_node(proc_type, ProcType, pd->type);
|
||||
|
||||
gbString params = expr_to_string(proc_type->params);
|
||||
defer (gb_string_free(params));
|
||||
gb_printf("proc %.*s(%s)", LIT(name), params);
|
||||
if (proc_type->results != nullptr) {
|
||||
ast_node(fl, FieldList, proc_type->results);
|
||||
isize count = fl->list.count;
|
||||
if (count > 0) {
|
||||
gbString results = expr_to_string(proc_type->results);
|
||||
defer (gb_string_free(results));
|
||||
gb_printf(" -> ");
|
||||
if (count != 1) {
|
||||
gb_printf("(");
|
||||
}
|
||||
gb_printf("%s", results);
|
||||
if (count != 1) {
|
||||
gb_printf(")");
|
||||
}
|
||||
}
|
||||
}
|
||||
gb_printf("\n\n");
|
||||
}
|
||||
#endif
|
||||
void print_declaration(AstNode *decl) {
|
||||
switch (decl->kind) {
|
||||
case_ast_node(gd, GenDecl, decl);
|
||||
for_array(spec_index, gd->specs) {
|
||||
AstNode *spec = gd->specs[spec_index];
|
||||
switch(gd->token.kind) {
|
||||
case Token_import:
|
||||
case Token_import_load:
|
||||
break;
|
||||
case Token_foreign_library:
|
||||
case Token_foreign_system_library:
|
||||
break;
|
||||
}
|
||||
}
|
||||
case_end;
|
||||
|
||||
// case_ast_node(pd, ProcDecl, decl);
|
||||
// print_proc_decl(pd);
|
||||
// case_end;
|
||||
|
||||
case_ast_node(fb, ForeignBlockDecl, decl);
|
||||
// TODO(bill)
|
||||
case_end;
|
||||
}
|
||||
}
|
||||
|
||||
void generate_documentation(Parser *parser) {
|
||||
for_array(file_index, parser->files) {
|
||||
AstFile *file = &parser->files[file_index];
|
||||
Tokenizer *tokenizer = &file->tokenizer;
|
||||
String fullpath = tokenizer->fullpath;
|
||||
gb_printf("%.*s\n", LIT(fullpath));
|
||||
|
||||
for_array(decl_index, file->decls) {
|
||||
AstNode *decl = file->decls[decl_index];
|
||||
print_declaration(decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
+126
-53
@@ -1,8 +1,9 @@
|
||||
typedef struct Scope Scope;
|
||||
typedef struct Checker Checker;
|
||||
typedef struct Type Type;
|
||||
typedef enum BuiltinProcId BuiltinProcId;
|
||||
typedef enum ImplicitValueId ImplicitValueId;
|
||||
struct Scope;
|
||||
struct Checker;
|
||||
struct Type;
|
||||
struct DeclInfo;
|
||||
// typedef enum BuiltinProcId BuiltinProcId;
|
||||
|
||||
|
||||
#define ENTITY_KINDS \
|
||||
ENTITY_KIND(Invalid) \
|
||||
@@ -11,17 +12,18 @@ typedef enum ImplicitValueId ImplicitValueId;
|
||||
ENTITY_KIND(TypeName) \
|
||||
ENTITY_KIND(Procedure) \
|
||||
ENTITY_KIND(Builtin) \
|
||||
ENTITY_KIND(Alias) \
|
||||
ENTITY_KIND(ImportName) \
|
||||
ENTITY_KIND(LibraryName) \
|
||||
ENTITY_KIND(Nil) \
|
||||
ENTITY_KIND(ImplicitValue) \
|
||||
ENTITY_KIND(Count)
|
||||
ENTITY_KIND(Label)
|
||||
|
||||
typedef enum EntityKind {
|
||||
enum EntityKind {
|
||||
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
|
||||
ENTITY_KINDS
|
||||
#undef ENTITY_KIND
|
||||
} EntityKind;
|
||||
Entity_Count,
|
||||
};
|
||||
|
||||
String const entity_strings[] = {
|
||||
#define ENTITY_KIND(k) {cast(u8 *)#k, gb_size_of(#k)-1},
|
||||
@@ -29,31 +31,48 @@ String const entity_strings[] = {
|
||||
#undef ENTITY_KIND
|
||||
};
|
||||
|
||||
typedef enum EntityFlag {
|
||||
EntityFlag_Visited = 1<<0,
|
||||
EntityFlag_Used = 1<<1,
|
||||
EntityFlag_Anonymous = 1<<2,
|
||||
EntityFlag_Field = 1<<3,
|
||||
EntityFlag_Param = 1<<4,
|
||||
EntityFlag_VectorElem = 1<<5,
|
||||
EntityFlag_Ellipsis = 1<<6,
|
||||
EntityFlag_NoAlias = 1<<7,
|
||||
} EntityFlag;
|
||||
enum EntityFlag {
|
||||
EntityFlag_Visited = 1<<0,
|
||||
EntityFlag_Used = 1<<1,
|
||||
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_Sret = 1<<10,
|
||||
EntityFlag_BitFieldValue = 1<<11,
|
||||
|
||||
typedef enum OverloadKind {
|
||||
Overload_No = -1,
|
||||
Overload_Unknown = 0,
|
||||
Overload_Yes = +1,
|
||||
} OverloadKind;
|
||||
EntityFlag_CVarArg = 1<<20,
|
||||
|
||||
typedef struct Entity Entity;
|
||||
};
|
||||
|
||||
// Zero value means the overloading process is not yet done
|
||||
enum OverloadKind {
|
||||
Overload_Unknown,
|
||||
Overload_No,
|
||||
Overload_Yes,
|
||||
};
|
||||
|
||||
enum EntityAliasKind {
|
||||
EntityAlias_Invalid,
|
||||
EntityAlias_Type,
|
||||
EntityAlias_Entity,
|
||||
};
|
||||
|
||||
|
||||
// An Entity is a named "thing" in the language
|
||||
struct Entity {
|
||||
EntityKind kind;
|
||||
u64 id;
|
||||
u32 flags;
|
||||
Token token;
|
||||
Scope * scope;
|
||||
Type * type;
|
||||
AstNode * identifier; // Can be NULL
|
||||
AstNode * identifier; // Can be nullptr
|
||||
DeclInfo * parent_proc_decl; // nullptr if in file/global scope
|
||||
|
||||
// TODO(bill): Cleanup how `using` works for entities
|
||||
Entity * using_parent;
|
||||
@@ -64,23 +83,37 @@ struct Entity {
|
||||
ExactValue value;
|
||||
} Constant;
|
||||
struct {
|
||||
i32 field_index;
|
||||
i32 field_src_index;
|
||||
bool is_immutable;
|
||||
bool is_thread_local;
|
||||
i32 field_index;
|
||||
i32 field_src_index;
|
||||
ExactValue default_value;
|
||||
bool default_is_nil;
|
||||
bool default_is_undef;
|
||||
bool default_is_location;
|
||||
bool is_immutable;
|
||||
bool is_thread_local;
|
||||
bool is_foreign;
|
||||
Entity * foreign_library;
|
||||
AstNode * foreign_library_ident;
|
||||
String link_name;
|
||||
} Variable;
|
||||
i32 TypeName;
|
||||
struct {
|
||||
bool is_foreign;
|
||||
String foreign_name;
|
||||
Entity * foreign_library;
|
||||
bool is_type_alias;
|
||||
Type *type_parameter_specialization;
|
||||
} TypeName;
|
||||
struct {
|
||||
OverloadKind overload_kind;
|
||||
String link_name;
|
||||
u64 tags;
|
||||
OverloadKind overload_kind;
|
||||
bool is_foreign;
|
||||
Entity * foreign_library;
|
||||
AstNode * foreign_library_ident;
|
||||
} Procedure;
|
||||
struct {
|
||||
BuiltinProcId id;
|
||||
i32 id;
|
||||
} Builtin;
|
||||
struct {
|
||||
Entity *base;
|
||||
} Alias;
|
||||
struct {
|
||||
String path;
|
||||
String name;
|
||||
@@ -94,13 +127,40 @@ struct Entity {
|
||||
} LibraryName;
|
||||
i32 Nil;
|
||||
struct {
|
||||
// TODO(bill): Should this be a user-level construct rather than compiler-level?
|
||||
ImplicitValueId id;
|
||||
Entity * backing;
|
||||
} ImplicitValue;
|
||||
String name;
|
||||
AstNode *node;
|
||||
} Label;
|
||||
};
|
||||
};
|
||||
|
||||
gb_global Entity *e_context = nullptr;
|
||||
|
||||
bool is_entity_kind_exported(EntityKind kind) {
|
||||
switch (kind) {
|
||||
case Entity_Builtin:
|
||||
case Entity_ImportName:
|
||||
case Entity_LibraryName:
|
||||
case Entity_Nil:
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_entity_exported(Entity *e) {
|
||||
// TODO(bill): Determine the actual exportation rules for imports of entities
|
||||
GB_ASSERT(e != nullptr);
|
||||
if (!is_entity_kind_exported(e->kind)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String name = e->token.string;
|
||||
if (name.len == 0) {
|
||||
return false;
|
||||
}
|
||||
return name[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);
|
||||
@@ -108,6 +168,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;
|
||||
}
|
||||
|
||||
@@ -118,11 +179,12 @@ Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *typ
|
||||
}
|
||||
|
||||
Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, Type *type) {
|
||||
GB_ASSERT(parent != NULL);
|
||||
GB_ASSERT(parent != nullptr);
|
||||
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->parent_proc_decl = parent->parent_proc_decl;
|
||||
entity->flags |= EntityFlag_Using;
|
||||
return entity;
|
||||
}
|
||||
|
||||
@@ -138,20 +200,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;
|
||||
}
|
||||
|
||||
@@ -170,12 +232,18 @@ Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *si
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, BuiltinProcId id) {
|
||||
Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, i32 id) {
|
||||
Entity *entity = alloc_entity(a, Entity_Builtin, scope, token, type);
|
||||
entity->Builtin.id = id;
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_alias(gbAllocator a, Scope *scope, Token token, Type *type, Entity *base) {
|
||||
Entity *entity = alloc_entity(a, Entity_Alias, scope, token, type);
|
||||
entity->Alias.base = base;
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *type,
|
||||
String path, String name, Scope *import_scope) {
|
||||
Entity *entity = alloc_entity(a, Entity_ImportName, scope, token, type);
|
||||
@@ -193,22 +261,27 @@ Entity *make_entity_library_name(gbAllocator a, Scope *scope, Token token, Type
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
|
||||
Token token = make_token_ident(name);
|
||||
Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
|
||||
Entity *entity = alloc_entity(a, Entity_Nil, nullptr, token, type);
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, ImplicitValueId id) {
|
||||
Token token = make_token_ident(name);
|
||||
Entity *entity = alloc_entity(a, Entity_ImplicitValue, NULL, token, type);
|
||||
entity->ImplicitValue.id = id;
|
||||
Entity *make_entity_label(gbAllocator a, Scope *scope, Token token, Type *type,
|
||||
AstNode *node) {
|
||||
Entity *entity = alloc_entity(a, Entity_Label, scope, token, type);
|
||||
entity->Label.node = node;
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Entity *make_entity_dummy_variable(gbAllocator a, Scope *scope, Token token) {
|
||||
token.string = str_lit("_");
|
||||
return make_entity_variable(a, scope, token, NULL, false);
|
||||
return make_entity_variable(a, scope, token, nullptr, false);
|
||||
}
|
||||
|
||||
@@ -1,428 +0,0 @@
|
||||
#include <math.h>
|
||||
|
||||
// TODO(bill): Big numbers
|
||||
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
|
||||
|
||||
typedef struct AstNode AstNode;
|
||||
|
||||
typedef enum ExactValueKind {
|
||||
ExactValue_Invalid,
|
||||
|
||||
ExactValue_Bool,
|
||||
ExactValue_String,
|
||||
ExactValue_Integer,
|
||||
ExactValue_Float,
|
||||
ExactValue_Pointer,
|
||||
ExactValue_Compound, // TODO(bill): Is this good enough?
|
||||
|
||||
ExactValue_Count,
|
||||
} 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;
|
||||
};
|
||||
} ExactValue;
|
||||
|
||||
HashKey hash_exact_value(ExactValue v) {
|
||||
return hashing_proc(&v, gb_size_of(ExactValue));
|
||||
}
|
||||
|
||||
|
||||
ExactValue make_exact_value_compound(AstNode *node) {
|
||||
ExactValue result = {ExactValue_Compound};
|
||||
result.value_compound = node;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_bool(bool b) {
|
||||
ExactValue result = {ExactValue_Bool};
|
||||
result.value_bool = (b != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
ExactValue result = {ExactValue_String};
|
||||
result.value_string = string;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_integer(i64 i) {
|
||||
ExactValue result = {ExactValue_Integer};
|
||||
result.value_integer = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_integer_from_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
i32 base = 10;
|
||||
bool has_prefix = false;
|
||||
if (string.len > 2 && string.text[0] == '0') {
|
||||
switch (string.text[1]) {
|
||||
case 'b': base = 2; has_prefix = true; break;
|
||||
case 'o': base = 8; has_prefix = true; break;
|
||||
case 'd': base = 10; has_prefix = true; break;
|
||||
case 'x': base = 16; has_prefix = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
u8 *text = string.text;
|
||||
isize len = string.len;
|
||||
if (has_prefix) {
|
||||
text += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
i64 result = 0;
|
||||
for (isize i = 0; i < len; i++) {
|
||||
Rune r = cast(Rune)text[i];
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
i64 v = 0;
|
||||
if (gb_char_is_digit(r)) {
|
||||
v = r - '0';
|
||||
} else if (gb_char_is_hex_digit(r)) {
|
||||
v = gb_hex_digit_to_int(r);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
result *= base;
|
||||
result += v;
|
||||
}
|
||||
|
||||
|
||||
return make_exact_value_integer(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
ExactValue make_exact_value_float_from_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
ExactValue result = {ExactValue_Float};
|
||||
result.value_float = gb_str_to_f64(cast(char *)string.text, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_float(f64 f) {
|
||||
ExactValue result = {ExactValue_Float};
|
||||
result.value_float = f;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_pointer(i64 ptr) {
|
||||
ExactValue result = {ExactValue_Pointer};
|
||||
result.value_pointer = ptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue make_exact_value_from_basic_literal(Token token) {
|
||||
switch (token.kind) {
|
||||
case Token_String: return make_exact_value_string(token.string);
|
||||
case Token_Integer: return make_exact_value_integer_from_string(token.string);
|
||||
case Token_Float: return make_exact_value_float_from_string(token.string);
|
||||
case Token_Rune: {
|
||||
Rune r = GB_RUNE_INVALID;
|
||||
gb_utf8_decode(token.string.text, token.string.len, &r);
|
||||
// gb_printf("%.*s rune: %d\n", LIT(token.string), r);
|
||||
return make_exact_value_integer(r);
|
||||
}
|
||||
default:
|
||||
GB_PANIC("Invalid token for basic literal");
|
||||
break;
|
||||
}
|
||||
|
||||
ExactValue result = {ExactValue_Invalid};
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue exact_value_to_integer(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
return v;
|
||||
case ExactValue_Float: {
|
||||
i64 i = cast(i64)v.value_float;
|
||||
f64 f = cast(f64)i;
|
||||
if (f == v.value_float) {
|
||||
return make_exact_value_integer(i);
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Pointer:
|
||||
return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
ExactValue exact_value_to_float(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
return make_exact_value_float(cast(i64)v.value_integer);
|
||||
case ExactValue_Float:
|
||||
return v;
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
|
||||
switch (op) {
|
||||
case Token_Add: {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid:
|
||||
case ExactValue_Integer:
|
||||
case ExactValue_Float:
|
||||
return v;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Token_Sub: {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return v;
|
||||
case ExactValue_Integer: {
|
||||
ExactValue i = v;
|
||||
i.value_integer = -i.value_integer;
|
||||
return i;
|
||||
}
|
||||
case ExactValue_Float: {
|
||||
ExactValue i = v;
|
||||
i.value_float = -i.value_float;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case Token_Xor: {
|
||||
i64 i = 0;
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return v;
|
||||
case ExactValue_Integer:
|
||||
i = v.value_integer;
|
||||
i = ~i;
|
||||
break;
|
||||
default:
|
||||
goto failure;
|
||||
}
|
||||
|
||||
// NOTE(bill): unsigned integers will be negative and will need to be
|
||||
// limited to the types precision
|
||||
if (precision > 0)
|
||||
i &= ~((~0ll)<<precision);
|
||||
|
||||
return make_exact_value_integer(i);
|
||||
} break;
|
||||
|
||||
case Token_Not: {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid: return v;
|
||||
case ExactValue_Bool:
|
||||
return make_exact_value_bool(!v.value_bool);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
failure:
|
||||
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op]));
|
||||
|
||||
ExactValue error_value = {0};
|
||||
return error_value;
|
||||
}
|
||||
|
||||
// NOTE(bill): Make sure things are evaluated in correct order
|
||||
i32 exact_value_order(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return 0;
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
return 1;
|
||||
case ExactValue_Integer:
|
||||
return 2;
|
||||
case ExactValue_Float:
|
||||
return 3;
|
||||
case ExactValue_Pointer:
|
||||
return 4;
|
||||
|
||||
default:
|
||||
GB_PANIC("How'd you get here? Invalid Value.kind");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||
if (exact_value_order(*y) < exact_value_order(*x)) {
|
||||
match_exact_values(y, x);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (x->kind) {
|
||||
case ExactValue_Invalid:
|
||||
*y = *x;
|
||||
return;
|
||||
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
return;
|
||||
|
||||
case ExactValue_Integer:
|
||||
switch (y->kind) {
|
||||
case ExactValue_Integer:
|
||||
return;
|
||||
case ExactValue_Float:
|
||||
// TODO(bill): Is this good enough?
|
||||
*x = make_exact_value_float(cast(f64)x->value_integer);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Float:
|
||||
if (y->kind == ExactValue_Float)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
compiler_error("How'd you get here? Invalid ExactValueKind");
|
||||
}
|
||||
|
||||
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
|
||||
ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
|
||||
match_exact_values(&x, &y);
|
||||
|
||||
switch (x.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return x;
|
||||
|
||||
case ExactValue_Bool:
|
||||
switch (op) {
|
||||
case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool);
|
||||
case Token_CmpOr: return make_exact_value_bool(x.value_bool || y.value_bool);
|
||||
case Token_And: return make_exact_value_bool(x.value_bool & y.value_bool);
|
||||
case Token_Or: return make_exact_value_bool(x.value_bool | y.value_bool);
|
||||
default: goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Integer: {
|
||||
i64 a = x.value_integer;
|
||||
i64 b = y.value_integer;
|
||||
i64 c = 0;
|
||||
switch (op) {
|
||||
case Token_Add: c = a + b; break;
|
||||
case Token_Sub: c = a - b; break;
|
||||
case Token_Mul: c = a * b; break;
|
||||
case Token_Quo: return make_exact_value_float(fmod(cast(f64)a, cast(f64)b));
|
||||
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
|
||||
case Token_Mod: c = a % b; break;
|
||||
case Token_And: c = a & b; break;
|
||||
case Token_Or: c = a | b; break;
|
||||
case Token_Xor: c = a ^ b; break;
|
||||
case Token_AndNot: c = a&(~b); break;
|
||||
case Token_Shl: c = a << b; break;
|
||||
case Token_Shr: c = a >> b; break;
|
||||
default: goto error;
|
||||
}
|
||||
|
||||
return make_exact_value_integer(c);
|
||||
} break;
|
||||
|
||||
case ExactValue_Float: {
|
||||
f64 a = x.value_float;
|
||||
f64 b = y.value_float;
|
||||
switch (op) {
|
||||
case Token_Add: return make_exact_value_float(a + b);
|
||||
case Token_Sub: return make_exact_value_float(a - b);
|
||||
case Token_Mul: return make_exact_value_float(a * b);
|
||||
case Token_Quo: return make_exact_value_float(a / b);
|
||||
default: goto error;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
error:
|
||||
ExactValue error_value = {0};
|
||||
// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op));
|
||||
return error_value;
|
||||
}
|
||||
|
||||
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Add, x, y); }
|
||||
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Sub, x, y); }
|
||||
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Mul, x, y); }
|
||||
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Quo, x, y); }
|
||||
gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
|
||||
|
||||
|
||||
i32 cmp_f64(f64 a, f64 b) {
|
||||
return (a > b) - (a < b);
|
||||
}
|
||||
|
||||
bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
||||
match_exact_values(&x, &y);
|
||||
|
||||
switch (x.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return false;
|
||||
|
||||
case ExactValue_Bool:
|
||||
switch (op) {
|
||||
case Token_CmpEq: return x.value_bool == y.value_bool;
|
||||
case Token_NotEq: return x.value_bool != y.value_bool;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Integer: {
|
||||
i64 a = x.value_integer;
|
||||
i64 b = y.value_integer;
|
||||
switch (op) {
|
||||
case Token_CmpEq: return a == b;
|
||||
case Token_NotEq: return a != b;
|
||||
case Token_Lt: return a < b;
|
||||
case Token_LtEq: return a <= b;
|
||||
case Token_Gt: return a > b;
|
||||
case Token_GtEq: return a >= b;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Float: {
|
||||
f64 a = x.value_float;
|
||||
f64 b = y.value_float;
|
||||
switch (op) {
|
||||
case Token_CmpEq: return cmp_f64(a, b) == 0;
|
||||
case Token_NotEq: return cmp_f64(a, b) != 0;
|
||||
case Token_Lt: return cmp_f64(a, b) < 0;
|
||||
case Token_LtEq: return cmp_f64(a, b) <= 0;
|
||||
case Token_Gt: return cmp_f64(a, b) > 0;
|
||||
case Token_GtEq: return cmp_f64(a, b) >= 0;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_String: {
|
||||
String a = x.value_string;
|
||||
String b = y.value_string;
|
||||
isize len = gb_min(a.len, b.len);
|
||||
// TODO(bill): gb_memcompare is used because the strings are UTF-8
|
||||
switch (op) {
|
||||
case Token_CmpEq: return gb_memcompare(a.text, b.text, len) == 0;
|
||||
case Token_NotEq: return gb_memcompare(a.text, b.text, len) != 0;
|
||||
case Token_Lt: return gb_memcompare(a.text, b.text, len) < 0;
|
||||
case Token_LtEq: return gb_memcompare(a.text, b.text, len) <= 0;
|
||||
case Token_Gt: return gb_memcompare(a.text, b.text, len) > 0;
|
||||
case Token_GtEq: return gb_memcompare(a.text, b.text, len) >= 0;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
GB_PANIC("Invalid comparison");
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,673 @@
|
||||
#include <math.h>
|
||||
|
||||
// TODO(bill): Big numbers
|
||||
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
|
||||
|
||||
struct AstNode;
|
||||
struct HashKey;
|
||||
struct Type;
|
||||
bool are_types_identical(Type *x, Type *y);
|
||||
|
||||
struct Complex128 {
|
||||
f64 real, imag;
|
||||
};
|
||||
|
||||
enum ExactValueKind {
|
||||
ExactValue_Invalid,
|
||||
|
||||
ExactValue_Bool,
|
||||
ExactValue_String,
|
||||
ExactValue_Integer,
|
||||
ExactValue_Float,
|
||||
ExactValue_Complex,
|
||||
ExactValue_Pointer,
|
||||
ExactValue_Compound, // TODO(bill): Is this good enough?
|
||||
ExactValue_Type,
|
||||
|
||||
ExactValue_Count,
|
||||
};
|
||||
|
||||
struct ExactValue {
|
||||
ExactValueKind kind;
|
||||
union {
|
||||
bool value_bool;
|
||||
String value_string;
|
||||
i128 value_integer; // NOTE(bill): This must be an integer and not a pointer
|
||||
f64 value_float;
|
||||
i64 value_pointer;
|
||||
Complex128 value_complex;
|
||||
AstNode * value_compound;
|
||||
Type * value_type;
|
||||
};
|
||||
};
|
||||
|
||||
gb_global ExactValue const empty_exact_value = {};
|
||||
|
||||
HashKey hash_exact_value(ExactValue v) {
|
||||
return hashing_proc(&v, gb_size_of(ExactValue));
|
||||
}
|
||||
|
||||
|
||||
ExactValue exact_value_compound(AstNode *node) {
|
||||
ExactValue result = {ExactValue_Compound};
|
||||
result.value_compound = node;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue exact_value_bool(bool b) {
|
||||
ExactValue result = {ExactValue_Bool};
|
||||
result.value_bool = (b != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue exact_value_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
ExactValue result = {ExactValue_String};
|
||||
result.value_string = string;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue exact_value_i64(i64 i) {
|
||||
ExactValue result = {ExactValue_Integer};
|
||||
result.value_integer = i128_from_i64(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue exact_value_i128(i128 i) {
|
||||
ExactValue result = {ExactValue_Integer};
|
||||
result.value_integer = i;
|
||||
return result;
|
||||
}
|
||||
ExactValue exact_value_u128(u128 i) {
|
||||
ExactValue result = {ExactValue_Integer};
|
||||
result.value_integer = u128_to_i128(i);
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue exact_value_float(f64 f) {
|
||||
ExactValue result = {ExactValue_Float};
|
||||
result.value_float = 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_pointer(i64 ptr) {
|
||||
ExactValue result = {ExactValue_Pointer};
|
||||
result.value_pointer = ptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue exact_value_type(Type *type) {
|
||||
ExactValue result = {ExactValue_Type};
|
||||
result.value_type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
ExactValue exact_value_integer_from_string(String string) {
|
||||
return exact_value_u128(u128_from_string(string));
|
||||
}
|
||||
|
||||
f64 float_from_string(String string) {
|
||||
isize i = 0;
|
||||
u8 *str = string.text;
|
||||
isize len = string.len;
|
||||
|
||||
f64 sign = 1.0;
|
||||
if (str[i] == '-') {
|
||||
sign = -1.0;
|
||||
i++;
|
||||
} else if (*str == '+') {
|
||||
i++;
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (len-i > 2 &&
|
||||
str[i] == '0' &&
|
||||
str[i+1] == 'h') {
|
||||
i += 2;
|
||||
u8 *text = string.text;
|
||||
isize len = string.len;
|
||||
if (has_prefix) {
|
||||
text += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
u64 base = 16;
|
||||
|
||||
u64 result = {0};
|
||||
for (isize i = 0; i < len; i++) {
|
||||
Rune r = cast(Rune)text[i];
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
u64 v = bit128__digit_value(r);
|
||||
if (v >= base) {
|
||||
break;
|
||||
}
|
||||
result *= base;
|
||||
result += v;
|
||||
}
|
||||
|
||||
|
||||
return *cast(f64 *)&result;
|
||||
}
|
||||
#endif
|
||||
|
||||
f64 value = 0.0;
|
||||
for (; i < len; i++) {
|
||||
Rune r = cast(Rune)str[i];
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
i64 v = digit_value(r);
|
||||
if (v >= 10) {
|
||||
break;
|
||||
}
|
||||
value *= 10.0;
|
||||
value += v;
|
||||
}
|
||||
|
||||
if (str[i] == '.') {
|
||||
f64 pow10 = 10.0;
|
||||
i++;
|
||||
for (; i < string.len; i++) {
|
||||
Rune r = cast(Rune)str[i];
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
i64 v = digit_value(r);
|
||||
if (v >= 10) {
|
||||
break;
|
||||
}
|
||||
value += v/pow10;
|
||||
pow10 *= 10.0;
|
||||
}
|
||||
}
|
||||
|
||||
bool frac = false;
|
||||
f64 scale = 1.0;
|
||||
if ((str[i] == 'e') || (str[i] == 'E')) {
|
||||
i++;
|
||||
|
||||
if (str[i] == '-') {
|
||||
frac = true;
|
||||
i++;
|
||||
} else if (str[i] == '+') {
|
||||
i++;
|
||||
}
|
||||
|
||||
u32 exp = 0;
|
||||
for (; i < len; i++) {
|
||||
Rune r = cast(Rune)str[i];
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
u32 d = cast(u32)digit_value(r);
|
||||
if (d >= 10) {
|
||||
break;
|
||||
}
|
||||
exp = exp * 10 + d;
|
||||
}
|
||||
if (exp > 308) exp = 308;
|
||||
|
||||
while (exp >= 50) { scale *= 1e50; exp -= 50; }
|
||||
while (exp >= 8) { scale *= 1e8; exp -= 8; }
|
||||
while (exp > 0) { scale *= 10.0; exp -= 1; }
|
||||
}
|
||||
|
||||
return sign * (frac ? (value / scale) : (value * scale));
|
||||
}
|
||||
|
||||
ExactValue exact_value_float_from_string(String string) {
|
||||
return exact_value_float(float_from_string(string));
|
||||
}
|
||||
|
||||
|
||||
ExactValue exact_value_from_basic_literal(Token token) {
|
||||
switch (token.kind) {
|
||||
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[str.len-1];
|
||||
str.len--; // Ignore the `i|j|k`
|
||||
f64 imag = float_from_string(str);
|
||||
|
||||
if (last_rune == 'i') {
|
||||
return exact_value_complex(0, imag);
|
||||
}
|
||||
}
|
||||
case Token_Rune: {
|
||||
Rune r = GB_RUNE_INVALID;
|
||||
gb_utf8_decode(token.string.text, token.string.len, &r);
|
||||
// gb_printf("%.*s rune: %d\n", LIT(token.string), r);
|
||||
return exact_value_i64(r);
|
||||
}
|
||||
default:
|
||||
GB_PANIC("Invalid token for basic literal");
|
||||
break;
|
||||
}
|
||||
|
||||
ExactValue result = {ExactValue_Invalid};
|
||||
return result;
|
||||
}
|
||||
|
||||
ExactValue exact_value_to_integer(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
return v;
|
||||
case ExactValue_Float: {
|
||||
i128 i = i128_from_f64(v.value_float);
|
||||
f64 f = i128_to_f64(i);
|
||||
if (f == v.value_float) {
|
||||
return exact_value_i128(i);
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Pointer:
|
||||
return exact_value_i64(cast(i64)cast(intptr)v.value_pointer);
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
ExactValue exact_value_to_float(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
return exact_value_float(i128_to_f64(v.value_integer));
|
||||
case ExactValue_Float:
|
||||
return v;
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
ExactValue exact_value_to_complex(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
return exact_value_complex(i128_to_f64(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_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);
|
||||
}
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
ExactValue exact_value_imag(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Integer:
|
||||
case ExactValue_Float:
|
||||
return exact_value_i64(0);
|
||||
case ExactValue_Complex:
|
||||
return exact_value_float(v.value_complex.imag);
|
||||
}
|
||||
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_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
|
||||
switch (op) {
|
||||
case Token_Add: {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid:
|
||||
case ExactValue_Integer:
|
||||
case ExactValue_Float:
|
||||
case ExactValue_Complex:
|
||||
return v;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Token_Sub: {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return v;
|
||||
case ExactValue_Integer: {
|
||||
ExactValue i = v;
|
||||
i.value_integer = -i.value_integer;
|
||||
return i;
|
||||
}
|
||||
case ExactValue_Float: {
|
||||
ExactValue i = v;
|
||||
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);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
|
||||
case Token_Xor: {
|
||||
i128 i = I128_ZERO;
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return v;
|
||||
case ExactValue_Integer:
|
||||
i = ~v.value_integer;
|
||||
break;
|
||||
default:
|
||||
goto failure;
|
||||
}
|
||||
|
||||
// NOTE(bill): unsigned integers will be negative and will need to be
|
||||
// limited to the types precision
|
||||
// IMPORTANT NOTE(bill): Max precision is 64 bits as that's how integers are stored
|
||||
if (0 < precision && precision < 128) {
|
||||
i = i & ~(I128_NEG_ONE << precision);
|
||||
}
|
||||
|
||||
return exact_value_i128(i);
|
||||
} break;
|
||||
|
||||
case Token_Not: {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid: return v;
|
||||
case ExactValue_Bool:
|
||||
return exact_value_bool(!v.value_bool);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
failure:
|
||||
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op]));
|
||||
|
||||
ExactValue error_value = {};
|
||||
return error_value;
|
||||
}
|
||||
|
||||
// NOTE(bill): Make sure things are evaluated in correct order
|
||||
i32 exact_value_order(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return 0;
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
return 1;
|
||||
case ExactValue_Integer:
|
||||
return 2;
|
||||
case ExactValue_Float:
|
||||
return 3;
|
||||
case ExactValue_Complex:
|
||||
return 4;
|
||||
case ExactValue_Pointer:
|
||||
return 5;
|
||||
|
||||
default:
|
||||
GB_PANIC("How'd you get here? Invalid Value.kind");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||
if (exact_value_order(*y) < exact_value_order(*x)) {
|
||||
match_exact_values(y, x);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (x->kind) {
|
||||
case ExactValue_Invalid:
|
||||
*y = *x;
|
||||
return;
|
||||
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
case ExactValue_Complex:
|
||||
return;
|
||||
|
||||
case ExactValue_Integer:
|
||||
switch (y->kind) {
|
||||
case ExactValue_Integer:
|
||||
return;
|
||||
case ExactValue_Float:
|
||||
// TODO(bill): Is this good enough?
|
||||
*x = exact_value_float(i128_to_f64(x->value_integer));
|
||||
return;
|
||||
case ExactValue_Complex:
|
||||
*x = exact_value_complex(i128_to_f64(x->value_integer), 0);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Float:
|
||||
switch (y->kind) {
|
||||
case ExactValue_Float:
|
||||
return;
|
||||
case ExactValue_Complex:
|
||||
*x = exact_value_to_complex(*x);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
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?
|
||||
ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
|
||||
match_exact_values(&x, &y);
|
||||
|
||||
switch (x.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return x;
|
||||
|
||||
case ExactValue_Bool:
|
||||
switch (op) {
|
||||
case Token_CmpAnd: return exact_value_bool(x.value_bool && y.value_bool);
|
||||
case Token_CmpOr: return exact_value_bool(x.value_bool || y.value_bool);
|
||||
case Token_And: return exact_value_bool(x.value_bool & y.value_bool);
|
||||
case Token_Or: return exact_value_bool(x.value_bool | y.value_bool);
|
||||
default: goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Integer: {
|
||||
i128 a = x.value_integer;
|
||||
i128 b = y.value_integer;
|
||||
i128 c = I128_ZERO;
|
||||
switch (op) {
|
||||
case Token_Add: c = a + b; break;
|
||||
case Token_Sub: c = a - b; break;
|
||||
case Token_Mul: c = a * b; break;
|
||||
case Token_Quo: return exact_value_float(fmod(i128_to_f64(a), i128_to_f64(b)));
|
||||
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
|
||||
case Token_Mod: c = a % b; break;
|
||||
case Token_ModMod: c = ((a % b) + b) % b; break;
|
||||
case Token_And: c = a & b; break;
|
||||
case Token_Or: c = a | b; break;
|
||||
case Token_Xor: c = a ^ b; break;
|
||||
case Token_AndNot: c = i128_and_not(a, b); break;
|
||||
case Token_Shl: c = a << cast(u32)i128_to_u64(b); break;
|
||||
case Token_Shr: c = a >> cast(u32)i128_to_u64(b); break;
|
||||
default: goto error;
|
||||
}
|
||||
|
||||
return exact_value_i128(c);
|
||||
} break;
|
||||
|
||||
case ExactValue_Float: {
|
||||
f64 a = x.value_float;
|
||||
f64 b = y.value_float;
|
||||
switch (op) {
|
||||
case Token_Add: return exact_value_float(a + b);
|
||||
case Token_Sub: return exact_value_float(a - b);
|
||||
case Token_Mul: return exact_value_float(a * b);
|
||||
case Token_Quo: return exact_value_float(a / b);
|
||||
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_String: {
|
||||
if (op != Token_Add) goto error;
|
||||
|
||||
// NOTE(bill): How do you minimize this over allocation?
|
||||
String sx = x.value_string;
|
||||
String sy = y.value_string;
|
||||
isize len = sx.len+sy.len;
|
||||
u8 *data = gb_alloc_array(heap_allocator(), u8, len);
|
||||
gb_memmove(data, sx.text, sx.len);
|
||||
gb_memmove(data+sx.len, sy.text, sy.len);
|
||||
return exact_value_string(make_string(data, len));
|
||||
} break;
|
||||
}
|
||||
|
||||
error:; // NOTE(bill): MSVC accepts this??? apparently you cannot declare variables immediately after labels...
|
||||
return empty_exact_value;
|
||||
}
|
||||
|
||||
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Add, x, y); }
|
||||
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Sub, x, y); }
|
||||
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Mul, x, y); }
|
||||
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Quo, x, y); }
|
||||
gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
|
||||
|
||||
|
||||
i32 cmp_f64(f64 a, f64 b) {
|
||||
return (a > b) - (a < b);
|
||||
}
|
||||
|
||||
bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
|
||||
match_exact_values(&x, &y);
|
||||
|
||||
switch (x.kind) {
|
||||
case ExactValue_Invalid:
|
||||
return false;
|
||||
|
||||
case ExactValue_Bool:
|
||||
switch (op) {
|
||||
case Token_CmpEq: return x.value_bool == y.value_bool;
|
||||
case Token_NotEq: return x.value_bool != y.value_bool;
|
||||
}
|
||||
break;
|
||||
|
||||
case ExactValue_Integer: {
|
||||
i128 a = x.value_integer;
|
||||
i128 b = y.value_integer;
|
||||
switch (op) {
|
||||
case Token_CmpEq: return a == b;
|
||||
case Token_NotEq: return a != b;
|
||||
case Token_Lt: return a < b;
|
||||
case Token_LtEq: return a <= b;
|
||||
case Token_Gt: return a > b;
|
||||
case Token_GtEq: return a >= b;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Float: {
|
||||
f64 a = x.value_float;
|
||||
f64 b = y.value_float;
|
||||
switch (op) {
|
||||
case Token_CmpEq: return cmp_f64(a, b) == 0;
|
||||
case Token_NotEq: return cmp_f64(a, b) != 0;
|
||||
case Token_Lt: return cmp_f64(a, b) < 0;
|
||||
case Token_LtEq: return cmp_f64(a, b) <= 0;
|
||||
case Token_Gt: return cmp_f64(a, b) > 0;
|
||||
case Token_GtEq: return cmp_f64(a, b) >= 0;
|
||||
}
|
||||
} 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_String: {
|
||||
String a = x.value_string;
|
||||
String b = y.value_string;
|
||||
// TODO(bill): gb_memcompare is used because the strings are UTF-8
|
||||
switch (op) {
|
||||
case Token_CmpEq: return a == b;
|
||||
case Token_NotEq: return a != b;
|
||||
case Token_Lt: return a < b;
|
||||
case Token_LtEq: return a <= b;
|
||||
case Token_Gt: return a > b;
|
||||
case Token_GtEq: return a >= b;
|
||||
}
|
||||
} break;
|
||||
|
||||
case ExactValue_Type:
|
||||
switch (op) {
|
||||
case Token_CmpEq: return are_types_identical(x.value_type, y.value_type);
|
||||
case Token_NotEq: return !are_types_identical(x.value_type, y.value_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
GB_PANIC("Invalid comparison");
|
||||
return false;
|
||||
}
|
||||
+650
-231
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,726 @@
|
||||
|
||||
#if defined(GB_COMPILER_MSVC) && defined(GB_ARCH_64_BIT) && defined(GB_CPU_X86)
|
||||
#define MSVC_AMD64_INTRINSICS
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_mul128)
|
||||
#endif
|
||||
|
||||
#define BIT128_U64_HIGHBIT 0x8000000000000000ull
|
||||
#define BIT128_U64_BITS62 0x7fffffffffffffffull
|
||||
#define BIT128_U64_ALLBITS 0xffffffffffffffffull
|
||||
|
||||
|
||||
typedef struct u128 { u64 lo; u64 hi; } u128;
|
||||
typedef struct i128 { u64 lo; i64 hi; } i128;
|
||||
|
||||
|
||||
static u128 const U128_ZERO = {0, 0};
|
||||
static u128 const U128_ONE = {1, 0};
|
||||
static i128 const I128_ZERO = {0, 0};
|
||||
static i128 const I128_ONE = {1, 0};
|
||||
static u128 const U128_NEG_ONE = {BIT128_U64_ALLBITS, BIT128_U64_ALLBITS};
|
||||
static i128 const I128_NEG_ONE = {BIT128_U64_ALLBITS, cast(i64)BIT128_U64_ALLBITS};
|
||||
|
||||
u128 u128_lo_hi (u64 lo, u64 hi);
|
||||
u128 u128_from_u32 (u32 u);
|
||||
u128 u128_from_u64 (u64 u);
|
||||
u128 u128_from_i64 (i64 u);
|
||||
u128 u128_from_f32 (f32 f);
|
||||
u128 u128_from_f64 (f64 f);
|
||||
u128 u128_from_string(String string);
|
||||
|
||||
i128 i128_lo_hi (u64 lo, i64 hi);
|
||||
i128 i128_from_u32 (u32 u);
|
||||
i128 i128_from_u64 (u64 u);
|
||||
i128 i128_from_i64 (i64 u);
|
||||
i128 i128_from_f32 (f32 f);
|
||||
i128 i128_from_f64 (f64 f);
|
||||
i128 i128_from_string(String string);
|
||||
|
||||
u64 u128_to_u64(u128 a);
|
||||
i64 u128_to_i64(u128 a);
|
||||
f64 u128_to_f64(u128 a);
|
||||
i128 u128_to_i128(u128 a);
|
||||
|
||||
u64 i128_to_u64(i128 a);
|
||||
i64 i128_to_i64(i128 a);
|
||||
f64 i128_to_f64(i128 a);
|
||||
u128 i128_to_u128(i128 a);
|
||||
|
||||
String u128_to_string(u128 a, char *buf, isize len);
|
||||
String i128_to_string(i128 a, char *buf, isize len);
|
||||
|
||||
i32 u128_cmp (u128 a, u128 b);
|
||||
bool u128_eq (u128 a, u128 b);
|
||||
bool u128_ne (u128 a, u128 b);
|
||||
bool u128_lt (u128 a, u128 b);
|
||||
bool u128_gt (u128 a, u128 b);
|
||||
bool u128_le (u128 a, u128 b);
|
||||
bool u128_ge (u128 a, u128 b);
|
||||
u128 u128_add (u128 a, u128 b);
|
||||
u128 u128_not (u128 a);
|
||||
u128 u128_neg (u128 a);
|
||||
u128 u128_sub (u128 a, u128 b);
|
||||
u128 u128_and (u128 a, u128 b);
|
||||
u128 u128_or (u128 a, u128 b);
|
||||
u128 u128_xor (u128 a, u128 b);
|
||||
u128 u128_and_not(u128 a, u128 b);
|
||||
u128 u128_shl (u128 a, u32 n);
|
||||
u128 u128_shr (u128 a, u32 n);
|
||||
u128 u128_mul (u128 a, u128 b);
|
||||
void u128_divide (u128 num, u128 den, u128 *quo, u128 *rem);
|
||||
u128 u128_quo (u128 a, u128 b);
|
||||
u128 u128_mod (u128 a, u128 b);
|
||||
|
||||
i128 i128_abs (i128 a);
|
||||
i32 i128_cmp (i128 a, i128 b);
|
||||
bool i128_eq (i128 a, i128 b);
|
||||
bool i128_ne (i128 a, i128 b);
|
||||
bool i128_lt (i128 a, i128 b);
|
||||
bool i128_gt (i128 a, i128 b);
|
||||
bool i128_le (i128 a, i128 b);
|
||||
bool i128_ge (i128 a, i128 b);
|
||||
i128 i128_add (i128 a, i128 b);
|
||||
i128 i128_not (i128 a);
|
||||
i128 i128_neg (i128 a);
|
||||
i128 i128_sub (i128 a, i128 b);
|
||||
i128 i128_and (i128 a, i128 b);
|
||||
i128 i128_or (i128 a, i128 b);
|
||||
i128 i128_xor (i128 a, i128 b);
|
||||
i128 i128_and_not(i128 a, i128 b);
|
||||
i128 i128_shl (i128 a, u32 n);
|
||||
i128 i128_shr (i128 a, u32 n);
|
||||
i128 i128_mul (i128 a, i128 b);
|
||||
void i128_divide (i128 num, i128 den, i128 *quo, i128 *rem);
|
||||
i128 i128_quo (i128 a, i128 b);
|
||||
i128 i128_mod (i128 a, i128 b);
|
||||
|
||||
bool operator==(u128 a, u128 b) { return u128_eq(a, b); }
|
||||
bool operator!=(u128 a, u128 b) { return u128_ne(a, b); }
|
||||
bool operator< (u128 a, u128 b) { return u128_lt(a, b); }
|
||||
bool operator> (u128 a, u128 b) { return u128_gt(a, b); }
|
||||
bool operator<=(u128 a, u128 b) { return u128_le(a, b); }
|
||||
bool operator>=(u128 a, u128 b) { return u128_ge(a, b); }
|
||||
|
||||
u128 operator+(u128 a, u128 b) { return u128_add(a, b); }
|
||||
u128 operator-(u128 a, u128 b) { return u128_sub(a, b); }
|
||||
u128 operator*(u128 a, u128 b) { return u128_mul(a, b); }
|
||||
u128 operator/(u128 a, u128 b) { return u128_quo(a, b); }
|
||||
u128 operator%(u128 a, u128 b) { return u128_mod(a, b); }
|
||||
u128 operator&(u128 a, u128 b) { return u128_and(a, b); }
|
||||
u128 operator|(u128 a, u128 b) { return u128_or (a, b); }
|
||||
u128 operator^(u128 a, u128 b) { return u128_xor(a, b); }
|
||||
u128 operator~(u128 a) { return u128_not(a); }
|
||||
u128 operator+(u128 a) { return a; }
|
||||
u128 operator-(u128 a) { return u128_neg(a); }
|
||||
u128 operator<<(u128 a, u32 b) { return u128_shl(a, b); }
|
||||
u128 operator>>(u128 a, u32 b) { return u128_shr(a, b); }
|
||||
|
||||
|
||||
bool operator==(i128 a, i128 b) { return i128_eq(a, b); }
|
||||
bool operator!=(i128 a, i128 b) { return i128_ne(a, b); }
|
||||
bool operator< (i128 a, i128 b) { return i128_lt(a, b); }
|
||||
bool operator> (i128 a, i128 b) { return i128_gt(a, b); }
|
||||
bool operator<=(i128 a, i128 b) { return i128_le(a, b); }
|
||||
bool operator>=(i128 a, i128 b) { return i128_ge(a, b); }
|
||||
|
||||
i128 operator+(i128 a, i128 b) { return i128_add(a, b); }
|
||||
i128 operator-(i128 a, i128 b) { return i128_sub(a, b); }
|
||||
i128 operator*(i128 a, i128 b) { return i128_mul(a, b); }
|
||||
i128 operator/(i128 a, i128 b) { return i128_quo(a, b); }
|
||||
i128 operator%(i128 a, i128 b) { return i128_mod(a, b); }
|
||||
i128 operator&(i128 a, i128 b) { return i128_and(a, b); }
|
||||
i128 operator|(i128 a, i128 b) { return i128_or (a, b); }
|
||||
i128 operator^(i128 a, i128 b) { return i128_xor(a, b); }
|
||||
i128 operator~(i128 a) { return i128_not(a); }
|
||||
i128 operator+(i128 a) { return a; }
|
||||
i128 operator-(i128 a) { return i128_neg(a); }
|
||||
i128 operator<<(i128 a, u32 b) { return i128_shl(a, b); }
|
||||
i128 operator>>(i128 a, u32 b) { return i128_shr(a, b); }
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
u64 bit128__digit_value(Rune r) {
|
||||
if ('0' <= r && r <= '9') {
|
||||
return r - '0';
|
||||
} else if ('a' <= r && r <= 'f') {
|
||||
return r - 'a' + 10;
|
||||
} else if ('A' <= r && r <= 'F') {
|
||||
return r - 'A' + 10;
|
||||
}
|
||||
return 16; // NOTE(bill): Larger than highest possible
|
||||
}
|
||||
|
||||
u128 u128_lo_hi(u64 lo, u64 hi) {
|
||||
u128 r = {};
|
||||
r.lo = lo;
|
||||
r.hi = hi;
|
||||
return r;
|
||||
}
|
||||
u128 u128_from_u32(u32 u) { return u128_lo_hi(cast(u64)u, 0); }
|
||||
u128 u128_from_u64(u64 u) { return u128_lo_hi(cast(u64)u, 0); }
|
||||
u128 u128_from_i64(i64 u) { return u128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
|
||||
u128 u128_from_f32(f32 f) { return u128_lo_hi(cast(u64)f, 0); }
|
||||
u128 u128_from_f64(f64 f) { return u128_lo_hi(cast(u64)f, 0); }
|
||||
u128 u128_from_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
u64 base = 10;
|
||||
bool has_prefix = false;
|
||||
if (string.len > 2 && string[0] == '0') {
|
||||
switch (string[1]) {
|
||||
case 'b': base = 2; has_prefix = true; break;
|
||||
case 'o': base = 8; has_prefix = true; break;
|
||||
case 'd': base = 10; has_prefix = true; break;
|
||||
case 'z': base = 12; has_prefix = true; break;
|
||||
case 'x': base = 16; has_prefix = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
u8 *text = string.text;
|
||||
isize len = string.len;
|
||||
if (has_prefix) {
|
||||
text += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
u128 base_ = u128_from_u64(base);
|
||||
|
||||
u128 result = {0};
|
||||
for (isize i = 0; i < len; i++) {
|
||||
Rune r = cast(Rune)text[i];
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
u64 v = bit128__digit_value(r);
|
||||
if (v >= base) {
|
||||
break;
|
||||
}
|
||||
result = u128_mul(result, base_);
|
||||
result = u128_add(result, u128_from_u64(v));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
i128 i128_lo_hi(u64 lo, i64 hi) {
|
||||
i128 i;
|
||||
i.lo = lo;
|
||||
i.hi = hi;
|
||||
return i;
|
||||
}
|
||||
i128 i128_from_u32(u32 u) { return i128_lo_hi(cast(u64)u, 0); }
|
||||
i128 i128_from_u64(u64 u) { return i128_lo_hi(cast(u64)u, 0); }
|
||||
i128 i128_from_i64(i64 u) { return i128_lo_hi(cast(u64)u, u < 0 ? -1 : 0); }
|
||||
i128 i128_from_f32(f32 f) { return i128_lo_hi(cast(u64)f, 0); }
|
||||
i128 i128_from_f64(f64 f) { return i128_lo_hi(cast(u64)f, 0); }
|
||||
i128 i128_from_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
u64 base = 10;
|
||||
bool has_prefix = false;
|
||||
if (string.len > 2 && string[0] == '0') {
|
||||
switch (string[1]) {
|
||||
case 'b': base = 2; has_prefix = true; break;
|
||||
case 'o': base = 8; has_prefix = true; break;
|
||||
case 'd': base = 10; has_prefix = true; break;
|
||||
case 'z': base = 12; has_prefix = true; break;
|
||||
case 'x': base = 16; has_prefix = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
u8 *text = string.text;
|
||||
isize len = string.len;
|
||||
if (has_prefix) {
|
||||
text += 2;
|
||||
len -= 2;
|
||||
}
|
||||
|
||||
i128 base_ = i128_from_u64(base);
|
||||
|
||||
i128 result = {0};
|
||||
for (isize i = 0; i < len; i++) {
|
||||
Rune r = cast(Rune)text[i];
|
||||
if (r == '_') {
|
||||
continue;
|
||||
}
|
||||
u64 v = bit128__digit_value(r);
|
||||
if (v >= base) {
|
||||
break;
|
||||
}
|
||||
result = i128_mul(result, base_);
|
||||
result = i128_add(result, i128_from_u64(v));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
u64 u128_to_u64(u128 a) {
|
||||
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
|
||||
}
|
||||
i64 u128_to_i64(u128 a) {
|
||||
return a.lo;
|
||||
}
|
||||
f64 u128_to_f64(u128 a) {
|
||||
if (a.hi >= 0) {
|
||||
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
|
||||
}
|
||||
i64 h = cast(i64)a.hi;
|
||||
u64 l = a.lo;
|
||||
h = ~h;
|
||||
l = ~l;
|
||||
l += 1;
|
||||
if (l == 0) {
|
||||
h += 1;
|
||||
}
|
||||
|
||||
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
|
||||
}
|
||||
i128 u128_to_i128(u128 a) {
|
||||
return *cast(i128 *)&a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
u64 i128_to_u64(i128 a) {
|
||||
return (a.lo&BIT128_U64_BITS62) | (a.hi&BIT128_U64_HIGHBIT);
|
||||
}
|
||||
i64 i128_to_i64(i128 a) {
|
||||
return cast(i64)a.lo;
|
||||
}
|
||||
f64 i128_to_f64(i128 a) {
|
||||
if (a.hi >= 0) {
|
||||
return (cast(f64)a.hi * 18446744073709551616.0) + cast(f64)a.lo;
|
||||
}
|
||||
i64 h = a.hi;
|
||||
u64 l = a.lo;
|
||||
h = ~h;
|
||||
l = ~l;
|
||||
l += 1;
|
||||
if (l == 0) {
|
||||
h += 1;
|
||||
}
|
||||
|
||||
return -((cast(f64)h * 18446744073709551616.0) + cast(f64)l);
|
||||
}
|
||||
u128 i128_to_u128(i128 a) {
|
||||
return *cast(u128 *)&a;
|
||||
}
|
||||
|
||||
|
||||
|
||||
String u128_to_string(u128 v, char *out_buf, isize out_buf_len) {
|
||||
char buf[200] = {0};
|
||||
isize i = gb_size_of(buf);
|
||||
|
||||
u128 b = u128_from_u64(10);;
|
||||
while (u128_ge(v, b)) {
|
||||
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
|
||||
v = u128_quo(v, b);
|
||||
}
|
||||
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
|
||||
|
||||
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
|
||||
gb_memcopy(out_buf, &buf[i], len);
|
||||
return make_string(cast(u8 *)out_buf, len);
|
||||
}
|
||||
String i128_to_string(i128 a, char *out_buf, isize out_buf_len) {
|
||||
char buf[200] = {0};
|
||||
isize i = gb_size_of(buf);
|
||||
bool negative = false;
|
||||
if (i128_lt(a, I128_ZERO)) {
|
||||
negative = true;
|
||||
a = i128_neg(a);
|
||||
}
|
||||
|
||||
u128 v = *cast(u128 *)&a;
|
||||
u128 b = u128_from_u64(10);;
|
||||
while (u128_ge(v, b)) {
|
||||
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
|
||||
v = u128_quo(v, b);
|
||||
}
|
||||
buf[--i] = gb__num_to_char_table[u128_to_i64(u128_mod(v, b))];
|
||||
|
||||
if (negative) {
|
||||
buf[--i] = '-';
|
||||
}
|
||||
|
||||
isize len = gb_min(gb_size_of(buf)-i, out_buf_len);
|
||||
gb_memcopy(out_buf, &buf[i], len);
|
||||
return make_string(cast(u8 *)out_buf, len);
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
i32 u128_cmp(u128 a, u128 b) {
|
||||
if (a.hi == b.hi && b.lo == b.lo) {
|
||||
return 0;
|
||||
}
|
||||
if (a.hi == b.hi) {
|
||||
return a.lo < b.lo ? -1 : +1;
|
||||
}
|
||||
return a.hi < b.hi ? -1 : +1;
|
||||
}
|
||||
|
||||
bool u128_eq(u128 a, u128 b) { return a.hi == b.hi && a.lo == b.lo; }
|
||||
bool u128_ne(u128 a, u128 b) { return !u128_eq(a, b); }
|
||||
bool u128_lt(u128 a, u128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
|
||||
bool u128_gt(u128 a, u128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
|
||||
bool u128_le(u128 a, u128 b) { return !u128_gt(a, b); }
|
||||
bool u128_ge(u128 a, u128 b) { return !u128_lt(a, b); }
|
||||
|
||||
u128 u128_add(u128 a, u128 b) {
|
||||
u128 old_a = a;
|
||||
a.lo += b.lo;
|
||||
a.hi += b.hi;
|
||||
if (a.lo < old_a.lo) {
|
||||
a.hi += 1;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
u128 u128_not(u128 a) { return u128_lo_hi(~a.lo, ~a.hi); }
|
||||
|
||||
u128 u128_neg(u128 a) {
|
||||
return u128_add(u128_not(a), u128_from_u64(1));
|
||||
}
|
||||
u128 u128_sub(u128 a, u128 b) {
|
||||
return u128_add(a, u128_neg(b));
|
||||
}
|
||||
u128 u128_and(u128 a, u128 b) { return u128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
|
||||
u128 u128_or (u128 a, u128 b) { return u128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
|
||||
u128 u128_xor(u128 a, u128 b) { return u128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
|
||||
u128 u128_and_not(u128 a, u128 b) { return u128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
|
||||
|
||||
|
||||
u128 u128_shl(u128 a, u32 n) {
|
||||
if (n >= 128) {
|
||||
return u128_lo_hi(0, 0);
|
||||
}
|
||||
#if 0 && defined(MSVC_AMD64_INTRINSICS)
|
||||
a.hi = __shiftleft128(a.lo, a.hi, n);
|
||||
a.lo = a.lo << n;
|
||||
return a;
|
||||
#else
|
||||
if (n >= 64) {
|
||||
n -= 64;
|
||||
a.hi = a.lo;
|
||||
a.lo = 0;
|
||||
}
|
||||
|
||||
if (n != 0) {
|
||||
u64 mask = ~(BIT128_U64_ALLBITS >> n);
|
||||
|
||||
a.hi <<= n;
|
||||
a.hi |= (a.lo&mask) >> (64 - n);
|
||||
a.lo <<= n;
|
||||
}
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
u128 u128_shr(u128 a, u32 n) {
|
||||
if (n >= 128) {
|
||||
return u128_lo_hi(0, 0);
|
||||
}
|
||||
#if 0 && defined(MSVC_AMD64_INTRINSICS)
|
||||
a.lo = __shiftright128(a.lo, a.hi, n);
|
||||
a.hi = a.hi >> n;
|
||||
return a;
|
||||
#else
|
||||
if (n >= 64) {
|
||||
n -= 64;
|
||||
a.lo = a.hi;
|
||||
a.hi = 0;
|
||||
}
|
||||
|
||||
if (n != 0) {
|
||||
u64 mask = ~(BIT128_U64_ALLBITS << n);
|
||||
a.lo >>= n;
|
||||
a.lo |= (a.hi&mask) << (64 - n);
|
||||
a.hi >>= n;
|
||||
}
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
u128 u128_mul(u128 a, u128 b) {
|
||||
if (a.lo == 0 && a.hi == 0) {
|
||||
return u128_from_u64(0);
|
||||
} else if (b.lo == 0 && b.hi == 0) {
|
||||
return u128_from_u64(0);
|
||||
}
|
||||
if (u128_eq(a, U128_ONE)) {
|
||||
return b;
|
||||
}
|
||||
if (u128_eq(b, U128_ONE)) {
|
||||
return a;
|
||||
}
|
||||
|
||||
|
||||
#if defined(MSVC_AMD64_INTRINSICS)
|
||||
if (a.hi == 0 && b.hi == 0) {
|
||||
a.lo = _umul128(a.lo, b.lo, &a.hi);
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
u128 res = {0};
|
||||
u128 t = b;
|
||||
for (u32 i = 0; i < 128; i++) {
|
||||
if ((t.lo&1) != 0) {
|
||||
res = u128_add(res, u128_shl(a, i));
|
||||
}
|
||||
|
||||
t = u128_shr(t, 1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool u128_hibit(u128 *d) { return (d->hi & BIT128_U64_HIGHBIT) != 0; }
|
||||
|
||||
void u128_divide(u128 num, u128 den, u128 *quo, u128 *rem) {
|
||||
if (u128_eq(den, U128_ZERO)) {
|
||||
if (quo) *quo = u128_from_u64(num.lo/den.lo);
|
||||
if (rem) *rem = U128_ZERO;
|
||||
} else {
|
||||
u128 n = num;
|
||||
u128 d = den;
|
||||
u128 x = U128_ONE;
|
||||
u128 r = U128_ZERO;
|
||||
|
||||
while (u128_ge(n, d) && !u128_hibit(&d)) {
|
||||
x = u128_shl(x, 1);
|
||||
d = u128_shl(d, 1);
|
||||
}
|
||||
|
||||
while (u128_ne(x, U128_ZERO)) {
|
||||
if (u128_ge(n, d)) {
|
||||
n = u128_sub(n, d);
|
||||
r = u128_or(r, x);
|
||||
}
|
||||
|
||||
x = u128_shr(x, 1);
|
||||
d = u128_shr(d, 1);
|
||||
}
|
||||
|
||||
if (quo) *quo = r;
|
||||
if (rem) *rem = n;
|
||||
}
|
||||
}
|
||||
|
||||
u128 u128_quo(u128 a, u128 b) {
|
||||
if (a.hi == 0 && b.hi == 0) {
|
||||
return u128_from_u64(a.lo/b.lo);
|
||||
}
|
||||
|
||||
u128 res = {0};
|
||||
u128_divide(a, b, &res, nullptr);
|
||||
return res;
|
||||
}
|
||||
u128 u128_mod(u128 a, u128 b) {
|
||||
if (a.hi == 0 && b.hi == 0) {
|
||||
return u128_from_u64(a.lo%b.lo);
|
||||
}
|
||||
u128 res = {0};
|
||||
u128_divide(a, b, nullptr, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
i128 i128_abs(i128 a) {
|
||||
if ((a.hi&BIT128_U64_HIGHBIT) != 0) {
|
||||
return i128_neg(a);
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
i32 i128_cmp(i128 a, i128 b) {
|
||||
if (a.hi == b.hi && b.lo == b.lo) {
|
||||
return 0;
|
||||
}
|
||||
if (a.hi == b.hi) {
|
||||
return a.lo < b.lo ? -1 : +1;
|
||||
}
|
||||
return a.hi < b.hi ? -1 : +1;
|
||||
}
|
||||
|
||||
bool i128_eq(i128 a, i128 b) { return a.hi == b.hi && a.lo == b.lo; }
|
||||
bool i128_ne(i128 a, i128 b) { return !i128_eq(a, b); }
|
||||
bool i128_lt(i128 a, i128 b) { return a.hi == b.hi ? a.lo < b.lo : a.hi < b.hi; }
|
||||
bool i128_gt(i128 a, i128 b) { return a.hi == b.hi ? a.lo > b.lo : a.hi > b.hi; }
|
||||
bool i128_le(i128 a, i128 b) { return a.hi == b.hi ? a.lo <= b.lo : a.hi <= b.hi; }
|
||||
bool i128_ge(i128 a, i128 b) { return a.hi == b.hi ? a.lo >= b.lo : a.hi >= b.hi; }
|
||||
|
||||
i128 i128_add(i128 a, i128 b) {
|
||||
i128 old_a = a;
|
||||
a.lo += b.lo;
|
||||
a.hi += b.hi;
|
||||
if (a.lo < old_a.lo) {
|
||||
a.hi += 1;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
i128 i128_not(i128 a) { return i128_lo_hi(~a.lo, ~a.hi); }
|
||||
|
||||
i128 i128_neg(i128 a) {
|
||||
return i128_add(i128_not(a), i128_from_u64(1));
|
||||
}
|
||||
i128 i128_sub(i128 a, i128 b) {
|
||||
return i128_add(a, i128_neg(b));
|
||||
}
|
||||
i128 i128_and(i128 a, i128 b) { return i128_lo_hi(a.lo&b.lo, a.hi&b.hi); }
|
||||
i128 i128_or (i128 a, i128 b) { return i128_lo_hi(a.lo|b.lo, a.hi|b.hi); }
|
||||
i128 i128_xor(i128 a, i128 b) { return i128_lo_hi(a.lo^b.lo, a.hi^b.hi); }
|
||||
i128 i128_and_not(i128 a, i128 b) { return i128_lo_hi(a.lo&(~b.lo), a.hi&(~b.hi)); }
|
||||
|
||||
|
||||
i128 i128_shl(i128 a, u32 n) {
|
||||
if (n >= 128) {
|
||||
return i128_lo_hi(0, 0);
|
||||
}
|
||||
|
||||
#if 0 && defined(MSVC_AMD64_INTRINSICS)
|
||||
a.hi = __shiftleft128(a.lo, a.hi, n);
|
||||
a.lo = a.lo << n;
|
||||
return a;
|
||||
#else
|
||||
if (n >= 64) {
|
||||
n -= 64;
|
||||
a.hi = a.lo;
|
||||
a.lo = 0;
|
||||
}
|
||||
|
||||
if (n != 0) {
|
||||
u64 mask = ~(BIT128_U64_ALLBITS >> n);
|
||||
|
||||
a.hi <<= n;
|
||||
a.hi |= (a.lo&mask) >> (64 - n);
|
||||
a.lo <<= n;
|
||||
}
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
i128 i128_shr(i128 a, u32 n) {
|
||||
if (n >= 128) {
|
||||
return i128_lo_hi(0, 0);
|
||||
}
|
||||
|
||||
#if 0 && defined(MSVC_AMD64_INTRINSICS)
|
||||
a.lo = __shiftright128(a.lo, a.hi, n);
|
||||
a.hi = a.hi >> n;
|
||||
return a;
|
||||
#else
|
||||
if (n >= 64) {
|
||||
n -= 64;
|
||||
a.lo = a.hi;
|
||||
a.hi = 0;
|
||||
}
|
||||
|
||||
if (n != 0) {
|
||||
u64 mask = ~(BIT128_U64_ALLBITS << n);
|
||||
a.lo >>= n;
|
||||
a.lo |= (a.hi&mask) << (64 - n);
|
||||
a.hi >>= n;
|
||||
}
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
i128 i128_mul(i128 a, i128 b) {
|
||||
if (a.lo == 0 && a.hi == 0) {
|
||||
return i128_from_u64(0);
|
||||
} else if (b.lo == 0 && b.hi == 0) {
|
||||
return i128_from_u64(0);
|
||||
}
|
||||
if (i128_eq(a, I128_ONE)) {
|
||||
return b;
|
||||
}
|
||||
if (i128_eq(b, I128_ONE)) {
|
||||
return a;
|
||||
}
|
||||
|
||||
#if defined(MSVC_AMD64_INTRINSICS)
|
||||
if (a.hi == 0 && b.hi == 0) {
|
||||
a.lo = _mul128(a.lo, b.lo, &a.hi);
|
||||
return a;
|
||||
}
|
||||
#endif
|
||||
|
||||
i128 res = {0};
|
||||
i128 t = b;
|
||||
for (u32 i = 0; i < 128; i++) {
|
||||
if ((t.lo&1) != 0) {
|
||||
res = i128_add(res, i128_shl(a, i));
|
||||
}
|
||||
|
||||
t = i128_shr(t, 1);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void i128_divide(i128 a, i128 b, i128 *quo, i128 *rem) {
|
||||
// TODO(bill): Which one is correct?!
|
||||
#if 1
|
||||
i128 s = i128_shr(b, 127);
|
||||
b = i128_sub(i128_xor(b, s), s);
|
||||
s = i128_shr(a, 127);
|
||||
b = i128_sub(i128_xor(a, s), s);
|
||||
|
||||
u128 n, r = {0};
|
||||
u128_divide(*cast(u128 *)&a, *cast(u128 *)&b, &n, &r);
|
||||
i128 ni = *cast(i128 *)&n;
|
||||
i128 ri = *cast(i128 *)&r;
|
||||
|
||||
if (quo) *quo = i128_sub(i128_xor(ni, s), s);
|
||||
if (rem) *rem = i128_sub(i128_xor(ri, s), s);
|
||||
#else
|
||||
if (i128_eq(b, I128_ZERO)) {
|
||||
if (quo) *quo = i128_from_u64(a.lo/b.lo);
|
||||
if (rem) *rem = I128_ZERO;
|
||||
} else {
|
||||
i128 n = a;
|
||||
i128 d = b;
|
||||
i128 x = I128_ONE;
|
||||
i128 r = I128_ZERO;
|
||||
|
||||
while (i128_ge(n, d) && ((i128_shr(d, 128-1).lo&1) == 0)) {
|
||||
x = i128_shl(x, 1);
|
||||
d = i128_shl(d, 1);
|
||||
}
|
||||
|
||||
while (i128_ne(x, I128_ZERO)) {
|
||||
if (i128_ge(n, d)) {
|
||||
n = i128_sub(n, d);
|
||||
r = i128_or(r, x);
|
||||
}
|
||||
|
||||
x = i128_shr(x, 1);
|
||||
d = i128_shr(d, 1);
|
||||
}
|
||||
|
||||
if (quo) *quo = r;
|
||||
if (rem) *rem = n;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
i128 i128_quo(i128 a, i128 b) {
|
||||
i128 res = {0};
|
||||
i128_divide(a, b, &res, nullptr);
|
||||
return res;
|
||||
}
|
||||
i128 i128_mod(i128 a, i128 b) {
|
||||
i128 res = {0};
|
||||
i128_divide(a, b, nullptr, &res);
|
||||
return res;
|
||||
}
|
||||
+8346
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
// Optimizations for the IR code
|
||||
|
||||
void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
|
||||
void ir_opt_add_operands(Array<irValue *> *ops, irInstr *i) {
|
||||
switch (i->kind) {
|
||||
case irInstr_Comment:
|
||||
break;
|
||||
@@ -39,7 +39,7 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
|
||||
array_add(ops, i->If.cond);
|
||||
break;
|
||||
case irInstr_Return:
|
||||
if (i->Return.value != NULL) {
|
||||
if (i->Return.value != nullptr) {
|
||||
array_add(ops, i->Return.value);
|
||||
}
|
||||
break;
|
||||
@@ -48,7 +48,7 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
|
||||
break;
|
||||
case irInstr_Phi:
|
||||
for_array(j, i->Phi.edges) {
|
||||
array_add(ops, i->Phi.edges.e[j]);
|
||||
array_add(ops, i->Phi.edges[j]);
|
||||
}
|
||||
break;
|
||||
case irInstr_Unreachable:
|
||||
@@ -66,20 +66,22 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
|
||||
array_add(ops, i->Call.args[j]);
|
||||
}
|
||||
break;
|
||||
case irInstr_VectorExtractElement:
|
||||
array_add(ops, i->VectorExtractElement.vector);
|
||||
array_add(ops, i->VectorExtractElement.index);
|
||||
break;
|
||||
case irInstr_VectorInsertElement:
|
||||
array_add(ops, i->VectorInsertElement.vector);
|
||||
array_add(ops, i->VectorInsertElement.elem);
|
||||
array_add(ops, i->VectorInsertElement.index);
|
||||
break;
|
||||
case irInstr_VectorShuffle:
|
||||
array_add(ops, i->VectorShuffle.vector);
|
||||
break;
|
||||
// case irInstr_VectorExtractElement:
|
||||
// array_add(ops, i->VectorExtractElement.vector);
|
||||
// array_add(ops, i->VectorExtractElement.index);
|
||||
// break;
|
||||
// case irInstr_VectorInsertElement:
|
||||
// array_add(ops, i->VectorInsertElement.vector);
|
||||
// array_add(ops, i->VectorInsertElement.elem);
|
||||
// array_add(ops, i->VectorInsertElement.index);
|
||||
// break;
|
||||
// case irInstr_VectorShuffle:
|
||||
// array_add(ops, i->VectorShuffle.vector);
|
||||
// break;
|
||||
case irInstr_StartupRuntime:
|
||||
break;
|
||||
|
||||
#if 0
|
||||
case irInstr_BoundsCheck:
|
||||
array_add(ops, i->BoundsCheck.index);
|
||||
array_add(ops, i->BoundsCheck.len);
|
||||
@@ -88,6 +90,7 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
|
||||
array_add(ops, i->SliceBoundsCheck.low);
|
||||
array_add(ops, i->SliceBoundsCheck.high);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,24 +100,24 @@ void ir_opt_add_operands(irValueArray *ops, irInstr *i) {
|
||||
|
||||
void ir_opt_block_replace_pred(irBlock *b, irBlock *from, irBlock *to) {
|
||||
for_array(i, b->preds) {
|
||||
irBlock *pred = b->preds.e[i];
|
||||
irBlock *pred = b->preds[i];
|
||||
if (pred == from) {
|
||||
b->preds.e[i] = to;
|
||||
b->preds[i] = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ir_opt_block_replace_succ(irBlock *b, irBlock *from, irBlock *to) {
|
||||
for_array(i, b->succs) {
|
||||
irBlock *succ = b->succs.e[i];
|
||||
irBlock *succ = b->succs[i];
|
||||
if (succ == from) {
|
||||
b->succs.e[i] = to;
|
||||
b->succs[i] = to;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ir_opt_block_has_phi(irBlock *b) {
|
||||
return b->instrs.e[0]->Instr.kind == irInstr_Phi;
|
||||
return b->instrs[0]->Instr.kind == irInstr_Phi;
|
||||
}
|
||||
|
||||
|
||||
@@ -126,10 +129,10 @@ bool ir_opt_block_has_phi(irBlock *b) {
|
||||
|
||||
|
||||
|
||||
irValueArray ir_get_block_phi_nodes(irBlock *b) {
|
||||
irValueArray phis = {0};
|
||||
Array<irValue *> ir_get_block_phi_nodes(irBlock *b) {
|
||||
Array<irValue *> phis = {0};
|
||||
for_array(i, b->instrs) {
|
||||
irInstr *instr = &b->instrs.e[i]->Instr;
|
||||
irInstr *instr = &b->instrs[i]->Instr;
|
||||
if (instr->kind != irInstr_Phi) {
|
||||
phis = b->instrs;
|
||||
phis.count = i;
|
||||
@@ -140,22 +143,22 @@ irValueArray ir_get_block_phi_nodes(irBlock *b) {
|
||||
}
|
||||
|
||||
void ir_remove_pred(irBlock *b, irBlock *p) {
|
||||
irValueArray phis = ir_get_block_phi_nodes(b);
|
||||
Array<irValue *> phis = ir_get_block_phi_nodes(b);
|
||||
isize i = 0;
|
||||
for_array(j, b->preds) {
|
||||
irBlock *pred = b->preds.e[j];
|
||||
irBlock *pred = b->preds[j];
|
||||
if (pred != p) {
|
||||
b->preds.e[i] = b->preds.e[j];
|
||||
b->preds[i] = b->preds[j];
|
||||
for_array(k, phis) {
|
||||
irInstrPhi *phi = &phis.e[k]->Instr.Phi;
|
||||
phi->edges.e[i] = phi->edges.e[j];
|
||||
irInstrPhi *phi = &phis[k]->Instr.Phi;
|
||||
phi->edges[i] = phi->edges[j];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
b->preds.count = i;
|
||||
for_array(k, phis) {
|
||||
irInstrPhi *phi = &phis.e[k]->Instr.Phi;
|
||||
irInstrPhi *phi = &phis[k]->Instr.Phi;
|
||||
phi->edges.count = i;
|
||||
}
|
||||
|
||||
@@ -164,13 +167,13 @@ void ir_remove_pred(irBlock *b, irBlock *p) {
|
||||
void ir_remove_dead_blocks(irProcedure *proc) {
|
||||
isize j = 0;
|
||||
for_array(i, proc->blocks) {
|
||||
irBlock *b = proc->blocks.e[i];
|
||||
if (b == NULL) {
|
||||
irBlock *b = proc->blocks[i];
|
||||
if (b == nullptr) {
|
||||
continue;
|
||||
}
|
||||
// NOTE(bill): Swap order
|
||||
b->index = j;
|
||||
proc->blocks.e[j++] = b;
|
||||
b->index = cast(i32)j;
|
||||
proc->blocks[j++] = b;
|
||||
}
|
||||
proc->blocks.count = j;
|
||||
}
|
||||
@@ -180,7 +183,7 @@ void ir_mark_reachable(irBlock *b) {
|
||||
isize const BLACK = -1;
|
||||
b->index = BLACK;
|
||||
for_array(i, b->succs) {
|
||||
irBlock *succ = b->succs.e[i];
|
||||
irBlock *succ = b->succs[i];
|
||||
if (succ->index == WHITE) {
|
||||
ir_mark_reachable(succ);
|
||||
}
|
||||
@@ -191,23 +194,23 @@ void ir_remove_unreachable_blocks(irProcedure *proc) {
|
||||
isize const WHITE = 0;
|
||||
isize const BLACK = -1;
|
||||
for_array(i, proc->blocks) {
|
||||
proc->blocks.e[i]->index = WHITE;
|
||||
proc->blocks[i]->index = WHITE;
|
||||
}
|
||||
|
||||
ir_mark_reachable(proc->blocks.e[0]);
|
||||
ir_mark_reachable(proc->blocks[0]);
|
||||
|
||||
for_array(i, proc->blocks) {
|
||||
irBlock *b = proc->blocks.e[i];
|
||||
irBlock *b = proc->blocks[i];
|
||||
if (b->index == WHITE) {
|
||||
for_array(j, b->succs) {
|
||||
irBlock *c = b->succs.e[j];
|
||||
irBlock *c = b->succs[j];
|
||||
if (c->index == BLACK) {
|
||||
ir_remove_pred(c, b);
|
||||
}
|
||||
}
|
||||
// NOTE(bill): Mark as empty but don't actually free it
|
||||
// As it's been allocated with an arena
|
||||
proc->blocks.e[i] = NULL;
|
||||
proc->blocks[i] = nullptr;
|
||||
}
|
||||
}
|
||||
ir_remove_dead_blocks(proc);
|
||||
@@ -217,7 +220,7 @@ bool ir_opt_block_fusion(irProcedure *proc, irBlock *a) {
|
||||
if (a->succs.count != 1) {
|
||||
return false;
|
||||
}
|
||||
irBlock *b = a->succs.e[0];
|
||||
irBlock *b = a->succs[0];
|
||||
if (b->preds.count != 1) {
|
||||
return false;
|
||||
}
|
||||
@@ -228,21 +231,21 @@ bool ir_opt_block_fusion(irProcedure *proc, irBlock *a) {
|
||||
|
||||
array_pop(&a->instrs); // Remove branch at end
|
||||
for_array(i, b->instrs) {
|
||||
array_add(&a->instrs, b->instrs.e[i]);
|
||||
ir_set_instr_parent(b->instrs.e[i], a);
|
||||
array_add(&a->instrs, b->instrs[i]);
|
||||
ir_set_instr_parent(b->instrs[i], a);
|
||||
}
|
||||
|
||||
array_clear(&a->succs);
|
||||
for_array(i, b->succs) {
|
||||
array_add(&a->succs, b->succs.e[i]);
|
||||
array_add(&a->succs, b->succs[i]);
|
||||
}
|
||||
|
||||
// Fix preds links
|
||||
for_array(i, b->succs) {
|
||||
ir_opt_block_replace_pred(b->succs.e[i], b, a);
|
||||
ir_opt_block_replace_pred(b->succs[i], b, a);
|
||||
}
|
||||
|
||||
proc->blocks.e[b->index] = NULL;
|
||||
proc->blocks[b->index] = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -254,11 +257,11 @@ void ir_opt_blocks(irProcedure *proc) {
|
||||
while (changed) {
|
||||
changed = false;
|
||||
for_array(i, proc->blocks) {
|
||||
irBlock *b = proc->blocks.e[i];
|
||||
if (b == NULL) {
|
||||
irBlock *b = proc->blocks[i];
|
||||
if (b == nullptr) {
|
||||
continue;
|
||||
}
|
||||
GB_ASSERT(b->index == i);
|
||||
GB_ASSERT_MSG(b->index == i, "%d, %td", b->index, i);
|
||||
|
||||
if (ir_opt_block_fusion(proc, b)) {
|
||||
changed = true;
|
||||
@@ -273,21 +276,21 @@ void ir_opt_blocks(irProcedure *proc) {
|
||||
void ir_opt_build_referrers(irProcedure *proc) {
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
||||
|
||||
irValueArray ops = {0}; // NOTE(bill): Act as a buffer
|
||||
array_init_reserve(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena
|
||||
Array<irValue *> ops = {0}; // NOTE(bill): Act as a buffer
|
||||
array_init(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena
|
||||
for_array(i, proc->blocks) {
|
||||
irBlock *b = proc->blocks.e[i];
|
||||
irBlock *b = proc->blocks[i];
|
||||
for_array(j, b->instrs) {
|
||||
irValue *instr = b->instrs.e[j];
|
||||
irValue *instr = b->instrs[j];
|
||||
array_clear(&ops);
|
||||
ir_opt_add_operands(&ops, &instr->Instr);
|
||||
for_array(k, ops) {
|
||||
irValue *op = ops.e[k];
|
||||
if (op == NULL) {
|
||||
irValue *op = ops[k];
|
||||
if (op == nullptr) {
|
||||
continue;
|
||||
}
|
||||
irValueArray *refs = ir_value_referrers(op);
|
||||
if (refs != NULL) {
|
||||
Array<irValue *> *refs = ir_value_referrers(op);
|
||||
if (refs != nullptr) {
|
||||
array_add(refs, instr);
|
||||
}
|
||||
}
|
||||
@@ -322,10 +325,10 @@ i32 ir_lt_depth_first_search(irLTState *lt, irBlock *p, i32 i, irBlock **preorde
|
||||
preorder[i] = p;
|
||||
p->dom.pre = i++;
|
||||
lt->sdom[p->index] = p;
|
||||
ir_lt_link(lt, NULL, p);
|
||||
ir_lt_link(lt, nullptr, p);
|
||||
for_array(index, p->succs) {
|
||||
irBlock *q = p->succs.e[index];
|
||||
if (lt->sdom[q->index] == NULL) {
|
||||
irBlock *q = p->succs[index];
|
||||
if (lt->sdom[q->index] == nullptr) {
|
||||
lt->parent[q->index] = p;
|
||||
i = ir_lt_depth_first_search(lt, q, i, preorder);
|
||||
}
|
||||
@@ -336,7 +339,7 @@ i32 ir_lt_depth_first_search(irLTState *lt, irBlock *p, i32 i, irBlock **preorde
|
||||
irBlock *ir_lt_eval(irLTState *lt, irBlock *v) {
|
||||
irBlock *u = v;
|
||||
for (;
|
||||
lt->ancestor[v->index] != NULL;
|
||||
lt->ancestor[v->index] != nullptr;
|
||||
v = lt->ancestor[v->index]) {
|
||||
if (lt->sdom[v->index]->dom.pre < lt->sdom[u->index]->dom.pre) {
|
||||
u = v;
|
||||
@@ -354,7 +357,7 @@ irDomPrePost ir_opt_number_dom_tree(irBlock *v, i32 pre, i32 post) {
|
||||
|
||||
v->dom.pre = pre++;
|
||||
for_array(i, v->dom.children) {
|
||||
result = ir_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post);
|
||||
result = ir_opt_number_dom_tree(v->dom.children[i], result.pre, result.post);
|
||||
}
|
||||
v->dom.post = post++;
|
||||
|
||||
@@ -370,7 +373,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
|
||||
|
||||
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
|
||||
|
||||
isize n = proc->blocks.count;
|
||||
i32 n = cast(i32)proc->blocks.count;
|
||||
irBlock **buf = gb_alloc_array(proc->module->tmp_allocator, irBlock *, 5*n);
|
||||
|
||||
irLTState lt = {0};
|
||||
@@ -381,7 +384,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
|
||||
|
||||
irBlock **preorder = &buf[3*n];
|
||||
irBlock **buckets = &buf[4*n];
|
||||
irBlock *root = proc->blocks.e[0];
|
||||
irBlock *root = proc->blocks[0];
|
||||
|
||||
// Step 1 - number vertices
|
||||
i32 pre_num = ir_lt_depth_first_search(<, root, 0, preorder);
|
||||
@@ -403,7 +406,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
|
||||
// Step 2 - Compute all sdoms
|
||||
lt.sdom[w->index] = lt.parent[w->index];
|
||||
for_array(pred_index, w->preds) {
|
||||
irBlock *v = w->preds.e[pred_index];
|
||||
irBlock *v = w->preds[pred_index];
|
||||
irBlock *u = ir_lt_eval(<, v);
|
||||
if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) {
|
||||
lt.sdom[w->index] = lt.sdom[u->index];
|
||||
@@ -429,7 +432,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
|
||||
for (isize i = 1; i < n; i++) {
|
||||
irBlock *w = preorder[i];
|
||||
if (w == root) {
|
||||
w->dom.idom = NULL;
|
||||
w->dom.idom = nullptr;
|
||||
} else {
|
||||
// Weird tree relationships here!
|
||||
|
||||
@@ -438,7 +441,7 @@ void ir_opt_build_dom_tree(irProcedure *proc) {
|
||||
}
|
||||
|
||||
// Calculate children relation as inverse of idom
|
||||
if (w->dom.idom->dom.children.e == NULL) {
|
||||
if (w->dom.idom->dom.children.data == nullptr) {
|
||||
// TODO(bill): Is this good enough for memory allocations?
|
||||
array_init(&w->dom.idom->dom.children, heap_allocator());
|
||||
}
|
||||
@@ -461,13 +464,13 @@ void ir_opt_tree(irGen *s) {
|
||||
s->opt_called = true;
|
||||
|
||||
for_array(member_index, s->module.procs) {
|
||||
irProcedure *proc = s->module.procs.e[member_index];
|
||||
irProcedure *proc = s->module.procs[member_index];
|
||||
if (proc->blocks.count == 0) { // Prototype/external procedure
|
||||
continue;
|
||||
}
|
||||
|
||||
ir_opt_blocks(proc);
|
||||
#if 1
|
||||
#if 0
|
||||
ir_opt_build_referrers(proc);
|
||||
ir_opt_build_dom_tree(proc);
|
||||
|
||||
-1513
File diff suppressed because it is too large
Load Diff
+1926
File diff suppressed because it is too large
Load Diff
-289
@@ -1,289 +0,0 @@
|
||||
#if defined(__cplusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "common.c"
|
||||
#include "timings.c"
|
||||
#include "unicode.c"
|
||||
#include "build.c"
|
||||
#include "tokenizer.c"
|
||||
#include "parser.c"
|
||||
// #include "printer.c"
|
||||
#include "checker.c"
|
||||
// #include "ssa.c"
|
||||
#include "ir.c"
|
||||
#include "ir_opt.c"
|
||||
#include "ir_print.c"
|
||||
// #include "vm.c"
|
||||
|
||||
// NOTE(bill): `name` is used in debugging and profiling modes
|
||||
i32 win32_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
char cmd_line[4096] = {0};
|
||||
isize cmd_len;
|
||||
va_list va;
|
||||
gbTempArenaMemory tmp;
|
||||
String16 cmd;
|
||||
i32 exit_code = 0;
|
||||
|
||||
start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||
start_info.wShowWindow = SW_SHOW;
|
||||
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
va_start(va, fmt);
|
||||
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
|
||||
va_end(va);
|
||||
// gb_printf("%.*s\n", cast(int)cmd_len, cmd_line);
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
|
||||
cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
|
||||
|
||||
if (CreateProcessW(NULL, cmd.text,
|
||||
NULL, NULL, true, 0, NULL, NULL,
|
||||
&start_info, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
} else {
|
||||
// NOTE(bill): failed to create process
|
||||
gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
void print_usage_line(i32 indent, char *fmt, ...) {
|
||||
while (indent --> 0) {
|
||||
gb_printf_err("\t");
|
||||
}
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
gb_printf_err_va(fmt, va);
|
||||
va_end(va);
|
||||
gb_printf_err("\n");
|
||||
}
|
||||
|
||||
void usage(char *argv0) {
|
||||
print_usage_line(0, "%s is a tool for managing Odin source code", argv0);
|
||||
print_usage_line(0, "Usage:");
|
||||
print_usage_line(1, "%s command [arguments]", argv0);
|
||||
print_usage_line(0, "Commands:");
|
||||
print_usage_line(1, "build compile .odin file as executable");
|
||||
print_usage_line(1, "build_dll compile .odin file as dll");
|
||||
print_usage_line(1, "run compile and run .odin file");
|
||||
print_usage_line(1, "version print version");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Timings timings = {0};
|
||||
timings_init(&timings, str_lit("Total Time"), 128);
|
||||
// defer (timings_destroy(&timings));
|
||||
init_string_buffer_memory();
|
||||
init_scratch_memory(gb_megabytes(10));
|
||||
init_global_error_collector();
|
||||
|
||||
#if 1
|
||||
|
||||
BuildContext build_context = {0};
|
||||
init_build_context(&build_context);
|
||||
|
||||
init_universal_scope(&build_context);
|
||||
|
||||
char *init_filename = NULL;
|
||||
bool run_output = false;
|
||||
String arg1 = make_string_c(argv[1]);
|
||||
if (str_eq(arg1, str_lit("run"))) {
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
init_filename = argv[2];
|
||||
run_output = true;
|
||||
} else if (str_eq(arg1, str_lit("build_dll"))) {
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
init_filename = argv[2];
|
||||
build_context.is_dll = true;
|
||||
} else if (str_eq(arg1, str_lit("build"))) {
|
||||
if (argc != 3) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
init_filename = argv[2];
|
||||
} else if (str_eq(arg1, str_lit("version"))) {
|
||||
gb_printf("%s version %.*s", argv[0], LIT(build_context.ODIN_VERSION));
|
||||
return 0;
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO(bill): prevent compiling without a linker
|
||||
|
||||
timings_start_section(&timings, str_lit("parse files"));
|
||||
|
||||
Parser parser = {0};
|
||||
if (!init_parser(&parser)) {
|
||||
return 1;
|
||||
}
|
||||
// defer (destroy_parser(&parser));
|
||||
|
||||
if (parse_files(&parser, init_filename) != ParseFile_None) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
timings_start_section(&timings, str_lit("type check"));
|
||||
|
||||
Checker checker = {0};
|
||||
|
||||
init_checker(&checker, &parser, &build_context);
|
||||
// defer (destroy_checker(&checker));
|
||||
|
||||
check_parsed_files(&checker);
|
||||
|
||||
|
||||
#endif
|
||||
#if 0
|
||||
if (global_error_collector.count != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (checker.parser->total_token_count < 2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssa_generate(&checker.info, &build_context);
|
||||
#endif
|
||||
#if 1
|
||||
|
||||
irGen ir_gen = {0};
|
||||
if (!ir_gen_init(&ir_gen, &checker, &build_context)) {
|
||||
return 1;
|
||||
}
|
||||
// defer (ssa_gen_destroy(&ir_gen));
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm ir gen"));
|
||||
ir_gen_tree(&ir_gen);
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm ir opt tree"));
|
||||
ir_opt_tree(&ir_gen);
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm ir print"));
|
||||
print_llvm_ir(&ir_gen);
|
||||
|
||||
// prof_print_all();
|
||||
|
||||
#if 1
|
||||
timings_start_section(&timings, str_lit("llvm-opt"));
|
||||
|
||||
char const *output_name = 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);
|
||||
|
||||
i32 optimization_level = 0;
|
||||
optimization_level = gb_clamp(optimization_level, 0, 3);
|
||||
|
||||
i32 exit_code = 0;
|
||||
// For more passes arguments: http://llvm.org/docs/Passes.html
|
||||
exit_code = win32_exec_command_line_app("llvm-opt", false,
|
||||
"\"%.*sbin/opt\" \"%s\" -o \"%.*s\".bc "
|
||||
"-mem2reg "
|
||||
"-memcpyopt "
|
||||
"-die "
|
||||
// "-dse "
|
||||
// "-dce "
|
||||
// "-S "
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
output_name, LIT(output));
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
#if 1
|
||||
timings_start_section(&timings, str_lit("llvm-llc"));
|
||||
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
|
||||
exit_code = win32_exec_command_line_app("llvm-llc", false,
|
||||
"\"%.*sbin/llc\" \"%.*s.bc\" -filetype=obj -O%d "
|
||||
"%.*s "
|
||||
// "-debug-pass=Arguments "
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
LIT(output),
|
||||
optimization_level,
|
||||
LIT(build_context.llc_flags));
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
timings_start_section(&timings, str_lit("msvc-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];
|
||||
// gb_printf_err("Linking lib: %.*s\n", LIT(lib));
|
||||
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||
" \"%.*s\"", LIT(lib));
|
||||
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
||||
}
|
||||
|
||||
char *output_ext = "exe";
|
||||
char *link_settings = "";
|
||||
if (build_context.is_dll) {
|
||||
output_ext = "dll";
|
||||
link_settings = "/DLL";
|
||||
} else {
|
||||
link_settings = "/ENTRY:mainCRTStartup";
|
||||
}
|
||||
|
||||
exit_code = win32_exec_command_line_app("msvc-link", true,
|
||||
"link \"%.*s\".obj -OUT:\"%.*s.%s\" %s "
|
||||
"/defaultlib:libcmt "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(output), LIT(output), 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) {
|
||||
win32_exec_command_line_app("odin run", false, "%.*s.exe", cast(int)base_name_len, output_name);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus)
|
||||
}
|
||||
#endif
|
||||
+794
@@ -0,0 +1,794 @@
|
||||
#define USE_CUSTOM_BACKEND 0
|
||||
#define USE_THREADED_PARSER 1
|
||||
// #define NO_ARRAY_BOUNDS_CHECK
|
||||
|
||||
#include "common.cpp"
|
||||
#include "timings.cpp"
|
||||
#include "build_settings.cpp"
|
||||
#include "tokenizer.cpp"
|
||||
#include "parser.cpp"
|
||||
#include "docs.cpp"
|
||||
#include "checker.cpp"
|
||||
#include "ssa.cpp"
|
||||
#include "ir.cpp"
|
||||
#include "ir_opt.cpp"
|
||||
#include "ir_print.cpp"
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
// NOTE(bill): `name` is used in debugging and profiling modes
|
||||
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
char cmd_line[4096] = {0};
|
||||
isize cmd_len;
|
||||
va_list va;
|
||||
gbTempArenaMemory tmp;
|
||||
String16 cmd;
|
||||
i32 exit_code = 0;
|
||||
|
||||
start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
|
||||
start_info.wShowWindow = SW_SHOW;
|
||||
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
||||
start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
|
||||
|
||||
va_start(va, fmt);
|
||||
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
|
||||
va_end(va);
|
||||
|
||||
// gb_printf_err("%.*s\n", cast(int)cmd_len, cmd_line);
|
||||
|
||||
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
|
||||
|
||||
cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
|
||||
|
||||
if (CreateProcessW(nullptr, cmd.text,
|
||||
nullptr, nullptr, true, 0, nullptr, nullptr,
|
||||
&start_info, &pi)) {
|
||||
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
|
||||
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
} else {
|
||||
// NOTE(bill): failed to create process
|
||||
gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
|
||||
exit_code = -1;
|
||||
}
|
||||
|
||||
gb_temp_arena_memory_end(tmp);
|
||||
return exit_code;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||
|
||||
char cmd_line[4096] = {0};
|
||||
isize cmd_len;
|
||||
va_list va;
|
||||
String cmd;
|
||||
i32 exit_code = 0;
|
||||
|
||||
va_start(va, fmt);
|
||||
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
|
||||
va_end(va);
|
||||
cmd = make_string(cast(u8 *)&cmd_line, cmd_len-1);
|
||||
|
||||
exit_code = system(&cmd_line[0]);
|
||||
|
||||
// pid_t pid = fork();
|
||||
// int status = 0;
|
||||
|
||||
// if(pid == 0) {
|
||||
// // in child, pid == 0.
|
||||
// int ret = execvp(cmd.text, (char* const*) cmd.text);
|
||||
|
||||
// if(ret == -1) {
|
||||
// gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
|
||||
|
||||
// // we're in the child, so returning won't do us any good -- just quit.
|
||||
// exit(-1);
|
||||
// }
|
||||
|
||||
// // unreachable
|
||||
// abort();
|
||||
// } else {
|
||||
// // wait for child to finish, then we can continue cleanup
|
||||
|
||||
// int s = 0;
|
||||
// waitpid(pid, &s, 0);
|
||||
|
||||
// status = WEXITSTATUS(s);
|
||||
// }
|
||||
|
||||
// exit_code = status;
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
Array<String> setup_args(int argc, char **argv) {
|
||||
Array<String> args = {};
|
||||
gbAllocator a = heap_allocator();
|
||||
int i;
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
int wargc = 0;
|
||||
wchar_t **wargv = command_line_to_wargv(GetCommandLineW(), &wargc);
|
||||
array_init(&args, a, wargc);
|
||||
for (i = 0; i < wargc; i++) {
|
||||
wchar_t *warg = wargv[i];
|
||||
isize wlen = string16_len(warg);
|
||||
String16 wstr = make_string16(warg, wlen);
|
||||
String arg = string16_to_string(a, wstr);
|
||||
if (arg.len > 0) {
|
||||
array_add(&args, arg);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
array_init(&args, a, argc);
|
||||
for (i = 0; i < argc; i++) {
|
||||
String arg = make_string_c(argv[i]);
|
||||
if (arg.len > 0) {
|
||||
array_add(&args, arg);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return args;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void print_usage_line(i32 indent, char *fmt, ...) {
|
||||
while (indent --> 0) {
|
||||
gb_printf_err("\t");
|
||||
}
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
gb_printf_err_va(fmt, va);
|
||||
va_end(va);
|
||||
gb_printf_err("\n");
|
||||
}
|
||||
|
||||
void usage(String argv0) {
|
||||
print_usage_line(0, "%.*s is a tool for managing Odin source code", LIT(argv0));
|
||||
print_usage_line(0, "Usage:");
|
||||
print_usage_line(1, "%.*s command [arguments]", LIT(argv0));
|
||||
print_usage_line(0, "Commands:");
|
||||
print_usage_line(1, "build compile .odin file as executable");
|
||||
print_usage_line(1, "build_dll compile .odin file as dll");
|
||||
print_usage_line(1, "run compile and run .odin file");
|
||||
print_usage_line(1, "docs generate documentation for a .odin file");
|
||||
print_usage_line(1, "version print version");
|
||||
}
|
||||
|
||||
|
||||
|
||||
enum BuildFlagKind {
|
||||
BuildFlag_Invalid,
|
||||
|
||||
BuildFlag_OptimizationLevel,
|
||||
BuildFlag_ShowTimings,
|
||||
BuildFlag_ThreadCount,
|
||||
BuildFlag_KeepTempFiles,
|
||||
|
||||
BuildFlag_COUNT,
|
||||
};
|
||||
|
||||
enum BuildFlagParamKind {
|
||||
BuildFlagParam_None,
|
||||
|
||||
BuildFlagParam_Boolean,
|
||||
BuildFlagParam_Integer,
|
||||
BuildFlagParam_Float,
|
||||
BuildFlagParam_String,
|
||||
|
||||
BuildFlagParam_COUNT,
|
||||
};
|
||||
|
||||
struct BuildFlag {
|
||||
BuildFlagKind kind;
|
||||
String name;
|
||||
BuildFlagParamKind param_kind;
|
||||
};
|
||||
|
||||
|
||||
void add_flag(Array<BuildFlag> *build_flags, BuildFlagKind kind, String name, BuildFlagParamKind param_kind) {
|
||||
BuildFlag flag = {kind, name, param_kind};
|
||||
array_add(build_flags, flag);
|
||||
}
|
||||
|
||||
bool parse_build_flags(Array<String> args) {
|
||||
Array<BuildFlag> build_flags = {};
|
||||
array_init(&build_flags, heap_allocator(), BuildFlag_COUNT);
|
||||
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
|
||||
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer);
|
||||
add_flag(&build_flags, BuildFlag_KeepTempFiles, str_lit("keep-temp-files"), BuildFlagParam_None);
|
||||
|
||||
|
||||
Array<String> flag_args = args;
|
||||
flag_args.data += 3;
|
||||
flag_args.count -= 3;
|
||||
|
||||
bool set_flags[BuildFlag_COUNT] = {};
|
||||
|
||||
bool bad_flags = false;
|
||||
for_array(i, flag_args) {
|
||||
String flag = flag_args[i];
|
||||
if (flag[0] != '-') {
|
||||
gb_printf_err("Invalid flag: %.*s\n", LIT(flag));
|
||||
continue;
|
||||
}
|
||||
String name = substring(flag, 1, flag.len);
|
||||
isize end = 0;
|
||||
for (; end < name.len; end++) {
|
||||
if (name[end] == '=') break;
|
||||
}
|
||||
name = substring(name, 0, end);
|
||||
String param = {};
|
||||
if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
|
||||
|
||||
bool found = false;
|
||||
for_array(build_flag_index, build_flags) {
|
||||
BuildFlag bf = build_flags[build_flag_index];
|
||||
if (bf.name == name) {
|
||||
found = true;
|
||||
if (set_flags[bf.kind]) {
|
||||
gb_printf_err("Previous flag set: `%.*s`\n", LIT(name));
|
||||
bad_flags = true;
|
||||
} else {
|
||||
ExactValue value = {};
|
||||
bool ok = false;
|
||||
if (bf.param_kind == BuildFlagParam_None) {
|
||||
if (param.len == 0) {
|
||||
ok = true;
|
||||
} else {
|
||||
gb_printf_err("Flag `%.*s` was not expecting a parameter `%.*s`\n", LIT(name), LIT(param));
|
||||
bad_flags = true;
|
||||
}
|
||||
} else if (param.len == 0) {
|
||||
gb_printf_err("Flag missing for `%.*s`\n", LIT(name));
|
||||
bad_flags = true;
|
||||
} else {
|
||||
ok = true;
|
||||
switch (bf.param_kind) {
|
||||
default: ok = false; break;
|
||||
case BuildFlagParam_Boolean: {
|
||||
if (param == "t") {
|
||||
value = exact_value_bool(true);
|
||||
} else if (param == "T") {
|
||||
value = exact_value_bool(true);
|
||||
} else if (param == "true") {
|
||||
value = exact_value_bool(true);
|
||||
} else if (param == "TRUE") {
|
||||
value = exact_value_bool(true);
|
||||
} else if (param == "1") {
|
||||
value = exact_value_bool(true);
|
||||
} else if (param == "f") {
|
||||
value = exact_value_bool(false);
|
||||
} else if (param == "F") {
|
||||
value = exact_value_bool(false);
|
||||
} else if (param == "false") {
|
||||
value = exact_value_bool(false);
|
||||
} else if (param == "FALSE") {
|
||||
value = exact_value_bool(false);
|
||||
} else if (param == "0") {
|
||||
value = exact_value_bool(false);
|
||||
} else {
|
||||
gb_printf_err("Invalid flag parameter for `%.*s` = `%.*s`\n", LIT(name), LIT(param));
|
||||
}
|
||||
} break;
|
||||
case BuildFlagParam_Integer:
|
||||
value = exact_value_integer_from_string(param);
|
||||
break;
|
||||
case BuildFlagParam_Float:
|
||||
value = exact_value_float_from_string(param);
|
||||
break;
|
||||
case BuildFlagParam_String:
|
||||
value = exact_value_string(param);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
switch (bf.param_kind) {
|
||||
case BuildFlagParam_None:
|
||||
if (value.kind != ExactValue_Invalid) {
|
||||
gb_printf_err("%.*s expected no value, got %.*s", LIT(name), LIT(param));
|
||||
bad_flags = true;
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case BuildFlagParam_Boolean:
|
||||
if (value.kind != ExactValue_Bool) {
|
||||
gb_printf_err("%.*s expected a boolean, got %.*s", LIT(name), LIT(param));
|
||||
bad_flags = true;
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case BuildFlagParam_Integer:
|
||||
if (value.kind != ExactValue_Integer) {
|
||||
gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
|
||||
bad_flags = true;
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case BuildFlagParam_Float:
|
||||
if (value.kind != ExactValue_Float) {
|
||||
gb_printf_err("%.*s expected a floating pointer number, got %.*s", LIT(name), LIT(param));
|
||||
bad_flags = true;
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
case BuildFlagParam_String:
|
||||
if (value.kind != ExactValue_String) {
|
||||
gb_printf_err("%.*s expected a string, got %.*s", LIT(name), LIT(param));
|
||||
bad_flags = true;
|
||||
ok = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (ok) switch (bf.kind) {
|
||||
case BuildFlag_OptimizationLevel:
|
||||
GB_ASSERT(value.kind == ExactValue_Integer);
|
||||
build_context.optimization_level = cast(i32)i128_to_i64(value.value_integer);
|
||||
break;
|
||||
case BuildFlag_ShowTimings:
|
||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||
build_context.show_timings = true;
|
||||
break;
|
||||
case BuildFlag_ThreadCount: {
|
||||
GB_ASSERT(value.kind == ExactValue_Integer);
|
||||
isize count = cast(isize)i128_to_i64(value.value_integer);
|
||||
if (count <= 0) {
|
||||
gb_printf_err("%.*s expected a positive non-zero number, got %.*s", LIT(name), LIT(param));
|
||||
build_context.thread_count = 0;
|
||||
} else {
|
||||
build_context.thread_count = count;
|
||||
}
|
||||
} break;
|
||||
case BuildFlag_KeepTempFiles:
|
||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||
build_context.keep_temp_files = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
set_flags[bf.kind] = ok;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
gb_printf_err("Unknown flag: `%.*s`\n", LIT(name));
|
||||
bad_flags = true;
|
||||
}
|
||||
}
|
||||
|
||||
return !bad_flags;
|
||||
}
|
||||
|
||||
|
||||
void show_timings(Checker *c, Timings *t) {
|
||||
Parser *p = c->parser;
|
||||
isize lines = p->total_line_count;
|
||||
isize tokens = p->total_token_count;
|
||||
isize files = p->files.count;
|
||||
{
|
||||
timings_print_all(t);
|
||||
gb_printf("\n");
|
||||
gb_printf("Total Lines - %td\n", lines);
|
||||
gb_printf("Total Tokens - %td\n", tokens);
|
||||
gb_printf("Total Files - %td\n", files);
|
||||
gb_printf("\n");
|
||||
}
|
||||
{
|
||||
TimeStamp ts = t->sections[0];
|
||||
GB_ASSERT(ts.label == "parse files");
|
||||
f64 parse_time = time_stamp_as_second(ts, t->freq);
|
||||
gb_printf("Parse pass\n");
|
||||
gb_printf("LOC/s - %.3f\n", cast(f64)lines/parse_time);
|
||||
gb_printf("us/LOC - %.3f\n", 1.0e6*parse_time/cast(f64)lines);
|
||||
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/parse_time);
|
||||
gb_printf("us/Token - %.3f\n", 1.0e6*parse_time/cast(f64)tokens);
|
||||
gb_printf("\n");
|
||||
}
|
||||
{
|
||||
f64 total_time = t->total_time_seconds;
|
||||
gb_printf("Total pass\n");
|
||||
gb_printf("LOC/s - %.3f\n", cast(f64)lines/total_time);
|
||||
gb_printf("us/LOC - %.3f\n", 1.0e6*total_time/cast(f64)lines);
|
||||
gb_printf("Tokens/s - %.3f\n", cast(f64)tokens/total_time);
|
||||
gb_printf("us/Token - %.3f\n", 1.0e6*total_time/cast(f64)tokens);
|
||||
gb_printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void remove_temp_files(String output_base) {
|
||||
if (build_context.keep_temp_files) return;
|
||||
|
||||
Array<u8> data = {};
|
||||
array_init_count(&data, heap_allocator(), output_base.len + 10);
|
||||
defer (array_free(&data));
|
||||
|
||||
isize n = output_base.len;
|
||||
gb_memcopy(data.data, output_base.text, n);
|
||||
#define EXT_REMOVE(s) do { \
|
||||
gb_memcopy(data.data+n, s, gb_size_of(s)); \
|
||||
gb_file_remove(cast(char *)data.data); \
|
||||
} while (0)
|
||||
EXT_REMOVE(".ll");
|
||||
EXT_REMOVE(".bc");
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
EXT_REMOVE(".obj");
|
||||
#else
|
||||
EXT_REMOVE(".o");
|
||||
#endif
|
||||
|
||||
#undef EXT_REMOVE
|
||||
}
|
||||
|
||||
int main(int arg_count, char **arg_ptr) {
|
||||
if (arg_count < 2) {
|
||||
usage(make_string_c(arg_ptr[0]));
|
||||
return 1;
|
||||
}
|
||||
|
||||
Timings timings = {0};
|
||||
timings_init(&timings, str_lit("Total Time"), 128);
|
||||
defer (timings_destroy(&timings));
|
||||
init_string_buffer_memory();
|
||||
init_scratch_memory(gb_megabytes(10));
|
||||
init_global_error_collector();
|
||||
|
||||
Array<String> args = setup_args(arg_count, arg_ptr);
|
||||
|
||||
|
||||
#if 1
|
||||
|
||||
String init_filename = {};
|
||||
bool run_output = false;
|
||||
if (args[1] == "run") {
|
||||
if (args.count < 3) {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
}
|
||||
init_filename = args[2];
|
||||
run_output = true;
|
||||
} else if (args[1] == "build_dll") {
|
||||
if (args.count < 3) {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
}
|
||||
init_filename = args[2];
|
||||
build_context.is_dll = true;
|
||||
} else if (args[1] == "build") {
|
||||
if (args.count < 3) {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
}
|
||||
init_filename = args[2];
|
||||
} else if (args[1] == "docs") {
|
||||
if (args.count < 3) {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
init_filename = args[2];
|
||||
build_context.generate_docs = true;
|
||||
#if 1
|
||||
print_usage_line(0, "Documentation generation is not yet supported");
|
||||
return 1;
|
||||
#endif
|
||||
} else if (args[1] == "version") {
|
||||
gb_printf("%.*s version %.*s\n", LIT(args[0]), LIT(ODIN_VERSION));
|
||||
return 0;
|
||||
} else {
|
||||
usage(args[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!parse_build_flags(args)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
init_build_context();
|
||||
if (build_context.word_size == 4) {
|
||||
print_usage_line(0, "%s 32-bit is not yet supported", args[0]);
|
||||
return 1;
|
||||
}
|
||||
init_universal_scope();
|
||||
|
||||
// TODO(bill): prevent compiling without a linker
|
||||
|
||||
timings_start_section(&timings, str_lit("parse files"));
|
||||
|
||||
Parser parser = {0};
|
||||
if (!init_parser(&parser)) {
|
||||
return 1;
|
||||
}
|
||||
defer (destroy_parser(&parser));
|
||||
|
||||
if (parse_files(&parser, init_filename) != ParseFile_None) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (build_context.generate_docs) {
|
||||
generate_documentation(&parser);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 1
|
||||
timings_start_section(&timings, str_lit("type check"));
|
||||
|
||||
Checker checker = {0};
|
||||
|
||||
init_checker(&checker, &parser);
|
||||
defer (destroy_checker(&checker));
|
||||
|
||||
check_parsed_files(&checker);
|
||||
|
||||
|
||||
#endif
|
||||
#if defined(USE_CUSTOM_BACKEND) && USE_CUSTOM_BACKEND
|
||||
if (global_error_collector.count != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (checker.parser->total_token_count < 2) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ssa_generate(&parser, &checker.info)) {
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
irGen ir_gen = {0};
|
||||
if (!ir_gen_init(&ir_gen, &checker)) {
|
||||
return 1;
|
||||
}
|
||||
defer (ir_gen_destroy(&ir_gen));
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm ir gen"));
|
||||
ir_gen_tree(&ir_gen);
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm ir opt tree"));
|
||||
ir_opt_tree(&ir_gen);
|
||||
|
||||
timings_start_section(&timings, str_lit("llvm ir print"));
|
||||
print_llvm_ir(&ir_gen);
|
||||
|
||||
// prof_print_all();
|
||||
|
||||
#if 1
|
||||
timings_start_section(&timings, str_lit("llvm-opt"));
|
||||
|
||||
String output_name = ir_gen.output_name;
|
||||
String output_base = ir_gen.output_base;
|
||||
int base_name_len = cast(int)output_base.len;
|
||||
|
||||
build_context.optimization_level = gb_clamp(build_context.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\".ll -o \"%.*s\".bc %.*s "
|
||||
"-mem2reg "
|
||||
"-memcpyopt "
|
||||
"-die "
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
LIT(output_base), LIT(output_base),
|
||||
LIT(build_context.opt_flags));
|
||||
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 %.*s "
|
||||
"-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
|
||||
"",
|
||||
LIT(output_base), LIT(output_base),
|
||||
LIT(build_context.opt_flags));
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
timings_start_section(&timings, str_lit("llvm-llc"));
|
||||
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
|
||||
exit_code = system_exec_command_line_app("llvm-llc", false,
|
||||
"\"%.*sbin/llc\" \"%.*s.bc\" -filetype=obj -O%d "
|
||||
"%.*s "
|
||||
// "-debug-pass=Arguments "
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
LIT(output_base),
|
||||
build_context.optimization_level,
|
||||
LIT(build_context.llc_flags));
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
timings_start_section(&timings, str_lit("msvc-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[i];
|
||||
// gb_printf_err("Linking lib: %.*s\n", LIT(lib));
|
||||
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
|
||||
" \"%.*s\"", LIT(lib));
|
||||
lib_str = gb_string_appendc(lib_str, lib_str_buf);
|
||||
}
|
||||
|
||||
char *output_ext = "exe";
|
||||
char *link_settings = "";
|
||||
if (build_context.is_dll) {
|
||||
output_ext = "dll";
|
||||
link_settings = "/DLL";
|
||||
} else {
|
||||
link_settings = "/ENTRY:mainCRTStartup";
|
||||
}
|
||||
|
||||
exit_code = system_exec_command_line_app("msvc-link", true,
|
||||
"link \"%.*s\".obj -OUT:\"%.*s.%s\" %s "
|
||||
"/defaultlib:libcmt "
|
||||
// "/nodefaultlib "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
" %.*s "
|
||||
" %s "
|
||||
"",
|
||||
LIT(output_base), LIT(output_base), output_ext,
|
||||
lib_str, LIT(build_context.link_flags),
|
||||
link_settings
|
||||
);
|
||||
if (exit_code != 0) {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
if (build_context.show_timings) {
|
||||
show_timings(&checker, &timings);
|
||||
}
|
||||
|
||||
remove_temp_files(output_base);
|
||||
|
||||
if (run_output) {
|
||||
system_exec_command_line_app("odin run", false, "%.*s.exe", LIT(output_base));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// 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 -relocation-model=pic -O%d "
|
||||
"%.*s "
|
||||
// "-debug-pass=Arguments "
|
||||
"",
|
||||
LIT(output_base),
|
||||
build_context.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[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[0] == '-' && lib[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;
|
||||
}
|
||||
|
||||
if (build_context.show_timings) {
|
||||
show_timings(&checker, &timings);
|
||||
}
|
||||
|
||||
remove_temp_files(output_base);
|
||||
|
||||
if (run_output) {
|
||||
system_exec_command_line_app("odin run", false, "%.*s", LIT(output_base));
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,358 +0,0 @@
|
||||
/*
|
||||
Example of usage:
|
||||
|
||||
#define MAP_TYPE String
|
||||
#define MAP_PROC map_string_
|
||||
#define MAP_NAME MapString
|
||||
#include "map.c"
|
||||
*/
|
||||
|
||||
#ifndef MAP_UTIL_STUFF
|
||||
#define MAP_UTIL_STUFF
|
||||
// NOTE(bill): This util stuff is the same for every `Map`
|
||||
typedef struct MapFindResult {
|
||||
isize hash_index;
|
||||
isize entry_prev;
|
||||
isize entry_index;
|
||||
} MapFindResult;
|
||||
|
||||
typedef enum HashKeyKind {
|
||||
HashKey_Default,
|
||||
HashKey_String,
|
||||
HashKey_Pointer,
|
||||
} HashKeyKind;
|
||||
|
||||
typedef struct HashKey {
|
||||
HashKeyKind kind;
|
||||
u64 key;
|
||||
union {
|
||||
String string; // if String, s.len > 0
|
||||
void * ptr;
|
||||
};
|
||||
} HashKey;
|
||||
|
||||
gb_inline HashKey hashing_proc(void const *data, isize len) {
|
||||
HashKey h = {HashKey_Default};
|
||||
h.kind = HashKey_Default;
|
||||
// h.key = gb_murmur64(data, len);
|
||||
h.key = gb_fnv64a(data, len);
|
||||
return h;
|
||||
}
|
||||
|
||||
gb_inline HashKey hash_string(String s) {
|
||||
HashKey h = hashing_proc(s.text, s.len);
|
||||
h.kind = HashKey_String;
|
||||
h.string = s;
|
||||
return h;
|
||||
}
|
||||
|
||||
gb_inline HashKey hash_pointer(void *ptr) {
|
||||
HashKey h = {HashKey_Default};
|
||||
h.key = cast(u64)cast(uintptr)ptr;
|
||||
h.ptr = ptr;
|
||||
h.kind = HashKey_Default;
|
||||
return h;
|
||||
}
|
||||
|
||||
bool hash_key_equal(HashKey a, HashKey b) {
|
||||
if (a.key == b.key) {
|
||||
// NOTE(bill): If two string's hashes collide, compare the strings themselves
|
||||
if (a.kind == HashKey_String) {
|
||||
if (b.kind == HashKey_String) {
|
||||
return str_eq(a.string, b.string);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define _J2_IND(a, b) a##b
|
||||
#define _J2(a, b) _J2_IND(a, b)
|
||||
|
||||
/*
|
||||
MAP_TYPE - Entry type
|
||||
MAP_PROC - Function prefix (e.g. entity_map_)
|
||||
MAP_NAME - Name of Map (e.g. EntityMap)
|
||||
*/
|
||||
#define MAP_ENTRY _J2(MAP_NAME,Entry)
|
||||
|
||||
typedef struct MAP_ENTRY {
|
||||
HashKey key;
|
||||
isize next;
|
||||
MAP_TYPE value;
|
||||
} MAP_ENTRY;
|
||||
|
||||
typedef struct MAP_NAME {
|
||||
Array(isize) hashes;
|
||||
Array(MAP_ENTRY) entries;
|
||||
} MAP_NAME;
|
||||
|
||||
void _J2(MAP_PROC,init) (MAP_NAME *h, gbAllocator a);
|
||||
void _J2(MAP_PROC,init_with_reserve)(MAP_NAME *h, gbAllocator a, isize capacity);
|
||||
void _J2(MAP_PROC,destroy) (MAP_NAME *h);
|
||||
MAP_TYPE *_J2(MAP_PROC,get) (MAP_NAME *h, HashKey key);
|
||||
void _J2(MAP_PROC,set) (MAP_NAME *h, HashKey key, MAP_TYPE value);
|
||||
void _J2(MAP_PROC,remove) (MAP_NAME *h, HashKey key);
|
||||
void _J2(MAP_PROC,clear) (MAP_NAME *h);
|
||||
void _J2(MAP_PROC,grow) (MAP_NAME *h);
|
||||
void _J2(MAP_PROC,rehash) (MAP_NAME *h, isize new_count);
|
||||
|
||||
// Mutlivalued map procedure
|
||||
MAP_ENTRY *_J2(MAP_PROC,multi_find_first)(MAP_NAME *h, HashKey key);
|
||||
MAP_ENTRY *_J2(MAP_PROC,multi_find_next) (MAP_NAME *h, MAP_ENTRY *e);
|
||||
|
||||
isize _J2(MAP_PROC,multi_count) (MAP_NAME *h, HashKey key);
|
||||
void _J2(MAP_PROC,multi_get_all) (MAP_NAME *h, HashKey key, MAP_TYPE *items);
|
||||
void _J2(MAP_PROC,multi_insert) (MAP_NAME *h, HashKey key, MAP_TYPE value);
|
||||
void _J2(MAP_PROC,multi_remove) (MAP_NAME *h, HashKey key, MAP_ENTRY *e);
|
||||
void _J2(MAP_PROC,multi_remove_all)(MAP_NAME *h, HashKey key);
|
||||
|
||||
|
||||
|
||||
gb_inline void _J2(MAP_PROC,init)(MAP_NAME *h, gbAllocator a) {
|
||||
array_init(&h->hashes, a);
|
||||
array_init(&h->entries, a);
|
||||
}
|
||||
|
||||
gb_inline void _J2(MAP_PROC,init_with_reserve)(MAP_NAME *h, gbAllocator a, isize capacity) {
|
||||
array_init_reserve(&h->hashes, a, capacity);
|
||||
array_init_reserve(&h->entries, a, capacity);
|
||||
}
|
||||
|
||||
gb_inline void _J2(MAP_PROC,destroy)(MAP_NAME *h) {
|
||||
array_free(&h->entries);
|
||||
array_free(&h->hashes);
|
||||
}
|
||||
|
||||
gb_internal isize _J2(MAP_PROC,_add_entry)(MAP_NAME *h, HashKey key) {
|
||||
MAP_ENTRY e = {0};
|
||||
e.key = key;
|
||||
e.next = -1;
|
||||
array_add(&h->entries, e);
|
||||
return h->entries.count-1;
|
||||
}
|
||||
|
||||
gb_internal MapFindResult _J2(MAP_PROC,_find)(MAP_NAME *h, HashKey key) {
|
||||
MapFindResult fr = {-1, -1, -1};
|
||||
if (h->hashes.count > 0) {
|
||||
fr.hash_index = key.key % h->hashes.count;
|
||||
fr.entry_index = h->hashes.e[fr.hash_index];
|
||||
while (fr.entry_index >= 0) {
|
||||
if (hash_key_equal(h->entries.e[fr.entry_index].key, key)) {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = h->entries.e[fr.entry_index].next;
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
gb_internal MapFindResult _J2(MAP_PROC,_find_from_entry)(MAP_NAME *h, MAP_ENTRY *e) {
|
||||
MapFindResult fr = {-1, -1, -1};
|
||||
if (h->hashes.count > 0) {
|
||||
fr.hash_index = e->key.key % h->hashes.count;
|
||||
fr.entry_index = h->hashes.e[fr.hash_index];
|
||||
while (fr.entry_index >= 0) {
|
||||
if (&h->entries.e[fr.entry_index] == e) {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = h->entries.e[fr.entry_index].next;
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
|
||||
gb_internal b32 _J2(MAP_PROC,_full)(MAP_NAME *h) {
|
||||
return 0.75f * h->hashes.count <= h->entries.count;
|
||||
}
|
||||
|
||||
gb_inline void _J2(MAP_PROC,grow)(MAP_NAME *h) {
|
||||
isize new_count = ARRAY_GROW_FORMULA(h->entries.count);
|
||||
_J2(MAP_PROC,rehash)(h, new_count);
|
||||
}
|
||||
|
||||
void _J2(MAP_PROC,rehash)(MAP_NAME *h, isize new_count) {
|
||||
isize i, j;
|
||||
MAP_NAME nh = {0};
|
||||
_J2(MAP_PROC,init)(&nh, h->hashes.allocator);
|
||||
array_resize(&nh.hashes, new_count);
|
||||
array_reserve(&nh.entries, h->entries.count);
|
||||
for (i = 0; i < new_count; i++) {
|
||||
nh.hashes.e[i] = -1;
|
||||
}
|
||||
for (i = 0; i < h->entries.count; i++) {
|
||||
MAP_ENTRY *e = &h->entries.e[i];
|
||||
MapFindResult fr;
|
||||
if (nh.hashes.count == 0) {
|
||||
_J2(MAP_PROC,grow)(&nh);
|
||||
}
|
||||
fr = _J2(MAP_PROC,_find)(&nh, e->key);
|
||||
j = _J2(MAP_PROC,_add_entry)(&nh, e->key);
|
||||
if (fr.entry_prev < 0) {
|
||||
nh.hashes.e[fr.hash_index] = j;
|
||||
} else {
|
||||
nh.entries.e[fr.entry_prev].next = j;
|
||||
}
|
||||
nh.entries.e[j].next = fr.entry_index;
|
||||
nh.entries.e[j].value = e->value;
|
||||
if (_J2(MAP_PROC,_full)(&nh)) {
|
||||
_J2(MAP_PROC,grow)(&nh);
|
||||
}
|
||||
}
|
||||
_J2(MAP_PROC,destroy)(h);
|
||||
*h = nh;
|
||||
}
|
||||
|
||||
gb_inline MAP_TYPE *_J2(MAP_PROC,get)(MAP_NAME *h, HashKey key) {
|
||||
isize index = _J2(MAP_PROC,_find)(h, key).entry_index;
|
||||
if (index >= 0) {
|
||||
return &h->entries.e[index].value;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void _J2(MAP_PROC,set)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
|
||||
isize index;
|
||||
MapFindResult fr;
|
||||
if (h->hashes.count == 0)
|
||||
_J2(MAP_PROC,grow)(h);
|
||||
fr = _J2(MAP_PROC,_find)(h, key);
|
||||
if (fr.entry_index >= 0) {
|
||||
index = fr.entry_index;
|
||||
} else {
|
||||
index = _J2(MAP_PROC,_add_entry)(h, key);
|
||||
if (fr.entry_prev >= 0) {
|
||||
h->entries.e[fr.entry_prev].next = index;
|
||||
} else {
|
||||
h->hashes.e[fr.hash_index] = index;
|
||||
}
|
||||
}
|
||||
h->entries.e[index].value = value;
|
||||
|
||||
if (_J2(MAP_PROC,_full)(h)) {
|
||||
_J2(MAP_PROC,grow)(h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void _J2(MAP_PROC,_erase)(MAP_NAME *h, MapFindResult fr) {
|
||||
MapFindResult last;
|
||||
if (fr.entry_prev < 0) {
|
||||
h->hashes.e[fr.hash_index] = h->entries.e[fr.entry_index].next;
|
||||
} else {
|
||||
h->entries.e[fr.entry_prev].next = h->entries.e[fr.entry_index].next;
|
||||
}
|
||||
if (fr.entry_index == h->entries.count-1) {
|
||||
array_pop(&h->entries);
|
||||
return;
|
||||
}
|
||||
h->entries.e[fr.entry_index] = h->entries.e[h->entries.count-1];
|
||||
last = _J2(MAP_PROC,_find)(h, h->entries.e[fr.entry_index].key);
|
||||
if (last.entry_prev >= 0) {
|
||||
h->entries.e[last.entry_prev].next = fr.entry_index;
|
||||
} else {
|
||||
h->hashes.e[last.hash_index] = fr.entry_index;
|
||||
}
|
||||
}
|
||||
|
||||
void _J2(MAP_PROC,remove)(MAP_NAME *h, HashKey key) {
|
||||
MapFindResult fr = _J2(MAP_PROC,_find)(h, key);
|
||||
if (fr.entry_index >= 0) {
|
||||
_J2(MAP_PROC,_erase)(h, fr);
|
||||
}
|
||||
}
|
||||
|
||||
gb_inline void _J2(MAP_PROC,clear)(MAP_NAME *h) {
|
||||
array_clear(&h->hashes);
|
||||
array_clear(&h->entries);
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
MAP_ENTRY *_J2(MAP_PROC,multi_find_first)(MAP_NAME *h, HashKey key) {
|
||||
isize i = _J2(MAP_PROC,_find)(h, key).entry_index;
|
||||
if (i < 0) {
|
||||
return NULL;
|
||||
}
|
||||
return &h->entries.e[i];
|
||||
}
|
||||
|
||||
MAP_ENTRY *_J2(MAP_PROC,multi_find_next)(MAP_NAME *h, MAP_ENTRY *e) {
|
||||
isize i = e->next;
|
||||
while (i >= 0) {
|
||||
if (hash_key_equal(h->entries.e[i].key, e->key)) {
|
||||
return &h->entries.e[i];
|
||||
}
|
||||
i = h->entries.e[i].next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
isize _J2(MAP_PROC,multi_count)(MAP_NAME *h, HashKey key) {
|
||||
isize count = 0;
|
||||
MAP_ENTRY *e = _J2(MAP_PROC,multi_find_first)(h, key);
|
||||
while (e != NULL) {
|
||||
count++;
|
||||
e = _J2(MAP_PROC,multi_find_next)(h, e);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
void _J2(MAP_PROC,multi_get_all)(MAP_NAME *h, HashKey key, MAP_TYPE *items) {
|
||||
isize i = 0;
|
||||
MAP_ENTRY *e = _J2(MAP_PROC,multi_find_first)(h, key);
|
||||
while (e != NULL) {
|
||||
items[i++] = e->value;
|
||||
e = _J2(MAP_PROC,multi_find_next)(h, e);
|
||||
}
|
||||
}
|
||||
|
||||
void _J2(MAP_PROC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
|
||||
MapFindResult fr;
|
||||
isize i;
|
||||
if (h->hashes.count == 0) {
|
||||
_J2(MAP_PROC,grow)(h);
|
||||
}
|
||||
// Make
|
||||
fr = _J2(MAP_PROC,_find)(h, key);
|
||||
i = _J2(MAP_PROC,_add_entry)(h, key);
|
||||
if (fr.entry_prev < 0) {
|
||||
h->hashes.e[fr.hash_index] = i;
|
||||
} else {
|
||||
h->entries.e[fr.entry_prev].next = i;
|
||||
}
|
||||
h->entries.e[i].next = fr.entry_index;
|
||||
h->entries.e[i].value = value;
|
||||
// Grow if needed
|
||||
if (_J2(MAP_PROC,_full)(h)) {
|
||||
_J2(MAP_PROC,grow)(h);
|
||||
}
|
||||
}
|
||||
|
||||
void _J2(MAP_PROC,multi_remove)(MAP_NAME *h, HashKey key, MAP_ENTRY *e) {
|
||||
MapFindResult fr = _J2(MAP_PROC,_find_from_entry)(h, e);
|
||||
if (fr.entry_index >= 0) {
|
||||
_J2(MAP_PROC,_erase)(h, fr);
|
||||
}
|
||||
}
|
||||
|
||||
void _J2(MAP_PROC,multi_remove_all)(MAP_NAME *h, HashKey key) {
|
||||
while (_J2(MAP_PROC,get)(h, key) != NULL) {
|
||||
_J2(MAP_PROC,remove)(h, key);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#undef _J2
|
||||
#undef MAP_TYPE
|
||||
#undef MAP_PROC
|
||||
#undef MAP_NAME
|
||||
#undef MAP_ENTRY
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
// A `Map` is an unordered hash table which can allow for a key to point to multiple values
|
||||
// with the use of the `multi_*` procedures.
|
||||
// TODO(bill): I should probably allow the `multi_map_*` stuff to be #ifdefed out
|
||||
|
||||
#ifndef MAP_UTIL_STUFF
|
||||
#define MAP_UTIL_STUFF
|
||||
// NOTE(bill): This util stuff is the same for every `Map`
|
||||
struct MapFindResult {
|
||||
isize hash_index;
|
||||
isize entry_prev;
|
||||
isize entry_index;
|
||||
};
|
||||
|
||||
enum HashKeyKind {
|
||||
HashKey_Default,
|
||||
HashKey_String,
|
||||
HashKey_Ptr,
|
||||
HashKey_PtrAndId,
|
||||
};
|
||||
|
||||
struct PtrAndId {
|
||||
void *ptr;
|
||||
u32 id;
|
||||
};
|
||||
|
||||
struct HashKey {
|
||||
HashKeyKind kind;
|
||||
// u128 key;
|
||||
u64 key;
|
||||
union {
|
||||
String string; // if String, s.len > 0
|
||||
void * ptr;
|
||||
PtrAndId ptr_and_id;
|
||||
};
|
||||
};
|
||||
|
||||
gb_inline HashKey hashing_proc(void const *data, isize len) {
|
||||
HashKey h = {HashKey_Default};
|
||||
h.kind = HashKey_Default;
|
||||
// h.key = u128_from_u64(gb_fnv64a(data, len));
|
||||
h.key = gb_fnv64a(data, len);
|
||||
|
||||
return h;
|
||||
}
|
||||
|
||||
gb_inline HashKey hash_string(String s) {
|
||||
HashKey h = hashing_proc(s.text, s.len);
|
||||
h.kind = HashKey_String;
|
||||
h.string = s;
|
||||
return h;
|
||||
}
|
||||
|
||||
gb_inline HashKey hash_pointer(void *ptr) {
|
||||
HashKey h = {HashKey_Ptr};
|
||||
h.key = cast(u64)cast(uintptr)ptr;
|
||||
h.ptr = ptr;
|
||||
return h;
|
||||
}
|
||||
gb_inline HashKey hash_ptr_and_id(void *ptr, u32 id) {
|
||||
HashKey h = {HashKey_PtrAndId};
|
||||
h.key = cast(u64)cast(uintptr)ptr;
|
||||
h.ptr_and_id.ptr = ptr;
|
||||
h.ptr_and_id.id = id;
|
||||
return h;
|
||||
}
|
||||
|
||||
bool hash_key_equal(HashKey a, HashKey b) {
|
||||
if (a.key == b.key) {
|
||||
// NOTE(bill): If two string's hashes collide, compare the strings themselves
|
||||
if (a.kind == HashKey_String) {
|
||||
if (b.kind == HashKey_String) {
|
||||
return a.string == b.string;
|
||||
}
|
||||
return false;
|
||||
} else if (a.kind == HashKey_PtrAndId) {
|
||||
if (b.kind == HashKey_PtrAndId) {
|
||||
return a.ptr_and_id.id == b.ptr_and_id.id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool operator==(HashKey a, HashKey b) { return hash_key_equal(a, b); }
|
||||
bool operator!=(HashKey a, HashKey b) { return !hash_key_equal(a, b); }
|
||||
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
struct MapEntry {
|
||||
HashKey key;
|
||||
isize next;
|
||||
T value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct Map {
|
||||
Array<isize> hashes;
|
||||
Array<MapEntry<T> > entries;
|
||||
};
|
||||
|
||||
|
||||
template <typename T> void map_init (Map<T> *h, gbAllocator a);
|
||||
template <typename T> void map_init_with_reserve(Map<T> *h, gbAllocator a, isize capacity);
|
||||
template <typename T> void map_destroy (Map<T> *h);
|
||||
template <typename T> T * map_get (Map<T> *h, HashKey key);
|
||||
template <typename T> void map_set (Map<T> *h, HashKey key, T const &value);
|
||||
template <typename T> void map_remove (Map<T> *h, HashKey key);
|
||||
template <typename T> void map_clear (Map<T> *h);
|
||||
template <typename T> void map_grow (Map<T> *h);
|
||||
template <typename T> void map_rehash (Map<T> *h, isize new_count);
|
||||
|
||||
// Mutlivalued map procedure
|
||||
template <typename T> MapEntry<T> * multi_map_find_first(Map<T> *h, HashKey key);
|
||||
template <typename T> MapEntry<T> * multi_map_find_next (Map<T> *h, MapEntry<T> *e);
|
||||
|
||||
template <typename T> isize multi_map_count (Map<T> *h, HashKey key);
|
||||
template <typename T> void multi_map_get_all (Map<T> *h, HashKey key, T *items);
|
||||
template <typename T> void multi_map_insert (Map<T> *h, HashKey key, T const &value);
|
||||
template <typename T> void multi_map_remove (Map<T> *h, HashKey key, MapEntry<T> *e);
|
||||
template <typename T> void multi_map_remove_all(Map<T> *h, HashKey key);
|
||||
|
||||
|
||||
template <typename T>
|
||||
gb_inline void map_init(Map<T> *h, gbAllocator a) {
|
||||
array_init(&h->hashes, a);
|
||||
array_init(&h->entries, a);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline void map_init_with_reserve(Map<T> *h, gbAllocator a, isize capacity) {
|
||||
array_init(&h->hashes, a, capacity);
|
||||
array_init(&h->entries, a, capacity);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline void map_destroy(Map<T> *h) {
|
||||
array_free(&h->entries);
|
||||
array_free(&h->hashes);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal isize map__add_entry(Map<T> *h, HashKey key) {
|
||||
MapEntry<T> e = {};
|
||||
e.key = key;
|
||||
e.next = -1;
|
||||
array_add(&h->entries, e);
|
||||
return h->entries.count-1;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal MapFindResult map__find(Map<T> *h, HashKey key) {
|
||||
MapFindResult fr = {-1, -1, -1};
|
||||
if (h->hashes.count > 0) {
|
||||
// fr.hash_index = u128_to_i64(key.key % u128_from_i64(h->hashes.count));
|
||||
fr.hash_index = key.key % h->hashes.count;
|
||||
fr.entry_index = h->hashes[fr.hash_index];
|
||||
while (fr.entry_index >= 0) {
|
||||
if (hash_key_equal(h->entries[fr.entry_index].key, key)) {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = h->entries[fr.entry_index].next;
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal MapFindResult map__find_from_entry(Map<T> *h, MapEntry<T> *e) {
|
||||
MapFindResult fr = {-1, -1, -1};
|
||||
if (h->hashes.count > 0) {
|
||||
fr.hash_index = e->key.key % h->hashes.count;
|
||||
fr.entry_index = h->hashes[fr.hash_index];
|
||||
while (fr.entry_index >= 0) {
|
||||
if (&h->entries[fr.entry_index] == e) {
|
||||
return fr;
|
||||
}
|
||||
fr.entry_prev = fr.entry_index;
|
||||
fr.entry_index = h->entries[fr.entry_index].next;
|
||||
}
|
||||
}
|
||||
return fr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_internal b32 map__full(Map<T> *h) {
|
||||
return 0.75f * h->hashes.count <= h->entries.count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline void map_grow(Map<T> *h) {
|
||||
isize new_count = ARRAY_GROW_FORMULA(h->entries.count);
|
||||
map_rehash(h, new_count);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void map_rehash(Map<T> *h, isize new_count) {
|
||||
isize i, j;
|
||||
Map<T> nh = {};
|
||||
map_init(&nh, h->hashes.allocator);
|
||||
array_resize(&nh.hashes, new_count);
|
||||
array_reserve(&nh.entries, h->entries.count);
|
||||
for (i = 0; i < new_count; i++) {
|
||||
nh.hashes[i] = -1;
|
||||
}
|
||||
for (i = 0; i < h->entries.count; i++) {
|
||||
MapEntry<T> *e = &h->entries[i];
|
||||
MapFindResult fr;
|
||||
if (nh.hashes.count == 0) {
|
||||
map_grow(&nh);
|
||||
}
|
||||
fr = map__find(&nh, e->key);
|
||||
j = map__add_entry(&nh, e->key);
|
||||
if (fr.entry_prev < 0) {
|
||||
nh.hashes[fr.hash_index] = j;
|
||||
} else {
|
||||
nh.entries[fr.entry_prev].next = j;
|
||||
}
|
||||
nh.entries[j].next = fr.entry_index;
|
||||
nh.entries[j].value = e->value;
|
||||
if (map__full(&nh)) {
|
||||
map_grow(&nh);
|
||||
}
|
||||
}
|
||||
map_destroy(h);
|
||||
*h = nh;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline T *map_get(Map<T> *h, HashKey key) {
|
||||
isize index = map__find(h, key).entry_index;
|
||||
if (index >= 0) {
|
||||
return &h->entries[index].value;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void map_set(Map<T> *h, HashKey key, T const &value) {
|
||||
isize index;
|
||||
MapFindResult fr;
|
||||
if (h->hashes.count == 0)
|
||||
map_grow(h);
|
||||
fr = map__find(h, key);
|
||||
if (fr.entry_index >= 0) {
|
||||
index = fr.entry_index;
|
||||
} else {
|
||||
index = map__add_entry(h, key);
|
||||
if (fr.entry_prev >= 0) {
|
||||
h->entries[fr.entry_prev].next = index;
|
||||
} else {
|
||||
h->hashes[fr.hash_index] = index;
|
||||
}
|
||||
}
|
||||
h->entries[index].value = value;
|
||||
|
||||
if (map__full(h)) {
|
||||
map_grow(h);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void map__erase(Map<T> *h, MapFindResult fr) {
|
||||
MapFindResult last;
|
||||
if (fr.entry_prev < 0) {
|
||||
h->hashes[fr.hash_index] = h->entries[fr.entry_index].next;
|
||||
} else {
|
||||
h->entries[fr.entry_prev].next = h->entries[fr.entry_index].next;
|
||||
}
|
||||
if (fr.entry_index == h->entries.count-1) {
|
||||
array_pop(&h->entries);
|
||||
return;
|
||||
}
|
||||
h->entries[fr.entry_index] = h->entries[h->entries.count-1];
|
||||
last = map__find(h, h->entries[fr.entry_index].key);
|
||||
if (last.entry_prev >= 0) {
|
||||
h->entries[last.entry_prev].next = fr.entry_index;
|
||||
} else {
|
||||
h->hashes[last.hash_index] = fr.entry_index;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void map_remove(Map<T> *h, HashKey key) {
|
||||
MapFindResult fr = map__find(h, key);
|
||||
if (fr.entry_index >= 0) {
|
||||
map__erase(h, fr);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gb_inline void map_clear(Map<T> *h) {
|
||||
array_clear(&h->hashes);
|
||||
array_clear(&h->entries);
|
||||
}
|
||||
|
||||
|
||||
#if 1
|
||||
template <typename T>
|
||||
MapEntry<T> *multi_map_find_first(Map<T> *h, HashKey key) {
|
||||
isize i = map__find(h, key).entry_index;
|
||||
if (i < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return &h->entries[i];
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MapEntry<T> *multi_map_find_next(Map<T> *h, MapEntry<T> *e) {
|
||||
isize i = e->next;
|
||||
while (i >= 0) {
|
||||
if (hash_key_equal(h->entries[i].key, e->key)) {
|
||||
return &h->entries[i];
|
||||
}
|
||||
i = h->entries[i].next;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
isize multi_map_count(Map<T> *h, HashKey key) {
|
||||
isize count = 0;
|
||||
MapEntry<T> *e = multi_map_find_first(h, key);
|
||||
while (e != nullptr) {
|
||||
count++;
|
||||
e = multi_map_find_next(h, e);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void multi_map_get_all(Map<T> *h, HashKey key, T *items) {
|
||||
isize i = 0;
|
||||
MapEntry<T> *e = multi_map_find_first(h, key);
|
||||
while (e != nullptr) {
|
||||
items[i++] = e->value;
|
||||
e = multi_map_find_next(h, e);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void multi_map_insert(Map<T> *h, HashKey key, T const &value) {
|
||||
MapFindResult fr;
|
||||
isize i;
|
||||
if (h->hashes.count == 0) {
|
||||
map_grow(h);
|
||||
}
|
||||
// Make
|
||||
fr = map__find(h, key);
|
||||
i = map__add_entry(h, key);
|
||||
if (fr.entry_prev < 0) {
|
||||
h->hashes[fr.hash_index] = i;
|
||||
} else {
|
||||
h->entries[fr.entry_prev].next = i;
|
||||
}
|
||||
h->entries[i].next = fr.entry_index;
|
||||
h->entries[i].value = value;
|
||||
// Grow if needed
|
||||
if (map__full(h)) {
|
||||
map_grow(h);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void multi_map_remove(Map<T> *h, HashKey key, MapEntry<T> *e) {
|
||||
MapFindResult fr = map__find_from_entry(h, e);
|
||||
if (fr.entry_index >= 0) {
|
||||
map__erase(h, fr);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void multi_map_remove_all(Map<T> *h, HashKey key) {
|
||||
while (map_get(h, key) != nullptr) {
|
||||
map_remove(h, key);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,225 @@
|
||||
//-----------------------------------------------------------------------------
|
||||
// MurmurHash3 was written by Austin Appleby, and is placed in the public
|
||||
// domain. The author hereby disclaims copyright to this source code.
|
||||
|
||||
// Note - The x86 and x64 versions do _not_ produce the same results, as the
|
||||
// algorithms are optimized for their respective platforms. You can still
|
||||
// compile and run any of them on any platform, but your performance with the
|
||||
// non-native version will be less than optimal.
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define ROTL32(x,y) _rotl(x,y)
|
||||
#define ROTL64(x,y) _rotl64(x,y)
|
||||
#else
|
||||
|
||||
gb_inline u32 rotl32(u32 x, i8 r) {
|
||||
return (x << r) | (x >> (32-r));
|
||||
}
|
||||
gb_inline u64 rotl64(u64 x, i8 r) {
|
||||
return (x << r) | (x >> (64-r));
|
||||
}
|
||||
|
||||
#define ROTL32(x,y) rotl32(x,y)
|
||||
#define ROTL64(x,y) rotl64(x,y)
|
||||
#endif
|
||||
|
||||
gb_inline u32 fmix32(u32 h) {
|
||||
h ^= h >> 16;
|
||||
h *= 0x85ebca6b;
|
||||
h ^= h >> 13;
|
||||
h *= 0xc2b2ae35;
|
||||
h ^= h >> 16;
|
||||
return h;
|
||||
}
|
||||
|
||||
gb_inline u64 fmix64(u64 k) {
|
||||
k ^= k >> 33;
|
||||
k *= 0xff51afd7ed558ccdULL;
|
||||
k ^= k >> 33;
|
||||
k *= 0xc4ceb9fe1a85ec53ULL;
|
||||
k ^= k >> 33;
|
||||
return k;
|
||||
}
|
||||
|
||||
gb_inline u32 mm3_getblock32(u32 const *p, isize i) {
|
||||
return p[i];
|
||||
}
|
||||
gb_inline u64 mm3_getblock64(u64 const *p, isize i) {
|
||||
return p[i];
|
||||
}
|
||||
|
||||
void MurmurHash3_x64_128(void const *key, isize len, u32 seed, void *out) {
|
||||
u8 const * data = cast(u8 const *)key;
|
||||
isize nblocks = len / 16;
|
||||
|
||||
u64 h1 = seed;
|
||||
u64 h2 = seed;
|
||||
|
||||
u64 const c1 = 0x87c37b91114253d5ULL;
|
||||
u64 const c2 = 0x4cf5ad432745937fULL;
|
||||
|
||||
u64 const * blocks = cast(u64 const *)data;
|
||||
|
||||
for (isize i = 0; i < nblocks; i++) {
|
||||
u64 k1 = mm3_getblock64(blocks, i*2 + 0);
|
||||
u64 k2 = mm3_getblock64(blocks, i*2 + 1);
|
||||
|
||||
k1 *= c1; k1 = ROTL64(k1, 31); k1 *= c2; h1 ^= k1;
|
||||
h1 = ROTL64(h1,27); h1 += h2; h1 = h1*5+0x52dce729;
|
||||
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||
h2 = ROTL64(h2,31); h2 += h1; h2 = h2*5+0x38495ab5;
|
||||
}
|
||||
|
||||
u8 const * tail = cast(u8 const *)(data + nblocks*16);
|
||||
|
||||
u64 k1 = 0;
|
||||
u64 k2 = 0;
|
||||
|
||||
switch(len & 15) {
|
||||
case 15: k2 ^= ((u64)tail[14]) << 48;
|
||||
case 14: k2 ^= ((u64)tail[13]) << 40;
|
||||
case 13: k2 ^= ((u64)tail[12]) << 32;
|
||||
case 12: k2 ^= ((u64)tail[11]) << 24;
|
||||
case 11: k2 ^= ((u64)tail[10]) << 16;
|
||||
case 10: k2 ^= ((u64)tail[ 9]) << 8;
|
||||
case 9: k2 ^= ((u64)tail[ 8]) << 0;
|
||||
k2 *= c2; k2 = ROTL64(k2,33); k2 *= c1; h2 ^= k2;
|
||||
|
||||
case 8: k1 ^= ((u64)tail[ 7]) << 56;
|
||||
case 7: k1 ^= ((u64)tail[ 6]) << 48;
|
||||
case 6: k1 ^= ((u64)tail[ 5]) << 40;
|
||||
case 5: k1 ^= ((u64)tail[ 4]) << 32;
|
||||
case 4: k1 ^= ((u64)tail[ 3]) << 24;
|
||||
case 3: k1 ^= ((u64)tail[ 2]) << 16;
|
||||
case 2: k1 ^= ((u64)tail[ 1]) << 8;
|
||||
case 1: k1 ^= ((u64)tail[ 0]) << 0;
|
||||
k1 *= c1; k1 = ROTL64(k1,31); k1 *= c2; h1 ^= k1;
|
||||
}
|
||||
|
||||
h1 ^= len;
|
||||
h2 ^= len;
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
h1 = fmix64(h1);
|
||||
h2 = fmix64(h2);
|
||||
|
||||
h1 += h2;
|
||||
h2 += h1;
|
||||
|
||||
((u64 *)out)[0] = h1;
|
||||
((u64 *)out)[1] = h2;
|
||||
}
|
||||
|
||||
void MurmurHash3_x86_128(void const *key, isize len, u32 seed, void *out) {
|
||||
u8 const * data = cast(u8 * const)key;
|
||||
isize nblocks = len / 16;
|
||||
|
||||
u32 h1 = seed;
|
||||
u32 h2 = seed;
|
||||
u32 h3 = seed;
|
||||
u32 h4 = seed;
|
||||
|
||||
u32 const c1 = 0x239b961b;
|
||||
u32 const c2 = 0xab0e9789;
|
||||
u32 const c3 = 0x38b34ae5;
|
||||
u32 const c4 = 0xa1e38b93;
|
||||
|
||||
//----------
|
||||
// body
|
||||
|
||||
u32 const * blocks = cast(u32 const *)(data + nblocks*16);
|
||||
|
||||
for (isize i = -nblocks; i != 0; i++) {
|
||||
u32 k1 = mm3_getblock32(blocks, i*4 + 0);
|
||||
u32 k2 = mm3_getblock32(blocks, i*4 + 1);
|
||||
u32 k3 = mm3_getblock32(blocks, i*4 + 2);
|
||||
u32 k4 = mm3_getblock32(blocks, i*4 + 3);
|
||||
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
|
||||
h1 = ROTL32(h1,19); h1 += h2; h1 = h1*5+0x561ccd1b;
|
||||
|
||||
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
h2 = ROTL32(h2,17); h2 += h3; h2 = h2*5+0x0bcaa747;
|
||||
|
||||
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
h3 = ROTL32(h3,15); h3 += h4; h3 = h3*5+0x96cd1c35;
|
||||
|
||||
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
h4 = ROTL32(h4,13); h4 += h1; h4 = h4*5+0x32ac3b17;
|
||||
}
|
||||
|
||||
//----------
|
||||
// tail
|
||||
|
||||
u8 const * tail = cast(u8 const *)(data + nblocks*16);
|
||||
|
||||
u32 k1 = 0;
|
||||
u32 k2 = 0;
|
||||
u32 k3 = 0;
|
||||
u32 k4 = 0;
|
||||
|
||||
switch(len & 15) {
|
||||
case 15: k4 ^= tail[14] << 16;
|
||||
case 14: k4 ^= tail[13] << 8;
|
||||
case 13: k4 ^= tail[12] << 0;
|
||||
k4 *= c4; k4 = ROTL32(k4,18); k4 *= c1; h4 ^= k4;
|
||||
|
||||
case 12: k3 ^= tail[11] << 24;
|
||||
case 11: k3 ^= tail[10] << 16;
|
||||
case 10: k3 ^= tail[ 9] << 8;
|
||||
case 9: k3 ^= tail[ 8] << 0;
|
||||
k3 *= c3; k3 = ROTL32(k3,17); k3 *= c4; h3 ^= k3;
|
||||
|
||||
case 8: k2 ^= tail[ 7] << 24;
|
||||
case 7: k2 ^= tail[ 6] << 16;
|
||||
case 6: k2 ^= tail[ 5] << 8;
|
||||
case 5: k2 ^= tail[ 4] << 0;
|
||||
k2 *= c2; k2 = ROTL32(k2,16); k2 *= c3; h2 ^= k2;
|
||||
|
||||
case 4: k1 ^= tail[ 3] << 24;
|
||||
case 3: k1 ^= tail[ 2] << 16;
|
||||
case 2: k1 ^= tail[ 1] << 8;
|
||||
case 1: k1 ^= tail[ 0] << 0;
|
||||
k1 *= c1; k1 = ROTL32(k1,15); k1 *= c2; h1 ^= k1;
|
||||
};
|
||||
|
||||
//----------
|
||||
// finalization
|
||||
|
||||
h1 ^= len; h2 ^= len; h3 ^= len; h4 ^= len;
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
h1 = fmix32(h1);
|
||||
h2 = fmix32(h2);
|
||||
h3 = fmix32(h3);
|
||||
h4 = fmix32(h4);
|
||||
|
||||
h1 += h2; h1 += h3; h1 += h4;
|
||||
h2 += h1; h3 += h1; h4 += h1;
|
||||
|
||||
|
||||
((u32 *)out)[0] = h1;
|
||||
((u32 *)out)[1] = h2;
|
||||
((u32 *)out)[2] = h3;
|
||||
((u32 *)out)[3] = h4;
|
||||
}
|
||||
|
||||
gb_inline u128 MurmurHash3_128(void const *key, isize len, u32 seed) {
|
||||
u128 res;
|
||||
#if defined(GB_ARCH_64_BIT)
|
||||
MurmurHash3_x64_128(key, len, seed, &res);
|
||||
#else
|
||||
MurmurHash3_x86_128(key, len, seed, &res);
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
-1305
File diff suppressed because it is too large
Load Diff
-3820
File diff suppressed because it is too large
Load Diff
+5168
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ gb_inline void print_indent(isize indent) {
|
||||
}
|
||||
|
||||
void print_ast(AstNode *node, isize indent) {
|
||||
if (node == NULL)
|
||||
if (node == nullptr)
|
||||
return;
|
||||
|
||||
switch (node->kind) {
|
||||
+2569
File diff suppressed because it is too large
Load Diff
+275
@@ -0,0 +1,275 @@
|
||||
#define SSA_OPS \
|
||||
SSA_OP(Invalid)\
|
||||
\
|
||||
SSA_OP(Unknown)\
|
||||
\
|
||||
SSA_OP(Comment) /* Does nothing */\
|
||||
\
|
||||
SSA_OP(SP) /* Stack Pointer */\
|
||||
SSA_OP(SB) /* Stack Base */\
|
||||
\
|
||||
SSA_OP(Local)\
|
||||
SSA_OP(Global)\
|
||||
SSA_OP(Proc)\
|
||||
\
|
||||
SSA_OP(Load)\
|
||||
SSA_OP(Store)\
|
||||
SSA_OP(Move)\
|
||||
SSA_OP(LoadReg)\
|
||||
SSA_OP(StoreReg)\
|
||||
SSA_OP(Zero) /* Zero initialize */\
|
||||
\
|
||||
SSA_OP(ArrayIndex) /* Index for a fixed array */\
|
||||
SSA_OP(PtrIndex) /* Index for a struct/tuple/etc */\
|
||||
SSA_OP(PtrOffset)\
|
||||
SSA_OP(ValueIndex) /* Extract for a value from a register */\
|
||||
\
|
||||
SSA_OP(Phi)\
|
||||
SSA_OP(Copy)\
|
||||
\
|
||||
/* TODO(bill): calling conventions */\
|
||||
SSA_OP(CallOdin)\
|
||||
SSA_OP(CallC)\
|
||||
SSA_OP(CallStd)\
|
||||
SSA_OP(CallFast)\
|
||||
\
|
||||
SSA_OP(BoundsCheck)\
|
||||
SSA_OP(SliceBoundsCheck)\
|
||||
\
|
||||
/* Built in operations/procedures */\
|
||||
SSA_OP(Bswap16)\
|
||||
SSA_OP(Bswap32)\
|
||||
SSA_OP(Bswap64)\
|
||||
\
|
||||
SSA_OP(Assume)\
|
||||
SSA_OP(DebugTrap)\
|
||||
SSA_OP(Trap)\
|
||||
SSA_OP(ReadCycleCounter)\
|
||||
\
|
||||
\
|
||||
SSA_OP(ConstBool)\
|
||||
SSA_OP(ConstString)\
|
||||
SSA_OP(ConstSlice)\
|
||||
SSA_OP(ConstNil)\
|
||||
SSA_OP(Const8)\
|
||||
SSA_OP(Const16)\
|
||||
SSA_OP(Const32)\
|
||||
SSA_OP(Const64)\
|
||||
SSA_OP(Const32F)\
|
||||
SSA_OP(Const64F)\
|
||||
\
|
||||
/* These should be all the operations I could possibly need for the mean time */\
|
||||
SSA_OP(Add8)\
|
||||
SSA_OP(Add16)\
|
||||
SSA_OP(Add32)\
|
||||
SSA_OP(Add64)\
|
||||
SSA_OP(AddPtr)\
|
||||
SSA_OP(Add32F)\
|
||||
SSA_OP(Add64F)\
|
||||
SSA_OP(Sub8)\
|
||||
SSA_OP(Sub16)\
|
||||
SSA_OP(Sub32)\
|
||||
SSA_OP(Sub64)\
|
||||
SSA_OP(SubPtr)\
|
||||
SSA_OP(Sub32F)\
|
||||
SSA_OP(Sub64F)\
|
||||
SSA_OP(Mul8)\
|
||||
SSA_OP(Mul16)\
|
||||
SSA_OP(Mul32)\
|
||||
SSA_OP(Mul64)\
|
||||
SSA_OP(Mul32F)\
|
||||
SSA_OP(Mul64F)\
|
||||
SSA_OP(Div8)\
|
||||
SSA_OP(Div8U)\
|
||||
SSA_OP(Div16)\
|
||||
SSA_OP(Div16U)\
|
||||
SSA_OP(Div32)\
|
||||
SSA_OP(Div32U)\
|
||||
SSA_OP(Div64)\
|
||||
SSA_OP(Div64U)\
|
||||
SSA_OP(Div32F)\
|
||||
SSA_OP(Div64F)\
|
||||
SSA_OP(Mod8)\
|
||||
SSA_OP(Mod8U)\
|
||||
SSA_OP(Mod16)\
|
||||
SSA_OP(Mod16U)\
|
||||
SSA_OP(Mod32)\
|
||||
SSA_OP(Mod32U)\
|
||||
SSA_OP(Mod64)\
|
||||
SSA_OP(Mod64U)\
|
||||
\
|
||||
SSA_OP(And8)\
|
||||
SSA_OP(And16)\
|
||||
SSA_OP(And32)\
|
||||
SSA_OP(And64)\
|
||||
SSA_OP(Or8)\
|
||||
SSA_OP(Or16)\
|
||||
SSA_OP(Or32)\
|
||||
SSA_OP(Or64)\
|
||||
SSA_OP(Xor8)\
|
||||
SSA_OP(Xor16)\
|
||||
SSA_OP(Xor32)\
|
||||
SSA_OP(Xor64)\
|
||||
SSA_OP(AndNot8)\
|
||||
SSA_OP(AndNot16)\
|
||||
SSA_OP(AndNot32)\
|
||||
SSA_OP(AndNot64)\
|
||||
\
|
||||
SSA_OP(Lsh8x8)\
|
||||
SSA_OP(Lsh8x16)\
|
||||
SSA_OP(Lsh8x32)\
|
||||
SSA_OP(Lsh8x64)\
|
||||
SSA_OP(Lsh16x8)\
|
||||
SSA_OP(Lsh16x16)\
|
||||
SSA_OP(Lsh16x32)\
|
||||
SSA_OP(Lsh16x64)\
|
||||
SSA_OP(Lsh32x8)\
|
||||
SSA_OP(Lsh32x16)\
|
||||
SSA_OP(Lsh32x32)\
|
||||
SSA_OP(Lsh32x64)\
|
||||
SSA_OP(Lsh64x8)\
|
||||
SSA_OP(Lsh64x16)\
|
||||
SSA_OP(Lsh64x32)\
|
||||
SSA_OP(Lsh64x64)\
|
||||
SSA_OP(Rsh8x8)\
|
||||
SSA_OP(Rsh8x16)\
|
||||
SSA_OP(Rsh8x32)\
|
||||
SSA_OP(Rsh8x64)\
|
||||
SSA_OP(Rsh16x8)\
|
||||
SSA_OP(Rsh16x16)\
|
||||
SSA_OP(Rsh16x32)\
|
||||
SSA_OP(Rsh16x64)\
|
||||
SSA_OP(Rsh32x8)\
|
||||
SSA_OP(Rsh32x16)\
|
||||
SSA_OP(Rsh32x32)\
|
||||
SSA_OP(Rsh32x64)\
|
||||
SSA_OP(Rsh64x8)\
|
||||
SSA_OP(Rsh64x16)\
|
||||
SSA_OP(Rsh64x32)\
|
||||
SSA_OP(Rsh64x64)\
|
||||
SSA_OP(Rsh8Ux8)\
|
||||
SSA_OP(Rsh8Ux16)\
|
||||
SSA_OP(Rsh8Ux32)\
|
||||
SSA_OP(Rsh8Ux64)\
|
||||
SSA_OP(Rsh16Ux8)\
|
||||
SSA_OP(Rsh16Ux16)\
|
||||
SSA_OP(Rsh16Ux32)\
|
||||
SSA_OP(Rsh16Ux64)\
|
||||
SSA_OP(Rsh32Ux8)\
|
||||
SSA_OP(Rsh32Ux16)\
|
||||
SSA_OP(Rsh32Ux32)\
|
||||
SSA_OP(Rsh32Ux64)\
|
||||
SSA_OP(Rsh64Ux8)\
|
||||
SSA_OP(Rsh64Ux16)\
|
||||
SSA_OP(Rsh64Ux32)\
|
||||
SSA_OP(Rsh64Ux64)\
|
||||
\
|
||||
SSA_OP(Eq8)\
|
||||
SSA_OP(Eq16)\
|
||||
SSA_OP(Eq32)\
|
||||
SSA_OP(Eq64)\
|
||||
SSA_OP(EqPtr)\
|
||||
SSA_OP(Eq32F)\
|
||||
SSA_OP(Eq64F)\
|
||||
SSA_OP(Ne8)\
|
||||
SSA_OP(Ne16)\
|
||||
SSA_OP(Ne32)\
|
||||
SSA_OP(Ne64)\
|
||||
SSA_OP(NePtr)\
|
||||
SSA_OP(Ne32F)\
|
||||
SSA_OP(Ne64F)\
|
||||
SSA_OP(Lt8)\
|
||||
SSA_OP(Lt16)\
|
||||
SSA_OP(Lt32)\
|
||||
SSA_OP(Lt64)\
|
||||
SSA_OP(LtPtr)\
|
||||
SSA_OP(Lt32F)\
|
||||
SSA_OP(Lt64F)\
|
||||
SSA_OP(Gt8)\
|
||||
SSA_OP(Gt16)\
|
||||
SSA_OP(Gt32)\
|
||||
SSA_OP(Gt64)\
|
||||
SSA_OP(GtPtr)\
|
||||
SSA_OP(Gt32F)\
|
||||
SSA_OP(Gt64F)\
|
||||
SSA_OP(Le8)\
|
||||
SSA_OP(Le16)\
|
||||
SSA_OP(Le32)\
|
||||
SSA_OP(Le64)\
|
||||
SSA_OP(LePtr)\
|
||||
SSA_OP(Le32F)\
|
||||
SSA_OP(Le64F)\
|
||||
SSA_OP(Ge8)\
|
||||
SSA_OP(Ge16)\
|
||||
SSA_OP(Ge32)\
|
||||
SSA_OP(Ge64)\
|
||||
SSA_OP(GePtr)\
|
||||
SSA_OP(Ge32F)\
|
||||
SSA_OP(Ge64F)\
|
||||
\
|
||||
SSA_OP(NotB)\
|
||||
SSA_OP(EqB)\
|
||||
SSA_OP(NeB)\
|
||||
\
|
||||
SSA_OP(Neg8)\
|
||||
SSA_OP(Neg16)\
|
||||
SSA_OP(Neg32)\
|
||||
SSA_OP(Neg64)\
|
||||
SSA_OP(Neg32F)\
|
||||
SSA_OP(Neg64F)\
|
||||
\
|
||||
SSA_OP(Not8)\
|
||||
SSA_OP(Not16)\
|
||||
SSA_OP(Not32)\
|
||||
SSA_OP(Not64)\
|
||||
\
|
||||
SSA_OP(SignExt8to16)\
|
||||
SSA_OP(SignExt8to32)\
|
||||
SSA_OP(SignExt8to64)\
|
||||
SSA_OP(SignExt16to32)\
|
||||
SSA_OP(SignExt16to64)\
|
||||
SSA_OP(SignExt32to64)\
|
||||
SSA_OP(ZeroExt8to16)\
|
||||
SSA_OP(ZeroExt8to32)\
|
||||
SSA_OP(ZeroExt8to64)\
|
||||
SSA_OP(ZeroExt16to32)\
|
||||
SSA_OP(ZeroExt16to64)\
|
||||
SSA_OP(ZeroExt32to64)\
|
||||
SSA_OP(Trunc16to8)\
|
||||
SSA_OP(Trunc32to8)\
|
||||
SSA_OP(Trunc32to16)\
|
||||
SSA_OP(Trunc64to8)\
|
||||
SSA_OP(Trunc64to16)\
|
||||
SSA_OP(Trunc64to32)\
|
||||
\
|
||||
SSA_OP(Cvt32to32F)\
|
||||
SSA_OP(Cvt32to64F)\
|
||||
SSA_OP(Cvt64to32F)\
|
||||
SSA_OP(Cvt64to64F)\
|
||||
SSA_OP(Cvt32Fto32)\
|
||||
SSA_OP(Cvt32Fto64)\
|
||||
SSA_OP(Cvt64Fto32)\
|
||||
SSA_OP(Cvt64Fto64)\
|
||||
SSA_OP(Cvt32Fto64F)\
|
||||
SSA_OP(Cvt64Fto32F)\
|
||||
SSA_OP(Cvt32Uto32F)\
|
||||
SSA_OP(Cvt32Uto64F)\
|
||||
SSA_OP(Cvt32Fto32U)\
|
||||
SSA_OP(Cvt64Fto32U)\
|
||||
SSA_OP(Cvt64Uto32F)\
|
||||
SSA_OP(Cvt64Uto64F)\
|
||||
SSA_OP(Cvt32Fto64U)\
|
||||
SSA_OP(Cvt64Fto64U)\
|
||||
|
||||
|
||||
enum ssaOp {
|
||||
#define SSA_OP(k) GB_JOIN2(ssaOp_, k),
|
||||
SSA_OPS
|
||||
#undef SSA_OP
|
||||
};
|
||||
|
||||
String const ssa_op_strings[] = {
|
||||
#define SSA_OP(k) {cast(u8 *)#k, gb_size_of(#k)-1},
|
||||
SSA_OPS
|
||||
#undef SSA_OP
|
||||
};
|
||||
-430
@@ -1,430 +0,0 @@
|
||||
gb_global gbArena string_buffer_arena = {0};
|
||||
gb_global gbAllocator string_buffer_allocator = {0};
|
||||
|
||||
void init_string_buffer_memory(void) {
|
||||
// NOTE(bill): This should be enough memory for file systems
|
||||
gb_arena_init_from_allocator(&string_buffer_arena, heap_allocator(), gb_megabytes(1));
|
||||
string_buffer_allocator = gb_arena_allocator(&string_buffer_arena);
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Used for UTF-8 strings
|
||||
typedef struct String {
|
||||
u8 * text;
|
||||
isize len;
|
||||
} String;
|
||||
// NOTE(bill): used for printf style arguments
|
||||
#define LIT(x) ((int)(x).len), (x).text
|
||||
|
||||
|
||||
typedef struct String16 {
|
||||
wchar_t *text;
|
||||
isize len;
|
||||
} String16;
|
||||
|
||||
|
||||
gb_inline String make_string(u8 *text, isize len) {
|
||||
String s;
|
||||
s.text = text;
|
||||
if (len < 0) {
|
||||
len = gb_strlen(cast(char *)text);
|
||||
}
|
||||
s.len = len;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
gb_inline String16 make_string16(wchar_t *text, isize len) {
|
||||
String16 s;
|
||||
s.text = text;
|
||||
s.len = len;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
gb_inline String make_string_c(char *text) {
|
||||
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
|
||||
}
|
||||
|
||||
#define str_lit(c_str) (String){cast(u8 *)c_str, gb_size_of(c_str)-1}
|
||||
|
||||
|
||||
|
||||
gb_inline bool str_eq_ignore_case(String a, String b) {
|
||||
if (a.len == b.len) {
|
||||
isize i;
|
||||
for (i = 0; i < a.len; i++) {
|
||||
char x = cast(char)a.text[i];
|
||||
char y = cast(char)b.text[i];
|
||||
if (gb_char_to_lower(x) != gb_char_to_lower(y))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int string_compare(String x, String y) {
|
||||
if (!(x.len == y.len &&
|
||||
x.text == y.text)) {
|
||||
isize n, fast, offset, curr_block;
|
||||
isize *la, *lb;
|
||||
isize pos;
|
||||
|
||||
n = gb_min(x.len, y.len);
|
||||
|
||||
fast = n/gb_size_of(isize) + 1;
|
||||
offset = (fast-1)*gb_size_of(isize);
|
||||
curr_block = 0;
|
||||
if (n <= gb_size_of(isize)) {
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
la = cast(isize *)x.text;
|
||||
lb = cast(isize *)y.text;
|
||||
|
||||
for (; curr_block < fast; curr_block++) {
|
||||
if (la[curr_block] ^ lb[curr_block]) {
|
||||
for (pos = curr_block*gb_size_of(isize); pos < n; pos++) {
|
||||
if (x.text[pos] ^ y.text[pos]) {
|
||||
return cast(int)x.text[pos] - cast(int)y.text[pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; offset < n; offset++) {
|
||||
if (x.text[offset] ^ y.text[offset]) {
|
||||
return cast(int)x.text[offset] - cast(int)y.text[offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
GB_COMPARE_PROC(string_cmp_proc) {
|
||||
String x = *(String *)a;
|
||||
String y = *(String *)b;
|
||||
return string_compare(x, y);
|
||||
}
|
||||
|
||||
gb_inline bool str_eq(String a, String b) { return a.len == b.len ? gb_memcompare(a.text, b.text, a.len) == 0 : false; }
|
||||
gb_inline bool str_ne(String a, String b) { return !str_eq(a, b); }
|
||||
gb_inline bool str_lt(String a, String b) { return string_compare(a, b) < 0; }
|
||||
gb_inline bool str_gt(String a, String b) { return string_compare(a, b) > 0; }
|
||||
gb_inline bool str_le(String a, String b) { return string_compare(a, b) <= 0; }
|
||||
gb_inline bool str_ge(String a, String b) { return string_compare(a, b) >= 0; }
|
||||
|
||||
gb_inline bool str_has_prefix(String s, String prefix) {
|
||||
isize i;
|
||||
if (prefix.len < s.len) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < prefix.len; i++) {
|
||||
if (s.text[i] != prefix.text[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_inline isize string_extension_position(String str) {
|
||||
isize dot_pos = -1;
|
||||
isize i = str.len;
|
||||
bool seen_dot = false;
|
||||
while (i --> 0) {
|
||||
if (str.text[i] == GB_PATH_SEPARATOR)
|
||||
break;
|
||||
if (str.text[i] == '.') {
|
||||
dot_pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dot_pos;
|
||||
}
|
||||
|
||||
gb_inline bool string_has_extension(String str, String ext) {
|
||||
if (str.len > ext.len+1) {
|
||||
u8 *s = str.text+str.len - ext.len-1;
|
||||
if (s[0] == '.') {
|
||||
s++;
|
||||
return gb_memcompare(s, ext.text, ext.len) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool string_contains_char(String s, u8 c) {
|
||||
isize i;
|
||||
for (i = 0; i < s.len; i++) {
|
||||
if (s.text[i] == c)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(bill): Make this non-windows specific
|
||||
String16 string_to_string16(gbAllocator a, String s) {
|
||||
int len, len1;
|
||||
wchar_t *text;
|
||||
|
||||
if (s.len < 1) {
|
||||
return make_string16(NULL, 0);
|
||||
}
|
||||
|
||||
len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||
cast(char *)s.text, s.len, NULL, 0);
|
||||
if (len == 0) {
|
||||
return make_string16(NULL, 0);
|
||||
}
|
||||
|
||||
text = gb_alloc_array(a, wchar_t, len+1);
|
||||
|
||||
len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
|
||||
cast(char *)s.text, s.len, text, len);
|
||||
if (len1 == 0) {
|
||||
gb_free(a, text);
|
||||
return make_string16(NULL, 0);
|
||||
}
|
||||
text[len] = 0;
|
||||
|
||||
return make_string16(text, len-1);
|
||||
}
|
||||
|
||||
String string16_to_string(gbAllocator a, String16 s) {
|
||||
int len, len1;
|
||||
u8 *text;
|
||||
|
||||
if (s.len < 1) {
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
|
||||
len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
s.text, s.len, NULL, 0, NULL, NULL);
|
||||
if (len == 0) {
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
|
||||
text = gb_alloc_array(a, u8, len+1);
|
||||
|
||||
len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
|
||||
s.text, s.len, cast(char *)text, len, NULL, NULL);
|
||||
if (len1 == 0) {
|
||||
gb_free(a, text);
|
||||
return make_string(NULL, 0);
|
||||
}
|
||||
text[len] = 0;
|
||||
|
||||
return make_string(text, len-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
|
||||
u8 c;
|
||||
|
||||
if (s.text[0] == quote &&
|
||||
(quote == '\'' || quote == '"')) {
|
||||
return false;
|
||||
} else if (s.text[0] >= 0x80) {
|
||||
Rune r = -1;
|
||||
isize size = gb_utf8_decode(s.text, s.len, &r);
|
||||
*rune = r;
|
||||
*multiple_bytes = true;
|
||||
*tail_string = make_string(s.text+size, s.len-size);
|
||||
return true;
|
||||
} else if (s.text[0] != '\\') {
|
||||
*rune = s.text[0];
|
||||
*tail_string = make_string(s.text+1, s.len-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s.len <= 1) {
|
||||
return false;
|
||||
}
|
||||
c = s.text[1];
|
||||
s = make_string(s.text+2, s.len-2);
|
||||
|
||||
switch (c) {
|
||||
default: return false;
|
||||
|
||||
case 'a': *rune = '\a'; break;
|
||||
case 'b': *rune = '\b'; break;
|
||||
case 'f': *rune = '\f'; break;
|
||||
case 'n': *rune = '\n'; break;
|
||||
case 'r': *rune = '\r'; break;
|
||||
case 't': *rune = '\t'; break;
|
||||
case 'v': *rune = '\v'; break;
|
||||
case '\\': *rune = '\\'; break;
|
||||
|
||||
|
||||
case '\'':
|
||||
case '"':
|
||||
if (c != quote) {
|
||||
return false;
|
||||
}
|
||||
*rune = c;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7': {
|
||||
isize i;
|
||||
i32 r = gb_digit_to_int(c);
|
||||
if (s.len < 2) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
i32 d = gb_digit_to_int(s.text[i]);
|
||||
if (d < 0 || d > 7) {
|
||||
return false;
|
||||
}
|
||||
r = (r<<3) | d;
|
||||
}
|
||||
s = make_string(s.text+2, s.len-2);
|
||||
if (r > 0xff) {
|
||||
return false;
|
||||
}
|
||||
*rune = r;
|
||||
} break;
|
||||
|
||||
case 'x':
|
||||
case 'u':
|
||||
case 'U': {
|
||||
Rune r = 0;
|
||||
isize i, count = 0;
|
||||
switch (c) {
|
||||
case 'x': count = 2; break;
|
||||
case 'u': count = 4; break;
|
||||
case 'U': count = 8; break;
|
||||
}
|
||||
|
||||
if (s.len < count) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
i32 d = gb_hex_digit_to_int(s.text[i]);
|
||||
if (d < 0) {
|
||||
return false;
|
||||
}
|
||||
r = (r<<4) | d;
|
||||
}
|
||||
s = make_string(s.text+count, s.len-count);
|
||||
if (c == 'x') {
|
||||
*rune = r;
|
||||
break;
|
||||
}
|
||||
if (r > GB_RUNE_MAX) {
|
||||
return false;
|
||||
}
|
||||
*rune = r;
|
||||
*multiple_bytes = true;
|
||||
} break;
|
||||
}
|
||||
*tail_string = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 0 == failure
|
||||
// 1 == original memory
|
||||
// 2 == new allocation
|
||||
i32 unquote_string(gbAllocator a, String *s_) {
|
||||
String s = *s_;
|
||||
isize n = s.len;
|
||||
u8 quote;
|
||||
if (n < 2) {
|
||||
return 0;
|
||||
}
|
||||
quote = s.text[0];
|
||||
if (quote != s.text[n-1]) {
|
||||
return 0;
|
||||
}
|
||||
s.text += 1;
|
||||
s.len -= 2;
|
||||
|
||||
if (quote == '`') {
|
||||
if (string_contains_char(s, '`')) {
|
||||
return 0;
|
||||
}
|
||||
*s_ = s;
|
||||
return 1;
|
||||
}
|
||||
if (quote != '"' && quote != '\'') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (string_contains_char(s, '\n')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
|
||||
if (quote == '"') {
|
||||
*s_ = s;
|
||||
return 1;
|
||||
} else if (quote == '\'') {
|
||||
Rune r = GB_RUNE_INVALID;
|
||||
isize size = gb_utf8_decode(s.text, s.len, &r);
|
||||
if ((size == s.len) && (r != -1 || size != 1)) {
|
||||
*s_ = s;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
u8 rune_temp[4] = {0};
|
||||
isize buf_len = 3*s.len / 2;
|
||||
u8 *buf = gb_alloc_array(a, u8, buf_len);
|
||||
isize offset = 0;
|
||||
while (s.len > 0) {
|
||||
String tail_string = {0};
|
||||
Rune r = 0;
|
||||
bool multiple_bytes = false;
|
||||
bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
|
||||
if (!success) {
|
||||
gb_free(a, buf);
|
||||
return 0;
|
||||
}
|
||||
s = tail_string;
|
||||
|
||||
if (r < 0x80 || !multiple_bytes) {
|
||||
buf[offset++] = cast(u8)r;
|
||||
} else {
|
||||
isize size = gb_utf8_encode_rune(rune_temp, r);
|
||||
gb_memmove(buf+offset, rune_temp, size);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
if (quote == '\'' && s.len != 0) {
|
||||
gb_free(a, buf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*s_ = make_string(buf, offset);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
+569
@@ -0,0 +1,569 @@
|
||||
gb_global gbArena string_buffer_arena = {};
|
||||
gb_global gbAllocator string_buffer_allocator = {};
|
||||
gb_global gbMutex string_buffer_mutex = {};
|
||||
|
||||
void init_string_buffer_memory(void) {
|
||||
// NOTE(bill): This should be enough memory for file systems
|
||||
gb_arena_init_from_allocator(&string_buffer_arena, heap_allocator(), gb_megabytes(1));
|
||||
string_buffer_allocator = gb_arena_allocator(&string_buffer_arena);
|
||||
gb_mutex_init(&string_buffer_mutex);
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): Used for UTF-8 strings
|
||||
struct String {
|
||||
u8 * text;
|
||||
isize len;
|
||||
|
||||
u8 &operator[](isize i) {
|
||||
GB_ASSERT(0 <= i && i < len);
|
||||
return text[i];
|
||||
}
|
||||
u8 const &operator[](isize i) const {
|
||||
GB_ASSERT(0 <= i && i < len);
|
||||
return text[i];
|
||||
}
|
||||
};
|
||||
// NOTE(bill): used for printf style arguments
|
||||
#define LIT(x) ((int)(x).len), (x).text
|
||||
#define STR_LIT(c_str) {cast(u8 *)c_str, gb_size_of(c_str)-1}
|
||||
#if defined(GB_COMPILER_MSVC) && _MSC_VER < 1700
|
||||
#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
|
||||
#else
|
||||
#define str_lit(c_str) String{cast(u8 *)c_str, gb_size_of(c_str)-1}
|
||||
#endif
|
||||
|
||||
// NOTE(bill): String16 is only used for Windows due to its file directories
|
||||
struct String16 {
|
||||
wchar_t *text;
|
||||
isize len;
|
||||
wchar_t &operator[](isize i) {
|
||||
GB_ASSERT(0 <= i && i < len);
|
||||
return text[i];
|
||||
}
|
||||
wchar_t const &operator[](isize i) const {
|
||||
GB_ASSERT(0 <= i && i < len);
|
||||
return text[i];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
gb_inline String make_string(u8 *text, isize len) {
|
||||
String s;
|
||||
s.text = text;
|
||||
if (len < 0) {
|
||||
len = gb_strlen(cast(char *)text);
|
||||
}
|
||||
s.len = len;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
gb_inline String16 make_string16(wchar_t *text, isize len) {
|
||||
String16 s;
|
||||
s.text = text;
|
||||
s.len = len;
|
||||
return s;
|
||||
}
|
||||
|
||||
isize string16_len(wchar_t *s) {
|
||||
if (s == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
wchar_t *p = s;
|
||||
while (*p) {
|
||||
p++;
|
||||
}
|
||||
return p - s;
|
||||
}
|
||||
|
||||
|
||||
gb_inline String make_string_c(char *text) {
|
||||
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
|
||||
}
|
||||
|
||||
String substring(String s, isize lo, isize hi) {
|
||||
isize max = s.len;
|
||||
GB_ASSERT(lo <= hi && hi <= max);
|
||||
|
||||
return make_string(s.text+lo, hi-lo);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
gb_inline bool str_eq_ignore_case(String a, String b) {
|
||||
if (a.len == b.len) {
|
||||
isize i;
|
||||
for (i = 0; i < a.len; i++) {
|
||||
char x = cast(char)a[i];
|
||||
char y = cast(char)b[i];
|
||||
if (gb_char_to_lower(x) != gb_char_to_lower(y))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int string_compare(String const &x, String const &y) {
|
||||
if (x.len != y.len || x.text != y.text) {
|
||||
isize n, fast, offset, curr_block;
|
||||
isize *la, *lb;
|
||||
isize pos;
|
||||
|
||||
n = gb_min(x.len, y.len);
|
||||
|
||||
fast = n/gb_size_of(isize) + 1;
|
||||
offset = (fast-1)*gb_size_of(isize);
|
||||
curr_block = 0;
|
||||
if (n <= gb_size_of(isize)) {
|
||||
fast = 0;
|
||||
}
|
||||
|
||||
la = cast(isize *)x.text;
|
||||
lb = cast(isize *)y.text;
|
||||
|
||||
for (; curr_block < fast; curr_block++) {
|
||||
if (la[curr_block] ^ lb[curr_block]) {
|
||||
for (pos = curr_block*gb_size_of(isize); pos < n; pos++) {
|
||||
if (x[pos] ^ y[pos]) {
|
||||
return cast(int)x[pos] - cast(int)y[pos];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; offset < n; offset++) {
|
||||
if (x[offset] ^ y[offset]) {
|
||||
return cast(int)x[offset] - cast(int)y[offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
GB_COMPARE_PROC(string_cmp_proc) {
|
||||
String x = *(String *)a;
|
||||
String y = *(String *)b;
|
||||
return string_compare(x, y);
|
||||
}
|
||||
|
||||
gb_inline bool str_eq(String const &a, String const &b) {
|
||||
if (a.len != b.len) return false;
|
||||
for (isize i = 0; i < a.len; i++) {
|
||||
if (a.text[i] != b.text[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
gb_inline bool str_ne(String const &a, String const &b) { return !str_eq(a, b); }
|
||||
gb_inline bool str_lt(String const &a, String const &b) { return string_compare(a, b) < 0; }
|
||||
gb_inline bool str_gt(String const &a, String const &b) { return string_compare(a, b) > 0; }
|
||||
gb_inline bool str_le(String const &a, String const &b) { return string_compare(a, b) <= 0; }
|
||||
gb_inline bool str_ge(String const &a, String const &b) { return string_compare(a, b) >= 0; }
|
||||
|
||||
gb_inline bool operator == (String const &a, String const &b) { return str_eq(a, b); }
|
||||
gb_inline bool operator != (String const &a, String const &b) { return str_ne(a, b); }
|
||||
gb_inline bool operator < (String const &a, String const &b) { return str_lt(a, b); }
|
||||
gb_inline bool operator > (String const &a, String const &b) { return str_gt(a, b); }
|
||||
gb_inline bool operator <= (String const &a, String const &b) { return str_le(a, b); }
|
||||
gb_inline bool operator >= (String const &a, String const &b) { return str_ge(a, b); }
|
||||
|
||||
template <isize N> bool operator == (String const &a, char const (&b)[N]) { return str_eq(a, make_string(cast(u8 *)b, N-1)); }
|
||||
template <isize N> bool operator != (String const &a, char const (&b)[N]) { return str_ne(a, make_string(cast(u8 *)b, N-1)); }
|
||||
template <isize N> bool operator < (String const &a, char const (&b)[N]) { return str_lt(a, make_string(cast(u8 *)b, N-1)); }
|
||||
template <isize N> bool operator > (String const &a, char const (&b)[N]) { return str_gt(a, make_string(cast(u8 *)b, N-1)); }
|
||||
template <isize N> bool operator <= (String const &a, char const (&b)[N]) { return str_le(a, make_string(cast(u8 *)b, N-1)); }
|
||||
template <isize N> bool operator >= (String const &a, char const (&b)[N]) { return str_ge(a, make_string(cast(u8 *)b, N-1)); }
|
||||
|
||||
|
||||
|
||||
gb_inline bool str_has_prefix(String s, String prefix) {
|
||||
isize i;
|
||||
if (prefix.len < s.len) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < prefix.len; i++) {
|
||||
if (s[i] != prefix[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_inline isize string_extension_position(String str) {
|
||||
isize dot_pos = -1;
|
||||
isize i = str.len;
|
||||
while (i --> 0) {
|
||||
if (str[i] == GB_PATH_SEPARATOR)
|
||||
break;
|
||||
if (str[i] == '.') {
|
||||
dot_pos = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return dot_pos;
|
||||
}
|
||||
|
||||
String string_trim_whitespace(String str) {
|
||||
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
|
||||
str.len--;
|
||||
}
|
||||
|
||||
while (str.len > 0 && rune_is_whitespace(str[0])) {
|
||||
str.text++;
|
||||
str.len--;
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
gb_inline bool string_has_extension(String str, String ext) {
|
||||
str = string_trim_whitespace(str);
|
||||
if (str.len <= ext.len+1) {
|
||||
return false;
|
||||
}
|
||||
isize len = str.len;
|
||||
for (isize i = len-1; i >= 0; i--) {
|
||||
if (str[i] == '.') {
|
||||
break;
|
||||
}
|
||||
len--;
|
||||
}
|
||||
if (len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
u8 *s = str.text + len;
|
||||
return gb_memcompare(s, ext.text, ext.len) == 0;
|
||||
}
|
||||
|
||||
bool string_contains_char(String s, u8 c) {
|
||||
isize i;
|
||||
for (i = 0; i < s.len; i++) {
|
||||
if (s[i] == c)
|
||||
return true;
|
||||
}
|
||||
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[j] == '/' ||
|
||||
s[j] == '\\') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
s.text += j+1;
|
||||
s.len = i-j-1;
|
||||
}
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
int convert_multibyte_to_widechar(char *multibyte_input, int input_length, wchar_t *output, int output_size) {
|
||||
return MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, multibyte_input, input_length, output, output_size);
|
||||
}
|
||||
int convert_widechar_to_multibyte(wchar_t *widechar_input, int input_length, char *output, int output_size) {
|
||||
return WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, widechar_input, input_length, output, output_size, nullptr, nullptr);
|
||||
}
|
||||
#elif defined(GB_SYSTEM_UNIX) || defined(GB_SYSTEM_OSX)
|
||||
|
||||
#include <iconv.h>
|
||||
|
||||
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);
|
||||
|
||||
return (int) result;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return (int) result;
|
||||
}
|
||||
#else
|
||||
#error Implement system
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
// TODO(bill): Make this non-windows specific
|
||||
String16 string_to_string16(gbAllocator a, String s) {
|
||||
int len, len1;
|
||||
wchar_t *text;
|
||||
|
||||
if (s.len < 1) {
|
||||
return make_string16(nullptr, 0);
|
||||
}
|
||||
|
||||
len = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, nullptr, 0);
|
||||
if (len == 0) {
|
||||
return make_string16(nullptr, 0);
|
||||
}
|
||||
|
||||
text = gb_alloc_array(a, wchar_t, len+1);
|
||||
|
||||
len1 = convert_multibyte_to_widechar(cast(char *)s.text, cast(int)s.len, text, cast(int)len);
|
||||
if (len1 == 0) {
|
||||
gb_free(a, text);
|
||||
return make_string16(nullptr, 0);
|
||||
}
|
||||
text[len] = 0;
|
||||
|
||||
return make_string16(text, len-1);
|
||||
}
|
||||
|
||||
|
||||
String string16_to_string(gbAllocator a, String16 s) {
|
||||
int len, len1;
|
||||
u8 *text;
|
||||
|
||||
if (s.len < 1) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
len = convert_widechar_to_multibyte(s.text, cast(int)s.len, nullptr, 0);
|
||||
if (len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
len += 1; // NOTE(bill): It needs an extra 1 for some reason
|
||||
|
||||
text = gb_alloc_array(a, u8, len+1);
|
||||
|
||||
len1 = convert_widechar_to_multibyte(s.text, cast(int)s.len, cast(char *)text, cast(int)len);
|
||||
if (len1 == 0) {
|
||||
gb_free(a, text);
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
text[len] = 0;
|
||||
|
||||
return make_string(text, len-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
|
||||
u8 c;
|
||||
|
||||
if (s[0] == quote &&
|
||||
(quote == '\'' || quote == '"')) {
|
||||
return false;
|
||||
} else if (s[0] >= 0x80) {
|
||||
Rune r = -1;
|
||||
isize size = gb_utf8_decode(s.text, s.len, &r);
|
||||
*rune = r;
|
||||
*multiple_bytes = true;
|
||||
*tail_string = make_string(s.text+size, s.len-size);
|
||||
return true;
|
||||
} else if (s[0] != '\\') {
|
||||
*rune = s[0];
|
||||
*tail_string = make_string(s.text+1, s.len-1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (s.len <= 1) {
|
||||
return false;
|
||||
}
|
||||
c = s[1];
|
||||
s = make_string(s.text+2, s.len-2);
|
||||
|
||||
switch (c) {
|
||||
default: return false;
|
||||
|
||||
case 'a': *rune = '\a'; break;
|
||||
case 'b': *rune = '\b'; break;
|
||||
case 'f': *rune = '\f'; break;
|
||||
case 'n': *rune = '\n'; break;
|
||||
case 'r': *rune = '\r'; break;
|
||||
case 't': *rune = '\t'; break;
|
||||
case 'v': *rune = '\v'; break;
|
||||
case '\\': *rune = '\\'; break;
|
||||
|
||||
|
||||
case '\'':
|
||||
case '"':
|
||||
if (c != quote) {
|
||||
return false;
|
||||
}
|
||||
*rune = c;
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7': {
|
||||
isize i;
|
||||
i32 r = gb_digit_to_int(c);
|
||||
if (s.len < 2) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < 2; i++) {
|
||||
i32 d = gb_digit_to_int(s[i]);
|
||||
if (d < 0 || d > 7) {
|
||||
return false;
|
||||
}
|
||||
r = (r<<3) | d;
|
||||
}
|
||||
s = make_string(s.text+2, s.len-2);
|
||||
if (r > 0xff) {
|
||||
return false;
|
||||
}
|
||||
*rune = r;
|
||||
} break;
|
||||
|
||||
case 'x':
|
||||
case 'u':
|
||||
case 'U': {
|
||||
Rune r = 0;
|
||||
isize i, count = 0;
|
||||
switch (c) {
|
||||
case 'x': count = 2; break;
|
||||
case 'u': count = 4; break;
|
||||
case 'U': count = 8; break;
|
||||
}
|
||||
|
||||
if (s.len < count) {
|
||||
return false;
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
i32 d = gb_hex_digit_to_int(s[i]);
|
||||
if (d < 0) {
|
||||
return false;
|
||||
}
|
||||
r = (r<<4) | d;
|
||||
}
|
||||
s = make_string(s.text+count, s.len-count);
|
||||
if (c == 'x') {
|
||||
*rune = r;
|
||||
break;
|
||||
}
|
||||
if (r > GB_RUNE_MAX) {
|
||||
return false;
|
||||
}
|
||||
*rune = r;
|
||||
*multiple_bytes = true;
|
||||
} break;
|
||||
}
|
||||
*tail_string = s;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// 0 == failure
|
||||
// 1 == original memory
|
||||
// 2 == new allocation
|
||||
i32 unquote_string(gbAllocator a, String *s_) {
|
||||
String s = *s_;
|
||||
isize n = s.len;
|
||||
u8 quote;
|
||||
if (n < 2) {
|
||||
return 0;
|
||||
}
|
||||
quote = s[0];
|
||||
if (quote != s[n-1]) {
|
||||
return 0;
|
||||
}
|
||||
s.text += 1;
|
||||
s.len -= 2;
|
||||
|
||||
if (quote == '`') {
|
||||
if (string_contains_char(s, '`')) {
|
||||
return 0;
|
||||
}
|
||||
*s_ = s;
|
||||
return 1;
|
||||
}
|
||||
if (quote != '"' && quote != '\'') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (string_contains_char(s, '\n')) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
|
||||
if (quote == '"') {
|
||||
*s_ = s;
|
||||
return 1;
|
||||
} else if (quote == '\'') {
|
||||
Rune r = GB_RUNE_INVALID;
|
||||
isize size = gb_utf8_decode(s.text, s.len, &r);
|
||||
if ((size == s.len) && (r != -1 || size != 1)) {
|
||||
*s_ = s;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
u8 rune_temp[4] = {};
|
||||
isize buf_len = 3*s.len / 2;
|
||||
u8 *buf = gb_alloc_array(a, u8, buf_len);
|
||||
isize offset = 0;
|
||||
while (s.len > 0) {
|
||||
String tail_string = {};
|
||||
Rune r = 0;
|
||||
bool multiple_bytes = false;
|
||||
bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
|
||||
if (!success) {
|
||||
gb_free(a, buf);
|
||||
return 0;
|
||||
}
|
||||
s = tail_string;
|
||||
|
||||
if (r < 0x80 || !multiple_bytes) {
|
||||
buf[offset++] = cast(u8)r;
|
||||
} else {
|
||||
isize size = gb_utf8_encode_rune(rune_temp, r);
|
||||
gb_memmove(buf+offset, rune_temp, size);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
if (quote == '\'' && s.len != 0) {
|
||||
gb_free(a, buf);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
*s_ = make_string(buf, offset);
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
@@ -1,16 +1,18 @@
|
||||
typedef struct TimeStamp {
|
||||
struct TimeStamp {
|
||||
u64 start;
|
||||
u64 finish;
|
||||
String label;
|
||||
} TimeStamp;
|
||||
};
|
||||
|
||||
typedef struct Timings {
|
||||
struct Timings {
|
||||
TimeStamp total;
|
||||
Array(TimeStamp) sections;
|
||||
Array<TimeStamp> sections;
|
||||
u64 freq;
|
||||
} Timings;
|
||||
f64 total_time_seconds;
|
||||
};
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
u64 win32_time_stamp_time_now(void) {
|
||||
LARGE_INTEGER counter;
|
||||
QueryPerformanceCounter(&counter);
|
||||
@@ -27,9 +29,38 @@ u64 win32_time_stamp__freq(void) {
|
||||
return win32_perf_count_freq.QuadPart;
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||
|
||||
#include <time.h>
|
||||
|
||||
u64 unix_time_stamp_time_now(void) {
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
|
||||
|
||||
return (ts.tv_sec * 1000000000) + ts.tv_nsec;
|
||||
}
|
||||
|
||||
u64 unix_time_stamp__freq(void) {
|
||||
gb_local_persist u64 freq = 0;
|
||||
|
||||
if (freq == 0) {
|
||||
struct timespec ts;
|
||||
clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts);
|
||||
freq = cast(u64) ((1.0 / ts.tv_nsec) * 1000000000.0);
|
||||
}
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
#else
|
||||
#error Implement system
|
||||
#endif
|
||||
|
||||
u64 time_stamp_time_now(void) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
return win32_time_stamp_time_now();
|
||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||
return unix_time_stamp_time_now();
|
||||
#else
|
||||
#error time_stamp_time_now
|
||||
#endif
|
||||
@@ -38,6 +69,8 @@ u64 time_stamp_time_now(void) {
|
||||
u64 time_stamp__freq(void) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
return win32_time_stamp__freq();
|
||||
#elif defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_UNIX)
|
||||
return unix_time_stamp__freq();
|
||||
#else
|
||||
#error time_stamp__freq
|
||||
#endif
|
||||
@@ -51,7 +84,7 @@ TimeStamp make_time_stamp(String label) {
|
||||
}
|
||||
|
||||
void timings_init(Timings *t, String label, isize buffer_size) {
|
||||
array_init_reserve(&t->sections, heap_allocator(), buffer_size);
|
||||
array_init(&t->sections, heap_allocator(), buffer_size);
|
||||
t->total = make_time_stamp(label);
|
||||
t->freq = time_stamp__freq();
|
||||
}
|
||||
@@ -62,7 +95,7 @@ void timings_destroy(Timings *t) {
|
||||
|
||||
void timings__stop_current_section(Timings *t) {
|
||||
if (t->sections.count > 0) {
|
||||
t->sections.e[t->sections.count-1].finish = time_stamp_time_now();
|
||||
t->sections[t->sections.count-1].finish = time_stamp_time_now();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,36 +104,46 @@ void timings_start_section(Timings *t, String label) {
|
||||
array_add(&t->sections, make_time_stamp(label));
|
||||
}
|
||||
|
||||
f64 time_stamp_as_ms(TimeStamp ts, u64 freq) {
|
||||
f64 time_stamp_as_second(TimeStamp ts, u64 freq) {
|
||||
GB_ASSERT_MSG(ts.finish >= ts.start, "time_stamp_as_ms - %.*s", LIT(ts.label));
|
||||
return 1000.0 * cast(f64)(ts.finish - ts.start) / cast(f64)freq;
|
||||
return cast(f64)(ts.finish - ts.start) / cast(f64)freq;
|
||||
}
|
||||
|
||||
f64 time_stamp_as_ms(TimeStamp ts, u64 freq) {
|
||||
return 1000.0*time_stamp_as_second(ts, freq);
|
||||
}
|
||||
|
||||
void timings_print_all(Timings *t) {
|
||||
char const SPACES[] = " ";
|
||||
isize max_len, i;
|
||||
isize max_len;
|
||||
|
||||
timings__stop_current_section(t);
|
||||
t->total.finish = time_stamp_time_now();
|
||||
|
||||
max_len = t->total.label.len;
|
||||
for_array(i, t->sections) {
|
||||
TimeStamp ts = t->sections.e[i];
|
||||
TimeStamp ts = t->sections[i];
|
||||
max_len = gb_max(max_len, ts.label.len);
|
||||
}
|
||||
|
||||
GB_ASSERT(max_len <= gb_size_of(SPACES)-1);
|
||||
|
||||
gb_printf("%.*s%.*s - %.3f ms\n",
|
||||
t->total_time_seconds = time_stamp_as_second(t->total, t->freq);
|
||||
|
||||
f64 total_ms = time_stamp_as_ms(t->total, t->freq);
|
||||
|
||||
gb_printf("%.*s%.*s - % 9.3f ms - %6.2f%%\n",
|
||||
LIT(t->total.label),
|
||||
cast(int)(max_len-t->total.label.len), SPACES,
|
||||
time_stamp_as_ms(t->total, t->freq));
|
||||
total_ms,
|
||||
cast(f64)100.0);
|
||||
|
||||
for_array(i, t->sections) {
|
||||
TimeStamp ts = t->sections.e[i];
|
||||
gb_printf("%.*s%.*s - %.3f ms\n",
|
||||
TimeStamp ts = t->sections[i];
|
||||
f64 section_ms = time_stamp_as_ms(ts, t->freq);
|
||||
gb_printf("%.*s%.*s - % 9.3f ms - %6.2f%%\n",
|
||||
LIT(ts.label),
|
||||
cast(int)(max_len-ts.label.len), SPACES,
|
||||
time_stamp_as_ms(ts, t->freq));
|
||||
section_ms, 100*section_ms/total_ms);
|
||||
}
|
||||
}
|
||||
+260
-218
@@ -7,54 +7,57 @@ 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"), \
|
||||
\
|
||||
TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
|
||||
TOKEN_KIND(Token_Eq, "="), \
|
||||
TOKEN_KIND(Token_Not, "!"), \
|
||||
TOKEN_KIND(Token_Hash, "#"), \
|
||||
TOKEN_KIND(Token_At, "@"), \
|
||||
TOKEN_KIND(Token_Pointer, "^"), \
|
||||
TOKEN_KIND(Token_Maybe, "?"), \
|
||||
TOKEN_KIND(Token_Add, "+"), \
|
||||
TOKEN_KIND(Token_Sub, "-"), \
|
||||
TOKEN_KIND(Token_Mul, "*"), \
|
||||
TOKEN_KIND(Token_Quo, "/"), \
|
||||
TOKEN_KIND(Token_Mod, "%"), \
|
||||
TOKEN_KIND(Token_And, "&"), \
|
||||
TOKEN_KIND(Token_Or, "|"), \
|
||||
TOKEN_KIND(Token_Xor, "~"), \
|
||||
TOKEN_KIND(Token_AndNot, "&~"), \
|
||||
TOKEN_KIND(Token_Shl, "<<"), \
|
||||
TOKEN_KIND(Token_Shr, ">>"), \
|
||||
\
|
||||
/*TOKEN_KIND(Token_as, "as"), */\
|
||||
/*TOKEN_KIND(Token_transmute, "transmute"), */\
|
||||
/*TOKEN_KIND(Token_down_cast, "down_cast"), */\
|
||||
/*TOKEN_KIND(Token_union_cast, "union_cast"), */\
|
||||
TOKEN_KIND(Token_Eq, "="), \
|
||||
TOKEN_KIND(Token_Not, "!"), \
|
||||
TOKEN_KIND(Token_Hash, "#"), \
|
||||
TOKEN_KIND(Token_At, "@"), \
|
||||
TOKEN_KIND(Token_Dollar, "$"), \
|
||||
TOKEN_KIND(Token_Pointer, "^"), \
|
||||
TOKEN_KIND(Token_Question, "?"), \
|
||||
TOKEN_KIND(Token_Add, "+"), \
|
||||
TOKEN_KIND(Token_Sub, "-"), \
|
||||
TOKEN_KIND(Token_Mul, "*"), \
|
||||
TOKEN_KIND(Token_Quo, "/"), \
|
||||
TOKEN_KIND(Token_Mod, "%"), \
|
||||
TOKEN_KIND(Token_ModMod, "%%"), \
|
||||
TOKEN_KIND(Token_And, "&"), \
|
||||
TOKEN_KIND(Token_Or, "|"), \
|
||||
TOKEN_KIND(Token_Xor, "~"), \
|
||||
TOKEN_KIND(Token_AndNot, "&~"), \
|
||||
TOKEN_KIND(Token_Shl, "<<"), \
|
||||
TOKEN_KIND(Token_Shr, ">>"), \
|
||||
\
|
||||
TOKEN_KIND(Token_CmpAnd, "&&"), \
|
||||
TOKEN_KIND(Token_CmpOr, "||"), \
|
||||
\
|
||||
TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \
|
||||
TOKEN_KIND(Token_AddEq, "+="), \
|
||||
TOKEN_KIND(Token_SubEq, "-="), \
|
||||
TOKEN_KIND(Token_MulEq, "*="), \
|
||||
TOKEN_KIND(Token_QuoEq, "/="), \
|
||||
TOKEN_KIND(Token_ModEq, "%="), \
|
||||
TOKEN_KIND(Token_AndEq, "&="), \
|
||||
TOKEN_KIND(Token_OrEq, "|="), \
|
||||
TOKEN_KIND(Token_XorEq, "~="), \
|
||||
TOKEN_KIND(Token_AndNotEq, "&~="), \
|
||||
TOKEN_KIND(Token_ShlEq, "<<="), \
|
||||
TOKEN_KIND(Token_ShrEq, ">>="), \
|
||||
TOKEN_KIND(Token_CmpAndEq, "&&="), \
|
||||
TOKEN_KIND(Token_CmpOrEq, "||="), \
|
||||
TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \
|
||||
TOKEN_KIND(Token_ArrowRight, "->"), \
|
||||
TOKEN_KIND(Token_ArrowLeft, "<-"), \
|
||||
TOKEN_KIND(Token_AddEq, "+="), \
|
||||
TOKEN_KIND(Token_SubEq, "-="), \
|
||||
TOKEN_KIND(Token_MulEq, "*="), \
|
||||
TOKEN_KIND(Token_QuoEq, "/="), \
|
||||
TOKEN_KIND(Token_ModEq, "%="), \
|
||||
TOKEN_KIND(Token_ModModEq, "%%="), \
|
||||
TOKEN_KIND(Token_AndEq, "&="), \
|
||||
TOKEN_KIND(Token_OrEq, "|="), \
|
||||
TOKEN_KIND(Token_XorEq, "~="), \
|
||||
TOKEN_KIND(Token_AndNotEq, "&~="), \
|
||||
TOKEN_KIND(Token_ShlEq, "<<="), \
|
||||
TOKEN_KIND(Token_ShrEq, ">>="), \
|
||||
TOKEN_KIND(Token_CmpAndEq, "&&="), \
|
||||
TOKEN_KIND(Token_CmpOrEq, "||="), \
|
||||
TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \
|
||||
TOKEN_KIND(Token_ArrowRight, "->"), \
|
||||
TOKEN_KIND(Token_ArrowLeft, "<-"), \
|
||||
TOKEN_KIND(Token_DoubleArrowRight, "=>"), \
|
||||
/* TOKEN_KIND(Token_Inc, "++"), */ \
|
||||
/* TOKEN_KIND(Token_Dec, "--"), */ \
|
||||
TOKEN_KIND(Token_Undef, "---"), \
|
||||
\
|
||||
TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
|
||||
TOKEN_KIND(Token_CmpEq, "=="), \
|
||||
@@ -65,64 +68,73 @@ 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_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_HalfClosed, ".."), \
|
||||
TOKEN_KIND(Token_BackSlash, "\\"), \
|
||||
TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
|
||||
\
|
||||
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
|
||||
TOKEN_KIND(Token_type, "type"), \
|
||||
TOKEN_KIND(Token_proc, "proc"), \
|
||||
TOKEN_KIND(Token_macro, "macro"), \
|
||||
TOKEN_KIND(Token_match, "match"), \
|
||||
TOKEN_KIND(Token_break, "break"), \
|
||||
TOKEN_KIND(Token_continue, "continue"), \
|
||||
TOKEN_KIND(Token_fallthrough, "fallthrough"), \
|
||||
TOKEN_KIND(Token_case, "case"), \
|
||||
TOKEN_KIND(Token_default, "default"), \
|
||||
TOKEN_KIND(Token_then, "then"), \
|
||||
TOKEN_KIND(Token_if, "if"), \
|
||||
TOKEN_KIND(Token_else, "else"), \
|
||||
TOKEN_KIND(Token_for, "for"), \
|
||||
TOKEN_KIND(Token_in, "in"), \
|
||||
TOKEN_KIND(Token_when, "when"), \
|
||||
TOKEN_KIND(Token_range, "range"), \
|
||||
TOKEN_KIND(Token_defer, "defer"), \
|
||||
TOKEN_KIND(Token_return, "return"), \
|
||||
TOKEN_KIND(Token_give, "give"), \
|
||||
TOKEN_KIND(Token_struct, "struct"), \
|
||||
TOKEN_KIND(Token_union, "union"), \
|
||||
TOKEN_KIND(Token_raw_union, "raw_union"), \
|
||||
TOKEN_KIND(Token_enum, "enum"), \
|
||||
TOKEN_KIND(Token_vector, "vector"), \
|
||||
TOKEN_KIND(Token_using, "using"), \
|
||||
TOKEN_KIND(Token_no_alias, "no_alias"), \
|
||||
TOKEN_KIND(Token_immutable, "immutable"), \
|
||||
TOKEN_KIND(Token_thread_local, "thread_local"), \
|
||||
TOKEN_KIND(Token_asm, "asm"), \
|
||||
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
|
||||
TOKEN_KIND(Token_push_context, "push_context"), \
|
||||
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_import, "import"), \
|
||||
TOKEN_KIND(Token_import_load, "import_load"), \
|
||||
TOKEN_KIND(Token_foreign, "foreign"), \
|
||||
TOKEN_KIND(Token_foreign_library, "foreign_library"), \
|
||||
TOKEN_KIND(Token_foreign_system_library, "foreign_system_library"), \
|
||||
TOKEN_KIND(Token_type, "type"), \
|
||||
TOKEN_KIND(Token_when, "when"), \
|
||||
TOKEN_KIND(Token_if, "if"), \
|
||||
TOKEN_KIND(Token_else, "else"), \
|
||||
TOKEN_KIND(Token_for, "for"), \
|
||||
TOKEN_KIND(Token_match, "match"), \
|
||||
TOKEN_KIND(Token_in, "in"), \
|
||||
TOKEN_KIND(Token_do, "do"), \
|
||||
TOKEN_KIND(Token_case, "case"), \
|
||||
TOKEN_KIND(Token_break, "break"), \
|
||||
TOKEN_KIND(Token_continue, "continue"), \
|
||||
TOKEN_KIND(Token_fallthrough, "fallthrough"), \
|
||||
TOKEN_KIND(Token_defer, "defer"), \
|
||||
TOKEN_KIND(Token_return, "return"), \
|
||||
TOKEN_KIND(Token_proc, "proc"), \
|
||||
TOKEN_KIND(Token_macro, "macro"), \
|
||||
TOKEN_KIND(Token_struct, "struct"), \
|
||||
TOKEN_KIND(Token_union, "union"), \
|
||||
TOKEN_KIND(Token_enum, "enum"), \
|
||||
TOKEN_KIND(Token_bit_field, "bit_field"), \
|
||||
TOKEN_KIND(Token_vector, "vector"), \
|
||||
TOKEN_KIND(Token_map, "map"), \
|
||||
TOKEN_KIND(Token_static, "static"), \
|
||||
TOKEN_KIND(Token_dynamic, "dynamic"), \
|
||||
TOKEN_KIND(Token_cast, "cast"), \
|
||||
TOKEN_KIND(Token_transmute, "transmute"), \
|
||||
TOKEN_KIND(Token_using, "using"), \
|
||||
TOKEN_KIND(Token_context, "context"), \
|
||||
TOKEN_KIND(Token_push_context, "push_context"), \
|
||||
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
|
||||
TOKEN_KIND(Token_size_of, "size_of"), \
|
||||
TOKEN_KIND(Token_align_of, "align_of"), \
|
||||
TOKEN_KIND(Token_offset_of, "offset_of"), \
|
||||
TOKEN_KIND(Token_type_of, "type_of"), \
|
||||
TOKEN_KIND(Token_type_info_of, "type_info_of"), \
|
||||
TOKEN_KIND(Token_asm, "asm"), \
|
||||
TOKEN_KIND(Token_yield, "yield"), \
|
||||
TOKEN_KIND(Token_await, "await"), \
|
||||
TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
|
||||
TOKEN_KIND(Token_Count, "")
|
||||
|
||||
typedef enum TokenKind {
|
||||
enum TokenKind {
|
||||
#define TOKEN_KIND(e, s) e
|
||||
TOKEN_KINDS
|
||||
#undef TOKEN_KIND
|
||||
} TokenKind;
|
||||
};
|
||||
|
||||
String const token_strings[] = {
|
||||
#define TOKEN_KIND(e, s) {cast(u8 *)s, gb_size_of(s)-1}
|
||||
@@ -131,11 +143,11 @@ String const token_strings[] = {
|
||||
};
|
||||
|
||||
|
||||
typedef struct TokenPos {
|
||||
struct TokenPos {
|
||||
String file;
|
||||
isize line;
|
||||
isize column;
|
||||
} TokenPos;
|
||||
};
|
||||
|
||||
i32 token_pos_cmp(TokenPos a, TokenPos b) {
|
||||
if (a.line == b.line) {
|
||||
@@ -153,11 +165,11 @@ bool token_pos_eq(TokenPos a, TokenPos b) {
|
||||
return token_pos_cmp(a, b) == 0;
|
||||
}
|
||||
|
||||
typedef struct Token {
|
||||
struct Token {
|
||||
TokenKind kind;
|
||||
String string;
|
||||
TokenPos pos;
|
||||
} Token;
|
||||
};
|
||||
|
||||
Token empty_token = {Token_Invalid};
|
||||
Token blank_token = {Token_Ident, {cast(u8 *)"_", 1}};
|
||||
@@ -168,12 +180,12 @@ Token make_token_ident(String s) {
|
||||
}
|
||||
|
||||
|
||||
typedef struct ErrorCollector {
|
||||
struct ErrorCollector {
|
||||
TokenPos prev;
|
||||
i64 count;
|
||||
i64 warning_count;
|
||||
i64 count;
|
||||
i64 warning_count;
|
||||
gbMutex mutex;
|
||||
} ErrorCollector;
|
||||
};
|
||||
|
||||
gb_global ErrorCollector global_error_collector;
|
||||
|
||||
@@ -227,6 +239,23 @@ void syntax_error_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_unlock(&global_error_collector.mutex);
|
||||
}
|
||||
|
||||
void syntax_warning_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_lock(&global_error_collector.mutex);
|
||||
global_error_collector.warning_count++;
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_eq(global_error_collector.prev, token.pos)) {
|
||||
global_error_collector.prev = token.pos;
|
||||
gb_printf_err("%.*s(%td:%td) Syntax Warning: %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
} else if (token.pos.line == 0) {
|
||||
gb_printf_err("Warning: %s\n", gb_bprintf_va(fmt, va));
|
||||
}
|
||||
|
||||
gb_mutex_unlock(&global_error_collector.mutex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void warning(Token token, char *fmt, ...) {
|
||||
va_list va;
|
||||
@@ -249,6 +278,13 @@ void syntax_error(Token token, char *fmt, ...) {
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
void syntax_warning(Token token, char *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
syntax_warning_va(token, fmt, va);
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
void compiler_error(char *fmt, ...) {
|
||||
va_list va;
|
||||
@@ -264,26 +300,26 @@ void compiler_error(char *fmt, ...) {
|
||||
|
||||
|
||||
|
||||
gb_inline bool token_is_literal(Token t) {
|
||||
return gb_is_between(t.kind, Token__LiteralBegin+1, Token__LiteralEnd-1);
|
||||
gb_inline bool token_is_literal(TokenKind t) {
|
||||
return gb_is_between(t, Token__LiteralBegin+1, Token__LiteralEnd-1);
|
||||
}
|
||||
gb_inline bool token_is_operator(Token t) {
|
||||
return gb_is_between(t.kind, Token__OperatorBegin+1, Token__OperatorEnd-1);
|
||||
gb_inline bool token_is_operator(TokenKind t) {
|
||||
return gb_is_between(t, Token__OperatorBegin+1, Token__OperatorEnd-1);
|
||||
}
|
||||
gb_inline bool token_is_keyword(Token t) {
|
||||
return gb_is_between(t.kind, Token__KeywordBegin+1, Token__KeywordEnd-1);
|
||||
gb_inline bool token_is_keyword(TokenKind t) {
|
||||
return gb_is_between(t, Token__KeywordBegin+1, Token__KeywordEnd-1);
|
||||
}
|
||||
gb_inline bool token_is_comparison(Token t) {
|
||||
return gb_is_between(t.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1);
|
||||
gb_inline bool token_is_comparison(TokenKind t) {
|
||||
return gb_is_between(t, Token__ComparisonBegin+1, Token__ComparisonEnd-1);
|
||||
}
|
||||
gb_inline bool token_is_shift(Token t) {
|
||||
return t.kind == Token_Shl || t.kind == Token_Shr;
|
||||
gb_inline bool token_is_shift(TokenKind t) {
|
||||
return t == Token_Shl || t == Token_Shr;
|
||||
}
|
||||
|
||||
gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }
|
||||
|
||||
|
||||
typedef enum TokenizerInitError {
|
||||
enum TokenizerInitError {
|
||||
TokenizerInit_None,
|
||||
|
||||
TokenizerInit_Invalid,
|
||||
@@ -292,18 +328,18 @@ typedef enum TokenizerInitError {
|
||||
TokenizerInit_Empty,
|
||||
|
||||
TokenizerInit_Count,
|
||||
} TokenizerInitError;
|
||||
};
|
||||
|
||||
|
||||
typedef struct TokenizerState {
|
||||
struct TokenizerState {
|
||||
Rune curr_rune; // current character
|
||||
u8 * curr; // character pos
|
||||
u8 * read_curr; // pos from start
|
||||
u8 * line; // current line pos
|
||||
isize line_count;
|
||||
} TokenizerState;
|
||||
};
|
||||
|
||||
typedef struct Tokenizer {
|
||||
struct Tokenizer {
|
||||
String fullpath;
|
||||
u8 *start;
|
||||
u8 *end;
|
||||
@@ -315,12 +351,12 @@ typedef struct Tokenizer {
|
||||
isize line_count;
|
||||
|
||||
isize error_count;
|
||||
Array(String) allocated_strings;
|
||||
} Tokenizer;
|
||||
Array<String> allocated_strings;
|
||||
};
|
||||
|
||||
|
||||
TokenizerState save_tokenizer_state(Tokenizer *t) {
|
||||
TokenizerState state = {0};
|
||||
TokenizerState state = {};
|
||||
state.curr_rune = t->curr_rune;
|
||||
state.curr = t->curr;
|
||||
state.read_curr = t->read_curr;
|
||||
@@ -392,13 +428,13 @@ 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
|
||||
gbFileContents fc = gb_file_read_contents(heap_allocator(), true, c_str);
|
||||
gb_zero_item(t);
|
||||
if (fc.data != NULL) {
|
||||
if (fc.data != nullptr) {
|
||||
t->start = cast(u8 *)fc.data;
|
||||
t->line = t->read_curr = t->curr = t->start;
|
||||
t->end = t->start + fc.size;
|
||||
@@ -412,7 +448,7 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
|
||||
|
||||
array_init(&t->allocated_strings, heap_allocator());
|
||||
} else {
|
||||
gbFile f = {0};
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
|
||||
switch (file_err) {
|
||||
@@ -433,11 +469,11 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
|
||||
}
|
||||
|
||||
gb_inline void destroy_tokenizer(Tokenizer *t) {
|
||||
if (t->start != NULL) {
|
||||
if (t->start != nullptr) {
|
||||
gb_free(heap_allocator(), t->start);
|
||||
}
|
||||
for_array(i, t->allocated_strings) {
|
||||
gb_free(heap_allocator(), t->allocated_strings.e[i].text);
|
||||
gb_free(heap_allocator(), t->allocated_strings[i].text);
|
||||
}
|
||||
array_free(&t->allocated_strings);
|
||||
}
|
||||
@@ -462,21 +498,14 @@ gb_inline i32 digit_value(Rune r) {
|
||||
return 16; // NOTE(bill): Larger than highest possible
|
||||
}
|
||||
|
||||
gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) {
|
||||
if (allow_underscore) {
|
||||
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
} else {
|
||||
while (digit_value(t->curr_rune) < base) {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
|
||||
while (digit_value(t->curr_rune) < base || t->curr_rune == '_') {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
|
||||
Token token = {0};
|
||||
Token token = {};
|
||||
token.kind = Token_Integer;
|
||||
token.string = make_string(t->curr, 1);
|
||||
token.pos.file = t->fullpath;
|
||||
@@ -485,7 +514,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
|
||||
|
||||
if (seen_decimal_point) {
|
||||
token.kind = Token_Float;
|
||||
scan_mantissa(t, 10, true);
|
||||
scan_mantissa(t, 10);
|
||||
goto exponent;
|
||||
}
|
||||
|
||||
@@ -494,31 +523,44 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
|
||||
advance_to_next_rune(t);
|
||||
if (t->curr_rune == 'b') { // Binary
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 2, true);
|
||||
scan_mantissa(t, 2);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else if (t->curr_rune == 'o') { // Octal
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 8, true);
|
||||
scan_mantissa(t, 8);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else if (t->curr_rune == 'd') { // Decimal
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 10, true);
|
||||
scan_mantissa(t, 10);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else if (t->curr_rune == 'z') { // Dozenal
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 12);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else if (t->curr_rune == 'x') { // Hexadecimal
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 16, true);
|
||||
scan_mantissa(t, 16);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} else {
|
||||
} /* else if (t->curr_rune == 'h') { // Hexadecimal Float
|
||||
token.kind = Token_Float;
|
||||
advance_to_next_rune(t);
|
||||
scan_mantissa(t, 16);
|
||||
if (t->curr - prev <= 2) {
|
||||
token.kind = Token_Invalid;
|
||||
}
|
||||
} */ else {
|
||||
seen_decimal_point = false;
|
||||
scan_mantissa(t, 10, true);
|
||||
scan_mantissa(t, 10);
|
||||
|
||||
if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
|
||||
seen_decimal_point = true;
|
||||
@@ -526,24 +568,24 @@ 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, true);
|
||||
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;
|
||||
}
|
||||
token.kind = Token_Float;
|
||||
scan_mantissa(t, 10, true);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
|
||||
exponent:
|
||||
@@ -553,7 +595,12 @@ exponent:
|
||||
if (t->curr_rune == '-' || t->curr_rune == '+') {
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
scan_mantissa(t, 10, false);
|
||||
scan_mantissa(t, 10);
|
||||
}
|
||||
|
||||
if (t->curr_rune == 'i') {
|
||||
token.kind = Token_Imag;
|
||||
advance_to_next_rune(t);
|
||||
}
|
||||
|
||||
end:
|
||||
@@ -590,20 +637,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;
|
||||
}
|
||||
|
||||
@@ -713,7 +762,7 @@ bool tokenizer_find_line_end(Tokenizer *t) {
|
||||
Token tokenizer_get_token(Tokenizer *t) {
|
||||
tokenizer_skip_whitespace(t);
|
||||
|
||||
Token token = {0};
|
||||
Token token = {};
|
||||
token.string = make_string(t->curr, 1);
|
||||
token.pos.file = t->fullpath;
|
||||
token.pos.line = t->line_count;
|
||||
@@ -730,20 +779,10 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
|
||||
// NOTE(bill): All keywords are > 1
|
||||
if (token.string.len > 1) {
|
||||
/* if (str_eq(token.string, token_strings[Token_as])) {
|
||||
token.kind = Token_as;
|
||||
} else if (str_eq(token.string, token_strings[Token_transmute])) {
|
||||
token.kind = Token_transmute;
|
||||
} else if (str_eq(token.string, token_strings[Token_down_cast])) {
|
||||
token.kind = Token_down_cast;
|
||||
} else if (str_eq(token.string, token_strings[Token_union_cast])) {
|
||||
token.kind = Token_union_cast;
|
||||
} else */{
|
||||
for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) {
|
||||
if (str_eq(token.string, token_strings[k])) {
|
||||
token.kind = cast(TokenKind)k;
|
||||
break;
|
||||
}
|
||||
for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) {
|
||||
if (token.string == token_strings[k]) {
|
||||
token.kind = cast(TokenKind)k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -847,67 +886,69 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
token.kind = Token_Period; // Default
|
||||
if (t->curr_rune == '.') { // Could be an ellipsis
|
||||
advance_to_next_rune(t);
|
||||
if (t->curr_rune == '<') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_HalfOpenRange;
|
||||
} else if (t->curr_rune == '.') {
|
||||
token.kind = Token_HalfClosed;
|
||||
if (t->curr_rune == '.') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_Ellipsis;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '#':
|
||||
token.kind = Token_Hash;
|
||||
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 0x2260: token.kind = Token_NotEq; break; // '≠'
|
||||
case 0x2264: token.kind = Token_LtEq; break; // '≤'
|
||||
case 0x2265: token.kind = Token_GtEq; break; // '≥'
|
||||
|
||||
case '%': token.kind = token_kind_dub_eq(t, '%', Token_Mod, Token_ModEq, Token_ModMod, Token_ModModEq); break;
|
||||
|
||||
case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break;
|
||||
case '=':
|
||||
token.kind = Token_Eq;
|
||||
if (t->curr_rune == '>') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_DoubleArrowRight;
|
||||
} else if (t->curr_rune == '=') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_CmpEq;
|
||||
}
|
||||
break;
|
||||
case '@':
|
||||
token.kind = Token_At;
|
||||
break;
|
||||
case '^':
|
||||
token.kind = Token_Pointer;
|
||||
break;
|
||||
case '?':
|
||||
token.kind = Token_Maybe;
|
||||
break;
|
||||
case ';':
|
||||
token.kind = Token_Semicolon;
|
||||
break;
|
||||
case ',':
|
||||
token.kind = Token_Comma;
|
||||
break;
|
||||
case ':':
|
||||
token.kind = Token_Colon;
|
||||
break;
|
||||
case '(':
|
||||
token.kind = Token_OpenParen;
|
||||
break;
|
||||
case ')':
|
||||
token.kind = Token_CloseParen;
|
||||
break;
|
||||
case '[':
|
||||
token.kind = Token_OpenBracket;
|
||||
break;
|
||||
case ']':
|
||||
token.kind = Token_CloseBracket;
|
||||
break;
|
||||
case '{':
|
||||
token.kind = Token_OpenBrace;
|
||||
break;
|
||||
case '}':
|
||||
token.kind = Token_CloseBrace;
|
||||
case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break;
|
||||
case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break;
|
||||
// case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Inc); break;
|
||||
case '+': token.kind = token_kind_variant2(t, Token_Add, Token_AddEq); break;
|
||||
case '-':
|
||||
token.kind = Token_Sub;
|
||||
if (t->curr_rune == '=') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_SubEq;
|
||||
} else if (t->curr_rune == '-') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_Invalid;
|
||||
if (t->curr_rune == '-') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_Undef;
|
||||
}
|
||||
} else if (t->curr_rune == '>') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_ArrowRight;
|
||||
}
|
||||
break;
|
||||
|
||||
case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break;
|
||||
case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break;
|
||||
case '=': token.kind = token_kind_variant2(t, Token_Eq, Token_CmpEq); break;
|
||||
case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break;
|
||||
case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break;
|
||||
case '+':
|
||||
token.kind = token_kind_variant2(t, Token_Add, Token_AddEq);
|
||||
break;
|
||||
case '-':
|
||||
token.kind = token_kind_variant3(t, Token_Sub, Token_SubEq, '>', Token_ArrowRight);
|
||||
break;
|
||||
case '/': {
|
||||
if (t->curr_rune == '/') {
|
||||
while (t->curr_rune != '\n' && t->curr_rune != GB_RUNE_EOF) {
|
||||
@@ -918,7 +959,9 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
isize comment_scope = 1;
|
||||
advance_to_next_rune(t);
|
||||
while (comment_scope > 0) {
|
||||
if (t->curr_rune == '/') {
|
||||
if (t->curr_rune == GB_RUNE_EOF) {
|
||||
break;
|
||||
} else if (t->curr_rune == '/') {
|
||||
advance_to_next_rune(t);
|
||||
if (t->curr_rune == '*') {
|
||||
advance_to_next_rune(t);
|
||||
@@ -942,14 +985,13 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
|
||||
case '<':
|
||||
if (t->curr_rune == '-') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = Token_ArrowLeft;
|
||||
} else {
|
||||
token.kind = token_kind_dub_eq(t, '<', Token_Lt, Token_LtEq, Token_Shl, Token_ShlEq);
|
||||
}
|
||||
break;
|
||||
case '>':
|
||||
token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq);
|
||||
break;
|
||||
case '>': token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq); break;
|
||||
|
||||
case '&':
|
||||
token.kind = Token_And;
|
||||
@@ -968,8 +1010,8 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
case '|': token.kind = token_kind_dub_eq(t, '|', Token_Or, Token_OrEq, Token_CmpOr, Token_CmpOrEq); break;
|
||||
|
||||
default:
|
||||
if (curr_rune != GB_RUNE_BOM) {
|
||||
u8 str[4] = {0};
|
||||
if (curr_rune != GB_RUNE_BOM) {
|
||||
u8 str[4] = {};
|
||||
int len = cast(int)gb_utf8_encode_rune(str, curr_rune);
|
||||
tokenizer_err(t, "Illegal character: %.*s (%d) ", len, str, curr_rune);
|
||||
}
|
||||
-1860
File diff suppressed because it is too large
Load Diff
+2445
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user